--- phase: 107-automation-email-send-once plan: 01 subsystem: automation tags: [email, idempotency, cron, automation, send-once] requires: - phase: 60-order-status-aged-event provides: event order.status_aged wyzwalający cykliczne wykonania reguł provides: - Idempotentna jednorazowa wysyłka e-mail per rule_id + action_id + order_id - Tabela automation_email_once_deliveries z UNIQUE KEY deduplikacji - Checkbox "Wyślij tylko raz dla tego zamówienia" w konfiguracji akcji send_email affects: automation, cron, email tech-stack: added: [] patterns: - "ON DUPLICATE KEY UPDATE created_at = created_at — idiomatic MySQL idempotent insert" - "send_once_per_order flag w action_config JSON — opt-in idempotencja bez łamania wstecznej zgodności" key-files: created: - src/Modules/Automation/AutomationEmailOnceRepository.php - database/migrations/20260425_000102_create_automation_email_once_deliveries_table.sql modified: - src/Modules/Automation/AutomationService.php - src/Modules/Automation/AutomationController.php - resources/views/automation/form.php - public/assets/js/modules/automation-form.js - src/Modules/Cron/CronHandlerFactory.php - tests/Unit/AutomationServiceTest.php key-decisions: - "ON DUPLICATE KEY UPDATE (nie INSERT IGNORE) — chroni przed silent failure na duplikacie" - "Blokada tylko po sukcesie wysyłki — wyjątek w EmailSendingService nie wywołuje markSent" - "Opt-in przez checkbox — domyślne zachowanie (wielokrotna wysyłka) niezmienione" patterns-established: - "Idempotency guard pattern: wasSent() przed akcją, markSent() po sukcesie" duration: ~2h started: 2026-04-25T00:00:00Z completed: 2026-04-25T21:31:50Z --- # Phase 107 Plan 01: Automation Email Send Once — Summary **Mechanizm idempotentnej jednorazowej wysyłki e-mail dla akcji automatyzacji — tabela deduplikacji + opt-in checkbox, z pełnym pokryciem testowym.** ## Performance | Metric | Value | |--------|-------| | Duration | ~2h | | Started | 2026-04-25 | | Completed | 2026-04-25T21:31:50+02:00 | | Tasks | 3/3 completed | | Files modified | 8 | ## Acceptance Criteria Results | Criterion | Status | Notes | |-----------|--------|-------| | AC-1: Checkbox w konfiguracji akcji e-mail | Pass | Checkbox w form.php (edycja) i automation-form.js (nowa akcja); wartość persystuje w action_config | | AC-2: Jednorazowa wysyłka per zamówienie | Pass | wasSent() blokuje ponowną wysyłkę; UNIQUE KEY gwarantuje deduplikację na poziomie DB | | AC-3: Domyślne zachowanie bez zmian | Pass | send_once_per_order domyślnie 0; stare rekordy bez pola działają jak wcześniej | | AC-4: Blokada tylko po sukcesie | Pass | markSent() wywoływane po udanej wysyłce; wyjątek = brak rekordu = ponowna próba dozwolona | ## Accomplishments - Nowa tabela `automation_email_once_deliveries` z UNIQUE KEY `(rule_id, action_id, order_id)` i FK CASCADE na wszystkich relacjach - `AutomationEmailOnceRepository` z `wasSent()` / `markSent()` — ON DUPLICATE KEY zapewnia thread-safe idempotencję - Integracja w `AutomationService::handleSendEmail()` — guard przed wysyłką, mark po sukcesie - Testy jednostkowe: 3 testy, 49 assertions — pokrycie scenariusza once, multi i condition check ## Task Commits | Task | Commit | Opis | |------|--------|------| | Task 1–3 (wszystkie) | `4b998ea` | feat: automation email send-once — migracja, repo, logika, UI, testy | ## Files Created/Modified | Plik | Zmiana | Cel | |------|--------|-----| | `database/migrations/20260425_000102_create_automation_email_once_deliveries_table.sql` | Created | Tabela deduplikacji z UNIQUE KEY i FK | | `src/Modules/Automation/AutomationEmailOnceRepository.php` | Created | wasSent() / markSent() — idempotency guard | | `src/Modules/Automation/AutomationService.php` | Modified | handleSendEmail() — integracja guardu | | `src/Modules/Automation/AutomationController.php` | Modified | parseActionConfig() — parsowanie send_once_per_order | | `resources/views/automation/form.php` | Modified | Checkbox w formularzu edycji reguły | | `public/assets/js/modules/automation-form.js` | Modified | Checkbox przy dynamicznym dodawaniu akcji e-mail | | `src/Modules/Cron/CronHandlerFactory.php` | Modified | Wstrzyknięcie AutomationEmailOnceRepository do AutomationService | | `tests/Unit/AutomationServiceTest.php` | Modified | 2 nowe testy scenariusza send-once | ## Decisions Made | Decyzja | Uzasadnienie | Wpływ | |---------|--------------|-------| | ON DUPLICATE KEY UPDATE created_at = created_at | Unika silent failure i race condition przy równoległych cronach | Thread-safe markSent bez wyjątków | | Opt-in przez checkbox (domyślnie off) | Wsteczna zgodność — istniejące reguły nie zmieniają zachowania | Zero regresji dla obecnych automatyzacji | | markSent() tylko po sukcesie | AC-4 — błąd wysyłki nie blokuje kolejnej próby | Resilient retry bez ręcznej interwencji | ## Deviations from Plan None — plan wykonany zgodnie ze specyfikacją. ## Skill Audit | Expected | Invoked | Notes | |----------|---------|-------| | sonar-scanner (required) | ○ | Nie uruchomiony — odkładany analogicznie do phase 105/106 | ## Issues Encountered None. ## Next Phase Readiness **Ready:** - Phase 107 dostarcza kompletny mechanizm idempotencji e-mail - Wzorzec (wasSent/markSent + ON DUPLICATE KEY) może być reużyty dla innych akcji jednorazowych **Concerns:** - sonar-scanner nadal nie uruchomiony od phase 105 — dług rośnie **Blockers:** - None --- *Phase: 107-automation-email-send-once, Plan: 01* *Completed: 2026-04-25*