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
312 lines
15 KiB
Markdown
312 lines
15 KiB
Markdown
---
|
|
phase: 128-erli-orders-import
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- 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
|
|
autonomous: true
|
|
delegation: auto
|
|
---
|
|
|
|
<objective>
|
|
## 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.
|
|
</objective>
|
|
|
|
<context>
|
|
<clarifications>
|
|
- **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.
|
|
</clarifications>
|
|
|
|
## 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.
|
|
</context>
|
|
|
|
<skills>
|
|
## 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.
|
|
</skills>
|
|
|
|
<acceptance_criteria>
|
|
|
|
## AC-1: Import Configuration And Cron
|
|
```gherkin
|
|
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
|
|
```gherkin
|
|
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
|
|
```gherkin
|
|
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
|
|
```gherkin
|
|
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
|
|
```gherkin
|
|
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
|
|
```gherkin
|
|
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>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Add Erli import controls, schedule and entry points</name>
|
|
<files>
|
|
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
|
|
</files>
|
|
<action>
|
|
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()`.
|
|
</action>
|
|
<verify>
|
|
`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`
|
|
</verify>
|
|
<done>AC-1 satisfied: Erli import can be enabled, scheduled, and manually triggered from settings.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Implement Erli inbox client, mapper and sync service</name>
|
|
<files>
|
|
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
|
|
</files>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
`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`
|
|
</verify>
|
|
<done>AC-2, AC-3, AC-4 and AC-5 satisfied for the runtime import path.</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 3: Add mapper tests and update technical docs</name>
|
|
<files>
|
|
tests/Unit/ErliOrderMapperTest.php,
|
|
DOCS/DB_SCHEMA.md,
|
|
DOCS/ARCHITECTURE.md,
|
|
DOCS/TECH_CHANGELOG.md
|
|
</files>
|
|
<action>
|
|
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.
|
|
</action>
|
|
<verify>
|
|
`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.
|
|
</verify>
|
|
<done>AC-6 satisfied: tests and documentation describe the new Erli import behavior and remaining live-smoke steps.</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<boundaries>
|
|
|
|
## 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.
|
|
|
|
</boundaries>
|
|
|
|
<verification>
|
|
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.
|
|
</verification>
|
|
|
|
<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>
|
|
|
|
<output>
|
|
After completion, create `.paul/phases/128-erli-orders-import/128-01-SUMMARY.md`.
|
|
</output>
|