feat(129): erli status mapping sync

Phase 129 complete:
- Add Erli pull/push status mapping tables, seeds and repositories
- Wire Erli status sync cron for inbox pull and manual-only push
- Add tabbed Erli settings UI, tests and documentation

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-05-16 00:27:08 +02:00
parent c127ebf04d
commit 7972bb9fa4
28 changed files with 2021 additions and 57 deletions

View File

@@ -69,7 +69,7 @@ HTTP Request
1. **Import** — Cron handler → API client → `OrderImportService``OrdersRepository::insertOrder()``AutomationService::executeForNewOrder()`
2. **Re-import (Phase 111 + 112 + 119)**`OrderImportRepository::upsertOrderAggregate` wykrywa tranzycje `payment_status` z 0/1 na 2 i zwraca `payment_transition=true`. `AllegroOrderImportService` i `ShopproOrdersSyncService` na tej fladze emituja `payment.status_changed`, co przez chain reguly automatyzacji #7 zmienia `status_code` na `w_realizacji`. Logika preservacji `status_code` z Phase 62 pozostaje rozdzielona (`statusOverwriteAllowed` = `currentStatus='nieoplacone' && newPaymentStatus===2`). **Phase 112-01 (delta-only re-import):** przy `created=false` repo nie wywoluje `replaceAddresses/replaceItems/replaceNotes/replaceShipments/replaceStatusHistory``order_items.id` i flagi lokalne (np. `project_generated` z Phase 97) pozostaja stabilne. `updateOrderDelta()` aktualizuje wylacznie `status_code` (warunkowo, z propagacja anulowania), `payment_status`, `total_paid`, `is_canceled_by_buyer`, `source_updated_at`, `payload_json`, `fetched_at`, `updated_at`. Anulowanie ze zrodla (`is_canceled_by_buyer=1` lub zmapowany pull `status_code='anulowane'`) nadpisuje preservacje statusu. Identical-payload guard (`normalizePayloadJson`) pomija UPDATE gdy znormalizowany payload nie rozni sie od DB i brak innych tranzycji. **Phase 119-01 (total_paid protection):** gdy `paymentStatusUnchanged=true` (`oldPaymentStatus === newPaymentStatus`), `updateOrderDelta()` nie dolacza `total_paid` do UPDATE — chroni reczne korekty kwoty (np. zwroty czesciowe). `is_canceled_by_buyer` jest pomijane analogicznie, chyba ze `cancelledBySource=true` (cancel propagation ze zrodla zawsze wymusza wpis flagi). Pozostale pola (`status_code`, `payment_status`, `source_updated_at`, `payload_json`, `fetched_at`, `updated_at`) zachowuja niezmieniony kontrakt z Phase 112-01.
3. **Status update**`OrdersController::updateStatus()``OrdersRepository::updateStatus()` → automation check
4. **Status sync** — Cron → `AllegroStatusSyncService` / `ShopproStatusSyncService` → carrier API
4. **Status sync** — Cron → `AllegroStatusSyncService` / `ShopproStatusSyncService` / `ErliStatusSyncService` → marketplace API
### Statistics Summary
1. **Request**`/statistics/summary``OrdersStatisticsController::summary()`
@@ -97,6 +97,7 @@ HTTP Request
|---------|------|
| `AllegroOrdersImportHandler` | Fetch new Allegro orders |
| `AllegroStatusSyncHandler` | Push status changes to Allegro |
| `ErliStatusSyncHandler` | Pull Erli status events via inbox or push manual local status changes to Erli |
| `AllegroTokenRefreshHandler` | OAuth token refresh (24h expiry) |
| `ShopproOrdersImportHandler` | Fetch new shopPRO orders |
| `ShopproStatusSyncHandler` | Push status to shopPRO |

View File

@@ -370,6 +370,7 @@ UNIQUE: `(order_id, source_payment_id)`
UNIQUE: `(integration_id, shoppro_status_code)`
**integration_order_sync_state** — Track order fetch progress per integration
- Phase 129 adds `last_status_pushed_at` for Erli manual status push cursor.
**integration_order_status_sync_state** — Track status sync progress per integration and direction
@@ -517,6 +518,14 @@ UNIQUE: `(type, name)`
| `created_at` | DATETIME | |
| `updated_at` | DATETIME | |
**erli_order_status_mappings** — orderPRO status → Erli status used for status push
- Seeded official Erli statuses: `created`, `canceled`, `readyToProcess`, `inProgress`, `sent`, `readyToPickup`, `received`, `returned`, `returningToSender`, `unknown`.
- `orderpro_status_code` is nullable; unmapped status rows are skipped by push sync.
**erli_order_status_pull_mappings** — Erli status → orderPRO status used during inbox import
- Seeded defaults: `pending -> nieoplacone`, `purchased -> nowe`, `cancelled -> anulowane`.
- Unknown Erli statuses are discovered from `/inbox` and inserted with nullable mapping.
**allegro_delivery_method_mappings** — Map order delivery method strings to Allegro services
| Column | Type | Notes |
|--------|------|-------|

View File

@@ -1,5 +1,16 @@
# Technical Changelog
## 2026-05-16 - Phase 129 Plan 01: Erli Status Mapping + Sync
**Co zrobiono:**
- Dodano mapowania statusow Erli pull/push, `ErliStatusSyncService`, `ErliStatusSyncHandler`, endpoint `PATCH /orders/{id}/status` w kliencie API oraz UI mapowan w `/settings/integrations/erli`.
- Import Erli odkrywa surowe statusy z inboxa i uzywa `erli_order_status_pull_mappings`; push obejmuje tylko reczne zmiany statusu z `order_status_history.change_source='manual'`.
**Dlaczego:**
- Phase 128 miala domyslne statusy; Phase 129 daje operatorowi kontrolowane mapowanie i bezpieczny push bez petli automatyzacji.
---
## 2026-05-13 - Phase 126 Plan 01: Invoice GUS Field Mapping Fix (KRS heuristic)
**Co zrobiono:**