update
This commit is contained in:
228
.paul/phases/66-allegro-delivery-tracking/66-02-PLAN.md
Normal file
228
.paul/phases/66-allegro-delivery-tracking/66-02-PLAN.md
Normal file
@@ -0,0 +1,228 @@
|
||||
---
|
||||
phase: 66-allegro-delivery-tracking
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["66-01"]
|
||||
files_modified:
|
||||
- src/Modules/Shipments/DeliveryStatus.php
|
||||
- src/Modules/Shipments/AllegroTrackingService.php
|
||||
autonomous: true
|
||||
delegation: auto
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
1. Uzupełnić mapę ALLEGRO_EDGE_MAP o brakujące statusy z realnych przesyłek
|
||||
2. Dodać mechanizm keyword-based fallback (guessStatusFromDescription) dla nieznanych opisów
|
||||
3. Logować nowe nierozpoznane statusy do activity_log
|
||||
|
||||
## Purpose
|
||||
Edge API Allegro zwraca opisy po polsku bez ustalonego słownika — mogą pojawić się nowe warianty. Hardcoded mapa nie wystarczy. Fallback + logowanie = system sam sobie radzi z nowymi opisami i informuje admina.
|
||||
|
||||
## Output
|
||||
- Rozszerzona mapa ALLEGRO_EDGE_MAP o 5 nowych slugów
|
||||
- Metoda guessStatusFromDescription() w DeliveryStatus jako keyword fallback
|
||||
- Logowanie nieznanych statusów w AllegroTrackingService
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Prior Work
|
||||
@.paul/phases/66-allegro-delivery-tracking/66-01-PLAN.md
|
||||
|
||||
## Nowe statusy z realnego zamówienia AD0243IOG6
|
||||
| Slug | Opis | Mapowanie |
|
||||
|------|------|-----------|
|
||||
| podjeta_z_maszyny_przez_kuriera | Przesyłka została podjęta z maszyny przez kuriera | in_transit |
|
||||
| przesylka_wyjechala_w_droge_do_punktu_docelowego | Przesyłka wyjechała w drogę do punktu docelowego | in_transit |
|
||||
| wyslana_z_sortowni | Wysłana z sortowni | in_transit |
|
||||
| wydana_do_doreczenia | Przesyłka została wydana do doręczenia | out_for_delivery |
|
||||
| przesylka_oczekuje_na_odbior | Przesyłka oczekuje na odbiór | ready_for_pickup |
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Nowe slugi w mapie
|
||||
```gherkin
|
||||
Given opis "Wysłana z sortowni" z edge API
|
||||
When slugify + normalize
|
||||
Then zwraca 'in_transit' (nie 'unknown')
|
||||
```
|
||||
|
||||
## AC-2: Fallback keyword matching
|
||||
```gherkin
|
||||
Given nieznany opis np. "Paczka jest w drodze do odbiorcy"
|
||||
When slug nie istnieje w ALLEGRO_EDGE_MAP
|
||||
Then guessStatusFromDescription() dopasowuje na podstawie słów kluczowych
|
||||
And zwraca odpowiedni znormalizowany status
|
||||
```
|
||||
|
||||
## AC-3: Logowanie nieznanych statusów
|
||||
```gherkin
|
||||
Given opis z edge API którego slug NIE jest w mapie i fallback zwraca unknown
|
||||
When AllegroTrackingService przetwarza taki status
|
||||
Then loguje do error_log: "[AllegroTracking] Nowy niezmapowany status: {opis} (slug: {slug})"
|
||||
And nadal zwraca wynik z status=unknown (nie null)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Rozszerzenie mapy + guessStatusFromDescription</name>
|
||||
<files>src/Modules/Shipments/DeliveryStatus.php</files>
|
||||
<action>
|
||||
1. Dodaj brakujące slugi do ALLEGRO_EDGE_MAP:
|
||||
```php
|
||||
'podjeta_z_maszyny_przez_kuriera' => self::IN_TRANSIT,
|
||||
'przesylka_wyjechala_w_droge_do_punktu_docelowego' => self::IN_TRANSIT,
|
||||
'wyjechala_w_droge_do_punktu_docelowego' => self::IN_TRANSIT,
|
||||
'wyslana_z_sortowni' => self::IN_TRANSIT,
|
||||
'wydana_do_doreczenia' => self::OUT_FOR_DELIVERY,
|
||||
'przesylka_oczekuje_na_odbior' => self::READY_FOR_PICKUP,
|
||||
```
|
||||
|
||||
2. Dodaj odpowiednie opisy do ALLEGRO_EDGE_DESCRIPTIONS:
|
||||
```php
|
||||
'podjeta_z_maszyny_przez_kuriera' => 'Podjęta z maszyny przez kuriera',
|
||||
'przesylka_wyjechala_w_droge_do_punktu_docelowego' => 'Wyjechała w drogę do punktu docelowego',
|
||||
'wyjechala_w_droge_do_punktu_docelowego' => 'Wyjechała w drogę do punktu docelowego',
|
||||
'wyslana_z_sortowni' => 'Wysłana z sortowni',
|
||||
'wydana_do_doreczenia' => 'Wydana do doręczenia',
|
||||
'przesylka_oczekuje_na_odbior' => 'Oczekuje na odbiór',
|
||||
```
|
||||
|
||||
3. Dodaj nową statyczną metodę `guessStatusFromDescription(string $description): string` — keyword-based fallback. Umieść ją po slugifyAllegroDescription():
|
||||
|
||||
```php
|
||||
public static function guessStatusFromDescription(string $description): string
|
||||
{
|
||||
$lower = mb_strtolower($description, 'UTF-8');
|
||||
|
||||
// Terminal statuses first
|
||||
if (str_contains($lower, 'doręczon') || str_contains($lower, 'dostarczono') || str_contains($lower, 'odebrana przez odbiorc')) {
|
||||
return self::DELIVERED;
|
||||
}
|
||||
if (str_contains($lower, 'zwrócon') || str_contains($lower, 'zwrocona')) {
|
||||
return self::RETURNED;
|
||||
}
|
||||
if (str_contains($lower, 'anulowan')) {
|
||||
return self::CANCELLED;
|
||||
}
|
||||
|
||||
// Active statuses
|
||||
if (str_contains($lower, 'doręczeni') || str_contains($lower, 'doreczenia') || str_contains($lower, 'wydana do')) {
|
||||
return self::OUT_FOR_DELIVERY;
|
||||
}
|
||||
if (str_contains($lower, 'odbiór') || str_contains($lower, 'odbior') || str_contains($lower, 'oczekuje na odb')) {
|
||||
return self::READY_FOR_PICKUP;
|
||||
}
|
||||
if (str_contains($lower, 'sortowni') || str_contains($lower, 'magazyn') || str_contains($lower, 'w drodze') || str_contains($lower, 'tranzyt') || str_contains($lower, 'kurier') || str_contains($lower, 'podjęta') || str_contains($lower, 'podjeta') || str_contains($lower, 'wyjechał') || str_contains($lower, 'wyjechala')) {
|
||||
return self::IN_TRANSIT;
|
||||
}
|
||||
if (str_contains($lower, 'nadana') || str_contains($lower, 'nadano')) {
|
||||
return self::CONFIRMED;
|
||||
}
|
||||
if (str_contains($lower, 'przygotowan') || str_contains($lower, 'utworzon')) {
|
||||
return self::CREATED;
|
||||
}
|
||||
if (str_contains($lower, 'uszkodzon') || str_contains($lower, 'problem') || str_contains($lower, 'zagubiła') || str_contains($lower, 'zagubion')) {
|
||||
return self::PROBLEM;
|
||||
}
|
||||
|
||||
return self::UNKNOWN;
|
||||
}
|
||||
```
|
||||
|
||||
WAŻNE: NIE zmieniaj istniejących metod normalize(), description(), slugifyAllegroDescription(). Tylko DODAWAJ nowe wpisy do map i nową metodę.
|
||||
</action>
|
||||
<verify>php -l przechodzi. Test: DeliveryStatus::normalize('allegro_edge', 'wyslana_z_sortowni') zwraca 'in_transit'. Test: DeliveryStatus::guessStatusFromDescription('Paczka jest w drodze') zwraca 'in_transit'.</verify>
|
||||
<done>AC-1 satisfied: nowe slugi w mapie. AC-2 satisfied: fallback method istnieje.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Integracja fallback + logowanie w AllegroTrackingService</name>
|
||||
<files>src/Modules/Shipments/AllegroTrackingService.php</files>
|
||||
<action>
|
||||
Zmodyfikuj metodę `fetchAllegroEdgeStatus()` — dodaj fallback i logowanie.
|
||||
|
||||
Po linii:
|
||||
```php
|
||||
$slug = DeliveryStatus::slugifyAllegroDescription($description);
|
||||
```
|
||||
|
||||
Zamień blok return na:
|
||||
```php
|
||||
$normalized = DeliveryStatus::normalize('allegro_edge', $slug);
|
||||
|
||||
// Fallback: jeśli slug nieznany, próbuj keyword matching
|
||||
if ($normalized === DeliveryStatus::UNKNOWN) {
|
||||
$normalized = DeliveryStatus::guessStatusFromDescription($description);
|
||||
|
||||
// Loguj niezmapowany status (do uzupełnienia mapy w przyszłości)
|
||||
error_log(sprintf(
|
||||
'[AllegroTracking] Niezmapowany status: "%s" (slug: %s, guessed: %s)',
|
||||
$description,
|
||||
$slug,
|
||||
$normalized
|
||||
));
|
||||
}
|
||||
|
||||
return [
|
||||
'status' => $normalized,
|
||||
'status_raw' => $description,
|
||||
'description' => $description,
|
||||
];
|
||||
```
|
||||
|
||||
To zastępuje istniejący blok:
|
||||
```php
|
||||
return [
|
||||
'status' => DeliveryStatus::normalize('allegro_edge', $slug),
|
||||
'status_raw' => $description,
|
||||
'description' => $description,
|
||||
];
|
||||
```
|
||||
|
||||
WAŻNE: Nie zmieniaj nic innego w tym pliku. Tylko modyfikacja wewnątrz fetchAllegroEdgeStatus().
|
||||
</action>
|
||||
<verify>php -l przechodzi. Metoda fetchAllegroEdgeStatus zawiera fallback guessStatusFromDescription i error_log.</verify>
|
||||
<done>AC-2 partially satisfied: fallback zintegrowany. AC-3 satisfied: logowanie nieznanych statusów.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Istniejące mapy INPOST_MAP, APACZKA_MAP, ALLEGRO_MAP
|
||||
- Metody: normalize(), description(), slugifyAllegroDescription(), fetchInpostStatus()
|
||||
- src/Modules/Cron/ShipmentTrackingHandler.php (rate limit z 66-01 bez zmian)
|
||||
- Wszystkie inne pliki
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie tworzymy UI do zarządzania mapowaniem (istniejący Delivery Status Mapping UI wystarczy)
|
||||
- Nie tworzymy unit testów w tym planie
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
- [ ] php -l na obu plikach
|
||||
- [ ] DeliveryStatus::normalize('allegro_edge', 'wyslana_z_sortowni') === 'in_transit'
|
||||
- [ ] DeliveryStatus::normalize('allegro_edge', 'wydana_do_doreczenia') === 'out_for_delivery'
|
||||
- [ ] DeliveryStatus::guessStatusFromDescription('Paczka jest w drodze do odbiorcy') === 'in_transit'
|
||||
- [ ] DeliveryStatus::guessStatusFromDescription('Przesyłka odebrana w punkcie') !== 'unknown'
|
||||
- [ ] fetchAllegroEdgeStatus fallback + error_log działa
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Oba taski completed
|
||||
- Wszystkie realne opisy z API (obu zamówień) mapują się na właściwe statusy
|
||||
- Nieznane przyszłe opisy są obsługiwane przez keyword fallback
|
||||
- Nieznane statusy logowane do error_log
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/66-allegro-delivery-tracking/66-02-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user