Sales Import Workflow
This page documents the core LogiDAV sales ingestion workflow for Menzzo Magento 2 orders.
It is primarily implemented by:
Why This Workflow Is Criticalβ
This workflow is one of the highest-risk paths in the system because it:
- imports new revenue-bearing orders into LogiDAV
- updates local order status from Magento
- can decrement stock when orders move to
processing - triggers downstream operational behavior such as RDV handling
- can affect customer communication for
RETRAITflows
If it breaks or becomes partially inconsistent, the impact can include:
- missing or duplicated orders
- incorrect order status in LogiDAV
- stock drift after late payment or status changes
- customer communication mismatches
- operational teams working from stale order data
Main Componentsβ
- Command:
src/AppBundle/Command/Sale/V2/SaleCommand.php - Command:
src/AppBundle/Command/Sale/V2/CheckSaleImportDisabledCommand.php - Command:
src/AppBundle/Command/Sale/V2/UpdateSaleCommand.php - Service:
src/AppBundle/Services/SaleService.php - Monitoring service:
src/AppBundle/Services/SaleImportMonitoringService.php - Stock mutation service:
src/AppBundle/Services/ProductQtyLogService.php
Workflow Overviewβ
Status Synchronization Overviewβ
Detailed Behaviorβ
1. New Order Importβ
menzzo:v2:sales does the following:
- Uses
LockableTraitto prevent concurrent execution. - Ensures the
MZ_ACTIVATE_SALES_IMPORTconfig row exists. - Stops with
Sales import is disabled!ifSaleService::canImportSales()returns false. - Reads the cursor from
SaleService::getLastMagentoId(). - Fetches Magento orders with
entity_id > last imported ID. - Imports each order via
SaleService::addOrder($sale, true). - Advances the stored cursor with
SaleService::setLastMagentoId($magentoId). - Runs RDV handling for imported sales that end in
processingorcomplete.
menzzo:v2:sales does not create or resolve disabled-import alerts. That work belongs to menzzo:v2:sales:check-disabled-import.
2. Disabled Import Monitoringβ
menzzo:v2:sales:check-disabled-import must run every 30 minutes and does the following:
- Ensures the
MZ_ACTIVATE_SALES_IMPORTconfig row exists. - If sales import is disabled, calls
SaleImportMonitoringService::checkAndNotifyIfDisabledTooLong(). - If sales import is enabled, calls
SaleImportMonitoringService::resolveOpenAlertIfImportEnabled(). - Exits without launching Magento import, reading
SaleService::getLastMagentoId(), or importing orders.
Alerting starts only when the config remains disabled for more than 30 minutes, measured from the row updated_at value. The alert is deduplicated while open, and it is resolved automatically when import is re-enabled and the monitoring command runs.
Teams notification is optional and non-blocking. If MZ_TEAMS_ALERT_WEBHOOK_URL is empty, no Teams message is sent; Teams delivery failures are logged and do not block the system alert path.
3. Create vs Update Semanticsβ
SaleService::addOrder() is not only a create method. It acts as an upsert:
- if the sale does not exist locally, it creates the sale and sale products
- if the sale already exists, it updates sale status, billing email, order payload, and missing items
That means both commands depend on the same normalization and persistence path.
4. Stock Side Effectsβ
Stock mutation happens in two different ways:
- during sale import, when a newly created or newly completed/processing sale is persisted
- during status synchronization, when a Magento status transition moves a sale into
processing
The stock change is logged through ProductQtyLogService::productChangeLog(...), which is why this workflow needs careful documentation of side effects and replay behavior.
5. RDV and Customer-Facing Side Effectsβ
The import and update flow can call handleRdv($sale) for relevant sales.
The update flow also attempts special handling when:
- the new Magento status becomes
processing - the real shipping method is
RETRAIT
In that branch, it calls sendRetraitEmail($sale). In the current code, sendRetraitEmail() returns immediately, so the operational intent exists even though the email logic is effectively disabled in the present implementation.
6. Delta Recovery Passβ
menzzo:v2:sales:update includes a final delta fetch of Magento orders updated in the last 48 hours. This appears intended to catch out-of-band status changes such as late payment-related updates that may fall outside the primary status buckets.
That recovery pass is important operationally because it reduces the chance of stale local order state after delayed upstream updates.
Inputs and Outputsβ
Inputsβ
- Magento order payloads from
mz.sale.api - local cursor from config (
last Magento sale id) - local sales filtered by date range and status
Outputsβ
- created or updated
SaleandSaleProductrecords - updated config cursor
- SaleLog entries for status synchronization
- stock movement logs
- system alerts on import failures
- one critical
SaleImportDisabledAlertwhenMZ_ACTIVATE_SALES_IMPORTremains disabled for more than 30 minutes - optional Microsoft Teams notification through
MZ_TEAMS_ALERT_WEBHOOK_URL, with Teams config bootstrapped by the monitoring notification path if missing - RDV handling side effects
Operational Risksβ
Cursor Riskβ
menzzo:v2:sales advances the Magento cursor after each successfully imported order. If an order fails, the command skips it and continues. This is safer than aborting the full batch, but it also means operators need a clear replay strategy for skipped orders.
Partial Replay Riskβ
Because SaleService::addOrder() is reused for both create and update behavior, replaying a payload is not a pure no-op. It may:
- update status
- add missing sale items
- trigger stock movements for newly created sale products in active statuses
Upstream Status Driftβ
menzzo:v2:sales:update is responsible for reconciling LogiDAV with Magento, especially when upstream payment or workflow events arrive later than expected.
Deadlock / EntityManager Failure Handlingβ
The import command explicitly downgrades some failures such as deadlocks or closed entity manager cases to warning-level alerts and continues processing the batch.
Disabled Import Alertingβ
MZ_ACTIVATE_SALES_IMPORT is the feature flag controlling LogiDAV sales import. If its config row is missing, both menzzo:v2:sales and menzzo:v2:sales:check-disabled-import recreate it idempotently with default value 1 (enabled). This default is intentional: a deployment or config corruption must not leave menzzo:v2:sales unusable. Existing values are never overwritten, so this does not change import behavior when the row already exists and a voluntary /config disable remains respected.
Disabling the import is allowed during deployment or operational work. No alert is sent immediately. Alerting starts only when the config remains disabled for more than 30 minutes, measured from the row updated_at value. If that timestamp is missing or malformed, monitoring logs/skips alerting rather than crashing the cron.
The import-disabled alert is deduplicated through SystemAlertService::add() using model SaleImportDisabledAlert::class and stable info {type: sale_import_disabled, config_code: MZ_ACTIVATE_SALES_IMPORT}. One open alert is expected for that stable info key. As long as the alert is open, repeated cron executions do not send repeated email or Teams notifications.
When the import is reactivated, the next menzzo:v2:sales:check-disabled-import run resolves matching open disabled-import alerts automatically. This lifecycle is intentionally owned by the monitoring cron; manual resolution is not the only recovery path.
Teams notification is optional. MZ_TEAMS_ALERT_WEBHOOK_URL is created idempotently by the monitoring notification path if missing, and an existing webhook value is never overwritten. If the row is empty, no Teams notification is sent. Teams delivery failures are caught and logged so they never block the cron or the SystemAlert email flow.
What Good Documentation Must Coverβ
For future maintainers, this workflow should always document:
- where the import cursor is stored and how to reset it safely
- how
SaleService::addOrder()behaves on create vs update - when stock is decremented
- when RDV handling is triggered
- whether
RETRAITemail behavior is active or intentionally disabled - how to replay skipped or failed Magento orders
- which logs and alerts operators should inspect first