--- phase: 107-automation-email-send-once plan: 01 type: execute wave: 1 depends_on: [] files_modified: - database/migrations/20260425_000102_create_automation_email_once_deliveries_table.sql - src/Modules/Automation/AutomationController.php - src/Modules/Automation/AutomationRepository.php - src/Modules/Automation/AutomationService.php - src/Modules/Automation/AutomationEmailOnceRepository.php - resources/views/automation/form.php - public/assets/js/modules/automation-form.js - src/Modules/Cron/CronHandlerFactory.php - tests/Unit/AutomationServiceTest.php - .paul/docs/DB_SCHEMA.md - .paul/docs/ARCHITECTURE.md - .paul/docs/TECH_CHANGELOG.md autonomous: true delegation: off --- ## Goal Dodac w akcji automatyzacji `send_email` opcje "wyslij tylko raz na zamowienie", ktora gwarantuje, ze dla tej samej reguly i tego samego zamowienia ten sam mail nie zostanie wyslany ponownie przy kolejnych przebiegach crona. ## Purpose Event `order.status_aged` jest uruchamiany cyklicznie. Bez mechanizmu idempotencji klient moze dostawac ten sam mail przy kazdym przebiegu. Potrzebujemy bezpiecznego "once per order", ale tylko tam, gdzie operator swiadomie zaznaczy taka opcje. ## Output - Checkbox w konfiguracji akcji `Wyslij e-mail`: "Wyslij tylko raz dla tego zamowienia" - Trwale zapamietanie wysylki jednorazowej per `rule_id + action_id + order_id` - Logika wykonania, ktora pomija ponowna wysylke tylko dla akcji z wlaczonym checkboxem - Testy jednostkowe dla scenariusza jednorazowego i scenariusza domyslnego (wielokrotnego) ## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md ## Prior Work @.paul/phases/60-order-status-aged-event/60-01-SUMMARY.md ## Source Files @src/Modules/Automation/AutomationController.php @src/Modules/Automation/AutomationRepository.php @src/Modules/Automation/AutomationService.php @src/Modules/Automation/OrderStatusAgedService.php @resources/views/automation/form.php @public/assets/js/modules/automation-form.js @src/Modules/Cron/CronHandlerFactory.php @database/migrations/20260318_000057_create_automation_tables.sql @database/migrations/20260328_000072_create_automation_execution_logs_table.sql ## Required Skills (from SPECIAL-FLOWS.md) | Skill | Priority | When to Invoke | Loaded? | |-------|----------|----------------|---------| | sonar-scanner (CLI) | required | Po APPLY, przed UNIFY | o | ## Skill Invocation Checklist - [ ] sonar-scanner uruchomiony po wdrozeniu zmian ## AC-1: Konfiguracja akcji e-mail ma opcje jednorazowej wysylki ```gherkin Given operator tworzy lub edytuje zadanie automatyczne When wybierze akcje "Wyslij e-mail" Then widzi checkbox "Wyslij tylko raz dla tego zamowienia" And wartosc checkboxa zapisuje sie w action_config i wraca poprawnie po ponownej edycji reguly ``` ## AC-2: Jednorazowa wysylka dziala per zamowienie ```gherkin Given regula z eventem "order.status_aged" i akcja send_email z wlaczona opcja "wyslij tylko raz" And zamowienie pozostaje w tym samym statusie przez kolejne uruchomienia crona When cron wyzwoli te sama regule wielokrotnie dla tego samego zamowienia Then e-mail zostanie wyslany tylko podczas pierwszego udanego wykonania tej akcji And kolejne wykonania pominaja tylko te akcje e-mail, nie przerywajac pozostalych akcji reguly ``` ## AC-3: Zachowanie domyslne pozostaje bez zmian ```gherkin Given regula z akcja send_email bez zaznaczonej opcji "wyslij tylko raz" When event uruchomi regule wielokrotnie dla tego samego zamowienia Then e-mail moze byc wysylany wielokrotnie jak dotychczas ``` ## AC-4: Oznaczenie "wyslano raz" powstaje tylko po sukcesie wysylki ```gherkin Given pierwsza proba wysylki zakonczyla sie bledem When kolejny przebieg crona ponowi wykonanie Then system ponownie sprobuje wyslac e-mail And blokada jednorazowosci nie jest zapisana po nieudanej probie ``` Task 1: Dodac model danych jednorazowej wysylki i repozytorium idempotencji database/migrations/20260425_000102_create_automation_email_once_deliveries_table.sql, src/Modules/Automation/AutomationEmailOnceRepository.php, src/Modules/Cron/CronHandlerFactory.php 1. Dodac migracje tworzenia tabeli `automation_email_once_deliveries` z kolumnami: - `id` (PK), - `rule_id` (FK -> automation_rules.id), - `action_id` (FK -> automation_actions.id), - `order_id` (FK -> orders.id), - `created_at`. 2. Dodac UNIQUE KEY na `(rule_id, action_id, order_id)` dla twardej deduplikacji. 3. Utworzyc `AutomationEmailOnceRepository` z metodami: - `wasSent(int $ruleId, int $actionId, int $orderId): bool` - `markSent(int $ruleId, int $actionId, int $orderId): void` 4. Podpiac repozytorium w `CronHandlerFactory` i konstruktorze `AutomationService`. Uruchomienie migracji lokalnie konczy sie bez bledu, a tabela i indeks UNIQUE istnieja. AC-2 i AC-4 maja trwaly mechanizm danych. Task 2: Rozszerzyc konfiguracje akcji send_email o flage "send_once_per_order" src/Modules/Automation/AutomationController.php, resources/views/automation/form.php, public/assets/js/modules/automation-form.js, src/Modules/Automation/AutomationRepository.php 1. W `AutomationController::parseActionConfig()` dla `send_email` dodac bool `send_once_per_order` (0/1), domyslnie `0`. 2. W widoku formularza (`resources/views/automation/form.php`) dodac checkbox dla akcji e-mail: `actions[idx][send_once_per_order]`. 3. W generatorze dynamicznym JS (`automation-form.js`) dodac ten sam checkbox przy dodawaniu nowej akcji e-mail. 4. Zachowac kompatybilnosc starych rekordow (brak pola = `false`). Utworzenie i edycja reguly zapisuje/odczytuje checkbox poprawnie; brak regresji dla innych typow akcji. AC-1 i AC-3 sa spelnione po stronie konfiguracji. Task 3: Wdrozyc wykonanie jednorazowe w AutomationService i testy src/Modules/Automation/AutomationService.php, tests/Unit/AutomationServiceTest.php, .paul/docs/DB_SCHEMA.md, .paul/docs/ARCHITECTURE.md, .paul/docs/TECH_CHANGELOG.md 1. W `AutomationService::executeActions()` przekazac `ruleId` i `actionId` do obslugi send_email. 2. W `handleSendEmail()`: - odczytac `send_once_per_order`, - gdy flaga aktywna: sprawdzic `wasSent(...)`; jesli true, pominac akcje e-mail, - po udanej wysylce (bez wyjatku): zapisac `markSent(...)`. 3. Nie oznaczac jako wyslane przy bledzie wysylki (wyjatek -> brak `markSent`). 4. Dodac testy: - `send_once_per_order=true` -> druga proba nie wywoluje `EmailSendingService::send`, - `send_once_per_order=false` -> kolejne proby wysylaja dalej. 5. Zaktualizowac `.paul/docs/DB_SCHEMA.md`, `.paul/docs/ARCHITECTURE.md`, `.paul/docs/TECH_CHANGELOG.md`. `php vendor/bin/phpunit tests/Unit/AutomationServiceTest.php` przechodzi; test reczny cron potwierdza pojedyncza wysylke dla zaznaczonej opcji. AC-2, AC-3, AC-4 sa spelnione end-to-end. ## DO NOT CHANGE - Logika eventow i warunkow automatyzacji niezwiązanych z `send_email` - Mechanizmy wysylki e-mail poza potrzebnym miejscem integracji w `AutomationService` - Runtime konfiguracji DB hostow (`DB_HOST` / `DB_HOST_REMOTE`) ## SCOPE LIMITS - Brak nowych eventow automatyzacji - Brak zmian w harmonogramie crona `order_status_aged` - Brak zmian UI listy historii poza tym, co konieczne do konfiguracji checkboxa Before declaring plan complete: - [ ] Migracja tworzy tabele i UNIQUE KEY dla deduplikacji - [ ] Checkbox "wyslij tylko raz" dziala przy create/edit reguly - [ ] Dla `send_once_per_order=1` e-mail idzie tylko raz na zamowienie - [ ] Dla `send_once_per_order=0` zachowanie pozostaje bez zmian - [ ] `php vendor/bin/phpunit tests/Unit/AutomationServiceTest.php` przechodzi - [ ] Dokumentacja `.paul/docs/*` zaktualizowana - Operator moze wlaczyc jednorazowa wysylke per zamowienie bez zmian kodu - Cron nie wysyla duplikatow dla tej samej jednorazowej akcji e-mail - Brak regresji istniejacych automatyzacji e-mail - Plan gotowy do uruchomienia przez `$paul-apply` After completion, create `.paul/phases/107-automation-email-send-once/107-01-SUMMARY.md`