From d30a459b1e0ee1bc5dc5351c728107864b47d048 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Mon, 18 May 2026 15:21:39 +0200 Subject: [PATCH] fix(145): correct polkurier cod return codes Phase 145 complete: - send COD.codtype=S for standard Polkurier COD return time - send COD.return_cod=BA for bank-account transfer - add regression coverage and PAUL documentation --- .paul/PROJECT.md | 8 +- .paul/ROADMAP.md | 21 +- .paul/STATE.md | 37 ++-- .paul/changelog/2026-05-18.md | 8 + .../145-01-PLAN.md | 180 ++++++++++++++++++ .../145-01-SUMMARY.md | 133 +++++++++++++ DOCS/ARCHITECTURE.md | 1 + DOCS/TECH_CHANGELOG.md | 14 ++ .../Shipments/PolkurierShipmentService.php | 40 ++-- tests/Unit/PolkurierShipmentServiceTest.php | 80 ++++++++ 10 files changed, 492 insertions(+), 30 deletions(-) create mode 100644 .paul/phases/145-polkurier-cod-return-time-hotfix/145-01-PLAN.md create mode 100644 .paul/phases/145-polkurier-cod-return-time-hotfix/145-01-SUMMARY.md create mode 100644 tests/Unit/PolkurierShipmentServiceTest.php diff --git a/.paul/PROJECT.md b/.paul/PROJECT.md index efae255..a004c7f 100644 --- a/.paul/PROJECT.md +++ b/.paul/PROJECT.md @@ -13,8 +13,8 @@ Sprzedawca moĹĽe obsĹ‚ugiwać zamĂłwienia ze wszystkich kanałów | Attribute | Value | |-----------|-------| | Version | 3.9.0-dev | -| Status | v3.13 Imported Notes Badge Count Hotfix complete - Phase 144 closed | -| Last Updated | 2026-05-18 (Phase 144 unified) | +| Status | v3.14 Polkurier COD Return Time Hotfix complete - Phase 145 closed | +| Last Updated | 2026-05-18 (Phase 145 unified) | ## Requirements @@ -144,6 +144,7 @@ Sprzedawca moĹĽe obsĹ‚ugiwać zamĂłwienia ze wszystkich kanałów - [x] Polkurier Shipment Prepare Prefill: `/orders/{id}/shipment/prepare` rozpoznaje mapowania shopPRO z `provider='polkurier'`, preselectuje przewoznika i usluge oraz nie fallbackuje do Allegro — Phase 142 - [x] Orders List Sidebar UI Hotfix: `/orders/list` startuje bez opisowego boksu "Zamowienia", a zapisany zwiniety sidebar jest stosowany przed pierwszym renderem strony — Phase 143 - [x] Imported Notes Badge Count Hotfix: badge `[N]` na `/orders/list` zlicza wszystkie `order_notes` zamowienia, lacznie z notatkami importowanymi z shopPRO i notatkami autorskimi operatora — Phase 144 +- [x] Polkurier COD Return Time Hotfix: payload pobrania wysyla `COD.codtype='S'` oraz `COD.return_cod='BA'`, zamiast blednego `transfer` w polu terminu zwrotu pobrania — Phase 145 - [x] Integracja polkurier.pl (fundament): pojedyncza globalna konfiguracja w `/settings/integrations/polkurier`, szyfrowany Token API + login, karta w hubie integracji obok Apaczki i realny test polaczenia przez `apimetod=test_auth_api` zweryfikowany na zywym koncie operatora; `ShipmentProviderRegistry` netkniety — `PolkurierShipmentService/TrackingService` w kolejnych fazach — Phase 127 - [x] polkurier ShipmentService + TrackingService + UI prepare panel: pelen kontrakt API (createShipment/getLabel/getStatus/cancelOrder/getAvailableCarriers), `PolkurierShipmentService` implementujacy `ShipmentProviderInterface` z normalizacja shipmenttype (lowercase) i splitem ulicy na street/housenumber/flatnumber, `PolkurierTrackingService` mapujacy statusy O/P/A/WP/D/Z/W na znormalizowane, panel "polkurier" w `prepare.php` z dynamiczna lista uslug z `available_carriers`, seed migracja `delivery_status_mappings(provider='polkurier')` z 7 wpisami z PDF v1.11; live test na #114/#115 zakonczony sukcesem po 4 iteracjach (ReferenceError → uppercase shipmenttype → orderno parsing → A4/A6); rozmiar etykiety sterowany w panelu klienta polkurier.pl (Ustawienia konta → Preferencje etykiet), NIE przez API — Phase 128 - [x] Order User Notes module (Phase 129): pelen CRUD notatek autorskich operatora per zamowienie. Reuse `order_notes` przez nowy `note_type='user'` z `user_id` (FK→users SET NULL) + `author_name` (snapshot) + indeks `idx_order_notes_type_order`. `OrderNotesService` z autoryzacja DB-level (`WHERE user_id = :user_id`, rowCount=0 ⇒ 403). Sekcja `#notes` w "Wiadomosci i zalaczniki" w `/orders/{id}` z inline edit form + delete przez `OrderProAlerts.confirm`. Badge `[N]` (indigo neutralny) przy nr zamowienia na `/orders/list`; od Phase 144 badge korzysta z `notes_count` i liczy wszystkie rekordy `order_notes`, takze importowane ze zrodla. Brak admin override (brak systemu rol w aplikacji) — edit/delete tylko dla autora — Phase 129/144 @@ -293,6 +294,7 @@ PHP (XAMPP/Laravel), integracje z API marketplace'Ăłw (Allegro, Erli) oraz API | Autoryzacja CRUD przez `WHERE user_id = :user_id` + rowCount=0 ⇒ `RuntimeException(403)` (Phase 129) | Eliminacja konieczności osobnego SELECT pre-check'a — atomowy UPDATE/DELETE z filtrem user_id robi to w jednym query. Wzorzec do reuse dla innych zasobow "ownership-based" w aplikacji. | 2026-05-14 | Active | | Brak admin override dla notatek (Phase 129) — tylko autor edit/delete | Aplikacja nie ma systemu rol (`grep is_admin\|role=` zwrocil 0 trafien). Odlozone do osobnej fazy gdy beda role; obecnie operator ktory dodal notatke moze ja modyfikowac, inni widzą ale nie modyfikują. | 2026-05-14 | Deferred | | Badge `[N]` w `order_ref` przy nr zamowienia (Phase 129) — neutralny indigo, NIE alertowy | Subtelniejszy niz `.risk-return-badge` (czerwony, alertowy) — notatki to informacja, nie ostrzezenie. Klik scrolluje do `#notes` w szczegolach zamowienia. Pattern do reuse dla kolejnych metryk per-order (np. liczba SMS, liczba dokumentow). | 2026-05-14 | Active | +| Polkurier COD defaults: `codtype='S'`, `return_cod='BA'` | API Polkurier rozdziela termin zwrotu pobrania (`S/1D/4D/16D`) od sposobu zwrotu (`BA/PO/MB`). orderPRO uzywa standardowego terminu i przelewu na konto, zgodnie z dotychczasowa walidacja rachunku bankowego. | 2026-05-18 | Active | | Provider-addition recipe dla `/settings/delivery-statuses?tab=mapping` (Phase 130) | 5 punktow edycji w 4 plikach: (1) const definition `XXX_MAP`/`XXX_DESCRIPTIONS` w `DeliveryStatus.php`, (2) rejestracja w `PROVIDER_MAPS`/`PROVIDER_DESCRIPTIONS`, (3) match arms w `normalize()`/`description()`, (4) `PROVIDERS` const w `DeliveryStatusesController` + `DeliveryStatusMappingController`, (5) lista providerow w `DeliveryStatusMappingRepository::countAllUnmappedForBadge()`. Widok `_delivery-status-mappings-content.php` automatycznie iteruje. Pattern do reuse dla kazdego nowego przewoznika. | 2026-05-14 | Active | | Defaultowe mapowania statusow dostawy hardcoded w kodzie (nie tylko z DB seed) | Spojnosc z InPost/Apaczka/Allegro — wszyscy maja hardcoded fallback w `DeliveryStatus.php`. UI dziala od razu po deploy, niezaleznie czy operator uruchomil migracje seed. DB override (`delivery_status_mappings`) nadal dziala dla kazdego raw statusu — pattern dual-source (kod default + DB override) zachowany. | 2026-05-14 | Active | @@ -326,6 +328,6 @@ Quick Reference: --- *PROJECT.md — Updated when requirements or context change* -*Last updated: 2026-05-18 after Phase 144 closure* +*Last updated: 2026-05-18 after Phase 145 closure* diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md index 6b7b7c9..77312d9 100644 --- a/.paul/ROADMAP.md +++ b/.paul/ROADMAP.md @@ -6,6 +6,23 @@ orderPRO to narzedzie do wielokanalowego zarzadzania sprzedaza. Projekt przechod ## Current Milestone +v3.14 Polkurier COD Return Time Hotfix - Complete + +Pilny hotfix tworzenia przesylek pobraniowych Polkurier: API odrzuca `create_order`, bo payload wysyla bledna wartosc czasu zwrotu pobrania. `codtype` ma uzywac kodu terminu (`S`, `1D`, `4D`, `16D`), a `return_cod` kodu sposobu zwrotu (`BA`, `PO`, `MB`). + +Progress: 1 of 1 phases complete (100%). + +| Phase | Name | Plans | Status | +|-------|------|-------|--------| +| 145 | Polkurier COD Return Time Hotfix | 1/1 | Complete (2026-05-18; PHPUnit/Sonar/live smoke follow-up pending) | + +### Phase 145: Polkurier COD Return Time Hotfix + +Focus: Naprawic payload COD w `PolkurierShipmentService`, aby przesylki pobraniowe wysylaly `codtype='S'` jako standardowy termin zwrotu pobrania oraz `return_cod='BA'` jako przelew na konto bankowe. Brak zmian DB i brak UI konfiguracji terminow w tym hotfixie. +Plans: 145-01 (complete; `.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-SUMMARY.md`) + +## Previous Milestone + v3.13 Imported Notes Badge Count Hotfix - Complete Pilny hotfix dla listy zamowien: notatki zaimportowane ze zrodla, np. shopPRO, maja byc zliczane razem z notatkami autorskimi w badge `[N]` przy numerze zamowienia. @@ -21,7 +38,7 @@ Progress: 1 of 1 phases complete (100%). Focus: Zmienic licznik badge notatek na `/orders/list`, aby uzywal wszystkich rekordow `order_notes` dla zamowienia, a nie tylko `note_type='user'`. Zamowienie `1034` z importowana notatka shopPRO powinno pokazac cyfre na liscie. Plans: 144-01 (complete; `.paul/phases/144-imported-notes-badge-count/144-01-SUMMARY.md`) -## Previous Milestone +## Earlier Milestone v3.12 Orders List Sidebar UI Hotfix - Complete @@ -687,4 +704,4 @@ Archive: `.paul/milestones/v0.1-ROADMAP.md` --- *Roadmap created: 2026-03-12* -*Last updated: 2026-05-18 - Phase 144 complete; v3.13 Imported Notes Badge Count Hotfix complete* +*Last updated: 2026-05-18 - Phase 145 complete; v3.14 Polkurier COD Return Time Hotfix complete* diff --git a/.paul/STATE.md b/.paul/STATE.md index 7cd8fcb..b7528d9 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -5,19 +5,19 @@ See: .paul/PROJECT.md (updated 2026-05-18) **Core value:** Sprzedawca moze obslugiwac zamowienia ze wszystkich kanalow sprzedazy i nadawac przesylki bez przelaczania sie miedzy platformami. -**Current focus:** v3.13 Imported Notes Badge Count Hotfix complete; Phase 144 unified. +**Current focus:** v3.14 Polkurier COD Return Time Hotfix complete; Phase 145 unified. ## Current Position -Milestone: v3.13 Imported Notes Badge Count Hotfix -Phase: 144 of 144 (Imported Notes Badge Count Hotfix) - Complete -Plan: 144-01 complete +Milestone: v3.14 Polkurier COD Return Time Hotfix +Phase: 145 of 145 (Polkurier COD Return Time Hotfix) - Complete +Plan: 145-01 complete Status: Milestone complete, ready for next milestone or release decision -Last activity: 2026-05-18 12:39 - Unified .paul/phases/144-imported-notes-badge-count/144-01-PLAN.md +Last activity: 2026-05-18 13:28 - Unified .paul/phases/145-polkurier-cod-return-time-hotfix/145-01-PLAN.md Progress: -- Milestone v3.13: [##########] 100% (1 of 1 phases complete) -- Phase 144: [##########] 100% (complete) +- Milestone v3.14: [##########] 100% (1 of 1 phases complete) +- Phase 145: [##########] 100% (complete) ## Loop Position @@ -29,19 +29,19 @@ PLAN -> APPLY -> UNIFY ## Session Continuity -Last session: 2026-05-18 12:39 -Stopped at: Phase 144 complete; v3.13 milestone complete +Last session: 2026-05-18 13:28 +Stopped at: Phase 145 complete; v3.14 milestone complete Next action: Run $paul-complete-milestone or start next milestone planning -Resume file: .paul/phases/144-imported-notes-badge-count/144-01-SUMMARY.md +Resume file: .paul/phases/145-polkurier-cod-return-time-hotfix/145-01-SUMMARY.md ## Pending parallel work - None — Phase 118, 121, 122 wszystkie zacommitowane (8f14851, 360eef1). ## Git State -Last commit: HEAD fix(144): count imported order notes in list badge -Last phase commit: HEAD fix(144): count imported order notes in list badge -Previous: fix(143): polish orders list and sidebar first paint +Last commit: HEAD fix(145): correct polkurier cod return codes +Last phase commit: HEAD fix(145): correct polkurier cod return codes +Previous: fix(144): count imported order notes in list badge Branch: main ### Skill Audit (Phase 139) @@ -81,6 +81,12 @@ Branch: main |----------|---------|-------| | `sonar-scanner` | gap documented | Attempted after APPLY with `sonar-scanner --version`; CLI is not available in PATH. | +### Skill Audit (Phase 145) + +| Expected | Invoked | Notes | +|----------|---------|-------| +| `sonar-scanner` | gap documented | Attempted after APPLY with `sonar-scanner --version`; CLI is not available in PATH. | + ### Skill Audit (Phase 129) | Expected | Invoked | Notes | @@ -139,6 +145,7 @@ Branch: main ### Recent Decisions +- Phase 145 fixed the Polkurier COD payload contract: `COD.codtype='S'` for standard return time and `COD.return_cod='BA'` for bank-account transfer. No UI configurability or schema change was added. - Phase 144 changed the `/orders/list` notes badge contract from operator-only notes to all `order_notes` rows; detail view grouping and note CRUD/import behavior remain unchanged. - Phase 134 is documentation-only: no runtime code or schema changes were made. - Backlog entries are annotated, not deleted; stale/implemented cleanup is deferred to later phases. @@ -159,6 +166,7 @@ Branch: main ### Blockers / Concerns +- Phase 145 APPLY: `vendor/bin/phpunit` is missing, so `tests/Unit/PolkurierShipmentServiceTest.php` was linted and covered by an ad-hoc runtime smoke instead of PHPUnit; `sonar-scanner` is unavailable in PATH. - Phase 144 APPLY: `vendor/bin/phpunit` is missing, so `tests/Unit/OrdersRepositoryNotesCountTest.php` was linted and covered by an ad-hoc SQLite runtime smoke instead of PHPUnit; `sonar-scanner` is unavailable in PATH. - Phase 134: `sonar-scanner` is still unavailable in PATH. - Phase 135: `vendor/bin/phpunit` and `sonar-scanner` are unavailable in PATH/checkout; syntax checks and ad-hoc SQLite/runtime smoke passed. @@ -177,6 +185,9 @@ Branch: main ## Pending Actions +- Phase 145 follow-up: after restoring `vendor/`, run `vendor/bin/phpunit tests/Unit/PolkurierShipmentServiceTest.php`. +- Phase 145 follow-up: run SonarQube scan after restoring `sonar-scanner` in PATH. +- Phase 145 follow-up: with explicit operator intent, create one live Polkurier COD shipment and confirm API accepts `codtype='S'` / `return_cod='BA'` without the previous return-time error. - Phase 138 follow-up: run `vendor/bin/phpunit tests/Unit/SmtpSecurityContextFactoryTest.php tests/Unit/TemplateVariableCatalogTest.php` after dependencies are installed. - Phase 139 follow-up: split `OrdersStatisticsRepository` (`php:S1448`, 43 methods) in a future god-class refactor if still relevant. - Phase 139 follow-up: continue with confirmed groups `php:S1142`, `php:S3776`, `php:S1172`, `php:S1192`, `php:S112`, plus Web table/accessibility issues. `php:S4833` is now only 3 core framework require issues. diff --git a/.paul/changelog/2026-05-18.md b/.paul/changelog/2026-05-18.md index ad5a6c2..142badb 100644 --- a/.paul/changelog/2026-05-18.md +++ b/.paul/changelog/2026-05-18.md @@ -24,6 +24,10 @@ - `OrdersRepository` zwraca `notes_count` liczony ze wszystkich rekordow `order_notes` dla zamowienia. - Dodano test regresyjny `OrdersRepositoryNotesCountTest` i wykonano SQLite smoke `notes_count=2` dla notatki `message` + `user`. - Gap: PHPUnit i SonarQube scan Phase 144 pozostaja do wykonania po przywroceniu `vendor/` i `sonar-scanner`. +- [Phase 145, Plan 01] Naprawiono payload COD Polkuriera, ktory powodowal blad `create_order` o dozwolonym zbiorze `[S, 1D, 4D, 16D]`. +- `PolkurierShipmentService` wysyla teraz `COD.codtype='S'` oraz `COD.return_cod='BA'`, zachowujac walidacje i czyszczenie numeru konta bankowego. +- Dodano test regresyjny `PolkurierShipmentServiceTest` oraz ad-hoc runtime smoke dla payloadu COD. +- Gap: PHPUnit i SonarQube scan Phase 145 pozostaja do wykonania po przywroceniu `vendor/` i `sonar-scanner`. ## Zmienione pliki @@ -61,3 +65,7 @@ - `src/Modules/Orders/OrdersController.php` - `tests/Unit/OrdersRepositoryNotesCountTest.php` - `DOCS/DB_SCHEMA.md` +- `.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-PLAN.md` +- `.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-SUMMARY.md` +- `src/Modules/Shipments/PolkurierShipmentService.php` +- `tests/Unit/PolkurierShipmentServiceTest.php` diff --git a/.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-PLAN.md b/.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-PLAN.md new file mode 100644 index 0000000..36a1387 --- /dev/null +++ b/.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-PLAN.md @@ -0,0 +1,180 @@ +--- +phase: 145-polkurier-cod-return-time-hotfix +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - src/Modules/Shipments/PolkurierShipmentService.php + - tests/Unit/PolkurierShipmentServiceTest.php + - DOCS/ARCHITECTURE.md + - DOCS/TECH_CHANGELOG.md +autonomous: true +delegation: off +--- + + +## Goal +Naprawic tworzenie przesylek pobraniowych Polkurier, aby payload COD wysylal do API dozwolony termin zwrotu pobrania. + +## Purpose +Operator nie moze utworzyc przesylki pobraniowej, bo Polkurier odrzuca `create_order` komunikatem: "Parametr czasu zwrotu pobrania musi zawierac sie w zbiorze [S, 1D, 4D, 16D]". Obecny payload myli sposob zwrotu pobrania z terminem zwrotu pobrania. + +## Output +Poprawiony payload COD w `PolkurierShipmentService`, test regresyjny budowania danych COD oraz aktualizacja dokumentacji technicznej. + + + + +- **Brak pytan** - Problem jest jednoznaczny po analizie kodu i dokumentacji Polkurier: `codtype` oznacza termin zwrotu pobrania (`S`, `1D`, `4D`, `16D`), a `return_cod` oznacza sposob zwrotu (`BA`, `PO`, `MB`). Obecny kod wysyla `return_cod='transfer'`, a `codtype='transfer'`. + -> Odpowiedz: No clarifications needed - proceeding. + + +## Project Context +@AGENTS.md +@.paul/PROJECT.md +@.paul/ROADMAP.md +@.paul/STATE.md +@DOCS/ARCHITECTURE.md +@DOCS/DB_SCHEMA.md + +## Prior Work +@.paul/phases/127-polkurier-integration-foundation/127-01-PLAN.md +@.paul/phases/128-polkurier-shipment-service/128-01-PLAN.md +@.paul/phases/140-shoppro-polkurier-delivery-mapping/140-01-PLAN.md +@.paul/phases/142-polkurier-shipment-prepare-prefill/142-01-PLAN.md + +## Source Files +@src/Modules/Shipments/PolkurierShipmentService.php +@src/Modules/Settings/PolkurierApiClient.php +@src/Modules/Shipments/ShipmentPackageRepository.php +@tests/Unit/ApaczkaShipmentServiceTest.php +@tests/Unit/ShipmentPreparePolkurierMappingTest.php + + + +## Required Skills (from SPECIAL-FLOWS.md) + +| Skill | Priority | When to Invoke | Loaded? | +|-------|----------|----------------|---------| +| `sonar-scanner` | required | Po APPLY, przed UNIFY | ○ | + + + + + +## AC-1: Poprawny Termin Zwrotu Pobrania +```gherkin +Given operator tworzy przesylke Polkurier z kwota pobrania wieksza od 0 +When orderPRO buduje payload `create_order` +Then sekcja `COD.codtype` zawiera dozwolona wartosc terminu zwrotu pobrania, domyslnie `S` +And nie zawiera juz blednej wartosci `transfer` +``` + +## AC-2: Poprawny Sposob Zwrotu Pobrania +```gherkin +Given operator tworzy przesylke Polkurier z pobraniem na konto bankowe firmy +When orderPRO buduje payload `create_order` +Then sekcja `COD.return_cod` zawiera dozwolony kod sposobu zwrotu, domyslnie `BA` +And `COD.codbankaccount` nadal zawiera oczyszczony numer konta bankowego +``` + +## AC-3: Brak Regresji Dla Przesylek Bez COD +```gherkin +Given operator tworzy zwykla przesylke Polkurier bez pobrania +When orderPRO buduje payload `create_order` +Then sekcja `COD` nie jest dodawana +And dotychczasowy zapis paczki i payload pozostaja bez zmian +``` + +## AC-4: Brak Zmiany Schematu +```gherkin +Given hotfix dotyczy tylko mapowania wartosci wysylanych do API Polkurier +When plan zostanie wdrozony +Then nie powstaje migracja ani zmiana struktury tabel +``` + + + + + + + Task 1: Wydzielic i poprawic budowanie payloadu COD Polkurier + src/Modules/Shipments/PolkurierShipmentService.php + + - Zastap obecny inline blok `COD` mala prywatna metoda odpowiedzialna tylko za COD. + - Metoda ma zwracac `null`, gdy `cod_amount <= 0`, zeby przesylki bez pobrania nie dostawaly sekcji `COD`. + - Dla pobrania ustaw `codtype` na dozwolony termin zwrotu pobrania. Domyslnie uzyj `S`, bo dokumentacja opisuje to jako standardowy zwrot 5-7 dni roboczych. + - Dla sposobu zwrotu ustaw `return_cod` na `BA`, czyli przelew na konto bankowe. + - Zachowaj obecna walidacje numeru konta: brak rachunku bankowego nadal ma rzucac `ShipmentException` z aktualnym komunikatem. + - Nie dodawaj UI ani nowych ustawien; to hotfix blednego payloadu, nie konfigurator terminow COD. + + C:\xampp\php\php.exe -l src/Modules/Shipments/PolkurierShipmentService.php + AC-1, AC-2 i AC-3 spelnione w kodzie budujacym payload. + + + + Task 2: Dodac test regresyjny COD dla Polkuriera + tests/Unit/PolkurierShipmentServiceTest.php + + - Dodaj test jednostkowy dla prywatnej metody budujacej COD przez `ReflectionMethod`, zgodnie ze stylem `ApaczkaShipmentServiceTest`. + - Assert dla pobrania: `codtype === 'S'`, `return_cod === 'BA'`, `codamount` jest liczba zaokraglona do 2 miejsc, a `codbankaccount` ma same cyfry. + - Dodaj test, ze `cod_amount=0` zwraca `null`. + - Dodaj test, ze pobranie bez numeru konta rzuca `ShipmentException`. + - Test ma uzywac mockow repozytoriow/API i nie ma laczyc sie z Polkurier ani baza danych. + + C:\xampp\php\php.exe -l tests/Unit/PolkurierShipmentServiceTest.php; vendor/bin/phpunit tests/Unit/PolkurierShipmentServiceTest.php jezeli vendor/bin/phpunit jest dostepny + AC-1, AC-2 i AC-3 zabezpieczone testami albo gap PHPUnit udokumentowany, jesli vendor nadal jest niedostepny. + + + + Task 3: Zaktualizowac dokumentacje techniczna + DOCS/ARCHITECTURE.md, DOCS/TECH_CHANGELOG.md + + - W `DOCS/ARCHITECTURE.md` dopisz kontrakt COD dla Polkuriera: `codtype='S'` jako standardowy termin zwrotu i `return_cod='BA'` jako przelew na konto. + - W `DOCS/TECH_CHANGELOG.md` dodaj wpis 2026-05-18 opisujacy hotfix bledu `create_order` przy pobraniu. + - Nie aktualizuj `DOCS/DB_SCHEMA.md`, bo nie ma migracji ani zmiany tabel. + + rg -n "codtype|return_cod|Polkurier|polkurier" DOCS/ARCHITECTURE.md DOCS/TECH_CHANGELOG.md + AC-4 spelnione i dokumentacja opisuje nowy kontrakt payloadu. + + + + + + +## DO NOT CHANGE +- `database/migrations/*` - brak zmiany schematu. +- `resources/views/shipments/prepare.php` - formularz prefillu byl naprawiany w Phase 142; ten blad jest w payloadzie API. +- `src/Modules/Settings/PolkurierApiClient.php` - wrapper API i obsluga bledow zostaja bez zmian. +- `src/Modules/Settings/ShopproIntegrationsController.php` oraz widoki mapowan shopPRO - mapowania dostaw nie sa przyczyna bledu COD. + +## SCOPE LIMITS +- Nie dodawac konfiguracji wyboru `S/1D/4D/16D` w UI w tym planie. +- Nie dodawac przekazu pocztowego `PO` ani skarbonki `MB`; domyslny i wymagany teraz przypadek to przelew bankowy. +- Nie wykonywac realnego utworzenia przesylki Polkurier bez jawnej decyzji operatora, bo to moze nadac prawdziwa paczke. +- Nie zmieniac logiki etykiet, statusow, trackingu ani mapowan shopPRO. + + + + +Before declaring plan complete: +- [ ] `C:\xampp\php\php.exe -l src/Modules/Shipments/PolkurierShipmentService.php` +- [ ] `C:\xampp\php\php.exe -l tests/Unit/PolkurierShipmentServiceTest.php` +- [ ] `vendor/bin/phpunit tests/Unit/PolkurierShipmentServiceTest.php` jezeli zaleznosci sa dostepne +- [ ] `rg -n "codtype|return_cod|Polkurier|polkurier" DOCS/ARCHITECTURE.md DOCS/TECH_CHANGELOG.md` +- [ ] `git diff --check` +- [ ] `sonar-scanner` jezeli CLI jest dostepny; w przeciwnym razie udokumentowac gap + + + +- Przesylka pobraniowa Polkurier nie wysyla juz `transfer` w polu wymagajacym `[S, 1D, 4D, 16D]`. +- Payload COD uzywa `codtype='S'` i `return_cod='BA'`. +- Przesylki bez pobrania nadal nie zawieraja sekcji `COD`. +- Walidacja numeru konta bankowego zostaje zachowana. +- Dokumentacja techniczna odzwierciedla hotfix. + + + +After completion, create `.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-SUMMARY.md` + diff --git a/.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-SUMMARY.md b/.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-SUMMARY.md new file mode 100644 index 0000000..65bdb48 --- /dev/null +++ b/.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-SUMMARY.md @@ -0,0 +1,133 @@ +--- +phase: 145-polkurier-cod-return-time-hotfix +plan: 01 +subsystem: shipments +tags: [polkurier, cod, payload, shipments] +requires: + - phase: 128-polkurier-shipment-service + provides: Polkurier shipment creation flow and COD payload support +provides: + - Polkurier COD payload uses API-valid return-time and return-method codes + - Regression test for COD payload construction +affects: [polkurier, shipments, cod] +tech-stack: + added: [] + patterns: [private payload builder covered by reflection-based unit test] +key-files: + created: + - tests/Unit/PolkurierShipmentServiceTest.php + modified: + - src/Modules/Shipments/PolkurierShipmentService.php + - DOCS/ARCHITECTURE.md + - DOCS/TECH_CHANGELOG.md +key-decisions: + - "Polkurier COD return time defaults to `S` and return method defaults to `BA`." +patterns-established: + - "Provider-specific API code mappings stay in the shipment service unless UI configurability is explicitly required." +duration: 13min +started: 2026-05-18T13:05:00+02:00 +completed: 2026-05-18T13:18:00+02:00 +--- + +# Phase 145 Plan 01: Polkurier COD Return Time Hotfix Summary + +Polkurier cash-on-delivery shipments now send API-valid COD codes: `COD.codtype='S'` and `COD.return_cod='BA'`. + +## Performance + +| Metric | Value | +|--------|-------| +| Duration | 13min | +| Started | 2026-05-18 13:05 | +| Completed | 2026-05-18 13:18 | +| Tasks | 3 completed | +| Files modified | 4 runtime/test/docs files + PAUL docs | + +## Acceptance Criteria Results + +| Criterion | Status | Notes | +|-----------|--------|-------| +| AC-1: Poprawny Termin Zwrotu Pobrania | Pass | `buildCodPayload()` sends `codtype='S'`, not `transfer`. | +| AC-2: Poprawny Sposob Zwrotu Pobrania | Pass | `return_cod='BA'`; `codbankaccount` is sanitized to digits. | +| AC-3: Brak Regresji Dla Przesylek Bez COD | Pass | `cod_amount <= 0` returns `null`, so `COD` is omitted. | +| AC-4: Brak Zmiany Schematu | Pass | No migrations or DB schema changes. | + +## Accomplishments + +- Replaced invalid Polkurier COD values that caused `create_order` to reject COD shipments. +- Preserved the existing company bank-account requirement for COD shipments. +- Added a unit-level regression test for COD payload shape, zero-COD omission, and missing bank-account validation. +- Updated architecture and technical changelog documentation. + +## Task Commits + +No task-level commits were created during APPLY. Phase commit is created during UNIFY. + +## Files Created/Modified + +| File | Change | Purpose | +|------|--------|---------| +| `src/Modules/Shipments/PolkurierShipmentService.php` | Modified | Extracted COD payload builder and set Polkurier-valid codes. | +| `tests/Unit/PolkurierShipmentServiceTest.php` | Created | Regression coverage for COD payload construction. | +| `DOCS/ARCHITECTURE.md` | Modified | Documents the Polkurier COD payload contract. | +| `DOCS/TECH_CHANGELOG.md` | Modified | Records the Phase 145 hotfix. | +| `.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-PLAN.md` | Created | Executable PAUL plan. | +| `.paul/phases/145-polkurier-cod-return-time-hotfix/145-01-SUMMARY.md` | Created | Completion record. | + +## Decisions Made + +| Decision | Rationale | Impact | +|----------|-----------|--------| +| Default COD return time is `S` | Polkurier accepts `[S, 1D, 4D, 16D]`; `S` is the standard return-time option. | Fixes the reported API error without adding UI scope. | +| Default COD return method is `BA` | Current orderPRO flow requires a company bank account, so bank transfer is the matching return method. | Keeps behavior aligned with existing validation. | +| No UI configurability for `1D/4D/16D` | The request is a hotfix for invalid payload values. | Avoids scope creep and schema/config changes. | + +## Deviations from Plan + +### Summary + +| Type | Count | Impact | +|------|-------|--------| +| Auto-fixed | 0 | None | +| Scope additions | 0 | None | +| Deferred | 2 | Environment-only verification gaps | + +### Deferred Items + +- Run `vendor/bin/phpunit tests/Unit/PolkurierShipmentServiceTest.php` after dependencies are restored. +- Run `sonar-scanner` after the CLI is restored in PATH. + +## Issues Encountered + +| Issue | Resolution | +|-------|------------| +| `vendor/bin/phpunit` missing | Linted the test and ran an ad-hoc runtime smoke through `C:\xampp\php\php.exe`. | +| `sonar-scanner` missing in PATH | Documented the required-skill gap in STATE/SUMMARY. | + +## Verification Results + +| Check | Result | +|-------|--------| +| `C:\xampp\php\php.exe -l src/Modules/Shipments/PolkurierShipmentService.php` | Pass | +| `C:\xampp\php\php.exe -l tests/Unit/PolkurierShipmentServiceTest.php` | Pass | +| Ad-hoc runtime smoke for `buildCodPayload()` | Pass | +| `rg -n "codtype|return_cod|Polkurier|polkurier" DOCS/ARCHITECTURE.md DOCS/TECH_CHANGELOG.md` | Pass | +| `git diff --check` | Pass; only Windows line-ending warnings | +| `vendor/bin/phpunit tests/Unit/PolkurierShipmentServiceTest.php` | Gap: binary missing | +| `sonar-scanner --version` | Gap: CLI missing in PATH | + +## Next Phase Readiness + +**Ready:** +- Polkurier COD shipments should no longer fail on invalid return-time value. +- Test file is ready to run once PHPUnit dependencies return. + +**Concerns:** +- Live Polkurier COD shipment smoke was not executed to avoid creating a real shipment without explicit operator action. + +**Blockers:** +- None for code completion. + +--- +*Phase: 145-polkurier-cod-return-time-hotfix, Plan: 01* +*Completed: 2026-05-18* diff --git a/DOCS/ARCHITECTURE.md b/DOCS/ARCHITECTURE.md index 1d382fb..b9c4d2c 100644 --- a/DOCS/ARCHITECTURE.md +++ b/DOCS/ARCHITECTURE.md @@ -156,6 +156,7 @@ Phase 135 fixes daily net totals: `OrdersStatisticsRepository::netAmountSql()` p Phase 130 adds an Erli-specific post-label step: for `orders.source='erli'`, `ShipmentController` calls `ErliExternalShipmentService::syncPackage()` after a local provider has a tracking number. Phase 131 extends the same Allegro-like contract into cron tracking: `ShipmentTrackingHandler` retries the Erli external parcel sync after a provider returns tracking status, but the retry is non-critical and never blocks local `delivery_status` updates. The service posts `vendor/status/trackingNumber/orderId` to Erli `POST /shipping/external` and stores the response in `shipment_packages.payload_json.erli_external_parcel`; failures are activity-log warnings. Phase 140 extends shopPRO delivery mapping with Polkurier. `/settings/integrations/shoppro?tab=delivery` now lists Polkurier services from `PolkurierShipmentService::getDeliveryServices()` and stores selected rows in `carrier_delivery_method_mappings` as `source_system='shoppro'`, `provider='polkurier'`, `provider_service_id=`, and `provider_service_name=`. No schema change is required; the shipment prepare flow reads the shared mapping and preselects `provider='polkurier'` with the stored delivery method id instead of falling back to Allegro. +Phase 145 fixes the Polkurier COD payload contract. For cash-on-delivery shipments, `PolkurierShipmentService` sends `COD.codtype='S'` (standard return time) and `COD.return_cod='BA'` (bank-account transfer), while preserving the existing bank-account requirement and sanitized numeric `codbankaccount`. 1. **Create** — `ShipmentController::create()` → `ShipmentProviderRegistry` → carrier `ShipmentService::createShipment()` → `ShipmentPackageRepository::insert()` 2. **Track** — Cron `ShipmentTrackingHandler` → `ShipmentTrackingRegistry` → carrier tracking API → optional Erli external parcel retry → `ShipmentPackageRepository::updateDeliveryStatus()` → shared `shipment.status_changed` automation event when normalized status really changes. diff --git a/DOCS/TECH_CHANGELOG.md b/DOCS/TECH_CHANGELOG.md index 18476c3..5cff406 100644 --- a/DOCS/TECH_CHANGELOG.md +++ b/DOCS/TECH_CHANGELOG.md @@ -1,5 +1,19 @@ # Technical Changelog +## 2026-05-18 - Phase 145 Plan 01: Polkurier COD Return Time Hotfix + +**Co zrobiono:** +- Naprawiono payload COD wysylany do Polkurier `create_order`. +- `PolkurierShipmentService` wysyla teraz `COD.codtype='S'` jako standardowy termin zwrotu pobrania oraz `COD.return_cod='BA'` jako przelew na konto bankowe. +- Zachowano oczyszczanie numeru rachunku bankowego do samych cyfr i dotychczasowy blad walidacji, gdy konto firmowe nie jest uzupelnione. +- Dodano test regresyjny budowania payloadu COD dla Polkuriera. + +**Dlaczego:** +- API Polkurier odrzucalo przesylki pobraniowe komunikatem o dozwolonym zbiorze `[S, 1D, 4D, 16D]`, bo orderPRO wysylal wartosc `transfer` w polu terminu zwrotu pobrania. + +**BREAKING / migracja:** +- Brak migracji DB i brak zmian breaking. Hotfix zmienia tylko wartosci wysylane w sekcji `COD` payloadu Polkurier. + ## 2026-05-18 - Phase 144 Plan 01: Imported Notes Badge Count Hotfix **Co zrobiono:** diff --git a/src/Modules/Shipments/PolkurierShipmentService.php b/src/Modules/Shipments/PolkurierShipmentService.php index 816c2f4..a3a5ee6 100644 --- a/src/Modules/Shipments/PolkurierShipmentService.php +++ b/src/Modules/Shipments/PolkurierShipmentService.php @@ -150,18 +150,9 @@ final class PolkurierShipmentService implements ShipmentProviderInterface } $cod = max(0.0, (float) ($formData['cod_amount'] ?? 0)); - if ($cod > 0) { - $companySettings = $this->companySettings->getSettings(); - $bankAccount = preg_replace('/[^0-9]/', '', (string) ($companySettings['bank_account'] ?? '')) ?? ''; - if ($bankAccount === '') { - throw new ShipmentException('Przesylka COD wymaga numeru konta bankowego. Uzupelnij go w Ustawienia > Firma.'); - } - $apiPayload['COD'] = [ - 'codtype' => 'transfer', - 'codamount' => round($cod, 2), - 'codbankaccount' => $bankAccount, - 'return_cod' => 'transfer', - ]; + $codPayload = $this->buildCodPayload($formData); + if ($codPayload !== null) { + $apiPayload['COD'] = $codPayload; } $carrierLabel = $this->resolveCarrierLabel($courierCode); @@ -618,6 +609,31 @@ final class PolkurierShipmentService implements ShipmentProviderInterface ]; } + /** + * @param array $formData + * @return array{codtype: string, codamount: float, codbankaccount: string, return_cod: string}|null + */ + private function buildCodPayload(array $formData): ?array + { + $cod = max(0.0, (float) ($formData['cod_amount'] ?? 0)); + if ($cod <= 0) { + return null; + } + + $companySettings = $this->companySettings->getSettings(); + $bankAccount = preg_replace('/[^0-9]/', '', (string) ($companySettings['bank_account'] ?? '')) ?? ''; + if ($bankAccount === '') { + throw new ShipmentException('Przesylka COD wymaga numeru konta bankowego. Uzupelnij go w Ustawienia > Firma.'); + } + + return [ + 'codtype' => 'S', + 'codamount' => round($cod, 2), + 'codbankaccount' => $bankAccount, + 'return_cod' => 'BA', + ]; + } + private function nextBusinessDay(): string { $ts = time(); diff --git a/tests/Unit/PolkurierShipmentServiceTest.php b/tests/Unit/PolkurierShipmentServiceTest.php new file mode 100644 index 0000000..5322608 --- /dev/null +++ b/tests/Unit/PolkurierShipmentServiceTest.php @@ -0,0 +1,80 @@ +companySettings = $this->createMock(CompanySettingsRepository::class); + $this->service = new PolkurierShipmentService( + $this->createMock(PolkurierIntegrationRepository::class), + $this->createMock(PolkurierApiClient::class), + $this->createMock(ShipmentPackageRepository::class), + $this->companySettings, + $this->createMock(OrdersRepository::class) + ); + } + + /** + * @param array $formData + * @return array|null + */ + private function invokeBuildCodPayload(array $formData): ?array + { + $method = new ReflectionMethod(PolkurierShipmentService::class, 'buildCodPayload'); + $method->setAccessible(true); + $result = $method->invoke($this->service, $formData); + + return is_array($result) ? $result : null; + } + + public function testBuildCodPayloadUsesPolkurierAllowedCodes(): void + { + $this->companySettings + ->method('getSettings') + ->willReturn(['bank_account' => '12 3456-7890 1234 5678 9012 3456']); + + $payload = $this->invokeBuildCodPayload(['cod_amount' => '123.456']); + + self::assertSame('S', $payload['codtype'] ?? null); + self::assertSame(123.46, $payload['codamount'] ?? null); + self::assertSame('12345678901234567890123456', $payload['codbankaccount'] ?? null); + self::assertSame('BA', $payload['return_cod'] ?? null); + } + + public function testBuildCodPayloadReturnsNullWithoutCodAmount(): void + { + $this->companySettings + ->expects(self::never()) + ->method('getSettings'); + + self::assertNull($this->invokeBuildCodPayload(['cod_amount' => '0'])); + } + + public function testBuildCodPayloadRequiresBankAccount(): void + { + $this->companySettings + ->method('getSettings') + ->willReturn(['bank_account' => '']); + + $this->expectException(ShipmentException::class); + $this->expectExceptionMessage('Przesylka COD wymaga numeru konta bankowego.'); + + $this->invokeBuildCodPayload(['cod_amount' => '50.00']); + } +}