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:
235
.paul/phases/16-automated-tasks/16-02-PLAN.md
Normal file
235
.paul/phases/16-automated-tasks/16-02-PLAN.md
Normal 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>
|
||||
Reference in New Issue
Block a user