Skip to content

Sales Import ​

The import and status-update sections below were reviewed at 42116ebc on 2026-10-28. Some other sections retain earlier source references listed above; they are partial historical evidence and have not all been rechecked against that revision.

What It Does ​

The reviewed code has one command path that imports Magento orders after a persisted entity-ID cursor and another that reconciles local order status in three passes. The installed schedules and execution frequency are not established here, and neither command guarantees that every failed or missed order is retried.

Start with the order import and carrier distribution journey for the end-to-end map. For the rules executed while creating order lines, see order import and line-item rules and order-line carrier assignment.

Configuration and triggers ​

The following behavior is source-reviewed at Logidav revision 42116ebc0ba509bce9cc6651a55ecefd9a5fdb38. Values in a running environment and its scheduler are not established by source code.

Setting or componentWhat the code doesWhat to verify in the environment
MZ_ACTIVATE_SALES_IMPORTmenzzo:v2:sales ensures the config row exists, then imports only when its value casts to integer 1. If the row is absent, the initializer creates it enabled; an existing value is not overwritten.Current stored value, ownership and change history. This flag is checked by the new-order command; do not assume it gates every update command.
LAST_MAGENTO_IDThe new-order command reads this cursor, fails if it is empty, and asks Magento for entity_id greater than that value. It writes the current order ID after addOrder() returns.The current value and affected Magento entity IDs. Do not change the cursor without an approved recovery procedure.
menzzo:v2:salesAcquires a lock; queries the first page with pageSize=300; processes returned orders one at a time. A competing run exits early.Actual cron/scheduler, API connection and run logs; source alone does not prove frequency or successful execution.
menzzo:v2:sales:updateSeparately compares selected local Menzzo sales with Magento. It has a processing pass, a pass for cancellation/payment/hold states, and a bounded recent processing delta query. --workOn=currentDay changes the date window to the previous seven days.Actual invocation, schedule, relevant order status and last successful result.
Magento API credentialsThe update command uses the configured sale API and refreshes its token using configured administrative credentials.Connection health and authorization through the approved operational procedure; never put credentials in a ticket.

The flag's default-on behavior is defined by ConfigService; the import's lock, flag and cursor are in SaleCommand.

What happens if the order already exists? ​

addOrder() looks up the sale by source and source order ID. If found, it updates selected sale-level data, adds missing lines, and calls the base updateOrderItem() for existing lines. That base method updates the two Alfy fields; it does not generally replace every existing line's price, quantity, delivery mode or carrier. A new line goes through addOrderItem() and carrier selection. Connector overrides and other explicit update paths can behave differently; see line-item rules.

How It Connects ​

View diagram source
flowchart LR
    Magento2["Magento 2"] -->|REST API| SaleCommand["SaleCommand"]
    SaleCommand --> SaleService["SaleService"]
    SaleService --> DB["Logidav DB"]
    SaleService --> PQL["ProductQtyLogService"]
    PQL --> DB
    SaleService --> StockMut["ProductQtyLogService sale mutation"]
    StockMut --> DB

Diagram

Trigger ​

CommandInstalled scheduleSource-backed behavior
SaleCommandUnknownReads LAST_MAGENTO_ID and requests Magento orders with entity_id > cursor.
UpdateSaleCommandUnknownFetches Magento state for local orders and applies selected status changes.

Components ​

ComponentFile PathRole
SaleCommandsrc/AppBundle/Command/Sale/V2/SaleCommand.phpImports new orders from Magento
UpdateSaleCommandsrc/AppBundle/Command/Sale/V2/UpdateSaleCommand.phpReconciles statuses with Magento
SaleServicesrc/AppBundle/Services/SaleService.phpUpsert logic for orders and line items
ProductQtyLogServicesrc/AppBundle/Services/ProductQtyLogService.phpJournals stock mutations triggered by imports

Step-by-Step Flow: New Order Import ​

View diagram source
flowchart TD
    A["SaleCommand (schedule unknown)"] --> B["Read LAST_MAGENTO_ID"]
    B --> C["Fetch Magento orders where entity_id > cursor"]
    C --> D{"For each Magento order"}
    D --> E["SaleService::addOrder(..., isV2Data=true)"]
    E --> F{"Sale exists locally?"}
    F -->|New order| G["Create Sale and line items"]
    F -->|Existing order| H["Update Sale and add any new line items"]
    G --> I{"New status processing or complete and not FROM_SAV?"}
    I -->|Yes| J["Apply sale mutation to all lines"]
    I -->|No| K["No sale mutation"]
    H --> N{"Existing order status processing or complete?"}
    N -->|Yes| O["Apply sale mutation only to newly added lines"]
    N -->|No| P["No sale mutation for existing lines"]
    J --> Q{"addOrder succeeded?"}
    K --> Q
    O --> Q
    P --> Q
    Q -->|Yes| L["Advance cursor to this Magento ID"]
    Q -->|No| R["Log/alert error and continue"]
    L --> S["Later higher ID may advance cursor past failed ID"]

Diagram

After successful addOrder(), the reviewed new-order command collects processing and complete sales and calls handleRdv() after the import loop. That appointment pass runs only after the cursor has been advanced for each successful import. The exact appointment eligibility and side effects require a separate trace. The command also has an old-order date check before the per-order try: an order from a previous year is rejected except December 31, so this case is not handled by the later per-order catch. Source.

Key concept: cursor-based import

The command stores the cursor after SaleService::addOrder() succeeds. A caught failure is logged/alerted and processing continues. If a later, higher-ID order succeeds, the cursor can advance past the failed order; the code does not establish automatic replay or a universal manual-replay procedure.

Step-by-Step Flow: Status Synchronization ​

View diagram source
flowchart TD
    A["UpdateSaleCommand (schedule unknown)"] --> B["Build date window"]
    B --> C["Pass 1: local processing sales"]
    C --> D["Fetch Magento records in chunks of 100"]
    D --> E{"Status changed?"}
    E -->|Yes| F["Log status and addOrder"]
    E -->|No| G["Run handleRdv for still-processing sale"]
    F --> H["Pass 2: cancellation/payment/hold states"]
    G --> H
    H --> I["Fetch Magento records in chunks of 100"]
    I --> J{"Status changed?"}
    J -->|Yes| K["Log status; processing adds stock logs and RETRAIT email; addOrder"]
    J -->|No| L["No status mutation"]
    K --> M["Pass 3: updated_at > 48h and status processing"]
    L --> M
    M --> N["Fetch first page of up to 200"]
    N --> O{"Local sale differs?"}
    O -->|Yes| P["Log status; processing adds stock logs and RETRAIT email; addOrder"]
    O -->|No| Q["Skip"]

Diagram

The update command is a reconciliation loop, not one generic “sync all orders” operation:

  1. Processing pass. It searches local Menzzo sales with status processing over the selected date window, requests Magento records in chunks of 100, skips local sales already marked complete, skips missing records and the configured Hipay refund states, then calls addOrder() for a status difference. If the Magento status is unchanged, it calls handleRdv() for the still-processing sale.
  2. Cancellation and payment pass. It repeats the window and 100-record chunking for the empty status plus canceled, pending, pending_payment, holded, payment_refused, hipay_authorization_requested, hipay_authorized, payment_review, hipay_capture_refused and payline_refused. On a transition to processing, it writes a quantity log for every line and sends the pickup email when the real shipping method is RETRAIT, then calls addOrder().
  3. Out-of-window delta pass. It requests only the first page of up to 200 Magento records updated in the last 48 hours with status processing. It skips Hipay refund states, missing/local-complete/unchanged sales, and applies the same quantity-log and pickup-email side effects before addOrder() when a transition is found.

The default date window starts at midnight one month before the current date and advances in one-month periods. With --workOn=currentDay, the command checks the previous seven days one day at a time. These are source-level windows; the installed schedule and runtime clock are unknown.

48-hour delta pass

The reviewed delta path requests the first page of up to 200 Magento orders with updated_at newer than two days ago and status processing, then applies status changes for matching local sales. Source comments describe late-webhook recovery intent, but this is not a general status replay or guarantee that every missed change will be recovered.

Lock and completion boundaries

Both commands acquire a Symfony command lock. The new-order command's visible release() call is inside the loop that handles imported processing/complete sales, so a run with no eligible sales has no explicit release on the inspected path. The update command releases only after all three passes. An exception or an early return before those points can leave lock behavior dependent on the command runtime; confirm the deployed behavior before treating a later “already running” message as a data failure.

Side Effects ​

Database Persistence ​

  • SaleService::addOrder() upserts by Magento order ID/source and may add line items to an existing order.
  • The import cursor is stored only after an order's addOrder() call succeeds; a later success can advance past a failed lower ID.
  • The status command records status changes in the reviewed path; it does not establish coverage for every Magento transition.
  • The second and delta passes explicitly log quantities and invoke the pickup-email path when a sale changes to processing; the first pass has different side-effect entry points. Do not assume all three passes are equivalent.

Stock Mutations ​

  • For a new order in processing or complete, addOrder() applies the sale mutation to all lines unless FROM_SAV is defined.
  • For an existing order in those statuses, addOrder() applies the mutation only to newly added line items. UpdateSaleCommand has a separate explicit mutation when a status changes to processing.
  • These conditions do not establish a generic double-decrement incident or a full replay-safety guarantee; see Stock Updates for the reviewed boundaries.

Customer Actions ​

  • Existing documentation describes appointment and pickup-email behavior, but these paths were not covered by the current source review and should be verified separately.

Failure Modes ​

Cursor offset

If addOrder() throws, the command logs/alerts and continues. A later successful higher Magento ID can move the cursor past the failed ID; retry/replay behavior for that order is not established by this path.

Non-idempotent replay

SaleService::addOrder() upserts by Magento order ID/source. For an existing active order, the reviewed stock mutation is limited to newly added lines; the source does not establish that replaying an unchanged payload repeats existing-line mutations or that every replay is safe. Check order lines and quantity-log history before an approved replay.

Upstream status drift

The two-day delta query is limited to recent Magento orders whose status is processing; it is not a general eventual-recovery guarantee for other statuses or older changes. The operational impact of a missed transition is not established here.

Deadlocks and EntityManager errors

Legacy documentation says deadlocks and EntityManager errors may be logged while a batch continues, but this behavior was not covered by the source packet. Confirm the current error handling in the command before relying on it.

Debugging Path ​

  1. For a missing order, compare the affected Magento entity ID with LAST_MAGENTO_ID; a later successful order can move the cursor past a failure.
  2. For a status mismatch, compare the local sale with the Magento response and inspect the status-change record through the approved operator procedure.
  3. For stock differences, identify whether the order was new or existing and whether the line was newly added; inspect the related quantity-log history before considering a replay.
  4. The delta query only covers the first page of recent processing orders. Do not assume it recovers every status or older change.
  5. If the command reports that it is already running, first establish which command acquired the lock and whether it reached its visible release path; do not delete a lock or restart a worker from this page alone.
  6. Do not force a cursor or replay from this page alone. Use the approved recovery procedure and capture the before/after state.
CommandPurpose
SaleCommandImports Magento orders after LAST_MAGENTO_ID; installed schedule is unknown.
UpdateSaleCommandReconciles selected local order statuses; installed schedule is unknown.
menzzo:v2:sales:paymentExisting documentation lists payment processing; the current evidence packet did not verify this command.
sale:expedition:importExpedition data import
menzzo:lengow:sales --marketplace=<account>Import Lengow V3 marketplace orders for accounts 5622, 6395, and 17220

Marketplace Connector Imports ​

Lengow marketplace imports follow the same final persistence model as Magento imports: the connector-specific command normalizes the remote order and calls SaleService::addOrder(). The main V3 entry point is src/ErpBundle/Command/Lengow/V3/LengowSalesCommand.php, and the import logic lives in src/ErpBundle/Services/Lengow/V3/LengowService.php.

Key differences from Magento:

  • duplicate detection uses Lengow marketplace_order_id plus source = Lengow;
  • V3 imports are account-scoped with --marketplace=5622, --marketplace=6395, or --marketplace=17220;
  • imported sales keep lengow_marketplace, lengow_account_id, and full orderinfo.lengow_order_info for feedback and diagnosis;
  • product resolution is connector-specific, including the Mosaikasa 17220 caveat where the merchant product id must match mz_product.ean_bis;
  • failed product/order normalization is held as status = holded and monitored through Lengow issue views and alerts.

See Lengow Integration for the full flow, menu views, status feedback, and retired V2 feed notes.

Evidence and Open Questions ​

Source-verified at axelites/logidav@42116ebc0ba509bce9cc6651a55ecefd9a5fdb38: cursor import, lock and exception handling, order upsert and line-item stock conditions, processing and status reconciliation passes, and bounded two-day delta query.

Installed command schedules, appointment and pickup-email paths, payment processing, deadlock/EntityManager recovery, and complete Lengow execution details were not covered by this packet. Do not infer replay safety or general status-recovery guarantees from the reviewed paths.

Internal developer documentation for Menzzo systems.