Technical Approach — New Platform¶
ZWE-394
Proposed technical approach for the Navision connection on the new Zwerfkei Backend, tracked on ZWE-394. Draft — pending review with Florian.
This page covers the new platform's approach (Laravel, DDD-structured backend). For how the current system works, see Legacy Integration.
Architecture layer¶
The backend has no SOAP client or Navision integration yet (checked composer.json and app/, both clean). Proposed placement, following the existing DDD structure (Domain/Application/Infrastructure/Presentation):
Infrastructure/Navision/— shared SOAP client wrapper (connection, WSDL config for the three services LS/NAV/Kei), used by every domain that talks to Navision. Mirrors the existing pattern where domain-specific service providers live inInfrastructure/{Domain}/Providers/.- Domain-specific jobs — e.g.
Application/Order/Jobs/SubmitOrderToNavision, dispatched from the existingOrderdomain when an order is confirmed. Same forProduct,Customeronce those flows are built.
Outbound (Web → NAV, e.g. SubmitOrder): a queued Job, not a cron-polled queue table like the legacy k3_synchronization pattern. QUEUE_CONNECTION=database is already configured — use Laravel's native queue instead of reimplementing the legacy's manual GET_LOCK + status-column queue.
Inbound (NAV → Web, e.g. price/stock sync): Laravel's scheduler (app/Console/Kernel.php) running a sync command at a fixed interval, replacing the legacy's continuous cron-loop polling.
Authentication & credentials¶
- Same pattern as legacy: HTTP Basic Auth per WSDL service (LS/NAV/Kei each have their own username/password).
- New
config/navision.php, values from.env(NAVISION_LS_USERNAME,NAVISION_LS_PASSWORD, etc.) — never hardcoded, mirrors how the legacyK3retail_gatewayconfig array works but through Laravel's config system instead of a raw PHP array inconfig.php. - NAV sandbox credentials for actually testing this are in 1Password — not yet shared with us (see comment on ZWE-459).
Error handling & retries¶
- Catch
SoapFaultexplicitly, log with full context (operation, request payload with sensitive fields redacted, fault message). - Use Laravel's built-in job retry mechanism (
$tries,backoff()) instead of the legacy's manual 10-attempt counter + 5-minute error lock — same intent, native Laravel tooling. - Failed jobs land in Laravel's standard
failed_jobstable, giving us a built-in audit trail without custom tables. - A rejected/failing submission must not leave the source record (order, etc.) in an inconsistent state — mirrors the same requirement already stated in ZWE-421's Definition of Done.
Synchronization strategy¶
- Outbound (orders): immediate dispatch on order confirmation, not batched — matches the legacy's queue-then-cron pattern but removes the up-to-5-minute delay.
- Inbound (price/stock): frequency is pending — the ZWE-389 open question on whether this is push or pull, and at what interval, hasn't been answered by the ERP administrator yet. Whatever frequency comes back, the scheduler-command approach above works unchanged; only the interval config value depends on the answer.
- Avoid overloading NAV: reuse the legacy's
$filter-style narrow queries where possible (see Business Central API README — filtering guidance for the future REST path too).
Monitoring¶
- Failed jobs table + Laravel's queue
failedevent, wired to the same alerting channel already used elsewhere in the backend (Mailhog locally, whatever's configured for staging/production). - A scheduled health-check command that flags if inbound sync hasn't run successfully within N × the expected interval — same intent as the legacy's
checkK3Sync, native Laravel implementation.
Risks¶
| Risk | Impact | Mitigation |
|---|---|---|
| NAV downtime/timeout during checkout-time calls | Order confirmation blocked or delayed | Queue the submission async, don't block the customer-facing request on NAV's response |
| WSDL schema drift between environments (test vs. production NAV) | Silent field mismatches, rejected submissions | Pin WSDL version/cache per environment, alert on schema fetch changes |
| Duplicate order submission on retry | Double order in NAV, financial impact | Idempotency key (own nav_id counter, same pattern as legacy) checked before resubmission |
| Missing/unmapped field silently dropped | Data loss, hard to detect | Explicit mapping table (ZWE-391) with a "field without equivalent" section, not implicit dropping |
| Credential rotation | Integration breaks unnoticed | Credentials in .env/secrets manager, not hardcoded; failed-auth alert on the health check above |
| Rate limiting / overloading NAV with frequent polls | NAV performance impact, Zwerfkei-side throttling | Narrow $filter-style queries, conservative polling interval pending the ERP administrator's answer above |
OCP sync queue growing unbounded (GetSyncBuffer/DeleteSyncBuffer) |
Orders and item-availability entries pile up in NAV's queue tables (11176106/11176114) if never processed, unbounded growth over time | DeleteSyncBuffer must run on a schedule to actually commit queued entries to the Transaction Journal, not just read them — flagged by Jasper as a risk during review, the final flow (when/how often to call DeleteSyncBuffer) is still pending alignment |
Open questions blocking full sign-off¶
- Inbound sync frequency for price/stock (push vs. pull, interval) — pending ERP administrator answer, tracked on ZWE-389.
- Review with Florian not yet done — this page is a draft proposal, not yet approved.