Order lifecycle
Every order carries an OrderState (a string on OrderBase) that moves through
an allocate leg (pick and dispatch) and/or a receive leg (scan in at the
destination), depending on the order's type. Two further fields ride alongside
the state:
OrderStatus(Ready/Processing/Errored) is a processing overlay, not part of the state machine.Processingis UI metadata only — the real mutex is a per-order SQL app lock.Erroredmeans the order has unresolved error tags: it blocks Dispatch and Complete, but not the operations that can resolve the errors (AddSnapshot, AppendTags, Allocate, Receive, Cancel, manual fulfilment).OrderType(Inbound,Replenishment,Outbound,Transfer,Return,Loan) selects which legs of the state machine the order uses.
Every transition writes an OrderTransactionHistory row — the audit trail
behind GET api/orders/getordertransactions.
States and transitions
Completed and Cancelled are terminal. Cancel is allowed from every
non-terminal state.
Allowed operations per state
| State | AddSnapshot | AppendTags | Allocate | Dispatch | Receive | Complete | Cancel | Revert |
|---|---|---|---|---|---|---|---|---|
Open | ✔ auto → Allocating (or Receiving for receive-only types) | ✔ same | ✖ | ✖ | ✖ | ✖ | ✔ | ✖ |
Allocating | ✔ | ✔ | ✔ gate | ✖ | ✖ | ✖ | ✔ | ✖ (remove-snapshot ✔) |
Allocated | ✖ | ✖ | ✖ | ✔ → Dispatched | ✖ | ✖ | ✔ | → Allocating |
Dispatched | ✔ auto → Receiving | ✔ auto → Receiving | ✖ | ✖ | ✖ | ✔ Outbound only → Completed | ✔ | ✖ |
Receiving | ✔ | ✔ | ✖ | ✖ | ✔ gate | ✖ | ✔ | ✖ (remove-snapshot ✔) |
Received | ✖ | ✖ | ✖ | ✖ | ✖ | ✔ → Completed | ✔ | → Receiving |
Completed / Cancelled | terminal — everything ✖ | ✖ |
Reverts are explicit endpoints:
reverttoallocating and
reverttoreceiving. Removing a
snapshot (allowed only in Allocating/Receiving) reverts each scanned item
to the OldItemState/OldPlaceId captured at scan time.
The six order types
| OrderType | Flow |
|---|---|
Inbound, Replenishment, Return | Open → Receiving → Received → Completed — receive leg only, no allocate/dispatch. |
Outbound | Open → Allocating → Allocated → Dispatched → Completed — allocate leg only, no receive. |
Transfer | Full chain: Open → Allocating → Allocated → Dispatched → Receiving → Received → Completed. |
Loan | Full chain, with the loan variants of the item states (Reserved/OnLoan/Returned) and reversed place moves. |
Stage gates: Allocate and Receive
Allocate and
Receive only succeed when:
- The order has at least one snapshot.
- Error tags are re-evaluated first — items that now pass are promoted.
- Every order line's quantity constraint is met (
OrderQtyActionType:Exact±0.01,OverOnly,UnderOnly, orAll— measured in outer-SKU units), and no error tags remain.
A failed gate logs an AllocateFailed/ReceiveFailed transaction and leaves
the state unchanged. On a successful Receive, allocated-but-unreceived items
are swept to Missing.
Dispatch, Receive
and Complete return HTTP 200 even when the
transition fails. Diff the returned OrderState against what you expected
and inspect the order's error collections — see
Errors & response conventions.
Item-state cascades
Order operations drive the order-owned item states — the other half of the Item lifecycle transition matrix:
| Order operation | Standard orders | Loan orders | Return orders |
|---|---|---|---|
| Allocating scan | Allocated | Reserved | — |
| Dispatch | Dispatched | OnLoan | — |
| Receiving scan | Received | Returned | Restored — the only exit from Consumed |
Place moves ride along: an allocating scan moves the item to the order's FromPlace; dispatch moves items only for Loan orders (to the ToPlace); a receiving scan moves the item to the ToPlace (Loan: back to the FromPlace).
Cancel reverts items depending on where the order was: from
Allocating/Allocated, items go back to Present; from Dispatched, to
Missing; from Receiving/Received, received items go to Present and
un-received items to Missing.
Asynchronous processing
AppendTags, AddSnapshot, Allocate, Dispatch, Receive and Complete accept
NotifyOnceComplete plus a NotificationRecipientId. The recipient id must
equal the caller's own user id — otherwise the request silently runs
synchronously. The async response returns
OrderOperationResultDto.BackgroundTaskId and auto-creates auto-delete event
subscriptions for the completion callback. See
Async operations for the full pattern.
AutoReceiptOrder (Transfer and Loan orders): dispatch automatically runs the
entire receive leg in one go.
Related
- Item lifecycle — the item-side states these cascades set, and the deletion locks on items in active orders.
- Async operations —
NotifyOnceComplete, background tasks and completion events. - Errors & response conventions — the 200-despite-failure family and the 400-not-404 convention.