PolkurierApiClient rozszerzony do pelnego kontraktu (7 metod):
createShipment/getLabel/getStatus/cancelOrder/getAvailableCarriers/
getInpostParcelMachines/getCourierPoints. Wspolny call() parsuje
envelope {status, response}. Kontrakt zweryfikowany na oficjalnej
dokumentacji PDF v1.11.
PolkurierShipmentService (implements ShipmentProviderInterface)
orchestruje pelen flow: normalizeShipmentType (lowercase), split
ulicy, build recipient/sender/pickup, COD z bank account z
company_settings, extractOrderNumber/extractTrackingNumber
priorytetujace SDK Order entity (number, waybills[0].number).
PolkurierTrackingService (implements ShipmentTrackingInterface)
mapuje statusy O/P/A/WP/D/Z/W przez delivery_status_mappings.
UI panel polkurier w prepare.php z dynamiczna lista uslug z
available_carriers. Bez dedykowanego selektora punktu — operator
wpisuje receiver_point_id w istniejace pole w sekcji Adres odbiorcy.
Migracja 20260514_000115 seedujaca 7 wpisow delivery_status_mappings
z oficjalnej tabeli ORDER_STATUS (O/P/A/WP/D/Z/W).
Live test #114/#115 zakonczony sukcesem po 4 iteracjach
(ReferenceError -> uppercase shipmenttype -> orderno parsing ->
A4/A6 etykieta). Rozmiar etykiety A4/A6 sterowany w panelu klienta
polkurier.pl, NIE przez API.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
20 KiB
phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, duration, started, completed
| phase | plan | subsystem | tags | requires | provides | affects | tech-stack | key-files | key-decisions | patterns-established | duration | started | completed | |||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 128-polkurier-shipment-service | 01 | shipments |
|
|
|
|
|
|
|
|
~120min (incl. 4 live test iteracje) | 2026-05-14T20:00:00Z | 2026-05-14T22:00:00Z |
Phase 128 Plan 01: polkurier ShipmentService + Tracking + UI prepare
polkurier zarejestrowany jako pelnoprawny przewoznik obok Apaczki — operator tworzy paczki przez UI /orders/{id}/shipment/prepare, etykieta A6 generowana, cron tracking gotowy do mapowania statusow O/P/A/WP/D/Z/W na znormalizowane created/confirmed/cancelled/in_transit/delivered/returned/problem.
Performance
| Metric | Value |
|---|---|
| Duration | ~120 min |
| Started | 2026-05-14T20:00:00Z |
| Completed | 2026-05-14T22:00:00Z |
| Tasks | 6/6 completed (5 auto + 1 checkpoint) |
| Files modified | 10 |
| Live test iteracje | 4 (ReferenceError → uppercase shipmenttype → orderno parsing → A6 panel) |
Acceptance Criteria Results
| Criterion | Status | Notes |
|---|---|---|
| AC-1: PolkurierApiClient pelny kontrakt API | Pass | 7 metod (createShipment/getLabel/getStatus/cancelOrder/getAvailableCarriers/getInpostParcelMachines/getCourierPoints) zweryfikowane na PDF v1.11. call() wspolny wrapper envelope {status, response}. |
| AC-2: PolkurierShipmentService implementuje ShipmentProviderInterface | Pass | code()='polkurier', getDeliveryServices cache per-request, createShipment orchestruje pelny flow z normalizacja shipmenttype i splitem ulicy, downloadLabel z base64 decode na klucz file. Verified on #114/#115. |
| AC-3: PolkurierTrackingService cron tracking | Pass (kod) | Implementacja kompletna, ale niezweryfikowane na zywej bazie podczas APPLY (operator anulowal paczki w panelu polkurier po teście — cron nie mial co pingowac). Graceful null przy bledach. Pierwszy passthrough nastapi przy nastepnej zywej paczce. |
| AC-4: UI prepare.php panel polkurier | Pass | Opcja "polkurier" w dropdownie, panel z dynamiczna lista uslug, hidden service_code. Bez dedykowanego selektora punktu — operator wpisuje w istniejacy input w sekcji Adres odbiorcy. |
| AC-5: delivery_status_mappings + /settings/delivery-statuses | Pass (kod) | Migracja idempotentna z 7 wpisami O/P/A/WP/D/Z/W. Operator uruchomi php bin/migrate.php gdy MySQL online. Widocznosc w /settings/delivery-statuses po migracji. |
| AC-6: Live test na #114 i #115 | Pass | 4 iteracje, ostatecznie obie paczki utworzone w polkurier, etykiety pobrane (A6 po zmianie w panelu klienta), operator anulowal w panelu polkuriera po weryfikacji. |
Accomplishments
- polkurier zarejestrowany jako 4. provider w
ShipmentProviderRegistry(obok allegro_wza, apaczka, inpost) — operator nadaje paczki z UI bez przelaczania platform. - Kontrakt API zweryfikowany na oficjalnej dokumentacji PDF v1.11 (pobranej i zachowanej w
.paul/phases/128-polkurier-shipment-service/polkurier-api-docs.txt— referencyjne zrodlo dla przyszlych faz). - Mapowanie statusow
O/P/A/WP/D/Z/Wna znormalizowane statusycreated/confirmed/cancelled/in_transit/delivered/returned/problemz idempotentna migracja — cron tracking gotowy do dzialania. - Diagnostyka silent-fail patternem (zapis fragmentu surowej odpowiedzi do
error_messageprzy nieudanym parsingu) — uratowala 3. iteracje live testu (parsingnumbervsorderno).
Task Commits
Wszystkie zmiany w jednym stanie WIP — commit zostanie wykonany w transition (feat(128): polkurier shipment service + tracking + UI prepare).
| Task | Status | Description |
|---|---|---|
| Task 1: PolkurierApiClient pelen kontrakt API | done | 7 metod, wspolny call() wrapper, parsowanie envelope |
| Task 2: PolkurierShipmentService + PolkurierTrackingService | done | ~520 + ~110 LOC, oba implementuja swoje interfejsy |
| Task 3: Wiring + UI prepare.php panel | done | Registry, CronHandlerFactory, ShipmentController.prepare/create, panel + JS |
| Task 4: Live test checkpoint na #114/#115 | done | Operator approved po 4 iteracjach, etykieta A6 po zmianie w panelu klienta polkurier |
| Task 5: Migracja seed delivery_status_mappings | done (kod) | 7 wpisow z PDF v1.11, idempotentna; operator uruchomi gdy MySQL online |
Task 6: Aktualizacja .paul/codebase/*.md |
done | architecture.md (Phase 128 sekcja), db_schema.md (seed mappings), tech_changelog.md (Phase 128 entry z 4 deviationami i iteracjami live testu) |
Files Created/Modified
| File | Change | Purpose |
|---|---|---|
src/Modules/Settings/PolkurierApiClient.php |
Modified | Stuby z Phase 127 zastapione 7 metodami: createShipment/getLabel/getStatus/cancelOrder/getAvailableCarriers/getInpostParcelMachines/getCourierPoints. Wspolny call() parser envelope. |
src/Modules/Shipments/PolkurierShipmentService.php |
Created | implements ShipmentProviderInterface, ~520 LOC. createShipment orchestracja, normalizeShipmentType, splitStreetAndNumber, buildRecipient/buildSender/buildPickup, downloadLabel z base64 decode, extractOrderNumber/extractTrackingNumber priorytetujace SDK shape. |
src/Modules/Shipments/PolkurierTrackingService.php |
Created | implements ShipmentTrackingInterface, ~110 LOC. getDeliveryStatus z graceful null + normalizacja przez DeliveryStatusMappingRepository. |
src/Modules/Shipments/DeliveryStatus.php |
Modified | +4 LOC: fallback URL https://polkurier.pl/sledz-paczke/<tracking>. Carrier_id routing przez matchCarrierByName automatyczny. |
src/Modules/Shipments/ShipmentController.php |
Modified | prepare() fetchuje polkurierServices, create() rozszerzony o service_code/pickup_date/pickup_time_from/pickup_time_to. |
src/Modules/Cron/CronHandlerFactory.php |
Modified | PolkurierTrackingService dodany do ShipmentTrackingRegistry. |
routes/web.php |
Modified | use PolkurierApiClient + PolkurierShipmentService, registry zarejestrowany. |
resources/views/shipments/prepare.php |
Modified | Opcja "polkurier" w carrier select, panel z select uslug, hidden service_code, JS handler. |
database/migrations/20260514_000115_seed_polkurier_delivery_status_mappings.sql |
Created | 7 wpisow O/P/A/WP/D/Z/W → normalized. Idempotentne. |
.paul/codebase/architecture.md |
Modified | Sekcja Phase 128 (PolkurierApiClient/ShipmentService/TrackingService/UI/wiring/seed/boundaries). |
.paul/codebase/db_schema.md |
Modified | Seedowane mapowania provider='polkurier' w sekcji delivery_status_mappings. |
.paul/codebase/tech_changelog.md |
Modified | Entry Phase 128 z opisem zmian + 4 iteracje live testu + deviations. |
.paul/phases/128-polkurier-shipment-service/polkurier-api-docs.txt |
Created | Tekst PDF v1.11 (pdftotext extract) — referencyjne zrodlo dla przyszlych faz polkuriera. |
Decisions Made
| Decision | Rationale | Impact |
|---|---|---|
shipmenttype lowercase + normalizeShipmentType() mapping |
polkurier API odrzuca uppercase BOX — wymaga lowercase z zbioru [box, envelope, palette, small_parcel, parcel_size_20] (komunikat bledu w live tescie). Aliasy dla PACKAGE/PARCEL/PACZKA/KOPERTA/PALETA pozwalaja reuse istniejacych wartosci formularza. |
Wszystkie kolejne paczki polkurier maja poprawny shipmenttype bez zmian w formularzu/preset. |
extractOrderNumber priorytetuje pole number (SDK Order entity) nad orderno |
polkurier create_order zwraca Order entity z polem number (zweryfikowane w SDK Order.php — setNumber/getNumber). orderno to nazwa parametru INPUT w innych metodach (get_label, get_status, cancel_order). |
Parsing dziala dla aktualnej wersji SDK + odporne na stary shape (orderno fallback). |
| Brak dedykowanego selektora punktu odbioru w UI | Operator zglosil ze Punkt odbioru jest juz polem w sekcji Adres odbiorcy z auto-fillem parcel_external_id z importu zamowienia. Dodatkowy selektor byl duplikatem. |
Usuniete: lookupPickupPoints, ShipmentController::polkurierPoints, route, JS handler. Operator wpisuje czysty ID (np. POP-RZE54) w istniejacy input. |
| Rozmiar etykiety A4/A6 sterowany w panelu klienta polkurier.pl | API polkurier nie udostepnia parametru rozmiaru w get_label ani create_order (zweryfikowane na PDF v1.11). Operator zmienia preferencje konta jednorazowo. |
Brak dodatkowego pola w polkurier_integration_settings ani formularzu; default_label_format (PDF/ZPL/EPL) odnosi sie tylko do typu pliku. |
Seed delivery_status_mappings z PDF v1.11 (nie z obserwacji live test) |
Live test obejmowal tylko status P (Potwierdzone) bezposrednio po create_order. Seedowanie bazujace na obserwacji wymagaloby kolejnych miesiecy zywych paczek. PDF ma kompletna tabele ORDER_STATUS. |
7 wpisow O/P/A/WP/D/Z/W ready od pierwszego dnia. |
Diagnostyka silent-fail patternem (zapis surowej odpowiedzi do error_message) |
3. iteracja live testu (parsing number vs orderno) byla niemozliwa do debugowania bez podgladu surowej odpowiedzi — payload_json w shipment_packages.update() jest poza whitelist. Zapis fragmentu (400 znakow) do error_message jest tani i widoczny operatorowi w UI. |
Pattern do reuse dla nowych integracji API z nieznanym shape odpowiedzi. |
Deviations from Plan
Summary
| Type | Count | Impact |
|---|---|---|
| Auto-fixed | 4 | Live test iteracje — wszystkie naprawione w tej samej sesji APPLY |
| Scope removals | 1 | UI selektor punktow paczkomatowych usuniety na zyczenie operatora |
| Scope additions | 1 | Pole service_code i pickup_* w ShipmentController::create() (potrzebne dla polkurier payload) |
| Deferred | 3 | Cron tracking weryfikacja, migracja MySQL, paczkomaty UI (kolejna faza) |
Total impact: Essential fixes (live test feedback), no scope creep — operator manual confirmation poszerzyl o jedno usuniecie (selektor punktu) i jedno dodanie (service_code przekazywany do service).
Auto-fixed Issues
1. [JS ReferenceError] polkurierPointIdInput is not defined w clearHiddenFields()
- Found during: Task 4 (live test, pierwszy submit polkurier)
- Issue: Po usunieciu duplikatu selektora punktu odbioru (po feedback operatora w Task 3 iteracji) zostala martwa referencja do zmiennej
polkurierPointIdInputwclearHiddenFields(). JS rzucal ReferenceError, handlercarrierSelect.changeprzerywal przed wywolaniemshowPanel(),provider_codezostawal na PHP-renderowanej wartosciapaczka(gdy$preselectedCarrier === 'apaczka'). Submit szedl do ApaczkaShipmentService → blad "Nie podano uslugi Apaczka." - Fix: Usuniecie linii
if (polkurierPointIdInput) polkurierPointIdInput.value = '';zclearHiddenFields(). - Files:
resources/views/shipments/prepare.php - Verification: Drugi submit polkurier → routing do PolkurierShipmentService.
2. [Polkurier API validation] shipmenttype musi byc lowercase
- Found during: Task 4 (live test, drugi submit po napraweniu #1)
- Issue: Wysylanie
BOXuppercase → API odrzucalo: "Typ paczki musi przyjmowac jeden z parametrow ze zbioru [box, envelope, palette, small_parcel, parcel_size_20]". - Fix: Nowa metoda
normalizeShipmentType()z lowercase + aliasami (PACKAGE→box, PARCEL→box, PACZKA→box, KOPERTA→envelope, PALETA→palette, MALA_PACZKA/SMALL→small_parcel). Defaultbox. - Files:
src/Modules/Shipments/PolkurierShipmentService.php - Verification: Trzeci submit → paczka utworzona w polkurier.
3. [Response shape mismatch] extractOrderNumber nie znajdowal pola number
- Found during: Task 4 (live test, trzeci submit — paczka utworzona w polkurier ale w orderPRO
status=pending) - Issue: Pierwotny parsing szukal kluczy
orderno/order_now odpowiedzi. polkurier zwraca SDK Order entity z polemnumber+ tablicawaybills[]zOrderWaybillentity (zweryfikowane wOrder.phpsetterachsetNumber(),addWaybill()). - Fix: Nowe metody
extractOrderNumber()(priorytetnumber, fallbackorderno/order_no/order_number/order_id/id, obsluga wrappera{order:{...}}i list) +extractTrackingNumber()(priorytetwaybills[0].number, fallback top-level klucze). Dodatkowo diagnostyka: gdyorderno='', zapis fragmentu surowej odpowiedzi doerror_message. - Files:
src/Modules/Shipments/PolkurierShipmentService.php - Verification: Czwarty submit →
status=created,tracking_numberustawiony, etykieta pobrana z polafile.
4. [API misunderstanding] Bogus parametry rozmiaru etykiety
- Found during: Task 4 (live test, czwarty submit — etykieta A4 zamiast A6)
- Issue: Iteracja w 3 bogus parametry (
format/label_size/paper_size) wyslanych doget_label— bez efektu, bo API ignoruje nieznane pola. Operator zglosil ze etykieta nadal A4. - Fix: Pobranie i przeczytanie oficjalnej dokumentacji PDF v1.11 potwierdzilo:
get_labelprzyjmuje WYLACZNIEorderno. Rozmiar A4/A6 sterowany jest w panelu klienta polkurier.pl. Usuniete bogus parametry,getLabel($login, $token, $orderno)ma tylko 3 argumenty. Operator zmienil ustawienie w panelu polkurier — etykieta A6 OK. - Files:
src/Modules/Settings/PolkurierApiClient.php,src/Modules/Shipments/PolkurierShipmentService.php - Verification: Operator nadal kolejna paczke → etykieta A6.
Scope Removals
UI selektor punktow paczkomatowych (AJAX endpoint + dropdown)
- Removed during: Task 3 iteracje (po feedback operatora)
- Reason: Istnieje juz pole
name="receiver_point_id"w sekcji Adres odbiorcy z auto-fillemparcel_external_idz importu zamowienia. Dodatkowy selektor byl duplikatem. Operator wpisuje czysty ID recznie (np.POP-RZE54). - Files removed:
PolkurierShipmentService::lookupPickupPoints(),ShipmentController::polkurierPoints(), route/shipments/polkurier/points, JS handlerloadPolkurierPoints/renderPolkurierPoints. - Zachowane:
PolkurierApiClient::getInpostParcelMachines()igetCourierPoints()— gotowe stuby na przyszle rozszerzenie (kolejna faza paczkomatow UI).
Scope Additions
service_code + pickup_* w ShipmentController::create()
- Reason: PolkurierShipmentService potrzebuje servicecode z available_carriers (osobne pole niz
delivery_method_idzeby JS mogl wstawic czysta wartosc) + optional pickup override. - Impact: Backward compatible — Apaczka/InPost/AllegroWZA ignoruja te pola w swoich createShipment.
Deferred Items
- Phase 128 follow-up: Operator uruchomi
php bin/migrate.phpgdy XAMPP MySQL online (utworzy 7 wpisowprovider='polkurier'wdelivery_status_mappings). - Phase 128 follow-up: Cron
shipment_tracking_syncweryfikacja przy pierwszej zywej paczce polkurier win_transit— pierwszy realny passthrough TrackingService dopiero przy nastepnej niezanulowanej paczce. - Kolejna faza: Paczkomaty UI panel (
InpostParcelMachines/PocztexPostOffices/Kurier48PostOfficesselectory wprepare.php), presety przesylek zprovider_code='polkurier',OrderValuationV2(wycena przed nadaniem).
Issues Encountered
| Issue | Resolution |
|---|---|
Migracja 20260514_000115 nie uruchomiona — MySQL offline z poziomu agenta (Bash) |
Operator uruchomi recznie php bin/migrate.php gdy XAMPP MySQL online. Migracja jest idempotentna. |
| AC-3 (cron tracking) nie zweryfikowane na zywej bazie | Operator anulowal obie paczki w panelu polkurier po teście — cron tracking nie mial co pingowac. Implementacja kompletna i defensywna (graceful null). Weryfikacja przy nastepnej zywej paczce. |
| PDF v1.11 polkurier API niedostepny przez WebFetch (binary content) | Pobrane przez WebFetch jako binarny PDF + pdftotext.exe (Git Bash bundle) → tekst w .paul/phases/128-polkurier-shipment-service/polkurier-api-docs.txt. Pattern dla przyszlych fetchy binary docs. |
Next Phase Readiness
Ready:
- polkurier dziala end-to-end w UI (tworzenie + etykieta + tracking gotowy).
- Kontrakt API zweryfikowany na oficjalnej dokumentacji (PDF v1.11) — przyszle fazy maja stale referencyjne zrodlo.
- Diagnostyka silent-fail pattern do reuse dla nowych integracji.
getInpostParcelMachines/getCourierPointsstuby gotowe dla kolejnej fazy paczkomaty UI.
Concerns:
- AC-3 (cron tracking) nie zweryfikowane na zywej bazie — pierwszy passthrough wymaga niezanulowanej paczki polkurier. Defensywne kodowanie (graceful null) chroni przed crashem crona, ale realne dzialanie testowalne dopiero na zywej paczce.
extractOrderNumber/extractTrackingNumberfallback chain moze nie pokryc 100% wariantow shape odpowiedzi (np. order zlecone z dodatkowymi opcjami). Pattern zerror_messagedump pomoze w iteracji.
Blockers:
- None.
Phase: 128-polkurier-shipment-service, Plan: 01 Completed: 2026-05-14