feat(130): erli shipments and labels

This commit is contained in:
2026-05-16 00:58:14 +02:00
parent 4258751e80
commit 13f570e5af
21 changed files with 1451 additions and 58 deletions

View File

@@ -13,8 +13,8 @@ Sprzedawca moĹĽe obsĹugiwać zamĂłwienia ze wszystkich kanaĹĂłw
| Attribute | Value |
|-----------|-------|
| Version | 3.8.0-dev |
| Status | v3.8 Erli Marketplace Integration in progress — Phase 129 shipped (Erli status mappings/sync); Phase 130 next |
| Last Updated | 2026-05-16 (Phase 129 closed) |
| Status | v3.8 Erli Marketplace Integration in progress — Phase 130 shipped (Erli shipments + labels/external parcel sync); Phase 131 next |
| Last Updated | 2026-05-16 (Phase 130 closed) |
## Requirements
@@ -129,6 +129,7 @@ Sprzedawca moĹĽe obsĹugiwać zamĂłwienia ze wszystkich kanaĹĂłw
- [x] Fundament integracji Erli: pojedyncza globalna konfiguracja `/settings/integrations/erli`, szyfrowany Bearer API key, realny test `GET /svc/shop-api/inbox`, karta w hubie integracji oraz dokumentacja schematu/architektury — Phase 127
- [x] Import zamowien Erli: pobieranie `/inbox` przez cron i recznie, mapper do orderPRO, delta-only re-import, `invoice_requested` z danych firmowych/NIP, bezpieczny ACK `/inbox/mark-read` po bezblednym batchu — Phase 128
- [x] Mapowanie i synchronizacja statusow Erli: osobne pull/push mappings, discovery statusow z inboxa, reczny-only push `PATCH /orders/{id}/status`, cron `erli_status_sync` i zakladki w ustawieniach Erli — Phase 129
- [x] Przesylki Erli: zakladka mapowania dostaw, etykiety przez lokalne providery InPost/Apaczka i rejestracja paczek zewnetrznych w Erli przez `POST /shipping/external` — Phase 130
### Deferred
@@ -137,11 +138,10 @@ Sprzedawca moĹĽe obsĹugiwać zamĂłwienia ze wszystkich kanaĹĂłw
### Active (In Progress)
- [ ] v3.8 Erli Marketplace Integration — Phase 130 next: generowanie etykiet i obsluga przesylek Erli.
- [ ] v3.8 Erli Marketplace Integration — Phase 131 next: tracking przesylek Erli i hooki automatyzacji.
### Planned (Next)
- [ ] Erli shipments + labels — Phase 130
- [ ] Erli tracking + automation hooks — Phase 131
- [ ] Erli hardening, observability + docs — Phase 132
- [ ] ZarzÄ…dzanie produktami
@@ -251,13 +251,15 @@ PHP (XAMPP/Laravel), integracje z API marketplace'Ăłw (Allegro, Erli) oraz API
| Push statusow Erli obejmuje tylko reczne zmiany orderPRO (`change_source='manual'`) | Chroni przed petlami po imporcie, automatyzacjach i systemowych zmianach statusu | 2026-05-16 | Active |
| Erli -> orderPRO status pull uzywa tego samego inbox + ACK flow co import zamowien | Jedno bezpieczne zrodlo zdarzen Erli; brak osobnego status endpointu do utrzymania | 2026-05-16 | Active |
| Erli settings korzysta z zakladek Integracja/Statusy/Ustawienia | Po dodaniu mapowan strona wymagala parytetu UX z Allegro/shopPRO | 2026-05-16 | Active |
| Erli etykiety uzywaja lokalnych providerow, a Erli dostaje paczke zewnetrzna przez `POST /shipping/external` | Operator nie chce nadawac na umowie Erli; API wspiera zewnetrzne paczki/tracking | 2026-05-16 | Active |
| `carrier_delivery_method_mappings` przechowuje `source_vendor_code`/`source_service_id` dla Erli | Vendor Erli i lokalny provider to osobne kontrakty, nie nalezy ich mieszac w polach Apaczki/InPost | 2026-05-16 | Active |
## Success Metrics
| Metric | Target | Current | Status |
|--------|--------|---------|--------|
| Liczba zintegrowanych źródeŠzamówień | ≥3 | 3 zrodla importu (Allegro, shopPRO, Erli); Erli wymaga manualnego smoke po migracji | In progress |
| Generowanie etykiet | DziaĹa | InPost | In progress |
| Generowanie etykiet | DziaĹa | InPost + Erli przez lokalne providery po mapowaniu; Erli wymaga manualnego smoke po migracji | In progress |
## Tech Stack
@@ -282,6 +284,6 @@ Quick Reference:
---
*PROJECT.md — Updated when requirements or context change*
*Last updated: 2026-05-16 after Phase 129 (Erli Status Mapping + Sync) closure; v3.8 milestone in progress*
*Last updated: 2026-05-16 after Phase 130 (Erli Shipments + Labels) closure; v3.8 milestone in progress*

View File

@@ -10,14 +10,14 @@ v3.8 Erli Marketplace Integration — In progress
Pelna integracja z erli.pl wzorowana na istniejacej integracji Allegro: konfiguracja konta/API, pobieranie zamowien, mapowanie i synchronizacja statusow, generowanie etykiet, tracking oraz wlaczenie Erli w istniejace przeplywy automatyzacji, statystyk i obslugi zamowien.
Progress: 3 of 6 phases complete (50%).
Progress: 4 of 6 phases complete (67%).
| Phase | Name | Plans | Status |
|-------|------|-------|--------|
| 127 | Erli Integration Foundation | 1/1 | Complete (2026-05-15; migration/manual Erli API smoke pending operator) |
| 128 | Erli Orders Import | 1/1 | Complete (2026-05-15; migration/manual Erli import smoke pending operator) |
| 129 | Erli Status Mapping + Sync | 1/1 | Complete (2026-05-16; migration/manual Erli status smoke pending operator) |
| 130 | Erli Shipments + Labels | TBD | Not started |
| 130 | Erli Shipments + Labels | 1/1 | Complete (2026-05-16; migration/manual Erli shipping smoke pending operator) |
| 131 | Erli Tracking + Automation Hooks | TBD | Not started |
| 132 | Erli Hardening, Observability + Docs | TBD | Not started |
@@ -39,7 +39,7 @@ Plans: 129-01 (complete)
### Phase 130: Erli Shipments + Labels
Focus: Generowanie etykiet dla zamowien Erli, mapowanie metod dostawy Erli na dostepne providery, zapis paczek w `shipment_packages`, pobieranie labeli i integracja z kolejka zdalnego druku.
Plans: TBD (defined during $paul-plan)
Plans: 130-01 (complete)
### Phase 131: Erli Tracking + Automation Hooks
@@ -555,4 +555,4 @@ Archive: `.paul/milestones/v0.1-ROADMAP.md`
---
*Roadmap created: 2026-03-12*
*Last updated: 2026-05-16 - Phase 129 complete, ready for Phase 130 planning*
*Last updated: 2026-05-16 - Phase 130 complete, ready for Phase 131 planning*

View File

@@ -5,33 +5,33 @@
See: .paul/PROJECT.md (updated 2026-05-16)
**Core value:** Sprzedawca moze obslugiwac zamowienia ze wszystkich kanalow sprzedazy i nadawac przesylki bez przelaczania sie miedzy platformami.
**Current focus:** v3.8 Erli Marketplace Integration - Phase 130 ready to plan.
**Current focus:** v3.8 Erli Marketplace Integration - Phase 131 ready to plan.
## Current Position
Milestone: v3.8 Erli Marketplace Integration
Phase: 130 of 132 (Erli Shipments + Labels) - Not started
Phase: 131 of 132 (Erli Tracking + Automation Hooks) - Not started
Plan: Not started
Status: Ready to plan
Last activity: 2026-05-16 00:23 - Phase 129 complete, transitioned to Phase 130
Last activity: 2026-05-16 00:51 - Phase 130 complete, transitioned to Phase 131
Progress:
- Milestone v3.8: [#####-----] 50% (Phases 127-129 complete; Phase 130 next)
- Phase 130: [----------] 0% (not planned)
- Milestone v3.8: [#######---] 67% (Phases 127-130 complete; Phase 131 next)
- Phase 131: [----------] 0% (not planned)
## Loop Position
Current loop state:
```
PLAN -> APPLY -> UNIFY
done done done [Loop complete - ready for next PLAN]
done done done [Loop complete, ready for next PLAN]
```
## Session Continuity
Last session: 2026-05-16 00:23
Stopped at: Phase 129 complete, ready to plan Phase 130
Next action: $paul-plan for Phase 130 (Erli Shipments + Labels)
Last session: 2026-05-16 00:51
Stopped at: Phase 130 complete, ready to plan Phase 131
Next action: $paul-plan for Phase 131 (Erli Tracking + Automation Hooks)
Resume file: .paul/ROADMAP.md
## Pending parallel work
@@ -39,8 +39,8 @@ Resume file: .paul/ROADMAP.md
## Git State
Last phase commit: 7972bb9 feat(129): erli status mapping sync
Previous: 2565d9b feat(128): erli orders import
Last phase commit: a73bd7f feat(130): erli shipments and labels
Previous: 7972bb9 feat(129): erli status mapping sync
Branch: main
### Skill Audit (Phase 129)
@@ -49,6 +49,12 @@ Branch: main
|----------|---------|-------|
| `sonar-scanner` | gap documented | Attempted before UNIFY; CLI is not available in PATH. |
### Skill Audit (Phase 130)
| Expected | Invoked | Notes |
|----------|---------|-------|
| `sonar-scanner` | gap documented | Attempted after APPLY; CLI is not available in PATH. |
## Pending Actions
- Manualne testy AC-1..AC-7 dla Phase 112 na zywej bazie (XAMPP online).
@@ -76,6 +82,9 @@ Branch: main
- Phase 128 verification gap: `vendor/bin/phpunit` nie istnieje w checkoutcie, wiec test `tests/Unit/ErliOrderMapperTest.php` nie zostal uruchomiony przez PHPUnit; wykonano `php -l` i runtime smoke mappera.
- Phase 129 follow-up: uruchom `php bin/migrate.php`, sprawdz `/settings/integrations/erli` mapowania pull/push i zakladki, zapisz mapowania, ustaw `orderPRO -> Erli`, zmien recznie status zamowienia Erli i uruchom cron `erli_status_sync`.
- Phase 129 verification gap: `vendor/bin/phpunit` nie istnieje w checkoutcie, a globalny XAMPP PHPUnit jest niekompatybilny z PHP (`each()` removed), wiec testy `ErliOrderMapperTest` i `ErliStatusSyncServiceTest` nie zostaly uruchomione przez PHPUnit; wykonano `php -l`, runtime smoke mappera i `git diff --check`.
- Phase 130 follow-up: uruchom `php bin/migrate.php` (dodaje `carrier_delivery_method_mappings.source_service_id/source_vendor_code`), otworz `/settings/integrations/erli?tab=delivery`, zapisz mapowanie metody Erli na InPost/Apaczka oraz vendor Erli, a potem utworz etykiete dla zamowienia Erli i potwierdz `POST /shipping/external`.
- Phase 130 verification gap: `vendor/bin/phpunit` nie istnieje w checkoutcie, wiec test `tests/Unit/ErliExternalShipmentServiceTest.php` nie zostal uruchomiony przez PHPUnit; wykonano `php -l` i `git diff --check`.
- Phase 130 skill gap: `sonar-scanner` nie jest dostepny w PATH, wiec skan SonarQube nie zostal uruchomiony.
## Deferred to Next Milestones

View File

@@ -7,6 +7,11 @@
- Dodano repozytoria mapowan, `ErliStatusSyncService`, `ErliStatusSyncHandler`, discovery nieznanych statusow Erli i testy jednostkowe dla mappera/status sync.
- Ujednolicono `/settings/integrations/erli` z innymi integracjami przez zakladki Integracja, Statusy i Ustawienia.
- Udokumentowano gapy srodowiskowe: brak `vendor/bin/phpunit`, globalny XAMPP PHPUnit niekompatybilny z PHP, brak `sonar-scanner` w PATH.
- [Phase 130, Plan 01] Wdrozono obsluge przesylek Erli: zakladke mapowan dostaw, lokalne generowanie etykiet przez zmapowanych providerow i rejestracje paczki zewnetrznej w Erli.
- Rozszerzono klienta Erli o slowniki shipping/delivery, vendorow, cenniki oraz `POST /shipping/external`.
- Rozszerzono mapowania dostaw o `source_service_id` i `source_vendor_code`, zeby oddzielic Erli vendor od lokalnego providera etykiety.
- Dodano niekrytyczna synchronizacje tracking number do Erli po utworzeniu lokalnej paczki.
- Udokumentowano gapy srodowiskowe Phase 130: brak `vendor/bin/phpunit`, brak `sonar-scanner` w PATH, smoke testy Erli po migracji do wykonania przez operatora.
## Zmienione pliki
@@ -35,6 +40,15 @@
- `resources/lang/pl.php`
- `tests/Unit/ErliOrderMapperTest.php`
- `tests/Unit/ErliStatusSyncServiceTest.php`
- `.paul/phases/130-erli-shipments-labels/130-01-PLAN.md`
- `.paul/phases/130-erli-shipments-labels/130-01-SUMMARY.md`
- `database/migrations/20260516_000117_extend_delivery_mappings_for_erli_shipping.sql`
- `src/Modules/Settings/CarrierDeliveryMethodMappingRepository.php`
- `src/Modules/Settings/ErliDeliveryMappingController.php`
- `src/Modules/Settings/ErliExternalShipmentService.php`
- `src/Modules/Shipments/ShipmentController.php`
- `resources/views/shipments/prepare.php`
- `tests/Unit/ErliExternalShipmentServiceTest.php`
- `DOCS/DB_SCHEMA.md`
- `DOCS/ARCHITECTURE.md`
- `DOCS/TECH_CHANGELOG.md`

View File

@@ -0,0 +1,255 @@
---
phase: 130-erli-shipments-labels
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
- src/Core/Constants/IntegrationSources.php
- src/Modules/Settings/ErliApiClient.php
- src/Modules/Settings/ErliDeliveryMappingController.php
- src/Modules/Settings/ErliIntegrationController.php
- src/Modules/Settings/CarrierDeliveryMethodMappingRepository.php
- src/Modules/Settings/ErliExternalShipmentService.php
- src/Modules/Shipments/ShipmentController.php
- routes/web.php
- resources/views/settings/erli.php
- resources/views/shipments/prepare.php
- resources/lang/pl.php
- tests/Unit/ErliExternalShipmentServiceTest.php
- DOCS/DB_SCHEMA.md
- DOCS/ARCHITECTURE.md
- DOCS/TECH_CHANGELOG.md
autonomous: true
delegation: auto
---
<objective>
## Goal
Dodac obsluge przesylek dla zamowien Erli: mapowanie metod dostawy w osobnej zakladce ustawien, uzycie istniejacych providerow etykiet (InPost/Apaczka tam, gdzie sa dostepne), zapis paczek w `shipment_packages` oraz rejestracje numeru nadania w Erli przez natywny endpoint przesylek zewnetrznych.
## Purpose
Sprzedawca ma nadawac zamowienia Erli z orderPRO bez przelaczania sie do panelu marketplace, ale bez wymuszania nadawania "na umowie Erli". Natywne Erli w tym planie oznacza wykorzystanie oficjalnych slownikow/cennikow/shipping API tam, gdzie pasuja do wlasnych umow operatora.
## Output
Nowa zakladka dostaw w `/settings/integrations/erli`, rozszerzony Erli API client, flow tworzenia etykiety dla zmapowanych zamowien Erli oraz synchronizacja paczki zewnetrznej do Erli po uzyskaniu numeru przesylki.
</objective>
<context>
<clarifications>
- **Natywne Erli** - Czy probujemy najpierw API shipping Erli, czy od razu tylko istniejacy provider etykiet?
-> Odpowiedz: Trzeba sprobowac natywnego z erli.
- **Konfiguracja** - Gdzie operator ma mapowac metody dostawy Erli?
-> Odpowiedz: zakladka.
- **Umowa/cenniki** - Czy nadawac przez Erli na ich umowie, czy tylko to, co da sie spiac z wlasnymi providerami/cennikami?
-> Odpowiedz: Nie chce nadawac przez Erli na ich umowie, to co sie da. Tak jak allegro ma swoje cenniki.
</clarifications>
## Project Context
@.paul/PROJECT.md
@.paul/ROADMAP.md
@.paul/STATE.md
@.paul/codebase/architecture.md
@.paul/codebase/db_schema.md
@AGENTS.md
## Prior Work
@.paul/phases/127-erli-integration-foundation/127-01-SUMMARY.md
@.paul/phases/128-erli-orders-import/128-01-SUMMARY.md
@.paul/phases/129-erli-status-mapping-sync/129-01-SUMMARY.md
## External Contract
@https://erli.pl/svc/shop-api/doc/swagger.json
Notes from official swagger checked during planning:
- `GET /dictionaries/shippingMethods`, `GET /dictionaries/deliveryMethods`, `GET /dictionaries/deliveryVendors`, `GET /delivery/priceLists`, `GET /delivery/priceListsDetails` expose Erli shipping/delivery dictionaries and cenniki.
- `POST /shipping/external` creates external parcels with `orderId`, `vendor`, `status`, `trackingNumber`.
- `POST /shipping/parcels/` creates Erli parcels, but this path is treated as optional/discovery only because the user does not want to ship on Erli's carrier agreement.
- No label-download endpoint was found in the swagger path list; local label generation must continue through existing provider services unless APPLY finds a documented endpoint with compatible contract.
## Source Files
@src/Modules/Settings/ErliApiClient.php
@src/Modules/Settings/ErliIntegrationController.php
@src/Modules/Settings/CarrierDeliveryMethodMappingRepository.php
@src/Modules/Settings/AllegroDeliveryMappingController.php
@src/Modules/Shipments/ShipmentController.php
@src/Modules/Shipments/ShipmentProviderInterface.php
@src/Modules/Shipments/InpostShipmentService.php
@src/Modules/Shipments/ApaczkaShipmentService.php
@resources/views/settings/erli.php
@resources/views/shipments/prepare.php
@routes/web.php
@DOCS/DB_SCHEMA.md
@DOCS/ARCHITECTURE.md
</context>
<skills>
## Required Skills (from SPECIAL-FLOWS.md)
| Skill | Priority | When to Invoke | Loaded? |
|-------|----------|----------------|---------|
| `sonar-scanner` | required | Po APPLY, przed UNIFY | ○ |
| /feature-dev | optional | Przed implementacja integracji marketplace/shipping | ○ |
| /frontend-design | optional | Przy dodaniu zakladki UI w ustawieniach Erli | ○ |
| /code-review | optional | Po implementacji, przed UNIFY | ○ |
**BLOCKING:** Required `sonar-scanner` must be attempted before UNIFY. If CLI is still unavailable in PATH, document the gap in SUMMARY and STATE like Phase 128/129.
## Skill Invocation Checklist
- [ ] `sonar-scanner` attempted after APPLY
- [ ] Optional flows considered if implementation risk warrants them
</skills>
<acceptance_criteria>
## AC-1: Erli Shipping Contract Is Used Where Safe
```gherkin
Given Erli API credentials are configured
When orderPRO loads shipment configuration for Erli
Then it can fetch Erli shipping/delivery dictionaries, delivery vendors and price list data from the official Erli API
And it does not assume native Erli label download exists unless a documented endpoint is confirmed during implementation.
```
## AC-2: Erli Delivery Mapping Tab Exists
```gherkin
Given the operator opens /settings/integrations/erli
When they select the delivery/shipping tab
Then they see distinct Erli delivery methods from imported orders plus Erli dictionary context
And they can map each method to an available local label provider/service and Erli vendor with a CSRF-protected save form.
```
## AC-3: Erli Orders Preselect Shipment Provider
```gherkin
Given an Erli order has a saved delivery mapping
When the operator opens the shipment prepare page for that order
Then orderPRO preselects the mapped provider/service and shows a clear unmapped diagnostic if no mapping exists.
```
## AC-4: Labels Are Generated Without Erli Carrier Agreement
```gherkin
Given an Erli order is mapped to an existing local provider such as InPost or Apaczka
When the operator creates a shipment
Then the existing provider creates the local shipment package, stores it in shipment_packages and makes the label available for download/print queue
And the flow does not create Erli-contract parcels unless explicitly supported and selected in future work.
```
## AC-5: External Parcel Is Registered In Erli
```gherkin
Given a local Erli shipment package has a tracking number and a mapped Erli vendor
When the package creation/status check reaches a label-ready or sent state
Then orderPRO calls POST /shipping/external with order id, vendor, status and tracking number
And API failures are logged as non-critical local shipment warnings rather than losing the label.
```
## AC-6: Documentation And Verification Cover The Flow
```gherkin
Given the implementation is complete
When verification runs
Then PHP syntax checks, focused tests or documented PHPUnit gap, diff checks and documentation updates cover the new Erli shipment behavior.
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Extend Erli API and mapping repository for shipping</name>
<files>src/Core/Constants/IntegrationSources.php, src/Modules/Settings/ErliApiClient.php, src/Modules/Settings/CarrierDeliveryMethodMappingRepository.php, DOCS/DB_SCHEMA.md, DOCS/ARCHITECTURE.md</files>
<action>
Add `erli` as an explicit integration source constant/path wherever current generic delivery mappings normalize only `allegro` and `shoppro`.
Extend `CarrierDeliveryMethodMappingRepository` so `listMappings`, `findByOrderMethod`, `saveMappings`, `hasMappingsForSource` and `getDistinctOrderDeliveryMethods` work for `source_system='erli'` and global `source_integration_id=0`.
Extend `ErliApiClient` with small focused methods for shipping dictionaries and external parcels:
- `getShippingMethods()`
- `getDeliveryMethods()`
- `getDeliveryVendors()`
- `getPriceLists()` / `getPriceListsDetails()` if the response is needed for operator context
- `createExternalParcel(array $payload)`
Keep request/response handling consistent with existing Erli client methods and keep runtime on `DB_HOST`, never `DB_HOST_REMOTE`.
Do not add a new table unless APPLY proves existing `carrier_delivery_method_mappings` cannot represent the mapping. If a schema change becomes unavoidable, add a migration and update DB docs in the same task.
</action>
<verify>`C:\xampp\php\php.exe -l src/Modules/Settings/ErliApiClient.php` and `C:\xampp\php\php.exe -l src/Modules/Settings/CarrierDeliveryMethodMappingRepository.php`; repository can list/save/find Erli mappings without falling back to Allegro.</verify>
<done>AC-1 foundation complete and AC-2 persistence ready.</done>
</task>
<task type="auto">
<name>Task 2: Add Erli delivery mapping tab</name>
<files>src/Modules/Settings/ErliDeliveryMappingController.php, src/Modules/Settings/ErliIntegrationController.php, routes/web.php, resources/views/settings/erli.php, resources/lang/pl.php, DOCS/ARCHITECTURE.md</files>
<action>
Create or wire a focused Erli delivery mapping controller, following the clarity of `AllegroDeliveryMappingController` but without Allegro OAuth assumptions.
Load:
- distinct Erli delivery methods from imported orders,
- current `carrier_delivery_method_mappings` rows for `erli`,
- available local label provider services already supported by the shipment flow,
- Erli vendors/dictionaries for choosing the vendor sent to `/shipping/external`.
Add a new Erli settings tab (for example `delivery`) next to existing integration/status/settings tabs.
Saving must use POST + `_token`, `Flash`, bounded validation and redirect back to `/settings/integrations/erli?tab=delivery`.
Escape all view output with `$e()`, avoid inline CSS in the view, and do not add native `alert()`/`confirm()`.
</action>
<verify>`C:\xampp\php\php.exe -l src/Modules/Settings/ErliDeliveryMappingController.php`, `C:\xampp\php\php.exe -l resources/views/settings/erli.php`, and manual route smoke: opening `/settings/integrations/erli?tab=delivery` renders the tab even when Erli API metadata fetch fails.</verify>
<done>AC-2 satisfied; UI is consistent with Phase 129 tab pattern.</done>
</task>
<task type="auto">
<name>Task 3: Use mappings during shipment creation and sync external parcel to Erli</name>
<files>src/Modules/Shipments/ShipmentController.php, src/Modules/Settings/ErliExternalShipmentService.php, resources/views/shipments/prepare.php, routes/web.php, tests/Unit/ErliExternalShipmentServiceTest.php, DOCS/ARCHITECTURE.md, DOCS/TECH_CHANGELOG.md</files>
<action>
Update shipment prepare flow so Erli orders are eligible for `CarrierDeliveryMethodMappingRepository::findByOrderMethod('erli', 0, ...)` just like Allegro/shopPRO.
Ensure the mapped provider/service can preselect existing local provider forms. Preserve existing Allegro/shopPRO behavior.
Add an `ErliExternalShipmentService` that reads the local package/order context and calls `ErliApiClient::createExternalParcel()` with:
- Erli external order id,
- mapped Erli vendor,
- tracking number from `shipment_packages`,
- status matching Erli's accepted external parcel status contract.
Trigger this service after a local provider has produced a tracking number/label-ready package (for example from `checkStatus()` and/or label-ready creation path), and make the call idempotent enough for retries by treating duplicate/already-existing responses as non-fatal where Erli exposes that signal.
Log or flash bounded warnings for Erli sync failure but never discard the local label.
Add focused unit coverage for payload building and failure behavior. If PHPUnit remains unavailable, leave the test file and document the run gap.
</action>
<verify>`C:\xampp\php\php.exe -l src/Modules/Shipments/ShipmentController.php`; `C:\xampp\php\php.exe -l src/Modules/Settings/ErliExternalShipmentService.php`; if dependencies exist, run `vendor/bin/phpunit tests/Unit/ErliExternalShipmentServiceTest.php`; manual smoke on a mapped Erli order reaches local `shipment_packages` and attempts `/shipping/external` only after tracking exists.</verify>
<done>AC-3, AC-4, AC-5 and AC-6 satisfied.</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- Do not change unrelated `.vscode/ftp-kr.sync.cache.json`.
- Do not regress Allegro, shopPRO, InPost or Apaczka shipment behavior.
- Do not connect runtime application code to `DB_HOST_REMOTE`.
- Do not add inline CSS to `resources/views/...`.
- Do not add native `alert()` / `confirm()` in views.
## SCOPE LIMITS
- This plan does not implement Phase 131 carrier tracking polling, automation expansion or delivery-status cron beyond the immediate external parcel registration.
- This plan does not force creating shipments through Erli's own carrier agreement. `POST /shipping/parcels/` may be inspected during APPLY, but shipping via Erli-contract parcels is out of scope unless it is clearly just external/seller-contract compatible.
- This plan does not build product/stock sync or Erli offer management.
- This plan keeps Erli as one global integration unless a future phase explicitly introduces multi-account support.
- This plan should avoid new schema if existing generic mapping storage is enough.
</boundaries>
<verification>
Before declaring plan complete:
- [ ] `C:\xampp\php\php.exe -l` for every changed PHP/view/test file.
- [ ] `git diff --check`.
- [ ] `vendor/bin/phpunit tests/Unit/ErliExternalShipmentServiceTest.php` if PHPUnit dependencies are available; otherwise document the environment gap.
- [ ] Attempt `sonar-scanner` after APPLY; if unavailable, document the gap in SUMMARY and STATE.
- [ ] Manual smoke: `/settings/integrations/erli?tab=delivery` renders, saves mapping with `_token`, and survives Erli API metadata errors.
- [ ] Manual smoke: a mapped Erli order preselects shipment provider/service on prepare page.
- [ ] Manual smoke: local provider label creation still stores `shipment_packages` and the Erli external parcel sync is attempted only with a tracking number.
- [ ] `DOCS/DB_SCHEMA.md`, `DOCS/ARCHITECTURE.md`, `DOCS/TECH_CHANGELOG.md` updated for changed behavior and any schema decision.
- [ ] All acceptance criteria met.
</verification>
<success_criteria>
- Erli settings has a working delivery/shipping mapping tab.
- Erli delivery mappings are stored and used by shipment prepare.
- Erli orders can generate local labels through existing providers when mapped.
- Erli receives external parcel/tracking data through native shipping API when tracking exists.
- Native Erli-contract parcel creation is either explicitly not used or documented as future/manual decision.
- Tests/lints/docs and required skill audit are completed or gaps documented.
</success_criteria>
<output>
After completion, create `.paul/phases/130-erli-shipments-labels/130-01-SUMMARY.md`.
</output>

View File

@@ -0,0 +1,165 @@
---
phase: 130-erli-shipments-labels
plan: 01
subsystem: settings, integrations, shipments, database
tags: [erli, shipping, labels, delivery-mapping, external-parcels]
requires:
- phase: 127-erli-integration-foundation
provides: global Erli credentials and API client
- phase: 128-erli-orders-import
provides: Erli orders in common order model
- phase: 129-erli-status-mapping-sync
provides: tabbed Erli settings UI and outbound API pattern
provides:
- Erli delivery mapping tab
- Erli shipping dictionary and external parcel API methods
- Local label provider preselection for Erli orders
- External parcel registration in Erli after tracking number exists
affects: [phase-131-erli-tracking-automation, erli-settings, shipment-flow]
tech-stack:
added: []
patterns: [marketplace delivery mapping with source vendor, non-critical external parcel sync]
key-files:
created:
- database/migrations/20260516_000117_extend_delivery_mappings_for_erli_shipping.sql
- src/Modules/Settings/ErliDeliveryMappingController.php
- src/Modules/Settings/ErliExternalShipmentService.php
- tests/Unit/ErliExternalShipmentServiceTest.php
modified:
- src/Modules/Settings/ErliApiClient.php
- src/Modules/Settings/CarrierDeliveryMethodMappingRepository.php
- src/Modules/Settings/ErliIntegrationController.php
- src/Modules/Shipments/ShipmentController.php
- routes/web.php
- resources/views/settings/erli.php
- resources/views/shipments/prepare.php
- resources/lang/pl.php
- DOCS/DB_SCHEMA.md
- DOCS/ARCHITECTURE.md
- DOCS/TECH_CHANGELOG.md
key-decisions:
- "Erli labels stay on local providers; Erli receives external parcel/tracking through POST /shipping/external."
- "Erli vendor code is stored separately from local provider service in carrier_delivery_method_mappings.source_vendor_code."
patterns-established:
- "External marketplace shipment sync is non-critical and must not block local labels."
duration: ~20min
started: 2026-05-16T00:37:00+02:00
completed: 2026-05-16T00:51:00+02:00
---
# Phase 130-01 Summary: Erli Shipments + Labels
Phase 130 adds Erli delivery mappings, local label provider preselection and external parcel registration through the native Erli shipping API without using Erli carrier-contract label flow.
## Performance
| Metric | Result |
|--------|--------|
| Duration | ~20min |
| Started | 2026-05-16T00:37:00+02:00 |
| Completed | 2026-05-16T00:51:00+02:00 |
| Tasks | 3/3 completed |
| Files changed | 18 phase files, excluding unrelated `.vscode/ftp-kr.sync.cache.json` |
## Acceptance Criteria
| AC | Result | Notes |
|----|--------|-------|
| AC-1: Erli Shipping Contract Is Used Where Safe | Pass | `ErliApiClient` now supports shipping/delivery dictionaries, vendors, price lists and `POST /shipping/external`; no native label download endpoint is assumed. |
| AC-2: Erli Delivery Mapping Tab Exists | Pass | `/settings/integrations/erli?tab=delivery` has a CSRF-protected mapping tab with imported delivery methods, Erli vendor context and local provider service choices. |
| AC-3: Erli Orders Preselect Shipment Provider | Pass | Shipment prepare includes Erli in delivery mapping lookup and preselects mapped local provider/service. |
| AC-4: Labels Are Generated Without Erli Carrier Agreement | Pass | Labels remain provider-driven through local InPost/Apaczka/Allegro WZA flow and keep writing `shipment_packages`. |
| AC-5: External Parcel Is Registered In Erli | Pass with live smoke pending | `ErliExternalShipmentService` registers external parcels only after tracking exists and logs non-critical errors. |
| AC-6: Documentation And Verification Cover The Flow | Pass with env gaps | PHP lint and diff checks passed; PHPUnit and Sonar are unavailable in this environment. |
## Accomplishments
- Extended generic carrier delivery mappings with Erli-specific source service/vendor metadata.
- Added an Erli delivery tab that maps Erli delivery methods to local label providers and Erli vendor codes.
- Added native Erli shipping dictionary and external parcel client methods.
- Wired Erli shipment preparation into the existing local label flow.
- Added non-blocking external parcel sync to Erli after local tracking numbers become available.
- Updated database, architecture and technical changelog documentation.
## Task Results
| Task | Result | Commit |
|------|--------|--------|
| Task 1: Extend Erli API and mapping repository for shipping | Done | Phase commit |
| Task 2: Add Erli delivery mapping tab | Done | Phase commit |
| Task 3: Use mappings during shipment creation and sync external parcel to Erli | Done | Phase commit |
## Files Created
| File | Purpose |
|------|---------|
| `database/migrations/20260516_000117_extend_delivery_mappings_for_erli_shipping.sql` | Adds Erli source service/vendor metadata to delivery mappings. |
| `src/Modules/Settings/ErliDeliveryMappingController.php` | Loads and saves Erli delivery mapping tab data. |
| `src/Modules/Settings/ErliExternalShipmentService.php` | Registers local tracking numbers as Erli external parcels. |
| `tests/Unit/ErliExternalShipmentServiceTest.php` | Focused unit coverage for Erli external parcel sync behavior. |
## Files Modified
| File | Purpose |
|------|---------|
| `src/Modules/Settings/ErliApiClient.php` | Added shipping dictionaries, price lists and external parcel API calls. |
| `src/Modules/Settings/CarrierDeliveryMethodMappingRepository.php` | Added Erli source support and source vendor/service persistence. |
| `src/Modules/Settings/ErliIntegrationController.php` | Added delivery tab data and tab routing. |
| `src/Modules/Shipments/ShipmentController.php` | Uses Erli delivery mappings and triggers external parcel sync after tracking exists. |
| `routes/web.php` | Wires Erli delivery save route and external shipment service dependencies. |
| `resources/views/settings/erli.php` | Adds Erli delivery tab UI. |
| `resources/views/shipments/prepare.php` | Preselects mapped local provider/service for Erli shipments. |
| `resources/lang/pl.php` | Adds Polish labels for Erli delivery mapping UI. |
| `DOCS/DB_SCHEMA.md` | Documents mapping columns. |
| `DOCS/ARCHITECTURE.md` | Documents Erli shipment flow. |
| `DOCS/TECH_CHANGELOG.md` | Records Phase 130 technical changes. |
## Decisions Made
| Decision | Rationale |
|----------|-----------|
| Use local label providers for Erli labels | Operator does not want to ship on Erli's carrier agreement; existing provider labels are the source of truth. |
| Register Erli parcels through `POST /shipping/external` | Official Erli API supports external parcels with order id, vendor, status and tracking number. |
| Store Erli vendor separately from local provider service | Erli vendor and local provider/service are different contracts and should not overload the same field. |
| Make Erli external parcel sync non-critical | Local label generation must survive Erli API failures. |
## Deviations
| Type | Description | Impact |
|------|-------------|--------|
| Scope addition | Added a small migration because the existing mapping schema could not cleanly store Erli vendor separately from the local provider service. | Low risk, improves contract clarity. |
| Implementation detail | Native InPost service data is preferred for Erli/InPost mappings where available, with existing Allegro WZA filtered service list as fallback. | Keeps Erli local-provider flow independent from Allegro where possible. |
| Verification gap | PHPUnit binary is missing and `sonar-scanner` is not available in PATH. | Tests exist but could not be executed locally. |
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| Erli `deliveryVendors` dictionary can return scalar rows | Normalization now preserves scalar values as both id and name. |
| MySQL `CREATE INDEX IF NOT EXISTS` support is environment-sensitive | Migration avoids that syntax and only adds required columns. |
| External parcel duplicate behavior could not be live-tested | Service stores successful sync payload and treats sync failures as non-critical activity entries. |
## Verification
| Check | Result |
|-------|--------|
| PHP syntax lint for changed PHP/view/test files | Passed |
| `git diff --check -- . ':!.vscode/ftp-kr.sync.cache.json'` | Passed |
| `vendor/bin/phpunit tests/Unit/ErliExternalShipmentServiceTest.php` | Not run: `vendor/bin/phpunit` missing |
| `sonar-scanner --version` | Not run: command unavailable in PATH |
| Manual Erli delivery tab smoke | Pending operator after migration/live configuration |
| Manual Erli label + external parcel smoke | Pending operator after migration/live configuration |
## Next Phase Readiness
Phase 131 can build on:
- Erli delivery mappings with local provider and Erli vendor metadata.
- Local shipment packages with tracking numbers.
- Non-critical Erli external parcel sync payloads stored in `shipment_packages.payload_json`.
Remaining concerns for Phase 131:
- Live Erli credentials, migration execution and browser smoke are still pending operator environment.
- Delivery tracking automation should decide how to poll/update delivery status after the external parcel exists.
- Duplicate external parcel semantics should be confirmed against live Erli responses.
No blocker prevents planning Phase 131.