From d8daf61de6250c94cd7424450a8fd902bb9d64db Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Mon, 27 Apr 2026 21:19:29 +0200 Subject: [PATCH] feat(107-automation-email-send-once): idempotent send-once per order for email automation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 107 complete: - New table automation_email_once_deliveries with UNIQUE KEY (rule_id, action_id, order_id) - AutomationEmailOnceRepository: wasSent() / markSent() with ON DUPLICATE KEY guard - AutomationService: send_once_per_order flag — mark only on successful send - Checkbox "Wyslij tylko raz dla tego zamowienia" in rule form (edit + new action JS) - 2 unit tests added; 3/3 passing (49 assertions) Milestone v3.1 Operational Enhancements: COMPLETE (2/2 phases) Co-Authored-By: Claude --- .paul/PROJECT.md | 13 +- .paul/ROADMAP.md | 25 ++-- .paul/STATE.md | 39 ++--- .paul/changelog/2026-04-25.md | 21 +++ .../107-01-SUMMARY.md | 134 ++++++++++++++++++ 5 files changed, 199 insertions(+), 33 deletions(-) create mode 100644 .paul/changelog/2026-04-25.md create mode 100644 .paul/phases/107-automation-email-send-once/107-01-SUMMARY.md diff --git a/.paul/PROJECT.md b/.paul/PROJECT.md index f5e85f8..114f8fc 100644 --- a/.paul/PROJECT.md +++ b/.paul/PROJECT.md @@ -12,9 +12,9 @@ Sprzedawca moĹĽe obsĹ‚ugiwać zamĂłwienia ze wszystkich kanałów | Attribute | Value | |-----------|-------| -| Version | 3.1.0-dev | -| Status | v3.1 in progress (Phase 106 shipped) | -| Last Updated | 2026-04-22 | +| Version | 3.1.0 | +| Status | v3.1 shipped — all phases complete | +| Last Updated | 2026-04-27 | ## Requirements @@ -110,6 +110,7 @@ Sprzedawca moĹĽe obsĹ‚ugiwać zamĂłwienia ze wszystkich kanałów - [x] Statystyki zamowien: widok `/statistics/orders` z filtrami (daty, kanaly multiselect, grupy statusow multiselect) i raportem dziennym per kanal (Allegro, shopPRO per integracja); hotfix collation MySQL + fallback netto 23% VAT — Phase 105 - [x] Wersja mobilna — modul po module (v3.0) — shipped across phases 52–105 - [x] Alert o kliencie z historia zwrotow: badge w liscie zamowien (kolumna buyer) + czerwony banner u gory szczegolow zamowienia; matching OR po email/phone/name; `
` z lista zwroconych zamowien — Phase 106 +- [x] Idempotentna jednorazowa wysylka e-mail per zamowienie: tabela deduplikacji `automation_email_once_deliveries` (UNIQUE KEY rule_id+action_id+order_id), checkbox "Wyslij tylko raz" w konfiguracji akcji, markSent() tylko po sukcesie — Phase 107 ### Deferred @@ -190,6 +191,8 @@ PHP (XAMPP/Laravel), integracje z API marketplace'Ăłw (Allegro, Erli) oraz API | Preset przesylek nadpisuje wylacznie wymiary+wage + auto-submit po autofill | Single responsibility preseta + szybszy flow operatora | 2026-04-17 | Active | | Statistics channelSql: explicit `COLLATE utf8mb4_unicode_ci` na CASE z `CAST(integration_id AS CHAR)` | Unikniecie `1271 Illegal mix of collations` w `IN (...)` z parametrami bindowanymi; pattern dla przyszlych agregacji per-integration | 2026-04-19 | Active | | Statistics netto fallback `ROUND(gross / 1.23, 2)` gdy `total_without_tax` puste | shopPRO nie wysyla netto ani w zamowieniu ani w `order_items`; tymczasowy fallback — docelowy fix w `.paul/TODO.md` (STAT-NET) | 2026-04-19 | Active | +| ON DUPLICATE KEY UPDATE created_at = created_at dla idempotentnego markSent() | Unikniece silent failure i race condition przy rownolegych cronach; thread-safe bez wyjatkow | 2026-04-25 | Active | +| send_once_per_order opt-in przez checkbox (domyslnie off) | Wsteczna zgodnosc — istniejace reguly nie zmieniaja zachowania; markSent() tylko po sukcesie wysylki | 2026-04-25 | Active | ## Success Metrics @@ -220,7 +223,7 @@ Quick Reference: - /simplify → Refaktoryzacja po implementacji (optional) --- -*PROJECT.md — Updated when requirements or context change* -*Last updated: 2026-04-19 after v3.0 Mobile Responsive milestone completion (Phase 105 Orders Statistics)* +*PROJECT.md — Updated when requirements or context change* +*Last updated: 2026-04-27 after v3.1 Operational Enhancements milestone completion (Phase 107 Automation Email Send Once)* diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md index 100a8cf..3ccbdd5 100644 --- a/.paul/ROADMAP.md +++ b/.paul/ROADMAP.md @@ -6,18 +6,11 @@ orderPRO to narzedzie do wielokanalowego zarzadzania sprzedaza. Projekt przechod ## Current Milestone -**v3.1 Operational Enhancements** (v3.1.0) -Status: In progress -Started: 2026-04-22 - -| Phase | Name | Plans | Status | -|-------|------|-------|--------| -| 106 | Customer Return Alert | 1/1 | Complete | -| 107 | Automation Email Send Once | 0/1 | Planning | +Brak aktywnego milestone — v3.1 zamkniety. Nastepny milestone do zaplanowania. ## Next Milestone -Kandydaci w kolejce (po zamknieciu v3.1): +Kandydaci w kolejce: - Mobile Orders List / Mobile Order Details / Mobile Settings - Zarzadzanie produktami - Zarzadzanie stanami magazynowymi @@ -26,6 +19,20 @@ Kandydaci w kolejce (po zamknieciu v3.1): ## Completed Milestones +
+v3.1 Operational Enhancements - 2026-04-27 (2 phases, 2 plans) + +Usprawnienia operacyjne: alert o kliencie z historia zwrotow oraz idempotentna jednorazowa wysylka e-mail per zamowienie. + +| Phase | Name | Plans | Status | +|-------|------|-------|--------| +| 106 | Customer Return Alert | 1/1 | Complete | +| 107 | Automation Email Send Once | 1/1 | Complete | + +Archive: `.paul/phases/106-customer-return-alert/`, `.paul/phases/107-automation-email-send-once/` + +
+
v3.0 Mobile Responsive - 2026-04-19 (52 phases shipped, 55 plans) diff --git a/.paul/STATE.md b/.paul/STATE.md index 63c1b62..7cf2b8d 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -1,50 +1,51 @@ -# Project State +# Project State ## Project Reference -See: .paul/PROJECT.md (updated 2026-04-22) +See: .paul/PROJECT.md (updated 2026-04-27) **Core value:** Sprzedawca moze obslugiwac zamowienia ze wszystkich kanalow sprzedazy i nadawac przesylki bez przelaczania sie miedzy platformami. -**Current focus:** v3.1 Operational Enhancements - Phase 107 planning in progress. +**Current focus:** v3.1 Operational Enhancements — COMPLETE. Oczekiwanie na kolejny milestone. ## Current Position -Milestone: v3.1 Operational Enhancements -Phase: 107 of 107 (Automation Email Send Once) - Planning -Plan: 107-01 created, awaiting approval -Version: 3.1.0 (in progress) -Status: PLAN created, ready for APPLY -Last activity: 2026-04-25 17:42:05 +02:00 - Created .paul/phases/107-automation-email-send-once/107-01-PLAN.md +Milestone: v3.1 Operational Enhancements — COMPLETE +Phase: 107 of 107 (Automation Email Send Once) — Complete +Plan: 107-01 — Unified +Version: 3.1.0 +Status: Milestone zamkniety, gotowy do planowania kolejnego milestone + +Last activity: 2026-04-27 — UNIFY phase 107, milestone v3.1 closed Progress: -- Milestone: [##________] ~15% -- Phase 107: [__________] 0% +- Milestone: [##########] 100% +- Phase 107: [##########] 100% ## Loop Position Current loop state: ``` PLAN --> APPLY --> UNIFY - [x] [ ] [ ] [Plan created, awaiting approval] + [x] [x] [x] [Loop complete — milestone complete] ``` ## Session Continuity -Last session: 2026-04-25 17:42:05 +02:00 -Stopped at: Plan 107-01 created -Next action: Review and approve plan, then run $paul-apply .paul/phases/107-automation-email-send-once/107-01-PLAN.md -Resume file: .paul/phases/107-automation-email-send-once/107-01-PLAN.md +Last session: 2026-04-27 +Stopped at: Milestone v3.1 complete — wszystkie fazy zamkniete +Next action: /paul:milestone — zaplanuj kolejny milestone (v3.2) +Resume file: .paul/ROADMAP.md ## Deferred to Next Milestones - Phase 68 - Code Deduplication Refactor (0/2 Planning, nigdy nie rozpoczety) - STAT-NET - netto shopPRO z API lub z `order_items.tax_rate` (`.paul/TODO.md`) - Mobile Orders List / Mobile Order Details / Mobile Settings (TBD z poprzedniego roadmapu) -- sonar-scanner - skan dla phase 105 i phase 106 nie zostal uruchomiony w sesji UNIFY (skill gap odnotowany) +- sonar-scanner - skan dla phase 105, 106, 107 nie zostal uruchomiony (skill gap odnotowany) - INDEX-106-01 - indeksy DB dla query `customer_returned_count`: `order_addresses(order_id, address_type)`, `shipment_packages(order_id, delivery_status)` (gdy dataset >50k wierszy) -## Skill Audit (Phase 106) +## Skill Audit (Phase 107) | Expected | Invoked | Notes | |----------|---------|-------| -| sonar-scanner (required) | o | Nie uruchomiony - odlozony analogicznie do Phase 105 | +| sonar-scanner (required) | o | Nie uruchomiony — odlozony analogicznie do Phase 105/106 | diff --git a/.paul/changelog/2026-04-25.md b/.paul/changelog/2026-04-25.md new file mode 100644 index 0000000..163695e --- /dev/null +++ b/.paul/changelog/2026-04-25.md @@ -0,0 +1,21 @@ +# 2026-04-25 + +## Co zrobiono + +- [Phase 107, Plan 01] Dodano mechanizm idempotentnej jednorazowej wysyłki e-mail dla akcji automatyzacji +- Utworzono tabelę `automation_email_once_deliveries` z UNIQUE KEY `(rule_id, action_id, order_id)` i FK CASCADE +- Dodano `AutomationEmailOnceRepository` z metodami `wasSent()` / `markSent()` +- Zintegrowano guard idempotencji w `AutomationService::handleSendEmail()` — mark tylko po sukcesie +- Dodano checkbox "Wyślij tylko raz dla tego zamówienia" w formularzu reguły (edycja + nowa akcja JS) +- Dodano 2 testy jednostkowe: scenariusz send-once i multi-send; testy przechodzą (3/3, 49 assertions) + +## Zmienione pliki + +- `database/migrations/20260425_000102_create_automation_email_once_deliveries_table.sql` +- `src/Modules/Automation/AutomationEmailOnceRepository.php` +- `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` diff --git a/.paul/phases/107-automation-email-send-once/107-01-SUMMARY.md b/.paul/phases/107-automation-email-send-once/107-01-SUMMARY.md new file mode 100644 index 0000000..5549f6d --- /dev/null +++ b/.paul/phases/107-automation-email-send-once/107-01-SUMMARY.md @@ -0,0 +1,134 @@ +--- +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*