Pre-order lifecycle
A pre-order moves through six states. The two events doing the heavy lifting are order placement (deposit + reservation + vault token) and stock arrival (status → ready, customer emailed a tokenised payment link, or vault-charged automatically on shipment).
The six statuses
| Status | What it means |
|---|---|
pending | Pre-order created, deposit captured. Stock not yet available. |
awaiting_stock | Explicit wait-for-stock state. Transitions to ready once stock arrives. |
ready | Stock has arrived; pre-order is ready to fulfil. With an outstanding balance, the Pay Remaining Balance email is queued for the drainer cron. |
partial_complete | Some items completed, others still ready/pending (multi-line pre-orders). |
complete | Balance fully captured and pre-order fulfilled. |
cancelled | Customer or admin cancelled — MSI reservation reverted, deposit refunded if configured. |
Event flow
1. Customer adds pre-order item to cart
- The PDP info block (
Block/Product/View/PreorderInfo.php) renders the deposit breakdown and availability date. - For configurable / bundle / grouped products, the eligibility is checked via AJAX (
Controller/Product/CheckPreorder.php). - The cart plugin (
Plugin/Checkout/Cart/AddPlugin.php) tags the quote item withis_preorderso it survives all the way to the order item.
2. Customer reaches checkout
- Pre-order line items render with a Pre-Order pill in minicart, cart, checkout summary, and the order success page.
- Totals collector adds the pre-order fee line where configured.
PreorderFeeConfigProviderinjects deposit data into the checkout config so the JS layer doesn't recompute.
3. Order placement
When the order is placed, Observer/CreatePreorderAfterOrderPlace.php fires on checkout_submit_all_after (which fires after the order has been persisted — using sales_order_place_after would trip the foreign-key constraint):
- Creates the
byte8_preorderrow + items viaPreorderManagement::createFromOrder. - Resolves the vault token from the order's payment method (
Model/Service/VaultTokenResolver.php) and stores it on the pre-order — when present, this is what makes shipment-day balance capture automatic. - Places an MSI reservation for the pre-ordered quantity (
Model/Inventory/ReservationManager.php) so warehouse sees the right available-to-promise number.
The order confirmation email goes out enhanced with a pre-order summary callout + per-item indicators. See Email configuration.
4. Stock arrives
Three triggers can flip a pre-order to ready:
- MSI stock save —
Plugin/Inventory/SourceItemsSavePlugin.php(afterExecuteonSourceItemsSaveInterface) catches the save, identifies SKUs that becameIN_STOCKwith qty > 0, and callsPreorderManagement::updateAvailabilityfor each. See MSI integration. - Legacy stock-item save —
Observer/CheckPreorderAvailability.phponcataloginventory_stock_item_save_afterfor non-MSI stores. - Manual scan —
byte8:preorder:scan-stockCLI runs the same code path on demand. Recommended as a 5–10 minute cron fallback against bulk imports or vendor-sync edge cases. See CLI commands.
updateAvailability matches both pending and awaiting_stock items, flips each to ready, and — when every sibling on the parent is now ready or complete — calls markPreorderReady to flag the row for the email drainer (next step).
Admins can also force this transition manually via Mark Ready in the pre-order grid, on the order-view Pre-Order tab, or via mass action.
5. Customer pays balance
When markPreorderReady runs on a pre-order with total_remaining_amount > 0:
- Status flips to
ready, the completion token is generated/persisted onbyte8_preorder.completion_token, andready_notification_sent_atis cleared. No email is sent inline — the action takes ~5ms. byte8_preorder_send_ready_notificationscron (every minute, see Cron jobs) drains the queue: sends the Your Pre-Order is Ready — Pay the Remaining Balance email with a tokenised completion link built against the storefront base URL.- The customer clicks the link (
/preorder/order/complete/preorder_id/{id}/token/{token}) — no login required, token validated viahash_equals. They pay the balance via Mollie / Stripe / your configured gateway. - On success, the completion-order observer (
ProcessCompletionOrder) records the payment, setscompletion_order_id, and callscompletePreorderonce the balance hits zero.
This is the flag-and-drain pattern — deliberately decouples status transition from SMTP latency so bulk paths (mass Mark Ready, MSI bulk import) stay fast and resilient to email infrastructure hiccups.
6. Shipment-day vault capture (when a token is stored)
If byte8_preorder.vault_token_public_hash is populated, the configured vault capture trigger (shipment / tracking-added / manual) fires:
Plugin/Sales/Model/Order/ShipmentPlugin.php(orTrackPlugin.php) marks the pre-order asvault_capture_pending.Cron/ProcessVaultCaptures.php(every 5 min) picks it up and charges viaVaultCaptureService.- On success — pre-order moves to
complete, capture recorded inbyte8_preorder_payment. - On failure — cron retries with backoff. After
max_attempts, the customer receives thevault_capture_failedemail pointing them to the same tokenised completion link.
Where to configure each step
| Step | Configuration |
|---|---|
| Deposit % / fixed / full | Payments configuration |
| Vault capture trigger + retries | Vault capture configuration |
| All emails | Email configuration |
Where to inspect a pre-order
- Admin grid: Sales → Operations → Pre-Orders. Per-row actions: View, Mark Ready, Capture Vault, Force Complete, Cancel. See Pre-order grid.
- Order-view tab: Sales → Orders → 000000XXX → Pre-Order tab. Same actions, in the context of the order. See Order-view Pre-Order tab.
- Mass actions: Available on both the pre-order grid and the sales order grid. See Mass actions.
- CLI:
bin/magento preorder:list— filter by status, vault state, balance, email. See CLI commands. - Customer account: My pre-orders — customer-facing list with cancel + pay-balance links.