feat(130): erli shipments and labels
This commit is contained in:
@@ -84,6 +84,8 @@ HTTP Request
|
||||
5. **Render** — `resources/views/statistics/summary.php` renders filters, chart JSON, two canvas targets, and table fallbacks.
|
||||
|
||||
### Shipment Flow
|
||||
|
||||
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. 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 and do not block local labels.
|
||||
1. **Create** — `ShipmentController::create()` → `ShipmentProviderRegistry` → carrier `ShipmentService::createShipment()` → `ShipmentPackageRepository::insert()`
|
||||
2. **Track** — Cron `ShipmentTrackingHandler` → `ShipmentTrackingRegistry` → carrier tracking API → `ShipmentPackageRepository::updateDeliveryStatus()`
|
||||
|
||||
@@ -121,12 +123,14 @@ HTTP Request
|
||||
|
||||
### Erli Integration Foundation
|
||||
|
||||
1. **Settings** - `/settings/integrations/erli` renders tabbed integration/status/settings panels and stores one global Erli API key encrypted via `IntegrationSecretCipher`, an optional account label, active flag, and last connection-test result.
|
||||
1. **Settings** - `/settings/integrations/erli` renders tabbed integration/status/delivery/settings panels and stores one global Erli API key encrypted via `IntegrationSecretCipher`, an optional account label, active flag, and last connection-test result.
|
||||
2. **Connection test** - `ErliIntegrationController::test()` loads active credentials, calls `ErliApiClient::testConnection()`, performs a real authenticated `GET https://erli.pl/svc/shop-api/inbox`, and stores the result in `integrations.last_test_*`.
|
||||
3. **Hub** - `IntegrationsHubController::buildErliRow()` adds Erli to `/settings/integrations` with configured/missing secret status, active status, last test timestamp, and configure URL.
|
||||
4. **Order import** - Phase 128 adds `/settings/integrations/erli/import` and cron `erli_orders_import`. Both call `ErliOrdersSyncService`, which fetches unread `/inbox` messages, maps supported order events through `ErliOrderMapper`, persists via `OrderImportRepository::upsertOrderAggregate()`, emits existing automation events, and acknowledges `POST /inbox/mark-read` only after a zero-failure batch.
|
||||
5. **Status mapping/sync** - Phase 129 adds pull/push status mapping tables, status controls in Erli settings, and cron `erli_status_sync`. Pull reuses inbox import; push sends manual orderPRO status changes to `PATCH /orders/{id}/status`.
|
||||
6. **Deferred** - Label generation, shipment creation, and tracking are planned for v3.8 Phases 130-131.
|
||||
6. **Delivery mapping and labels** - Phase 130 adds `ErliDeliveryMappingController` and a Delivery tab. It maps imported Erli delivery method labels to local shipment providers (`inpost`/`apaczka`) and stores Erli `source_vendor_code` for external parcel registration. Label files are still produced by local providers, not by Erli carrier-contract parcels.
|
||||
7. **External shipment sync** - Phase 130 extends `ErliApiClient` with shipping dictionary calls and `createExternalParcel()` (`POST /shipping/external`). `ErliExternalShipmentService` registers a local package in Erli only after `shipment_packages.tracking_number` exists; failures are activity-log warnings and do not block local labels.
|
||||
8. **Deferred** - Carrier tracking automation and broader delivery-status hooks are planned for v3.8 Phase 131.
|
||||
|
||||
## Dependency Injection
|
||||
|
||||
@@ -187,15 +191,25 @@ tests/
|
||||
- `testConnection()` wykonuje realny `GET /inbox` do Erli z naglowkiem `Authorization: Bearer ...`.
|
||||
- Phase 128: `fetchInbox()` pobiera do 500 nieprzeczytanych wiadomosci; `markInboxRead()` potwierdza `POST /inbox/mark-read` z `lastMessageId` dopiero po udanym batchu.
|
||||
- Phase 129: `updateOrderStatus()` wysyla `PATCH /orders/{id}/status` z body `{"status": "..."}` dla recznych zmian statusu orderPRO mapowanych na status Erli.
|
||||
- Phase 130: `getShippingMethods()`, `getDeliveryMethods()`, `getDeliveryVendors()`, `getPriceLists()`, `getPriceListsDetails()` i `createExternalParcel()` obsluguja natywne shipping API Erli bez wymuszania nadawania przez umowe Erli.
|
||||
- Wysyla `Accept: application/json` i `User-Agent: orderPRO/1.0 (erli-integration)`.
|
||||
- Traktuje HTTP 2xx jako sukces; 401/403 jako blad autoryzacji, 429 jako limit zapytan, pozostale bledy jako czytelny komunikat z odpowiedzi.
|
||||
- Uzywa `SslCertificateResolver` i nie wywoluje `curl_close()` (PHP 8.5 compatible).
|
||||
|
||||
### ErliIntegrationController (`src/Modules/Settings/ErliIntegrationController.php`)
|
||||
- Endpointy: `GET /settings/integrations/erli`, `POST /settings/integrations/erli/save`, `POST /settings/integrations/erli/test`, `POST /settings/integrations/erli/import`, `POST /settings/integrations/erli/statuses/save-pull`, `POST /settings/integrations/erli/statuses/save-push`.
|
||||
- Endpointy: `GET /settings/integrations/erli`, `POST /settings/integrations/erli/save`, `POST /settings/integrations/erli/test`, `POST /settings/integrations/erli/import`, `POST /settings/integrations/erli/statuses/save-pull`, `POST /settings/integrations/erli/statuses/save-push`, `POST /settings/integrations/erli/delivery/save`.
|
||||
- `save` zapisuje label, aktywnosc, sekret, ustawienia importu (`orders_fetch_enabled`, `orders_fetch_start_date`, interwal crona) oraz kierunek/interwal `erli_status_sync`; `test` wykonuje realny test API i zapisuje wynik przez `IntegrationsRepository::updateTestResult()`.
|
||||
- `importNow()` uruchamia reczny import Erli z pominieciem flagi cron enable, ale nadal wymaga aktywnych credentials.
|
||||
|
||||
### ErliDeliveryMappingController (`src/Modules/Settings/ErliDeliveryMappingController.php`)
|
||||
- Buduje dane zakladki Dostawy: metody z `orders.external_carrier_id` dla `source='erli'`, aktualne `carrier_delivery_method_mappings`, slowniki vendorow/metod z Erli oraz lokalne uslugi InPost/Apaczka.
|
||||
- `saveDeliveryMappings()` zapisuje mapowanie globalne `source_system='erli'`, `source_integration_id=0` z lokalnym providerem oraz `source_vendor_code` wymaganym przez Erli `POST /shipping/external`.
|
||||
|
||||
### ErliExternalShipmentService (`src/Modules/Settings/ErliExternalShipmentService.php`)
|
||||
- `syncPackage(int $packageId)` sprawdza, czy paczka nalezy do zamowienia Erli i ma tracking number.
|
||||
- Pobiera vendor Erli z mapowania dostawy albo proboje go wywnioskowac z lokalnego providera/carrier id.
|
||||
- Wysyla `POST /shipping/external` z `orderId`, `vendor`, `status='sent'`, `trackingNumber`; sukces zapisuje w `shipment_packages.payload_json.erli_external_parcel`, blad trafia do activity log jako niekrytyczny.
|
||||
|
||||
### ErliOrdersSyncService / ErliOrderMapper (`src/Modules/Settings/`)
|
||||
- `ErliOrdersSyncService::sync()` jest wspolnym entrypointem dla crona i importu recznego. Zwraca liczniki `processed`, `imported_created`, `imported_updated`, `failed`, `skipped`, `acknowledged`.
|
||||
- Obsluguje tylko zdarzenia order inbox (`orderCreated`, `orderStatusChanged`, `orderSellerStatusChanged`); wiadomosci produktowe sa pomijane do przyszlych faz.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Database Schema
|
||||
|
||||
**Updated:** 2026-05-15 | **Total tables:** 63 | **Engine:** InnoDB | **Charset:** utf8mb4_unicode_ci
|
||||
**Updated:** 2026-05-16 | **Total tables:** 63 | **Engine:** InnoDB | **Charset:** utf8mb4_unicode_ci
|
||||
|
||||
---
|
||||
|
||||
@@ -385,6 +385,26 @@ UNIQUE: `(integration_id, shoppro_status_code)`
|
||||
|
||||
## Shipments & Delivery
|
||||
|
||||
**carrier_delivery_method_mappings** — Map marketplace delivery methods to local shipment providers
|
||||
| Column | Type | Nullable | Notes |
|
||||
|--------|------|----------|-------|
|
||||
| `id` | INT UNSIGNED | NO | PK |
|
||||
| `source_system` | VARCHAR(32) | NO | `allegro`, `shoppro`, `erli` |
|
||||
| `source_integration_id` | INT UNSIGNED | NO | `0` for global Allegro/Erli, shopPRO integration id for shopPRO |
|
||||
| `order_delivery_method` | VARCHAR(200) | NO | Delivery method label/code from imported order |
|
||||
| `source_service_id` | VARCHAR(128) | YES | Source delivery/shipping method id when available; used by Erli shipping dictionaries |
|
||||
| `source_vendor_code` | VARCHAR(64) | YES | Source carrier/vendor code; Erli uses this for `POST /shipping/external.vendor` |
|
||||
| `provider` | VARCHAR(50) | NO | Local shipment provider, e.g. `allegro_wza`, `inpost`, `apaczka` |
|
||||
| `provider_service_id` | VARCHAR(128) | NO | Local provider service id |
|
||||
| `provider_account_id` | VARCHAR(128) | YES | Provider account/credentials id when required |
|
||||
| `provider_carrier_id` | VARCHAR(128) | YES | Provider carrier code/id when required |
|
||||
| `provider_service_name` | VARCHAR(255) | YES | Display snapshot of provider service name |
|
||||
| `created_at` | DATETIME | NO | |
|
||||
| `updated_at` | DATETIME | NO | |
|
||||
|
||||
UNIQUE: `(source_system, source_integration_id, order_delivery_method)`.
|
||||
Phase 130 adds `source_service_id` and `source_vendor_code`; no new table is required for Erli delivery mappings.
|
||||
|
||||
**shipment_packages** — Prepared shipments with tracking
|
||||
| Column | Type | Nullable | Notes |
|
||||
|--------|------|----------|-------|
|
||||
@@ -414,7 +434,7 @@ UNIQUE: `(integration_id, shoppro_status_code)`
|
||||
| `sender_point_id` | VARCHAR(64) | YES | |
|
||||
| `reference_number` | VARCHAR(128) | YES | |
|
||||
| `error_message` | TEXT | YES | |
|
||||
| `payload_json` | JSON | YES | |
|
||||
| `payload_json` | JSON | YES | Provider request/response snapshot; Phase 130 may add `erli_external_parcel` after successful Erli external shipment registration |
|
||||
| `created_at` | DATETIME | NO | |
|
||||
| `updated_at` | DATETIME | NO | |
|
||||
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
# Technical Changelog
|
||||
|
||||
## 2026-05-16 - Phase 130 Plan 01: Erli Shipments + Labels
|
||||
|
||||
**Co zrobiono:**
|
||||
- Rozszerzono `carrier_delivery_method_mappings` o `source_service_id` i `source_vendor_code`, zeby mapowanie Erli przechowywalo osobno metode marketplace i vendora wymaganego przez `POST /shipping/external`.
|
||||
- Dodano zakladke Dostawy w `/settings/integrations/erli` oraz `ErliDeliveryMappingController` do mapowania metod z zamowien Erli na lokalne uslugi InPost/Apaczka.
|
||||
- Rozszerzono `ErliApiClient` o slowniki shipping/delivery/vendor/cenniki i tworzenie paczek zewnetrznych przez `POST /shipping/external`.
|
||||
- `ShipmentController` uzywa mapowan Erli przy przygotowaniu przesylki i po uzyskaniu tracking number wywoluje `ErliExternalShipmentService`.
|
||||
- `ErliExternalShipmentService` rejestruje paczke zewnetrzna w Erli, zapisuje odpowiedz w `shipment_packages.payload_json.erli_external_parcel` i loguje bledy jako niekrytyczne.
|
||||
|
||||
**Dlaczego:**
|
||||
- Operator chce nadawac zamowienia Erli z orderPRO, ale bez wymuszania nadawania na umowie Erli. Etykiety pozostaja u lokalnych providerow, a Erli dostaje informację o zewnetrznej paczce/tracking number.
|
||||
|
||||
**BREAKING / migracja:**
|
||||
- Wymagana migracja `20260516_000117_extend_delivery_mappings_for_erli_shipping.sql`.
|
||||
- Brak breaking changes w istniejacych mapowaniach Allegro/shopPRO; nowe kolumny sa opcjonalne.
|
||||
|
||||
## 2026-05-16 - Erli Settings Tabs UI Fix
|
||||
|
||||
**Co zrobiono:**
|
||||
|
||||
Reference in New Issue
Block a user