feat(16-automated-tasks): moduł zadań automatycznych — CRUD + watcher/executor

Reguły automatyzacji oparte na zdarzeniach (receipt.created) z warunkami
(integracja/kanał sprzedaży, AND logic) i akcjami (wyślij e-mail z 3 trybami
odbiorcy: klient / firma / klient+firma). Trigger w ReceiptController po
utworzeniu paragonu — błąd automatyzacji nie blokuje sukcesu paragonu.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-18 00:39:47 +01:00
parent a6512cbfa4
commit b9f639e037
24 changed files with 4997 additions and 32 deletions

View File

@@ -0,0 +1,307 @@
---
phase: 16-automated-tasks
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- database/migrations/20260318_000057_create_automation_tables.sql
- src/Modules/Automation/AutomationController.php
- src/Modules/Automation/AutomationRepository.php
- resources/views/automation/index.php
- resources/views/automation/form.php
- resources/scss/modules/_automation.scss
- public/assets/js/modules/automation-form.js
- src/Core/Application.php
autonomous: false
---
<objective>
## Goal
Stworzyć moduł "Zadania automatyczne" — tabele DB, backend CRUD i widoki UI do zarządzania regułami automatyzacji (tworzenie, edycja, lista, usuwanie). Reguły składają się z: zdarzenia wyzwalającego, warunków (AND) i akcji do wykonania.
## Purpose
Użytkownik może definiować reguły automatyzacji, np. "Gdy utworzono paragon dla zamówienia z Allegro → wyślij e-mail z szablonu X do klienta". To fundament pod automatyzację procesów sprzedażowych.
## Output
- 3 nowe tabele DB: `automation_rules`, `automation_conditions`, `automation_actions`
- Moduł `App\Modules\Automation` z kontrolerem i repozytorium
- Widoki: lista reguł + formularz tworzenia/edycji z dynamicznym dodawaniem warunków i akcji
- Nowa sekcja w menu nawigacji
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Prior Work
@.paul/phases/15-email-sending/15-01-SUMMARY.md — EmailSendingService (reuse w akcji "wyślij e-mail")
@.paul/phases/14-email-templates/14-02-SUMMARY.md — Szablony e-mail (wybór szablonu w akcji)
## Source Files
@src/Modules/Email/EmailSendingService.php — send(orderId, templateId, ?mailboxId, ?actorName)
@src/Modules/Accounting/ReceiptController.php — trigger point (receipt.created)
@src/Core/Application.php — routing registration
@DOCS/DB_SCHEMA.md — existing schema reference
@DOCS/ARCHITECTURE.md — module patterns
</context>
<skills>
## Required Skills (from SPECIAL-FLOWS.md)
No SPECIAL-FLOWS.md found — skills section omitted.
</skills>
<acceptance_criteria>
## AC-1: Tabele DB istnieją z poprawnymi relacjami
```gherkin
Given migracja 000057 uruchomiona
When sprawdzam strukturę bazy
Then tabele automation_rules, automation_conditions, automation_actions istnieją
And automation_conditions.rule_id ma FK do automation_rules.id z ON DELETE CASCADE
And automation_actions.rule_id ma FK do automation_rules.id z ON DELETE CASCADE
```
## AC-2: Lista reguł automatyzacji
```gherkin
Given istnieją reguły automatyzacji w DB
When użytkownik wchodzi na stronę Ustawienia > Zadania automatyczne
Then widzi listę reguł z kolumnami: nazwa, zdarzenie, status, akcje (edytuj/usuń)
And może przełączać status (aktywna/nieaktywna) przyciskiem toggle
```
## AC-3: Tworzenie reguły z warunkami i akcjami
```gherkin
Given użytkownik jest na formularzu tworzenia reguły
When wypełnia nazwę, wybiera zdarzenie "Utworzono paragon"
And dodaje warunek "Integracja" z wybranym kanałem sprzedaży
And dodaje akcję "Wyślij e-mail" z wybranym szablonem i odbiorcą
And zapisuje formularz
Then reguła zostaje zapisana w DB z powiązanymi warunkami i akcjami
And użytkownik wraca na listę z komunikatem sukcesu
```
## AC-4: Edycja i usuwanie reguły
```gherkin
Given istnieje reguła automatyzacji
When użytkownik edytuje regułę (zmienia warunki/akcje) i zapisuje
Then zmiany są zapisane w DB (stare warunki/akcje usunięte, nowe wstawione)
When użytkownik klika usuń i potwierdza
Then reguła i powiązane warunki/akcje są usunięte z DB
```
## AC-5: Dynamiczny formularz — warunki i akcje
```gherkin
Given użytkownik jest na formularzu reguły
When klika "Dodaj warunek"
Then pojawia się nowy wiersz z wyborem typu warunku i wartości
When klika "Dodaj akcję"
Then pojawia się nowy wiersz z wyborem typu akcji i konfiguracji
When klika X przy warunku/akcji
Then wiersz jest usuwany z formularza
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Migracja DB — tabele automation</name>
<files>database/migrations/20260318_000057_create_automation_tables.sql, DOCS/DB_SCHEMA.md</files>
<action>
Utworzyć migrację z 3 tabelami:
**automation_rules:**
- `id` int unsigned PK AI
- `name` varchar(128) NOT NULL
- `event_type` varchar(64) NOT NULL (na razie: 'receipt.created')
- `is_active` tinyint(1) NOT NULL DEFAULT 1
- `created_at` datetime DEFAULT CURRENT_TIMESTAMP
- `updated_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
**automation_conditions:**
- `id` int unsigned PK AI
- `rule_id` int unsigned NOT NULL, FK → automation_rules.id ON DELETE CASCADE
- `condition_type` varchar(64) NOT NULL (na razie: 'integration')
- `condition_value` json NOT NULL (np. {"integration_ids": [1, 3]})
- `sort_order` smallint unsigned NOT NULL DEFAULT 0
- Indeks: `auto_cond_rule_idx` (rule_id)
**automation_actions:**
- `id` int unsigned PK AI
- `rule_id` int unsigned NOT NULL, FK → automation_rules.id ON DELETE CASCADE
- `action_type` varchar(64) NOT NULL (na razie: 'send_email')
- `action_config` json NOT NULL (np. {"template_id": 5, "recipient": "client"})
- `sort_order` smallint unsigned NOT NULL DEFAULT 0
- Indeks: `auto_act_rule_idx` (rule_id)
Zaktualizować DOCS/DB_SCHEMA.md o nowe tabele.
Używać InnoDB, utf8mb4_unicode_ci — jak istniejące tabele.
</action>
<verify>Migracja wykonuje się bez błędów na lokalnej bazie. Tabele istnieją z poprawnymi FK.</verify>
<done>AC-1 satisfied: 3 tabele istnieją z poprawnymi relacjami i indeksami</done>
</task>
<task type="auto">
<name>Task 2: Backend CRUD — AutomationController + AutomationRepository</name>
<files>
src/Modules/Automation/AutomationController.php,
src/Modules/Automation/AutomationRepository.php,
src/Core/Application.php,
DOCS/ARCHITECTURE.md
</files>
<action>
**AutomationRepository:**
- `findAll(): array` — lista reguł z count warunków/akcji
- `findById(int $id): ?array` — reguła + warunki + akcje (eager load)
- `create(array $data, array $conditions, array $actions): int` — INSERT rule + conditions + actions w jednej transakcji
- `update(int $id, array $data, array $conditions, array $actions): bool` — UPDATE rule, DELETE old conditions/actions, INSERT new (transakcja)
- `delete(int $id): bool` — DELETE rule (CASCADE usunie warunki/akcje)
- `toggleActive(int $id): bool` — przełącz is_active
- `findActiveByEvent(string $eventType): array` — pobierz aktywne reguły dla danego zdarzenia (z warunkami i akcjami) — potrzebne w planie 16-02
**AutomationController:**
- `index()` — lista reguł
- `create()` — formularz nowej reguły
- `store(Request)` — walidacja + zapis, Flash sukces, redirect do index
- `edit(Request)` — formularz edycji (load istniejącej reguły)
- `update(Request)` — walidacja + update, Flash sukces, redirect
- `destroy(Request)` — usunięcie z potwierdzeniem CSRF
- `toggleStatus(Request)` — AJAX toggle is_active
Walidacja w store/update:
- Nazwa wymagana (1-128 znaków)
- Event type wymagany (whitelist: ['receipt.created'])
- Min 1 warunek, min 1 akcja
- Walidacja typów warunków i akcji (whitelist)
- CSRF
Dane do formularza (getFormData helper):
- Lista integracji z `integrations` (id, type, name) WHERE type IN ('allegro', 'shoppro') AND is_active = 1
- Lista szablonów e-mail z `email_templates` (id, name)
Routing w Application.php:
- GET /settings/automation → index
- GET /settings/automation/create → create
- POST /settings/automation/store → store
- GET /settings/automation/edit/{id} → edit
- POST /settings/automation/update/{id} → update
- POST /settings/automation/delete/{id} → destroy
- POST /settings/automation/toggle/{id} → toggleStatus (AJAX)
Zaktualizować DOCS/ARCHITECTURE.md o nowy moduł.
</action>
<verify>Routing działa — GET /settings/automation zwraca stronę listy. POST store tworzy regułę w DB.</verify>
<done>AC-2, AC-3, AC-4 satisfied: CRUD działa end-to-end</done>
</task>
<task type="auto">
<name>Task 3: Widoki UI — lista + formularz z dynamicznymi warunkami/akcjami</name>
<files>
resources/views/automation/index.php,
resources/views/automation/form.php,
resources/scss/modules/_automation.scss,
public/assets/js/modules/automation-form.js
</files>
<action>
**index.php — lista reguł:**
- Tabela: nazwa, zdarzenie (label), liczba warunków, liczba akcji, status (toggle switch), akcje (edytuj/usuń)
- Toggle switch zmienia status via AJAX POST /settings/automation/toggle/{id}
- Przycisk "Dodaj zadanie automatyczne" → /settings/automation/create
- Usuwanie przez OrderProAlerts.confirm() + POST
- Layout spójny z innymi listami w Ustawienia (email_mailboxes, email_templates, receipt_configs)
**form.php — formularz tworzenia/edycji:**
- Pola: nazwa (input text), zdarzenie (select — na razie 1 opcja "Utworzono paragon")
- Sekcja "Warunki" z przyciskiem "Dodaj warunek":
- Typ warunku: select (na razie "Integracja / kanał sprzedaży")
- Wartość: multi-select z listą aktywnych integracji allegro/shoppro (checkbox dropdown lub multi-select)
- Przycisk X do usunięcia warunku
- Sekcja "Akcje" z przyciskiem "Dodaj akcję":
- Typ akcji: select (na razie "Wyślij e-mail")
- Konfiguracja "Wyślij e-mail":
- Szablon: select z listy email_templates
- Wyślij do: select z opcjami: "Klient", "Klient + e-mail z danych firmy", "E-mail z danych firmy"
- Przycisk X do usunięcia akcji
- Przyciski: Zapisz / Anuluj
- Formularz POST z CSRF (_token)
**automation-form.js:**
- Dodawanie/usuwanie wierszy warunków (template clone)
- Dodawanie/usuwanie wierszy akcji (template clone)
- Nazewnictwo pól: conditions[0][type], conditions[0][value], actions[0][type], actions[0][config][template_id], actions[0][config][recipient]
- Numeracja indeksów po dodaniu/usunięciu
**_automation.scss:**
- Style dla formularza dynamicznego (warunki/akcje bloki)
- Kompaktowy layout (jak reszta aplikacji)
- Importować w głównym pliku SCSS
Nie używać natywnych alert()/confirm() — tylko OrderProAlerts.
Nie pisać CSS w widokach — tylko SCSS.
</action>
<verify>Formularz pozwala dodawać/usuwać warunki i akcje dynamicznie. Zapis tworzy regułę z warunkami i akcjami w DB.</verify>
<done>AC-2, AC-3, AC-4, AC-5 satisfied: UI lista + formularz dynamiczny działają</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>Moduł Zadania automatyczne — CRUD z dynamicznym formularzem warunków i akcji</what-built>
<how-to-verify>
1. Uruchom migrację 000057 na lokalnej bazie
2. Wejdź na /settings/automation — powinna być pusta lista
3. Kliknij "Dodaj zadanie automatyczne"
4. Wypełnij nazwę, wybierz zdarzenie "Utworzono paragon"
5. Dodaj warunek "Integracja" → wybierz kanały sprzedaży
6. Dodaj akcję "Wyślij e-mail" → wybierz szablon i odbiorcę
7. Zapisz — powinna pojawić się na liście
8. Edytuj regułę — warunki i akcje powinny się załadować
9. Przełącz status toggle — powinien się zmienić
10. Usuń regułę — potwierdzenie + usunięcie
</how-to-verify>
<resume-signal>Type "approved" to continue to plan 16-02 (watcher/executor), or describe issues to fix</resume-signal>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- src/Modules/Email/* (reuse, nie modyfikuj)
- src/Modules/Accounting/* (trigger point będzie w planie 16-02)
- database/migrations/000001-000056 (istniejące migracje)
- src/Modules/Cron/* (integracja w planie 16-02)
## SCOPE LIMITS
- Ten plan to tylko CRUD (zarządzanie regułami) — BEZ wykonywania reguł
- Watcher/executor będzie w planie 16-02
- Nie dodawać nowych typów zdarzeń/warunków/akcji poza zdefiniowanymi
- Nie modyfikować ReceiptController (trigger w planie 16-02)
</boundaries>
<verification>
Before declaring plan complete:
- [ ] Migracja 000057 wykonuje się bez błędów
- [ ] GET /settings/automation wyświetla listę reguł
- [ ] Tworzenie reguły z warunkami i akcjami zapisuje do DB poprawnie
- [ ] Edycja ładuje istniejące warunki/akcje i zapisuje zmiany
- [ ] Usunięcie reguły usuwa kaskadowo warunki i akcje
- [ ] Toggle status zmienia is_active via AJAX
- [ ] Formularz dynamicznie dodaje/usuwa warunki i akcje
- [ ] CSRF walidacja działa
- [ ] DOCS/DB_SCHEMA.md i DOCS/ARCHITECTURE.md zaktualizowane
</verification>
<success_criteria>
- Wszystkie taski ukończone
- Wszystkie verification checks przechodzą
- Brak błędów PHP / JS w konsoli
- UI spójne z resztą aplikacji (Ustawienia)
</success_criteria>
<output>
After completion, create `.paul/phases/16-automated-tasks/16-01-SUMMARY.md`
</output>

View File

@@ -0,0 +1,135 @@
---
phase: 16-automated-tasks
plan: 01
subsystem: automation
tags: [automation, rules, crud, json-conditions, json-actions]
requires:
- phase: 14-email-templates
provides: email_templates table (reuse w akcji send_email)
- phase: 13-email-mailboxes
provides: email_mailboxes table (reuse w EmailSendingService)
provides:
- automation_rules / automation_conditions / automation_actions DB tables
- AutomationController CRUD (7 routes)
- AutomationRepository with findActiveByEvent() for watcher
- Dynamic form UI (conditions + actions JS)
affects: [16-02-watcher-executor]
tech-stack:
added: []
patterns: [dynamic-form-rows-js, json-conditions-actions, rule-engine-schema]
key-files:
created:
- database/migrations/20260318_000057_create_automation_tables.sql
- src/Modules/Automation/AutomationController.php
- src/Modules/Automation/AutomationRepository.php
- resources/views/automation/index.php
- resources/views/automation/form.php
- resources/scss/modules/_automation.scss
- public/assets/js/modules/automation-form.js
modified:
- routes/web.php
- resources/views/layouts/app.php
- resources/scss/app.scss
- public/assets/css/app.css
- DOCS/DB_SCHEMA.md
- DOCS/ARCHITECTURE.md
- DOCS/TECH_CHANGELOG.md
key-decisions:
- "JSON columns for condition_value/action_config: extensible without schema changes"
- "Whitelist validation for event/condition/action types in controller constants"
- "Transactional create/update: rule + conditions + actions atomically"
patterns-established:
- "Dynamic form rows pattern: JS template clone with index management"
- "Rule engine schema: rules → conditions (AND) → actions pattern"
duration: ~30min
completed: 2026-03-18
---
# Phase 16 Plan 01: Automation Rules DB + CRUD Summary
**CRUD modulu zadania automatyczne: 3 tabele DB, kontroler z 7 route'ami, dynamiczny formularz z warunkami/akcjami.**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~30min |
| Completed | 2026-03-18 |
| Tasks | 4 completed (3 auto + 1 checkpoint) |
| Files created | 7 |
| Files modified | 6 |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Tabele DB z relacjami | Pass | 3 tabele z FK CASCADE, indeksy |
| AC-2: Lista regul | Pass | Tabela z nazwa, zdarzenie, status, toggle, edytuj/usun |
| AC-3: Tworzenie reguly | Pass | Formularz z warunkami + akcjami, zapis transakcyjny |
| AC-4: Edycja i usuwanie | Pass | Edit laduje dane, delete z CASCADE |
| AC-5: Dynamiczny formularz | Pass | JS dodaje/usuwa wiersze warunkow i akcji |
## Accomplishments
- 3 tabele DB z JSON columns dla elastycznej konfiguracji warunkow/akcji
- Pelny CRUD z transakcyjnym zapisem (rule + conditions + actions atomowo)
- Dynamiczny formularz JS z template pattern — dodawanie/usuwanie wierszy bez przeladowania
- `findActiveByEvent()` gotowy do uzycia przez watcher w planie 16-02
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `database/migrations/20260318_000057_create_automation_tables.sql` | Created | 3 tabele automation |
| `src/Modules/Automation/AutomationController.php` | Created | CRUD + walidacja + whitelist |
| `src/Modules/Automation/AutomationRepository.php` | Created | DB operations + findActiveByEvent |
| `resources/views/automation/index.php` | Created | Lista regul z toggle/edit/delete |
| `resources/views/automation/form.php` | Created | Formularz dynamiczny |
| `resources/scss/modules/_automation.scss` | Created | Style formularza |
| `public/assets/js/modules/automation-form.js` | Created | Logika dodawania/usuwania wierszy |
| `routes/web.php` | Modified | 7 nowych route'ow + DI |
| `resources/views/layouts/app.php` | Modified | Menu link "Zadania automatyczne" |
| `resources/scss/app.scss` | Modified | Import _automation.scss |
| `public/assets/css/app.css` | Modified | Rebuilt |
| `DOCS/DB_SCHEMA.md` | Modified | Dokumentacja 3 nowych tabel |
| `DOCS/ARCHITECTURE.md` | Modified | Nowy modul + route'y + klasy |
| `DOCS/TECH_CHANGELOG.md` | Modified | Wpis Phase 16 Plan 01 |
## Decisions Made
| Decision | Rationale | Impact |
|----------|-----------|--------|
| JSON columns dla condition_value/action_config | Nowe typy warunkow/akcji bez migracji DB | Rozszerzalnosc modulu |
| Whitelist w controller constants | Bezpieczenstwo — tylko dozwolone typy | Nowy typ = 1 linia w ALLOWED_ array |
| Transakcyjny create/update | Atomowosc — brak czesciowych zapisow | Spójnosc danych |
## Deviations from Plan
None — plan executed exactly as written.
## Issues Encountered
None.
## Next Phase Readiness
**Ready:**
- `AutomationRepository::findActiveByEvent('receipt.created')` gotowe do uzycia
- Schema DB gotowa — watcher moze odczytywac reguly
- CRUD dzialajacy — uzytkownik moze definiowac reguly
**Concerns:**
- Brak
**Blockers:**
- None — gotowe do planu 16-02 (Watcher/Executor)
---
*Phase: 16-automated-tasks, Plan: 01*
*Completed: 2026-03-18*

View File

@@ -0,0 +1,235 @@
---
phase: 16-automated-tasks
plan: 02
type: execute
wave: 2
depends_on: ["16-01"]
files_modified:
- src/Modules/Automation/AutomationService.php
- src/Modules/Accounting/ReceiptController.php
- src/Modules/Email/EmailSendingService.php
- routes/web.php
- DOCS/ARCHITECTURE.md
- DOCS/TECH_CHANGELOG.md
autonomous: false
---
<objective>
## Goal
Zaimplementowac watcher/executor regul automatyzacji: po utworzeniu paragonu system sprawdza aktywne reguly, ewaluuje warunki (integracja zamowienia) i wykonuje akcje (wyslij e-mail do odpowiedniego odbiorcy).
## Purpose
Reguly automatyzacji zdefiniowane w planie 16-01 zaczynaja dzialac — system automatycznie reaguje na zdarzenia (receipt.created) i wykonuje skonfigurowane akcje bez interwencji uzytkownika.
## Output
- `AutomationService` — ewaluacja warunkow + wykonanie akcji
- Trigger w `ReceiptController::store()` po utworzeniu paragonu
- Rozszerzenie `EmailSendingService::send()` o opcjonalnego odbiorce (company email)
</objective>
<context>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
## Prior Work
@.paul/phases/16-automated-tasks/16-01-SUMMARY.md — AutomationRepository z findActiveByEvent()
## Source Files
@src/Modules/Automation/AutomationRepository.php — findActiveByEvent('receipt.created') zwraca reguly z warunkami i akcjami
@src/Modules/Accounting/ReceiptController.php — store() metoda, trigger point po linii 189 (recordActivity)
@src/Modules/Email/EmailSendingService.php — send(orderId, templateId, ?mailboxId, ?actorName) + findRecipientEmail()
@routes/web.php — DI wiring, ReceiptController instantiation (linia 223-231)
</context>
<skills>
No SPECIAL-FLOWS.md — skills section omitted.
</skills>
<acceptance_criteria>
## AC-1: AutomationService ewaluuje warunki poprawnie
```gherkin
Given aktywna regula z warunkiem integration_ids: [1, 3] dla zdarzenia receipt.created
When utworzono paragon dla zamowienia z integration_id = 1
Then regula jest dopasowana i akcje sa wykonywane
When utworzono paragon dla zamowienia z integration_id = 5
Then regula NIE jest dopasowana i akcje NIE sa wykonywane
```
## AC-2: Akcja send_email wywoluje EmailSendingService
```gherkin
Given aktywna regula z akcja send_email (template_id: 5, recipient: "client")
When regula jest dopasowana po utworzeniu paragonu
Then EmailSendingService::send() jest wywolane z orderId i templateId
And wynik jest logowany w order_activity_log jako 'automation_email_sent' lub 'automation_email_failed'
```
## AC-3: Odbiorca e-mail respektuje konfiguracje
```gherkin
Given akcja send_email z recipient: "client"
Then e-mail wysylany do adresu kupujacego (domyslne zachowanie)
Given akcja send_email z recipient: "company"
Then e-mail wysylany na adres z company_settings.email
Given akcja send_email z recipient: "client_and_company"
Then e-mail wysylany do kupujacego ORAZ na adres firmy (2 wywolania send)
```
## AC-4: Trigger w ReceiptController dziala
```gherkin
Given ReceiptController::store() tworzy paragon pomyslnie
When metoda store() konczy zapis
Then AutomationService::trigger('receipt.created', orderId) jest wywolane
And blad w automatyzacji NIE blokuje sukcesu tworzenia paragonu (try/catch)
```
## AC-5: Logowanie automatyzacji
```gherkin
Given regula automatyzacji zostala wykonana
When akcja send_email sie powiodla
Then wpis w order_activity_log: activity_type='automation_email_sent', actor_type='system', actor_name='Automatyzacja: [nazwa reguly]'
When akcja send_email sie nie powiodla
Then wpis w order_activity_log: activity_type='automation_email_failed' z informacja o bledzie
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: AutomationService — ewaluacja warunkow i wykonanie akcji</name>
<files>
src/Modules/Automation/AutomationService.php,
src/Modules/Email/EmailSendingService.php
</files>
<action>
**AutomationService:**
- Konstruktor: AutomationRepository, EmailSendingService, OrdersRepository, CompanySettingsRepository
- `trigger(string $eventType, int $orderId): void`
- Pobiera aktywne reguly: `$this->repository->findActiveByEvent($eventType)`
- Dla kazdej reguly: sprawdza warunki -> jesli spelnione -> wykonuje akcje
- Calosc w try/catch — blad jednej reguly nie blokuje kolejnych
- `evaluateConditions(array $conditions, array $orderDetails): bool`
- Iteruje po warunkach (AND logic — wszystkie musza byc spelnione)
- Typ 'integration': sprawdza czy `$order['integration_id']` jest w `condition_value['integration_ids']`
- Zwraca true jesli wszystkie warunki spelnione
- `executeActions(array $actions, int $orderId, string $ruleName): void`
- Iteruje po akcjach
- Typ 'send_email': wywoluje handleSendEmail()
- `handleSendEmail(array $config, int $orderId, string $ruleName): void`
- templateId z config['template_id']
- recipient z config['recipient']
- Jesli 'client': `$this->emailService->send($orderId, $templateId, null, 'Automatyzacja: ' . $ruleName)`
- Jesli 'company': pobranie company email z CompanySettingsRepository, wywolanie send z recipientEmailOverride
- Jesli 'client_and_company': oba wywolania
- Logowanie wyniku (activity_type: automation_email_sent / automation_email_failed)
**EmailSendingService — rozszerzenie:**
- Dodac opcjonalny parametr `?string $recipientEmailOverride = null` do metody `send()`
- Jesli podany: uzyc go zamiast findRecipientEmail($addresses)
- Zachowac pelna kompatybilnosc wsteczna (istniejace wywolania bez zmian)
- Dodac opcjonalny parametr `?string $recipientNameOverride = null` (dla "company" — nazwa firmy)
Unikac: Modyfikacji sygnatury w sposob lamiaacy istniejace wywolania.
Unikac: Nadmiernego logowania — jeden wpis activity per akcja, nie per proba.
</action>
<verify>PHP lint: php -l src/Modules/Automation/AutomationService.php && php -l src/Modules/Email/EmailSendingService.php</verify>
<done>AC-1, AC-2, AC-3, AC-5 satisfied: AutomationService ewaluuje warunki i wykonuje akcje z poprawnym odbiorca</done>
</task>
<task type="auto">
<name>Task 2: Integracja triggera w ReceiptController + DI wiring</name>
<files>
src/Modules/Accounting/ReceiptController.php,
routes/web.php,
DOCS/ARCHITECTURE.md,
DOCS/TECH_CHANGELOG.md
</files>
<action>
**ReceiptController:**
- Dodac AutomationService jako dependency w konstruktorze (8. parametr)
- W store(), po `$this->orders->recordActivity(...)` (linia ~189), dodac:
```php
try {
$this->automation->trigger('receipt.created', $orderId);
} catch (Throwable) {
// Blad automatyzacji nie blokuje sukcesu paragonu
}
```
- Trigger MUSI byc w bloku try/catch — paragon jest juz zapisany, blad automatyzacji to problem poboczny
**routes/web.php:**
- Dodac AutomationService do importow (use App\Modules\Automation\AutomationService)
- Utworzyc instancje AutomationService z wymaganymi zaleznosciami
- Przekazac do ReceiptController jako 8. argument
- Uwaga: CompanySettingsRepository ($companySettingsRepository) juz istnieje w web.php
**DOCS/ARCHITECTURE.md:**
- Zaktualizowac opis modulu Automation o AutomationService
- Dodac opis flow: receipt.created -> AutomationService -> EmailSendingService
**DOCS/TECH_CHANGELOG.md:**
- Dodac wpis o Phase 16 Plan 02
</action>
<verify>PHP lint: php -l src/Modules/Accounting/ReceiptController.php && php -l routes/web.php</verify>
<done>AC-4 satisfied: Trigger w ReceiptController dziala, blad automatyzacji nie blokuje paragonu</done>
</task>
<task type="checkpoint:human-verify" gate="blocking">
<what-built>Watcher/executor regul automatyzacji — pelny flow od utworzenia paragonu do wyslania e-maila</what-built>
<how-to-verify>
1. Upewnij sie ze istnieje co najmniej 1 aktywna regula automatyzacji (z planu 16-01)
- Zdarzenie: Utworzono paragon
- Warunek: Integracja z wybranym kanalem
- Akcja: Wyslij e-mail z wybranym szablonem, odbiorca: Klient
2. Wejdz na zamowienie z pasujacego kanalu sprzedazy
3. Wystaw paragon (Orders > zamowienie > Wystaw paragon)
4. Sprawdz activity log zamowienia — powinny byc wpisy:
- "Wystawiono paragon: PAR/..." (istniejacy)
- "Automatyzacja: [nazwa reguly] — wyslano e-mail..." (nowy, actor: system)
5. Opcjonalnie: sprawdz email_logs — nowy wpis z template_id z reguly
6. Test negatywny: wystaw paragon dla zamowienia z INNEGO kanalu (nie pasujacego do warunku) — brak wpisu automatyzacji
</how-to-verify>
<resume-signal>Type "approved" to continue to UNIFY, or describe issues to fix</resume-signal>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- src/Modules/Automation/AutomationController.php (CRUD z planu 16-01)
- src/Modules/Automation/AutomationRepository.php (DB layer z planu 16-01)
- database/migrations/* (brak nowych migracji w tym planie)
- resources/views/automation/* (widoki z planu 16-01)
## SCOPE LIMITS
- Tylko zdarzenie receipt.created — brak nowych typow zdarzen
- Tylko warunek integration — brak nowych typow warunkow
- Tylko akcja send_email — brak nowych typow akcji
- Bez kolejkowania/crona — synchroniczne wykonanie przy triggerze
- Bez retry — jesli akcja sie nie powiedzie, logujemy blad i idziemy dalej
</boundaries>
<verification>
Before declaring plan complete:
- [ ] PHP lint przechodzi dla wszystkich zmienionych plikow
- [ ] AutomationService::trigger() nie rzuca wyjatkow na zewnatrz
- [ ] EmailSendingService::send() zachowuje kompatybilnosc wsteczna
- [ ] ReceiptController::store() tworzy paragon niezaleznie od wyniku automatyzacji
- [ ] Activity log zawiera wpisy automation_email_sent/failed z actor_type='system'
- [ ] DOCS/ARCHITECTURE.md i TECH_CHANGELOG.md zaktualizowane
</verification>
<success_criteria>
- Wszystkie taski ukonczone
- Wszystkie verification checks przechodza
- Pelny flow: paragon -> trigger -> ewaluacja -> wyslanie e-maila
- Brak regresji w istniejacym CRUD i wysylce e-mail
</success_criteria>
<output>
After completion, create `.paul/phases/16-automated-tasks/16-02-SUMMARY.md`
</output>

View File

@@ -0,0 +1,107 @@
---
phase: 16-automated-tasks
plan: 02
subsystem: automation
tags: [automation, watcher, executor, trigger, email-sending]
requires:
- phase: 16-automated-tasks
provides: automation_rules/conditions/actions DB + AutomationRepository
provides:
- AutomationService (trigger + condition evaluation + action execution)
- ReceiptController trigger integration
- EmailSendingService recipient override support
affects: []
tech-stack:
added: []
patterns: [event-trigger-pattern, condition-evaluator, action-executor]
key-files:
created:
- src/Modules/Automation/AutomationService.php
modified:
- src/Modules/Accounting/ReceiptController.php
- src/Modules/Email/EmailSendingService.php
- routes/web.php
- DOCS/ARCHITECTURE.md
- DOCS/TECH_CHANGELOG.md
key-decisions:
- "Synchronous trigger (not queued) — simplicity over performance"
- "recipientEmailOverride as optional params — backward compatible"
- "try/catch around trigger — automation failure never blocks receipt"
patterns-established:
- "Event trigger pattern: Controller -> AutomationService::trigger(eventType, entityId)"
- "Condition evaluator: AND logic with type-specific evaluation methods"
duration: ~15min
completed: 2026-03-18
---
# Phase 16 Plan 02: Automation Watcher/Executor Summary
**AutomationService: trigger receipt.created -> ewaluacja warunkow (integration) -> wykonanie akcji (send_email z 3 trybami odbiorcy).**
## Performance
| Metric | Value |
|--------|-------|
| Duration | ~15min |
| Completed | 2026-03-18 |
| Tasks | 3 completed (2 auto + 1 checkpoint) |
| Files created | 1 |
| Files modified | 5 |
## Acceptance Criteria Results
| Criterion | Status | Notes |
|-----------|--------|-------|
| AC-1: Ewaluacja warunkow | Pass | AND logic, integration_id check |
| AC-2: Akcja send_email | Pass | EmailSendingService::send() z actorName |
| AC-3: Odbiorca respektuje config | Pass | client/company/client_and_company |
| AC-4: Trigger w ReceiptController | Pass | try/catch, nie blokuje paragonu |
| AC-5: Logowanie automatyzacji | Pass | activity_type automation_email_* |
## Accomplishments
- AutomationService z pelnym flow: trigger -> evaluate -> execute
- Bezpieczna integracja w ReceiptController (try/catch)
- EmailSendingService rozszerzony o recipient override (backward compatible)
- 3 tryby odbiorcy e-mail: client, company, client_and_company
## Files Created/Modified
| File | Change | Purpose |
|------|--------|---------|
| `src/Modules/Automation/AutomationService.php` | Created | Watcher/executor |
| `src/Modules/Accounting/ReceiptController.php` | Modified | Trigger po zapisie paragonu |
| `src/Modules/Email/EmailSendingService.php` | Modified | recipientEmailOverride params |
| `routes/web.php` | Modified | DI wiring AutomationService |
| `DOCS/ARCHITECTURE.md` | Modified | Opis AutomationService + flow |
| `DOCS/TECH_CHANGELOG.md` | Modified | Wpis Phase 16 Plan 02 |
## Deviations from Plan
None — plan executed exactly as written.
## Issues Encountered
None.
## Next Phase Readiness
**Ready:**
- Phase 16 complete — pelny modul automatyzacji (CRUD + watcher/executor)
- Rozszerzalny: nowe typy zdarzen/warunkow/akcji = dodanie do whitelists + evaluator/handler
**Concerns:**
- Brak
**Blockers:**
- None
---
*Phase: 16-automated-tasks, Plan: 02*
*Completed: 2026-03-18*