Files
orderPRO/.paul/phases/128-erli-orders-import/128-01-PLAN.md
Jacek Pyziak 2565d9b754 feat(128): erli orders import
Phase 128 complete:
- add Erli /inbox order import with safe mark-read ACK
- add cron/manual import controls and sync state tracking
- map Erli orders into orderPRO aggregates with mapper tests and docs
2026-05-15 23:54:22 +02:00

15 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
phase plan type wave depends_on files_modified autonomous delegation
128-erli-orders-import 01 execute 1
database/migrations/20260515_000115_add_erli_orders_import_schedule.sql
src/Core/Constants/IntegrationSources.php
src/Modules/Settings/ErliApiClient.php
src/Modules/Settings/ErliIntegrationRepository.php
src/Modules/Settings/ErliIntegrationController.php
src/Modules/Settings/ErliOrderMapper.php
src/Modules/Settings/ErliOrderSyncStateRepository.php
src/Modules/Settings/ErliOrdersSyncService.php
src/Modules/Cron/ErliOrdersImportHandler.php
src/Modules/Cron/CronHandlerFactory.php
routes/web.php
resources/views/settings/erli.php
resources/lang/pl.php
tests/Unit/ErliOrderMapperTest.php
DOCS/DB_SCHEMA.md
DOCS/ARCHITECTURE.md
DOCS/TECH_CHANGELOG.md
true auto
## Goal Wdrozyc realny import zamowien Erli do orderPRO na bazie Erli `/inbox`: cron job, reczny import z ustawien, mapper payloadu zamowienia do wspolnego modelu `OrderImportRepository` oraz bezpieczne potwierdzanie przeczytania inboxa po udanym batchu.

Purpose

Phase 127 dala konfiguracje i test API. Phase 128 ma sprawic, ze Erli zaczyna dostarczac realne zamowienia do listy orderPRO, z zachowaniem kontraktow delta-only re-import, invoice_requested i automatyzacji order.imported / payment.status_changed.

Output

Nowe klasy importu Erli, cron schedule erli_orders_import, przycisk recznego importu w /settings/integrations/erli, testy mappera oraz dokumentacja techniczna.

- **Zrodlo importu** - Czy Phase 128 ma uzywac Erli `/inbox`, czy klasycznej listy zamowien po `updated`? -> Odpowiedz: Wg rekomendacji; uzyc `/inbox` jako glownego zrodla. - **ACK inboxa** - Czy po udanym przetworzeniu oznaczac wiadomosci Erli jako przeczytane? -> Odpowiedz: Wg rekomendacji; oznaczac po udanym batchu, z notatka jezeli trzeba cos pozniej zrobic. - **Reczny import** - Czy dodac reczna akcje importu w ustawieniach, czy tylko cron? -> Odpowiedz: Obie.

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

Source Files

@src/Modules/Settings/ErliApiClient.php @src/Modules/Settings/ErliIntegrationRepository.php @src/Modules/Settings/ErliIntegrationController.php @resources/views/settings/erli.php @src/Modules/Settings/AllegroOrdersSyncService.php @src/Modules/Settings/AllegroOrderImportService.php @src/Modules/Settings/ShopproOrdersSyncService.php @src/Modules/Settings/ShopproOrderMapper.php @src/Modules/Settings/AllegroOrderSyncStateRepository.php @src/Modules/Orders/OrderImportRepository.php @src/Modules/Cron/CronHandlerFactory.php @src/Modules/Cron/AllegroOrdersImportHandler.php @src/Modules/Cron/ShopproOrdersImportHandler.php @routes/web.php @resources/lang/pl.php @tests/Unit/OrderImportRepositoryTest.php @tests/Unit/AllegroOrderImportServiceTest.php

External API Notes

@https://erli.pl/svc/shop-api/doc/

  • Erli API uses REST over HTTPS with Authorization: Bearer ..., Accept: application/json and a meaningful User-Agent.
  • Orders and order changes are available through /svc/shop-api/inbox.
  • One fetch returns up to 500 unread messages.
  • Messages should be marked read only after processing, using the id of the newest/last message.
  • Status basics: pending means unpaid PayU; purchased means paid PayU or COD; cancelled means cancelled.
  • If the exact ACK endpoint/method is not recoverable from the public reference during APPLY, import must stay non-destructive, skip ACK, and SUMMARY must record the follow-up.
## Required Skills / Tools (from SPECIAL-FLOWS.md)
Skill / Tool Priority When to Invoke Loaded?
sonar-scanner required After APPLY, before UNIFY o

Optional Flows

  • /feature-dev optional before implementation of this marketplace feature.
  • /code-review optional after implementation, before UNIFY.

<acceptance_criteria>

AC-1: Import Configuration And Cron

Given Erli settings have a saved API key
When the operator enables Erli order import and saves the settings
Then `orders_fetch_enabled`, optional `orders_fetch_start_date`, cron interval, and `erli_orders_import` schedule are persisted
And the settings page offers a CSRF-protected "Importuj teraz" action.

AC-2: Inbox Fetch And Safe Acknowledgement

Given Erli returns unread `/inbox` messages containing order events
When the cron or manual import processes the batch without per-order failures
Then every supported order event is imported or re-imported
And Erli inbox is marked read only up to the newest processed message id.

AC-3: No Data Loss On Partial Failure

Given an Erli inbox batch contains at least one order that cannot be mapped or saved
When import finishes with failures
Then the sync state records the failure
And the inbox acknowledgement is not sent for that batch
And the result exposes processed/imported/failed/skipped counters plus sampled errors.

AC-4: Order Aggregate Mapping

Given an Erli order payload contains buyer, delivery, payment, line items, totals and optional invoice/company data
When the mapper builds an order aggregate
Then `orders`, `order_addresses`, `order_items`, `order_payments`, `order_notes`, `order_status_history` receive orderPRO-compatible data
And new orders with invoice/company markers set `orders.invoice_requested=1`.

AC-5: Existing Import Contracts Preserved

Given an Erli order already exists in orderPRO
When the same order is imported again from a changed inbox event
Then `OrderImportRepository::upsertOrderAggregate()` performs delta-only re-import
And local items/addresses/notes are not replaced on re-import
And payment transition can still trigger `payment.status_changed`.

AC-6: Observability And Documentation

Given Phase 128 is complete
When maintainers read the docs or run tests
Then Erli import architecture, schema/schedule changes, verification gaps and manual smoke steps are documented
And mapper/unit checks cover the core Erli payload shapes.

</acceptance_criteria>

Task 1: Add Erli import controls, schedule and entry points database/migrations/20260515_000115_add_erli_orders_import_schedule.sql, src/Modules/Settings/ErliIntegrationRepository.php, src/Modules/Settings/ErliIntegrationController.php, resources/views/settings/erli.php, resources/lang/pl.php, routes/web.php, src/Modules/Cron/ErliOrdersImportHandler.php, src/Modules/Cron/CronHandlerFactory.php Add an idempotent migration seeding `cron_schedules.job_type='erli_orders_import'` with a conservative default interval (5 minutes), disabled until the operator enables import. Reuse existing `integrations.orders_fetch_enabled` and `integrations.orders_fetch_start_date`; do not add duplicate Erli-only columns for the same settings. Extend Erli settings save/read to expose: - import enabled checkbox, - optional start date, - order import interval minutes using `CronRepository::upsertSchedule`. Add a POST `/settings/integrations/erli/import` action protected by CSRF that calls the Erli sync service with `ignore_orders_fetch_enabled=true` and small manual limits. Wire `ErliOrdersImportHandler` into `CronHandlerFactory` as `erli_orders_import`. Keep UI compact and reuse existing alert component; do not add inline CSS or native `alert()` / `confirm()`. `C:\xampp\php\php.exe -l src/Modules/Settings/ErliIntegrationRepository.php` `C:\xampp\php\php.exe -l src/Modules/Settings/ErliIntegrationController.php` `C:\xampp\php\php.exe -l src/Modules/Cron/ErliOrdersImportHandler.php` `C:\xampp\php\php.exe -l src/Modules/Cron/CronHandlerFactory.php` `C:\xampp\php\php.exe -l routes/web.php` `C:\xampp\php\php.exe -l resources/views/settings/erli.php` AC-1 satisfied: Erli import can be enabled, scheduled, and manually triggered from settings. Task 2: Implement Erli inbox client, mapper and sync service src/Core/Constants/IntegrationSources.php, src/Modules/Settings/ErliApiClient.php, src/Modules/Settings/ErliOrderMapper.php, src/Modules/Settings/ErliOrderSyncStateRepository.php, src/Modules/Settings/ErliOrdersSyncService.php Add `IntegrationSources::ERLI = 'erli'`. Extend `ErliApiClient` with reusable JSON request helpers: - `fetchInbox(base_url, api_key, timeout)` via `GET /inbox`, - `ackInboxRead(base_url, api_key, timeout, latest_message_id)` after confirming the exact ACK method/path in Erli reference before coding, - consistent handling for 401/403, 429, non-JSON bodies and cURL errors. Build `ErliOrderMapper` that accepts supported inbox event payloads (`orderCreated`, `orderStatusChanged` and equivalent shape variants) and produces the aggregate arrays required by `OrderImportRepository::upsertOrderAggregate()`. Mapping rules: - source/integration: `source='erli'`, `external_platform_id='erli'`, - status defaults: `pending -> nieoplacone`, `purchased -> nowe`, `cancelled -> anulowane`; richer pull/push mapping is deferred to Phase 129, - payment status: `pending -> 0`, `purchased -> 2`, COD `purchased -> 2`, cancelled -> 0 unless payload clearly says paid/refunded, - totals, currency and delivery price from payload when present, - customer, delivery and invoice addresses from payload; company tax number/company name should set `invoice_detected=true`, - items with source ids, names, quantity, gross price, SKU/EAN/image when present, - buyer message/comment as order note when present, - status history row with raw Erli status in payload/comment. Build `ErliOrdersSyncService` that: - reads active credentials from `ErliIntegrationRepository`, - respects `orders_fetch_enabled` unless manual import overrides it, - filters/skips messages older than `orders_fetch_start_date` where payload dates allow it, - imports each supported order event through `OrderImportRepository`, - records import activity with source `Erli`, - sets `invoice_requested` only for newly created orders when mapper detects invoice/company data, - triggers `order.imported` for created orders and `payment.status_changed` for re-import payment transitions, - advances `integration_order_sync_state` on success and stores errors on failure, - sends ACK only if the full batch had zero import failures and ACK endpoint was confirmed. If the ACK endpoint cannot be confirmed in APPLY, implement the service with ACK disabled by default, return `acknowledged=false`, and add a clear follow-up in SUMMARY/STATE; do not guess a destructive endpoint. `C:\xampp\php\php.exe -l src/Modules/Settings/ErliApiClient.php` `C:\xampp\php\php.exe -l src/Modules/Settings/ErliOrderMapper.php` `C:\xampp\php\php.exe -l src/Modules/Settings/ErliOrderSyncStateRepository.php` `C:\xampp\php\php.exe -l src/Modules/Settings/ErliOrdersSyncService.php` Static check: `rg -n "IntegrationSources::ERLI|erli_orders_import|ackInboxRead|order.imported|payment.status_changed" src routes` AC-2, AC-3, AC-4 and AC-5 satisfied for the runtime import path. Task 3: Add mapper tests and update technical docs tests/Unit/ErliOrderMapperTest.php, DOCS/DB_SCHEMA.md, DOCS/ARCHITECTURE.md, DOCS/TECH_CHANGELOG.md Add PHPUnit tests for `ErliOrderMapper` covering: - paid/purchased order maps to an importable aggregate with source `erli`, - pending order maps payment status 0 and status `nieoplacone`, - cancelled order maps status `anulowane` and cancellation flag, - invoice/company/tax id data sets `invoice_detected=true`, - malformed/unsupported inbox messages are skipped or throw controlled mapper exceptions as designed. Update DB docs with the new cron schedule and any sync-state usage/migration changes. Update architecture docs with Erli import flow: settings/manual import/cron -> inbox client -> mapper -> `OrderImportRepository` -> automation. Update technical changelog with Phase 128 scope, status defaults, ACK safety rule, manual verification steps and any deferred ACK follow-up if needed. `C:\xampp\php\php.exe -l tests/Unit/ErliOrderMapperTest.php` If dependencies are installed: `vendor\bin\phpunit tests\Unit\ErliOrderMapperTest.php` `git diff --check` `sonar-scanner` after APPLY when available in PATH. AC-6 satisfied: tests and documentation describe the new Erli import behavior and remaining live-smoke steps.

DO NOT CHANGE

  • Do not change Allegro/shopPRO import behavior except for shared constants or wiring required by Erli.
  • Do not weaken Phase 112/119 delta-only re-import protections in OrderImportRepository.
  • Do not implement Erli status push, pull status mapping UI, label generation, shipment creation or tracking in this plan.
  • Do not add a sandbox/environment switch; Phase 127 decision says one production/global config.
  • Do not introduce native JS alert() / confirm() or CSS inside views.
  • Do not use DB_HOST_REMOTE in runtime code.

SCOPE LIMITS

  • Phase 128 imports orders from Erli; Phase 129 owns configurable status mappings and status sync.
  • Phase 130 owns shipments/labels.
  • Phase 131 owns tracking/automation hooks beyond existing order.imported and payment.status_changed.
  • Product catalog/stock sync is out of scope even though Erli inbox may include product sync messages.
  • Live import verification requires real Erli credentials and local DB migration; if unavailable, record as manual follow-up.
Before declaring plan complete: - [ ] `C:\xampp\php\php.exe -l` passes for all created/modified PHP files. - [ ] `vendor\bin\phpunit tests\Unit\ErliOrderMapperTest.php` passes when dependencies are installed. - [ ] `git diff --check` passes. - [ ] `sonar-scanner` run or documented as unavailable. - [ ] Manual smoke documented: run `php bin/migrate.php`, enable Erli import, click "Importuj teraz", confirm Erli orders appear with `source='erli'`. - [ ] If ACK endpoint was confirmed: successful import returns `acknowledged=true`; failure batch returns `acknowledged=false`. - [ ] All acceptance criteria met or deferred with explicit reason in SUMMARY.

<success_criteria>

  • Erli orders can be imported by cron and manually from settings.
  • Successful supported inbox events create/update orderPRO orders with addresses, items, payments, notes and status history.
  • Re-import keeps existing delta-only protections.
  • Inbox read acknowledgement is safe: only after all processed messages in the batch succeed, or explicitly disabled with follow-up if the ACK endpoint cannot be confirmed.
  • Operator-visible result counters exist for manual import and cron payload result.
  • Documentation and tests are updated. </success_criteria>
After completion, create `.paul/phases/128-erli-orders-import/128-01-SUMMARY.md`.