Срокове от договори: от клаузата до напомнянето
В договора пише „30 дни от подписването“, „един месец предизвестие“, „5 дни след получаване на фактурата“. Кога точно изтича срокът? Строим помощник, който намира такива клаузи, смята датата по правилата на чл. 72 ЗЗД (с празниците и неприсъствените дни за 2026 и 2027), пита човек да потвърди всеки срок и чак тогава праща напомняния. Датата я смята обикновен код, не моделът. Помощникът подготвя, не решава.
01Какво ще научиш
- Как се чете чл. 72 ЗЗД и как всяко правило става ред код — дни, седмици, месеци, неприсъствен последен ден, „дни преди известен ден“.
- Кои са неприсъствените дни през 2026 и 2027 и как се прилага преместването по чл. 154, ал. 2 КТ.
- Как да накараш локален модел да намира клаузите, без да му позволиш да смята дати, и как да сверяваш цитата му с договора.
- Как човек потвърждава всеки срок и защо без потвърждение няма напомняне.
- Как от потвърден срок стават напомняния (30, 14, 7 и 1 ден по-рано) и как n8n ги праща всяка работна сутрин.
- Как да тестваш датите, преди да им се довериш.
02Преди да започнеш
- Машина от класа NVIDIA GB10 с Ollama и модел (урока за анализ на договор, 04-135), PostgreSQL и n8n 2.x със зона
Europe/Sofia(Обучение 7). Датите можеш да изпробваш и без тях — нужен е само Python 3.10 или по-нов. - Само измислени договори. Реален договор влиза едва когато е решено кой има право да го вижда и къде се пази.
- Юрист, който потвърждава какво значат думите в конкретния договор. Кодът смята датата; не решава какво означава клаузата.
- Пощенска кутия за ролята (например
deadlines@example.com), а не лична — напомнянията не трябва да зависят от един човек.
Правилото: чл. 72 ЗЗД, дословно
Когато последният ден от срока е неприсъствен, срокът свършва в първия следващ присъствен ден.
Когато срокът изтича определено число дни преди известен ден, този ден, както и денят на изтичането на срока не се броят.
Писмените изявления и съобщения, от каквото и да било естество, се считат за извършени в срока, ако са предадени на пощата, по телеграфа или с радиограма до изтичането на двадесет и четвъртия час от последния ден на срока.
Под начало на месеца се разбира първото число, под среда на месеца - петнадесетото число и под край на месеца - последният ден на месеца.“
Това са петте алинеи на члена, по реда им в lex.bg. Кодът в урока покрива първите три; четвъртата и петата не са в него (виж таблицата по-долу).
Кои дни са „неприсъствени“: чл. 154 КТ, дословно
Чл. 72 казва „неприсъствен“, но не изброява кои са тези дни. Използваме общоприетото четене: събота, неделя и официалните празници по Кодекса на труда. Това четене е практика, не буква на чл. 72 ⚠️ — юристът го потвърждава за конкретния договор.
1 януари - Нова година;
3 март - Ден на Освобождението на България от османско иго - национален празник;
1 май - Ден на труда и на международната работническа солидарност;
6 май - Гергьовден, Ден на храбростта и Българската армия;
24 май - Ден на светите братя Кирил и Методий, на българската азбука, просвета и култура и на славянската книжовност;
6 септември - Ден на Съединението;
22 септември - Ден на Независимостта на България;
1 ноември - Ден на народните будители - неприсъствен за всички учебни заведения;
24 декември - Бъдни вечер, 25 и 26 декември - Рождество Христово;
Велики петък, Велика събота и Великден - неделя и понеделник, които в съответната година са определени за празнуването му.
(2) (Нова - ДВ, бр. 105 от 2016 г., в сила от 01.01.2017 г.) Когато официалните празници по ал. 1, с изключение на Великденските празници, съвпадат със събота и/или неделя, първият или първите два работни дни след тях са неприсъствени.
(3) (Доп. - ДВ, бр. 52 от 2004 г., в сила от 01.08.2004 г., изм. - ДВ, бр. 15 от 2010 г., предишна ал. 2, изм. - ДВ, бр. 105 от 2016 г., в сила от 01.01.2017 г.) Министерският съвет може да обявява еднократно и други дни за неприсъствени за оказване на обществена почит към важни исторически, политически, културни или други особено значими събития, както и дни за честване на определени професии и за оказване на признателност.“
Редакционните бележки за минали изменения в началото на чл. 154 и на ал. 1 са пропуснати; текстът на нормата и на трите алинеи е цялостен.
03Стъпки
-
Какво строим
Малки части, всяка се проверява отделно. Защо датата не я смята моделът? Езиков модел греши в аритметиката с дати, а грешката е тиха: изглежда убедително. Кодът със 9 теста е предвидим и може да се провери от юрист ред по ред.
Част Върши Пуснато deadline_calc.pyправилата на чл. 72 и празниците 2026–2027 да, 9/9 теста confirm_flow.pyпредложен → потвърден / отхвърлен; напомняния само след „да“ да deadline_extract.pyлокалният модел намира клаузи; цитатът се сверява с текста не (⚠️) SQL схема срокове и напомняния не (⚠️) n8n процес всяка работна сутрин в 08:00 праща за днес не (⚠️) -
Как чл. 72 става код
Всяко правило от члена е един ред в таблицата. Примерите са изчислени с кода по-долу (пясъчник, 01.10.2026).
Какво казва чл. 72 Какво прави кодът Измислен пример → резултат дни: денят на събитието не се брои; изтича в края на последния ден начало + N дни 30 дни от подписване на 01.06.2026 (пн) → 01.07.2026 (ср) седмици: в съответния ден на последната седмица начало + 7·N дни 2 седмици от 20.05.2026 (ср) → 03.06.2026 (ср) месеци: съответното число; няма ли го — последния ден на месеца add_months1 месец от 31.01.2027 → 28.02.2027 (нд) последният ден е неприсъствен → първият следващ присъствен next_working_day28.02.2027 (нд) → 01.03.2027 (пн) същото над празник пропуска и преместените дни 5 дни от получена на 08.04.2026 фактура → 13.04 (Великденски понеделник) → 14.04.2026 (вт) последен ден в неделя, а понеделникът е преместен почивен ден пропуска и двата 2 дни от 22.05.2026 → 24.05 (нд), 25.05 (пн, ал. 2) → 26.05.2026 (вт) „N дни преди известен ден“: нито той, нито денят на изтичане се броят days_before; при неприсъствен ден не мести сам, вдига знак3 дни преди 14.10.2026 → 10.10.2026 (сб) → за човека ал. 4: изявление, предадено на пощата до 24:00 на последния ден, е в срока не е в кода — това е правило за как се доказва спазването — ал. 5: начало / среда / край на месеца (1-во, 15-о, последния ден) не е в кода — ще се въведе като отделен вид начало — 🧩Два прочита — решава човекДоговор: „за срок от 12 месеца, считано от 01.07.2026“. Дали денят 01.07 е първият ден на срока или „денят на събитието“, който не се брои, зависи от думите и от тълкуването. Кодът със „start = 01.07.2026“ връща 01.07.2027 (чисто прилагане на чл. 72, ал. 1 за месеци); друг прочит дава 30.06.2027. Затова при всяка клауза с „считано от“ е нужен човек и, ако има пари или права, юрист ⚠️. -
Кодът за изчисление
Празниците са за 2026 и 2027 — за друга година кодът спира с грешка, вместо да върне невярна дата. Неприсъствените дни се строят от фиксираните празници, Великден (изчислен, не въведен на ръка) и преместването по ал. 2. 1 ноември не е включен (виж кутията по-горе).
python · deadline_calc.py"""Изчисляване на срокове по чл. 72 ЗЗД. Празници 2026-2027 по чл. 154 КТ.""" import calendar from datetime import date, timedelta SUPPORTED_YEARS = (2026, 2027) # Фиксирани официални празници по чл. 154, ал. 1 КТ (месец, ден). # 1 ноември е в списъка на закона, но е неприсъствен само за учебните # заведения - затова не е тук (за общия случай не е почивен ден). FIXED = [(1, 1), (3, 3), (5, 1), (5, 6), (5, 24), (9, 6), (9, 22), (12, 24), (12, 25), (12, 26)] def orthodox_easter(year: int) -> date: """Православен Великден (юлиански алгоритъм, превод към григориански; важи за 1900-2099).""" a, b, c = year % 4, year % 7, year % 19 d = (19 * c + 15) % 30 e = (2 * a + 4 * b - d + 34) % 7 month, rest = divmod(d + e + 114, 31) return date(year, month, rest + 1) + timedelta(days=13) def non_working_days(year: int) -> set: """Великденските дни, фиксираните празници и преместването по чл. 154, ал. 2 КТ.""" if year not in SUPPORTED_YEARS: raise ValueError(f"Празниците са заредени само за {SUPPORTED_YEARS}") holidays = {date(year, m, d) for m, d in FIXED} easter = orthodox_easter(year) off = holidays | {easter + timedelta(days=k) for k in (-2, -1, 0, 1)} # ал. 2: празник (без Великден) в събота/неделя -> първият работен # ден след него е неприсъствен; при два такива дни - първите два. for h in sorted(holidays): if h.weekday() >= 5: n = h + timedelta(days=1) while n.weekday() >= 5 or n in off: n += timedelta(days=1) off.add(n) return off def is_non_working(d: date) -> bool: return d.weekday() >= 5 or d in non_working_days(d.year) def next_working_day(d: date) -> date: while is_non_working(d): d += timedelta(days=1) return d def previous_working_day(d: date) -> date: while is_non_working(d): d -= timedelta(days=1) return d def add_months(start: date, n: int) -> date: """Същото число на последния месец; ако няма такова - последният му ден.""" y, m = divmod(start.month - 1 + n, 12) year, month = start.year + y, m + 1 last = calendar.monthrange(year, month)[1] return date(year, month, min(start.day, last)) def compute_deadline(start: date, n: int, unit: str) -> dict: """Срок, броен от събитие в ден `start` (денят на събитието не се брои).""" if unit == "days": raw = start + timedelta(days=n) elif unit == "weeks": raw = start + timedelta(weeks=n) elif unit == "months": raw = add_months(start, n) else: raise ValueError("unit: days | weeks | months") final = next_working_day(raw) return {"raw": raw, "final": final, "rolled": final != raw} def days_before(known_day: date, n: int) -> dict: """Срок 'n дни преди известен ден': нито този ден, нито денят на изтичане се броят. Ако падне в неприсъствен ден - не местим сами.""" d = known_day - timedelta(days=n + 1) return {"raw": d, "final": d, "needs_human": is_non_working(d)} def reminder_dates(deadline: date, offsets=(30, 14, 7, 1), today=None) -> list: """Наши напомняния: броим назад, а в неприсъствен ден - към предишния работен ден (по-рано е по-безопасно). Минали - не.""" today = today or date.today() out = [] for k in offsets: r = previous_working_day(deadline - timedelta(days=k)) if r >= today and r not in out: out.append(r) return sorted(out)🧮Как е проверен ВеликденАлгоритъмът дава 12.04.2026 и 02.05.2027 за православния Великден. Сверено с таблицата за датите на Великден в Уикипедия (колоната за православния Великден; не е официален източник) ⚠️. Чл. 154 само казва, че Великден е „определен за празнуването му“; че се ползва православният календар е общоприета практика. -
Тестовете
Очакваните стойности са смятани на ръка по текста на закона. Ако промениш правилата, тестовете трябва да паднат.
python · test_deadline_calc.pyimport unittest from datetime import date as D from deadline_calc import (orthodox_easter, non_working_days, compute_deadline, days_before, reminder_dates, is_non_working) from confirm_flow import Proposed, review def weekday_offs(y): return sorted(d for d in non_working_days(y) if d.weekday() < 5) class T(unittest.TestCase): def test_easter(self): self.assertEqual(orthodox_easter(2026), D(2026, 4, 12)) self.assertEqual(orthodox_easter(2027), D(2027, 5, 2)) def test_2026(self): exp = [D(2026, *x) for x in [(1,1),(3,3),(4,10),(4,13),(5,1),(5,6), (5,25),(9,7),(9,22),(12,24),(12,25),(12,28)]] self.assertEqual(weekday_offs(2026), exp) def test_2027(self): exp = [D(2027, *x) for x in [(1,1),(3,3),(4,30),(5,3),(5,4),(5,6), (5,24),(9,6),(9,22),(12,24),(12,27),(12,28)]] self.assertEqual(weekday_offs(2027), exp) def test_days(self): r = compute_deadline(D(2026, 6, 1), 30, "days") self.assertEqual((r["final"], r["rolled"]), (D(2026, 7, 1), False)) r = compute_deadline(D(2026, 4, 26), 14, "days") # нд 10.05 self.assertEqual((r["raw"], r["final"]), (D(2026, 5, 10), D(2026, 5, 11))) r = compute_deadline(D(2026, 4, 8), 5, "days") # Великд. понеделник self.assertEqual((r["raw"], r["final"]), (D(2026, 4, 13), D(2026, 4, 14))) r = compute_deadline(D(2026, 5, 22), 2, "days") # нд 24.05, пн 25.05 self.assertEqual((r["raw"], r["final"]), (D(2026, 5, 24), D(2026, 5, 26))) r = compute_deadline(D(2027, 12, 23), 3, "days") # нд 26, пропуска 27-28 self.assertEqual((r["raw"], r["final"]), (D(2027, 12, 26), D(2027, 12, 29))) def test_months_weeks(self): r = compute_deadline(D(2027, 1, 31), 1, "months") self.assertEqual((r["raw"], r["final"]), (D(2027, 2, 28), D(2027, 3, 1))) self.assertEqual(compute_deadline(D(2026, 7, 1), 12, "months")["final"], D(2027, 7, 1)) self.assertEqual(compute_deadline(D(2026, 5, 20), 2, "weeks")["final"], D(2026, 6, 3)) def test_before(self): self.assertEqual(days_before(D(2026, 10, 10), 3)["raw"], D(2026, 10, 6)) self.assertTrue(days_before(D(2026, 10, 14), 3)["needs_human"]) # събота 10.10 self.assertFalse(days_before(D(2026, 10, 10), 3)["needs_human"]) def test_reminders(self): self.assertEqual(reminder_dates(D(2026, 7, 1), today=D(2026, 5, 1)), [D(2026, 6, 1), D(2026, 6, 17), D(2026, 6, 24), D(2026, 6, 30)]) self.assertEqual(reminder_dates(D(2026, 5, 26), (1,), today=D(2026, 5, 1)), [D(2026, 5, 22)]) # 25.05 е неприсъствен -> пт 22.05 self.assertEqual(reminder_dates(D(2026, 7, 1), today=D(2026, 6, 20)), [D(2026, 6, 24), D(2026, 6, 30)]) # минали не се пращат def test_year_guard(self): with self.assertRaises(ValueError): is_non_working(D(2028, 1, 3)) def test_human_confirms(self): p = Proposed("Договор А", "в срок от 30 дни", D(2026, 6, 1), 30, "days") self.assertEqual(p.reminders, []) review(p, "да", D(2026, 6, 1)) self.assertEqual((p.status, p.final), ("confirmed", D(2026, 7, 1))) q = review(Proposed("Договор Б", "x", D(2026, 6, 1), 30, "days"), "не", D(2026, 6, 1)) self.assertEqual((q.status, q.final, q.reminders), ("rejected", None, [])) unittest.main(verbosity=2)Пускане:
python3 test_deadline_calc.py. Резултатът в пясъчника (Linux, Python 3.10.12, 01.10.2026):изход · пясъчникtest_2026 (__main__.T) ... ok test_2027 (__main__.T) ... ok test_before (__main__.T) ... ok test_days (__main__.T) ... ok test_easter (__main__.T) ... ok test_human_confirms (__main__.T) ... ok test_months_weeks (__main__.T) ... ok test_reminders (__main__.T) ... ok test_year_guard (__main__.T) ... ok Ran 9 tests in 0.001s OK -
Празниците, които кодът получава
Работните дни, които са неприсъствени, изчислени от кода (уикендите не са в списъка):
Година Дни в седмицата, които са неприсъствени 2026 01.01 (чт) · 03.03 (вт) · 10.04 (пт, Велики петък) · 13.04 (пн, Великденски понеделник) · 01.05 (пт) · 06.05 (ср) · 25.05 (пн, за 24.05 в неделя) · 07.09 (пн, за 06.09 в неделя) · 22.09 (вт) · 24.12 (чт) · 25.12 (пт) · 28.12 (пн, за 26.12 в събота) 2027 01.01 (пт) · 03.03 (ср) · 30.04 (пт, Велики петък) · 03.05 (пн, Великденски понеделник) · 04.05 (вт, за 01.05 в събота) · 06.05 (чт) · 24.05 (пн) · 06.09 (пн) · 22.09 (ср) · 24.12 (пт) · 27.12 (пн) и 28.12 (вт), за 25 и 26.12 в събота и неделя ⚠️Какво е сверено и какво — неСверено: списъкът и правилото за преместване — с пълния текст на чл. 154 КТ; списъкът за 2026 съвпада с този в Обучение 7. Не е сверено: 04.05.2027 и 27–28.12.2027 са резултат от правилото, не от публикуван календар; 01.05.2027 съвпада с Велика събота, а ал. 2 изключва само „Великденските празници“ — как точно се прилага при такова припокриване, потвърждава юристът ⚠️. Министерският съвет може да обяви допълнителни неприсъствени дни (ал. 3) и размени на работни дни: провери решението му за годината, преди да разчиташ на датите. -
Моделът намира клаузата, не датата
Моделът връща за всеки срок цитат, брой, единица и как започва срокът. Не му даваме да връща крайна дата. После кодът проверява, че цитатът го има в договора, а числото и единицата са допустими. Непотвърдените остават видими със знак
verified: false— човекът ги вижда, не ги губи. Този код не е пускан срещу модел (⚠️); проверен е само синтаксисът.python · deadline_extract.py (не е пускан)import json import re import httpx OLLAMA_CHAT = "http://localhost:11434/api/chat" UNITS = {"days", "weeks", "months"} PROMPT = ( "Намери в договора всички срокове, които се броят във дни, седмици или " "месеци. Върни САМО JSON във вида " '{"deadlines":[{"quote":"дословен цитат от текста","n":30,' '"unit":"days|weeks|months","anchor":"signing|delivery|invoice_received|' 'fixed_date|other","wording":"точните думи за началото на срока"}]}. ' "НЕ изчислявай крайна дата. Не добавяй нищо извън текста. " "Ако няма срокове: {\"deadlines\":[]}.\n\nДОГОВОР:\n" ) def norm(s: str) -> str: return re.sub(r"\s+", " ", s).strip() def extract(text: str, model: str = "llama3.1:70b") -> list[dict]: r = httpx.post(OLLAMA_CHAT, timeout=600, json={ "model": model, "messages": [{"role": "user", "content": PROMPT + text}], "format": "json", "stream": False, "options": {"temperature": 0.1, "num_ctx": 16384}, }) r.raise_for_status() found = json.loads(r.json()["message"]["content"]).get("deadlines", []) out = [] for d in found: d["verified"] = ( isinstance(d.get("n"), int) and d.get("unit") in UNITS and norm(str(d.get("quote", ""))) in norm(text) ) out.append(d) # непотвърдените остават видими - човекът ги вижда return outLlama 3.1 официално не поддържа български (картата на модела), затова очаквай пропуснати и сгрешени клаузи. Начална дата (кога е подписан договорът, кога е получена фактурата) идва от човека или от данните около договора, а не от модела.
-
Човекът потвърждава всеки срок
Предложеният срок няма дата. Човек вижда цитата, началото и изчислението (с отбелязан неприсъствен ден) и казва „да“ или „не“. Без „да“ не се създават напомняния. Състоянията са три:
proposed,confirmed,rejected.python · confirm_flow.pyfrom dataclasses import dataclass, field from datetime import date from deadline_calc import compute_deadline, reminder_dates @dataclass class Proposed: contract: str # измислено име на договор quote: str # дословен цитат от договора start: date n: int unit: str status: str = "proposed" # proposed | confirmed | rejected final: date | None = None reminders: list = field(default_factory=list) def review(p: Proposed, answer: str, today: date) -> Proposed: """Човекът вижда цитата и изчислението и казва да/не. Без 'да' няма дата и няма напомняния.""" r = compute_deadline(p.start, p.n, p.unit) if answer.strip().lower() in ("y", "yes", "да"): p.status, p.final = "confirmed", r["final"] p.reminders = reminder_dates(p.final, today=today) else: p.status = "rejected" return pМалък диалог върху три измислени срока, с въведени отговори „да“, „да“, „не“ (пясъчник, „днес“ е зададено 01.04.2026):
изход · пясъчникДоговор А (измислен) цитат: „в 30-дневен срок от подписването“ начало: 01.06.2026 (пн) · 30 days по чл. 72: 01.07.2026 (ср) Потвърждаваш ли срока? (да/не): статус: confirmed | 01.07.2026 (ср) | напомняния: 01.06, 17.06, 24.06, 30.06 Договор А (измислен) цитат: „5 дни след получаване на фактурата“ начало: 08.04.2026 (ср) · 5 days по чл. 72: 13.04.2026 (пн) -> 14.04.2026 (вт) (неприсъствен ден) Потвърждаваш ли срока? (да/не): статус: confirmed | 14.04.2026 (вт) | напомняния: 07.04, 09.04 Договор Б (измислен) цитат: „предизвестие от един месец“ начало: 31.01.2027 (нд) · 1 months по чл. 72: 28.02.2027 (нд) -> 01.03.2027 (пн) (неприсъствен ден) Потвърждаваш ли срока? (да/не): статус: rejected | няма срок | напомняния: нямаВиж втория срок: напомнянията са 07.04 и 09.04, не 13.04 и 10.04, защото 10.04 (Велики петък) и 13.04 (Великденски понеделник) са неприсъствени — напомнянето се мести към предишния работен ден. По-рано е по-безопасно. Минали дати не се създават (30 и 14 дни преди 14.04 са преди „днес“).
✅Напомнянето не е срокътПравният срок се смята по чл. 72 и може да се мести напред. Напомнянето е наше вътрешно: то се мести назад, за да не падне в празник, и никога не удължава срока. -
Таблиците в PostgreSQL
Ограничението
CHECKне позволява „потвърден“ срок без дата и без име на този, който го е потвърдил — така правилото „няма потвърждение, няма срок“ важи и когато някой пише в базата директно. Схемата не е пускана (⚠️).sql · PostgreSQL (не е пускано)-- Предложените срокове и потвърдените. Дата има само след потвърждение. CREATE TABLE contract_deadlines ( id serial PRIMARY KEY, contract text NOT NULL, clause_quote text NOT NULL, start_date date NOT NULL, n integer NOT NULL CHECK (n > 0), unit text NOT NULL CHECK (unit IN ('days', 'weeks', 'months')), deadline date, status text NOT NULL DEFAULT 'proposed' CHECK (status IN ('proposed', 'confirmed', 'rejected')), confirmed_by text, confirmed_at timestamptz, CHECK (status <> 'confirmed' OR (deadline IS NOT NULL AND confirmed_by IS NOT NULL)) ); -- По един ред за всяка дата на напомняне (след потвърждение). CREATE TABLE reminders ( id serial PRIMARY KEY, deadline_id integer NOT NULL REFERENCES contract_deadlines (id), remind_on date NOT NULL, sent_at timestamptz, UNIQUE (deadline_id, remind_on) ); -- Пример: потвърден срок 01.07.2026 и неговите напомняния INSERT INTO reminders (deadline_id, remind_on) VALUES (1, '2026-06-01'), (1, '2026-06-17'), (1, '2026-06-24'), (1, '2026-06-30'); -- Заявка за n8n: какво да се изпрати днес (<= хваща и пропуснат ден) SELECT r.id, d.contract, d.clause_quote, d.deadline, (d.deadline - CURRENT_DATE) AS days_left FROM reminders r JOIN contract_deadlines d ON d.id = r.deadline_id WHERE d.status = 'confirmed' AND r.sent_at IS NULL AND r.remind_on <= CURRENT_DATE AND d.deadline >= CURRENT_DATE ORDER BY d.deadline; -- След изпращане (id идва от предишната стъпка) UPDATE reminders SET sent_at = now() WHERE id = $1; -
n8n: всяка работна сутрин в 08:00
Процесът е верига от четири нода: Schedule Trigger (Custom Cron
0 0 8 * * 1-5, понеделник до петък в 08:00, зонаEurope/Sofia— виж Обучение 7) → Postgres (заявката от предишната стъпка) → Send Email (доdeadlines@example.com: договор, цитат, срок, оставащи дни) → Postgres (UPDATE … sent_at).- Защо Mon–Fri стига: датите на напомнянията са вече работни дни, а Schedule Trigger не знае празници (Обучение 7).
- Защо
remind_on <= CURRENT_DATE: по подразбиране n8n пропуска изпълнение, ако е бил изключен в часа му. Със „по-малко или равно“ пропуснато напомняне се изпраща следващата сутрин, аsent_atне позволява двойно изпращане. - Публикуване: графикът върви само след „Publish“; „Execute Workflow“ не доказва, че часът работи.
- Пощата е външна услуга: в писмото влиза само цитатът и датата, не целият договор.
Имената на полетата за параметри на Postgres нода (за
$1) и на изпращача се различават по версия — виж документацията на твоята версия ⚠️; тази част не е пускана. -
Минимум, преди да му повярваш
- Пусни тестовете; промени една дата в тест и виж как падат.
- Сложи три измислени договора и сверѝ ръчно с календар всяка дата.
- Покажи на юриста таблицата от стъпка 2 и кутията „Два прочита“ — той решава как се четат „считано от“ и „преди известен ден“.
- Сверявай празниците всяка година с решението на Министерския съвет.
04Проверка
- Тестовете минават (9 от 9); при дата извън 2026–2027 кодът спира с грешка.
- Всеки предложен срок е с цитат, който го има в договора.
- Нищо не е изпратено за срок, който човек не е потвърдил.
- Напомнянията падат само в работни дни; минали дати няма.
- Юристът е видял таблицата за правилата и двата „прочита“.
- Празниците са сверени с решението на Министерския съвет за годината.
Тест
1. Срок „30 дни от подписването“; договорът е подписан на 01.06.2026. Кога изтича по чл. 72 ЗЗД?
2. Последният ден от срока е неделя, а понеделникът е почивен по чл. 154, ал. 2 КТ. Кога свършва срокът?
3. Защо датата не я смята езиковият модел?
4. Кога се създават напомняния за един предложен срок?
05Какво следва
06Източници
- Закон за задълженията и договорите (lex.bg) — чл. 72 и чл. 111, цитирани дословно; проверено 01.10.2026. Lex.bg не е официалното издание: сверка с Държавен вестник (dv.parliament.bg) ⚠️.
- Кодекс на труда (lex.bg) — чл. 154, ал. 1–3, дословно; проверено 01.10.2026.
- Дати на Великден (Уикипедия) — само за сверка на 12.04.2026 и 02.05.2027; не е официален източник.
- Обучение 7 · Schedules и Cron jobs · n8n: Schedule Trigger · n8n: Send Email 🌐 външна услуга — зона, пропуснати изпълнения, изпращане на писма (както са посочени в Обучение 7).
- llama3.1 в Ollama 🔒 локално · Ollama: API — размери, поддържани езици,
format(както в урок 04-135).