- Email notyfikacji zawiera numer zamówienia, klienta, datę, kwotę - Order joby (send_order, sync_payment, sync_status) ponawiane w nieskończoność co 30 min - Rozróżnienie PONAWIANY vs TRWAŁY BŁĄD w emailu - Cleanup stuck jobów po udanym wysłaniu zamówienia - +2 testy infinite retry w CronJobRepositoryTest Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
7.9 KiB
7.9 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous
| phase | plan | type | wave | depends_on | files_modified | autonomous | |||
|---|---|---|---|---|---|---|---|---|---|
| 09-apilo-email-fix | 01 | execute | 1 |
|
true |
Purpose
Administrator dostaje email bez informacji o którym zamówieniu chodzi. Dodatkowo, po 10 próbach zamówienie przestaje być synchronizowane — to niedopuszczalne, bo zamówienie musi trafić do Apilo.
Output
- Zmodyfikowany
cron.php— lepsza treść emaila + czyszczenie jobów po sukcesie - Zmodyfikowany
CronJobRepository— obsługa infinite retry - Zmodyfikowany
CronJobType— stała backoffu 30min dla Apilo
Prior Work
@.paul/phases/08-apilo-orders-fix/08-01-SUMMARY.md
Source Files
@cron.php (linie 198-529 — handler APILO_SEND_ORDER, linie 763-781 — email notification) @autoload/Domain/CronJob/CronJobRepository.php (markFailed — linie 131-156) @autoload/Domain/CronJob/CronJobType.php (stałe backoff)
<acceptance_criteria>
AC-1: Email zawiera czytelne dane zamówienia
Given trwale nieudane zadanie Apilo z payload zawierającym order_id
When system wysyła email notyfikacji
Then email zawiera: numer zamówienia, dane klienta, datę zamówienia, kwotę
And temat emaila zawiera numery zamówień
AC-2: Brak order_id w payload nie powoduje błędu
Given trwale nieudane zadanie Apilo bez order_id w payload (np. apilo_token_keepalive)
When system wysyła email notyfikacji
Then email wyświetla dane job-a bez sekcji zamówienia, bez błędów
AC-3: Joby zamówień Apilo ponawiają się w nieskończoność co 30 minut
Given job typu apilo_send_order, apilo_sync_payment lub apilo_sync_status
When job osiąga max_attempts
Then job NIE jest oznaczany jako failed
And job wraca do pending ze scheduled_at = now + 30 minut
And email ostrzegawczy jest wysyłany (z informacją że job dalej jest ponawiany)
AC-4: Inne joby Apilo (token, product sync) nadal mają limit prób
Given job typu apilo_token_keepalive lub apilo_product_sync
When job osiąga max_attempts
Then job jest oznaczany jako failed (zachowanie bez zmian)
AC-5: Po udanym wysłaniu zamówienia czyszczone są powiązane failed joby
Given zamówienie wysłane pomyślnie do Apilo (apilo_order_id ustawiony)
When handler APILO_SEND_ORDER kończy się sukcesem
Then powiązane joby apilo_sync_payment i apilo_sync_status ze statusem failed
zostają usunięte lub anulowane (żeby nie zaśmiecały kolejki)
</acceptance_criteria>
Task 1: Infinite retry dla order-related Apilo jobów autoload/Domain/CronJob/CronJobType.php, autoload/Domain/CronJob/CronJobRepository.php **CronJobType.php:** 1. Dodać stałą `APILO_ORDER_BACKOFF_SECONDS = 1800` (30 minut) 2. Dodać statyczną metodę `isOrderRelatedApiloJob($jobType)` zwracającą true dla: - APILO_SEND_ORDER, APILO_SYNC_PAYMENT, APILO_SYNC_STATUS**CronJobRepository::markFailed():**
3. Przed sprawdzeniem `$attempts >= $maxAttempts`:
- Pobrać `job_type` z bazy (dodać do selecta w linia 133)
- Jeśli `CronJobType::isOrderRelatedApiloJob($jobType)`:
- ZAWSZE wracaj do pending (nigdy failed)
- Użyj stałego backoffu `APILO_ORDER_BACKOFF_SECONDS` zamiast exponential
- Ustaw `last_error` jak normalnie
- Dla pozostałych jobów — logika bez zmian
UWAGA: Nie zmieniaj sygnatury markFailed() — dodaj job_type do wewnętrznego selecta
1. Przeczytaj kod i zweryfikuj że:
- isOrderRelatedApiloJob zwraca true tylko dla 3 typów
- markFailed nigdy nie ustawia status=failed dla tych typów
- Inne joby zachowują się jak dotychczas
2. Uruchom: ./test.ps1 tests/Unit/Domain/CronJob/
AC-3, AC-4 satisfied: Order joby retry w nieskończoność, inne bez zmian
Task 2: Lepszy email + ostrzeżenie zamiast trwałego błędu + czyszczenie po sukcesie
cron.php
**Email notification (linie ~763-781):**
1. Zmienić query o failed joby — RÓWNIEŻ szukać order-related jobów w statusie pending z dużą liczbą prób (np. attempts >= 10), żeby wysyłać ostrzeżenie
2. Dla każdego job-a: sparsować payload (json_decode jeśli string), wyciągnąć order_id
3. Jeśli order_id istnieje — pobrać z pp_shop_orders:
- `order_number` (lub `id` jeśli brak), `client_name`/`client_surname`, `date_order`, `total_brutto`
4. Sformatować email:
```
Job #X (apilo_send_order) — PONAWIANY CO 30 MIN
Zamówienie: #12345 (ID: 678)
Klient: Jan Kowalski
Data zamówienia: 2026-03-19 14:30:00
Kwota: 199.99 PLN
Próby: 15
Błąd: [last_error]
Ostatnia próba: [updated_at lub scheduled_at]
```
5. Dla jobów permanent failed (nie-order): zachować stary format "trwały błąd"
6. Temat: dodać numery zamówień jeśli dostępne
7. Email ma rozróżniać: "PONAWIANY" vs "TRWAŁY BŁĄD" w zależności od typu joba
**Czyszczenie po sukcesie (w handlerze APILO_SEND_ORDER, po linii ~522-524):**
8. Po pomyślnym wysłaniu zamówienia (`apilo_order_id` ustawiony):
- Usunąć/anulować failed/pending joby `apilo_sync_payment` i `apilo_sync_status`
z payload zawierającym ten sam order_id
- Użyć: `$mdb->delete('pp_cron_jobs', [...])` lub update status=cancelled
- To zapobiega zaśmiecaniu kolejki starymi retry jobami
UWAGA:
- Nazwy kolumn zamówienia: sprawdź jakie faktycznie są w pp_shop_orders (mogą być polskie)
- Payload w bazie to JSON string — json_decode($fj['payload'], true)
- Nie zmieniaj logiki wysyłania zamówień — tylko email i cleanup
1. Przeczytaj zmodyfikowany kod
2. Zweryfikuj że query do pp_shop_orders używa poprawnych kolumn
3. Zweryfikuj brak błędów PHP (null handling, json_decode guard)
4. Uruchom: ./test.ps1
AC-1, AC-2, AC-5 satisfied: Email czytelny, cleanup po sukcesie
DO NOT CHANGE
- Logikę wysyłania zamówień do Apilo (curl, payload budowanie)
- Logikę exponential backoff dla NIE-order jobów
- Handlery APILO_SYNC_PAYMENT, APILO_SYNC_STATUS, APILO_STATUS_POLL (poza cleanup)
- Odbiorcę emaila i warunki wysyłki (poza rozszerzeniem query)
- Tabelę pp_shop_orders — żadnych nowych kolumn
SCOPE LIMITS
- Tylko retry logic, email formatting, i cleanup
- Nie dodawać nowych tabel
- Nie zmieniać enqueue() ani fetchNext()
<success_criteria>
- Zamówienia Apilo są ponawiane w nieskończoność co 30 min
- Email notyfikacji zawiera czytelne dane zamówienia
- Po udanym wysłaniu czyszczone są stare joby
- Zero regresji w testach </success_criteria>