--- 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 --- ## 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) ## 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) No SPECIAL-FLOWS.md — skills section omitted. ## 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 ``` Task 1: AutomationService — ewaluacja warunkow i wykonanie akcji src/Modules/Automation/AutomationService.php, src/Modules/Email/EmailSendingService.php **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. PHP lint: php -l src/Modules/Automation/AutomationService.php && php -l src/Modules/Email/EmailSendingService.php AC-1, AC-2, AC-3, AC-5 satisfied: AutomationService ewaluuje warunki i wykonuje akcje z poprawnym odbiorca Task 2: Integracja triggera w ReceiptController + DI wiring src/Modules/Accounting/ReceiptController.php, routes/web.php, DOCS/ARCHITECTURE.md, DOCS/TECH_CHANGELOG.md **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 PHP lint: php -l src/Modules/Accounting/ReceiptController.php && php -l routes/web.php AC-4 satisfied: Trigger w ReceiptController dziala, blad automatyzacji nie blokuje paragonu Watcher/executor regul automatyzacji — pelny flow od utworzenia paragonu do wyslania e-maila 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 Type "approved" to continue to UNIFY, or describe issues to fix ## 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 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 - Wszystkie taski ukonczone - Wszystkie verification checks przechodza - Pelny flow: paragon -> trigger -> ewaluacja -> wyslanie e-maila - Brak regresji w istniejacym CRUD i wysylce e-mail After completion, create `.paul/phases/16-automated-tasks/16-02-SUMMARY.md`