Files
orderPRO/.paul/phases/129-erli-status-mapping-sync/129-01-PLAN.md
Jacek Pyziak 7972bb9fa4 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>
2026-05-16 00:27:28 +02:00

15 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
phase plan type wave depends_on files_modified autonomous delegation
129-erli-status-mapping-sync 01 execute 1
database/migrations/20260515_000116_add_erli_status_mapping_sync.sql
src/Modules/Settings/ErliApiClient.php
src/Modules/Settings/ErliIntegrationController.php
src/Modules/Settings/ErliIntegrationRepository.php
src/Modules/Settings/ErliOrderMapper.php
src/Modules/Settings/ErliOrdersSyncService.php
src/Modules/Settings/ErliStatusMappingRepository.php
src/Modules/Settings/ErliPullStatusMappingRepository.php
src/Modules/Settings/ErliStatusSyncService.php
src/Modules/Cron/ErliStatusSyncHandler.php
src/Modules/Cron/CronHandlerFactory.php
routes/web.php
resources/views/settings/erli.php
resources/lang/pl.php
tests/Unit/ErliOrderMapperTest.php
tests/Unit/ErliStatusSyncServiceTest.php
DOCS/DB_SCHEMA.md
DOCS/ARCHITECTURE.md
DOCS/TECH_CHANGELOG.md
true auto
## Goal Wdrozyc mapowanie i synchronizacje statusow Erli w obu kierunkach: Erli -> orderPRO przy imporcie `/inbox` oraz orderPRO -> Erli przez cron push na `PATCH /orders/{id}/status`.

Purpose

Phase 128 importuje zamowienia Erli, ale statusy sa jeszcze mapowane sztywnymi defaultami. Phase 129 ma dac operatorowi kontrolowane mapowania pull/push, bezpieczne odkrywanie nowych statusow z inboxa oraz automatyczny push recznych zmian statusu z orderPRO do Erli.

Output

Nowe tabele mapowan statusow Erli, UI w ustawieniach Erli, endpointy zapisu mapowan, serwis synchronizacji statusow, handler crona erli_status_sync, rozszerzony mapper importu oraz testy i dokumentacja.

- **Zakres** — Czy Phase 129 ma objac od razu oba kierunki synchronizacji statusow Erli? -> Odpowiedz: Tak, oba kierunki. - **Statusy** — Jak traktowac liste statusow Erli w UI mapowan? -> Odpowiedz: Seed + discovery. - **Push** — Ktore lokalne zmiany statusu orderPRO maja byc wysylane do Erli? -> Odpowiedz: Tylko reczne zmiany statusu (`order_status_history.change_source='manual'`).

Project Context

@.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md @.paul/codebase/architecture.md @.paul/codebase/db_schema.md @DOCS/DB_SCHEMA.md @DOCS/ARCHITECTURE.md

Prior Work

@.paul/phases/127-erli-integration-foundation/127-01-SUMMARY.md @.paul/phases/128-erli-orders-import/128-01-SUMMARY.md

External API Context

  • Official Erli docs checked 2026-05-15: https://erli.pl/svc/shop-api/doc/
  • Relevant Erli contract:
    • /inbox contains new orders and order status changes.
    • Base order status values documented for import: pending, purchased, cancelled.
    • Swagger exposes PATCH /orders/{id}/status with body {"status": "created|canceled|readyToProcess|inProgress|sent|readyToPickup|received|returned|returningToSender|unknown"}.
    • Erli docs state that order update for Erli shipments should not send trackingNumber; shipment/tracking remains Phase 130-131.

Source Files

@src/Modules/Settings/ErliApiClient.php @src/Modules/Settings/ErliIntegrationRepository.php @src/Modules/Settings/ErliIntegrationController.php @src/Modules/Settings/ErliOrderMapper.php @src/Modules/Settings/ErliOrdersSyncService.php @src/Modules/Settings/AllegroStatusMappingRepository.php @src/Modules/Settings/AllegroPullStatusMappingRepository.php @src/Modules/Settings/AllegroStatusMappingController.php @src/Modules/Settings/AllegroStatusSyncService.php @src/Modules/Settings/ShopproStatusSyncService.php @src/Modules/Settings/OrderStatusRepository.php @src/Modules/Cron/CronHandlerFactory.php @src/Modules/Cron/AllegroStatusSyncHandler.php @resources/views/settings/erli.php @resources/views/settings/allegro.php @routes/web.php @resources/lang/pl.php @tests/Unit/AllegroStatusSyncServiceTest.php @tests/Unit/ErliOrderMapperTest.php

## Required Skills (from SPECIAL-FLOWS.md)
Skill Priority When to Invoke Loaded?
sonar-scanner required After APPLY, before UNIFY
/feature-dev optional New marketplace integration feature
/code-review optional Before UNIFY if broad risks remain
/frontend-design optional If Erli settings UI needs significant redesign

BLOCKING: Required skills MUST be attempted before UNIFY. If sonar-scanner is unavailable in PATH, document the gap in SUMMARY and STATE as in Phase 128.

Skill Invocation Checklist

  • sonar-scanner run or documented unavailable

<acceptance_criteria>

AC-1: Erli status mapping schema and seeds

Given Phase 129 migration is applied
When the database is inspected
Then Erli has separate pull and push mapping tables, seeded with documented Erli statuses, and `erli_status_sync` exists as a disabled-by-default or settings-controlled cron schedule

AC-2: Erli settings UI exposes status mappings

Given an operator opens `/settings/integrations/erli`
When they review the settings page
Then they can configure Erli -> orderPRO pull mappings, orderPRO -> Erli push mappings, status sync direction, and status sync interval without editing code

AC-3: Import uses configurable pull mappings with discovery

Given an Erli inbox message contains status `pending`, `purchased`, `cancelled`, or a new status
When `ErliOrdersSyncService` imports the message
Then `ErliOrderMapper` uses the configured pull mapping when present, falls back to safe defaults when absent, and stores unknown Erli statuses for later mapping

AC-4: Cron pushes only manual orderPRO status changes to Erli

Given an Erli order has a manual status change in `order_status_history`
When the `erli_status_sync` cron handler runs in orderPRO -> Erli direction
Then the service maps the current orderPRO status to an Erli status and calls `PATCH /orders/{id}/status` only for mapped manual changes after the last pushed cursor

AC-5: Push is safe and observable

Given Erli credentials are missing, mappings are incomplete, or Erli API returns an error
When status sync runs
Then the result reports pushed/skipped/failed counts, records bounded errors, and advances the push cursor only for successfully processed change timestamps

AC-6: Documentation and tests cover status behavior

Given Phase 129 implementation is complete
When verification runs
Then mapper/status-sync unit tests, PHP lint, docs, and PAUL summary describe the new status mapping and sync behavior

</acceptance_criteria>

Task 1: Add Erli status mapping persistence, seeds, API client method database/migrations/20260515_000116_add_erli_status_mapping_sync.sql, src/Modules/Settings/ErliApiClient.php, src/Modules/Settings/ErliIntegrationRepository.php, src/Modules/Settings/ErliStatusMappingRepository.php, src/Modules/Settings/ErliPullStatusMappingRepository.php, DOCS/DB_SCHEMA.md Create the persistence foundation: - Add `erli_order_status_mappings` for push mappings (`orderpro_status_code` -> `erli_status_code`, name, timestamps). - Add `erli_order_status_pull_mappings` for pull mappings (`erli_status_code` -> `orderpro_status_code`, name, timestamps). - Seed documented pull statuses: `pending -> nieoplacone`, `purchased -> nowe`, `cancelled -> anulowane`. - Seed documented push status options from official swagger: `created`, `canceled`, `readyToProcess`, `inProgress`, `sent`, `readyToPickup`, `received`, `returned`, `returningToSender`, `unknown`. Keep orderPRO mappings nullable where no safe default exists. - Seed `app_settings` keys `erli_status_sync_direction=erli_to_orderpro` and `erli_status_sync_interval_minutes=15`. - Seed `cron_schedules.job_type='erli_status_sync'` idempotently; prefer disabled until operator enables/import settings are confirmed, unless existing local pattern makes enabled safer. - Add repositories mirroring Allegro style: list, replaceAll, find mapped status, upsertDiscoveredStatus, build reverse map. - Add `ErliApiClient::updateOrderStatus(array $credentials, string $orderId, string $erliStatus): array` calling `PATCH /orders/{id}/status` with JSON `{status: ...}`. - Extend repository credentials/settings only as needed for status sync settings; do not introduce per-account Erli complexity. - Update DB schema docs. `C:\xampp\php\php.exe -l src/Modules/Settings/ErliApiClient.php` `C:\xampp\php\php.exe -l src/Modules/Settings/ErliStatusMappingRepository.php` `C:\xampp\php\php.exe -l src/Modules/Settings/ErliPullStatusMappingRepository.php` Inspect migration for idempotent DDL/seed and no raw runtime use of `DB_HOST_REMOTE`. AC-1 foundation is implemented and AC-5 has API error result shape available. Task 2: Add Erli settings UI and routes for pull/push mappings src/Modules/Settings/ErliIntegrationController.php, routes/web.php, resources/views/settings/erli.php, resources/lang/pl.php, DOCS/ARCHITECTURE.md Extend the Erli settings page without creating inline CSS or native alert/confirm: - Inject `OrderStatusRepository`, `ErliStatusMappingRepository`, and `ErliPullStatusMappingRepository`. - Pass orderPRO statuses, Erli push statuses, Erli pull mappings, current sync direction and interval to the view. - Add POST handlers for saving pull mappings and push mappings with CSRF validation. - Add status sync direction and interval fields to the existing Erli settings form, validating direction against `erli_to_orderpro` / `orderpro_to_erli` and interval 1-1440. - Add routes under `/settings/integrations/erli/statuses/save-pull` and `/settings/integrations/erli/statuses/save-push` (or one clear equivalent route if controller shape is cleaner). - Render compact mapping tables similar to Allegro, but keep Erli page simple; do not build a landing/marketing page. - Add Polish translations for labels, hints, empty states, validation and flash messages. - Preserve existing test/import flash behavior from Phase 128. `C:\xampp\php\php.exe -l src/Modules/Settings/ErliIntegrationController.php` `C:\xampp\php\php.exe -l routes/web.php` `C:\xampp\php\php.exe -l resources/views/settings/erli.php` `C:\xampp\php\php.exe -l resources/lang/pl.php` Manual inspect that forms include `_token` and use escaped output via `$e()`. AC-2 is satisfied and existing Phase 127/128 settings actions remain reachable. Task 3: Wire pull discovery and push cron sync src/Modules/Settings/ErliOrderMapper.php, src/Modules/Settings/ErliOrdersSyncService.php, src/Modules/Settings/ErliStatusSyncService.php, src/Modules/Cron/ErliStatusSyncHandler.php, src/Modules/Cron/CronHandlerFactory.php, tests/Unit/ErliOrderMapperTest.php, tests/Unit/ErliStatusSyncServiceTest.php, DOCS/ARCHITECTURE.md, DOCS/TECH_CHANGELOG.md Implement runtime behavior: - Let `ErliOrderMapper` accept an optional pull mapping dependency or mapping array while preserving the existing no-dependency unit-test usage. - During import, discover any raw Erli order status from inbox and upsert it into the pull mapping repository before/while mapping. - Use configured pull mapping when available; otherwise keep Phase 128 defaults (`pending`, `purchased`, `cancelled`) and safe fallback. - Add `ErliStatusSyncService` similar to Allegro/shopPRO: - Direction `erli_to_orderpro`: run Erli inbox import with `ignore_orders_fetch_enabled=true` so status-change messages are pulled via the same safe ACK path. - Direction `orderpro_to_erli`: find only Erli orders with `order_status_history.change_source='manual'` newer than `last_status_pushed_at` for the active Erli integration. - Use push mapping to call `ErliApiClient::updateOrderStatus()`. - Count `pushed`, `skipped`, `failed`; keep bounded errors; advance cursor only to latest successfully processed change timestamp. - Add `ErliStatusSyncHandler` and register `erli_status_sync` in `CronHandlerFactory`. - Add/update unit tests for pull mapping override, unknown status discovery behavior, skipped unmapped push, successful push, and failed push cursor behavior. - Update architecture/changelog docs. `C:\xampp\php\php.exe -l src/Modules/Settings/ErliOrderMapper.php` `C:\xampp\php\php.exe -l src/Modules/Settings/ErliOrdersSyncService.php` `C:\xampp\php\php.exe -l src/Modules/Settings/ErliStatusSyncService.php` `C:\xampp\php\php.exe -l src/Modules/Cron/ErliStatusSyncHandler.php` `C:\xampp\php\php.exe -l src/Modules/Cron/CronHandlerFactory.php` `C:\xampp\php\php.exe -l tests/Unit/ErliStatusSyncServiceTest.php` If available: `vendor/bin/phpunit tests/Unit/ErliOrderMapperTest.php tests/Unit/ErliStatusSyncServiceTest.php` Required skill: `sonar-scanner` or document unavailable. AC-3, AC-4, AC-5 and AC-6 are satisfied.

DO NOT CHANGE

  • Do not change shipment/label generation flows; Erli shipment creation belongs to Phase 130.
  • Do not send Erli trackingNumber in status sync; official docs say shipment number is handled after shipment generation.
  • Do not broaden push to automation/system/import changes; Phase 129 push covers only manual order_status_history rows.
  • Do not alter Allegro/shopPRO mapping semantics except for reading patterns.
  • Do not use DB_HOST_REMOTE in runtime code.
  • Do not add native alert()/confirm() or inline CSS in Erli settings.

SCOPE LIMITS

  • No Erli webhook registration in this plan; inbox/cron remains the source.
  • No product stock adjustment on Erli cancellation; document if needed for a later products/inventory phase.
  • No manual live Erli API call is required during APPLY; live smoke is an operator follow-up unless credentials and DB are ready.
  • No redesign of the whole integrations UI; keep changes scoped to Erli settings.
Before declaring plan complete: - [ ] PHP lint passes for all changed PHP/view/lang/test files. - [ ] Migration is idempotent and documented in `DOCS/DB_SCHEMA.md`. - [ ] Erli settings page renders pull/push mapping forms with CSRF and escaped values. - [ ] Mapper tests cover configured mapping fallback and unknown status handling. - [ ] Status sync tests cover successful push, skipped unmapped push, failed push, and cursor behavior. - [ ] `vendor/bin/phpunit tests/Unit/ErliOrderMapperTest.php tests/Unit/ErliStatusSyncServiceTest.php` run if available; otherwise gap documented. - [ ] `sonar-scanner` attempted per SPECIAL-FLOWS; unavailable CLI documented as gap. - [ ] `git diff --check` passes. - [ ] All acceptance criteria met.

<success_criteria>

  • Erli status pull/push mappings can be configured from UI.
  • Erli import uses configured pull mapping and discovers unknown raw statuses.
  • Erli status cron can push only manual orderPRO status changes to Erli using PATCH /orders/{id}/status.
  • Failures are observable and do not incorrectly advance push cursor.
  • Docs and tests reflect the new behavior. </success_criteria>
After completion, create `.paul/phases/129-erli-status-mapping-sync/129-01-SUMMARY.md`.