ZuloOne
English
Sign in
Menu

📥 AutoReceiveOnOrder auto-receipt

Updated

← Developer wiki For the user: what the interface shows.

PurchasingSettings.AutoReceiveOnOrder receives the goods with the same click that places the order. "Place order" writes Ordered, then Received — two saves, because the transition table cuts off the jump Draft → Received.

Where the checks live#

Receipt checks (the RECEIVING cell when warehouse discipline is enabled, an active tax rate as of the receipt date) live in IPurchaseReceiptService — one implementation for both paths:

Path Call
The "Receive order" button (ReceiveOrderCommand) ReceiveAsync(orderId)
"Place order" with AutoReceiveOnOrder (PlaceOrderCommand) ValidateReceiveAsync while still in Draft, then ReceiveAsync

Two copies of these checks would drift silently, and the drift is visible only on the posting entry, so both commands go through one door.

ReceiveQty on a line is split in the same ReceiveAsync, before the move to Received. Zero on all lines is left alone: old orders are received in full. Otherwise the line quantity becomes "receive now", and the remainder becomes a new PurchaseOrder in Ordered linked to this one. There is no posting for the remainder until it is received. ReceiveQty greater than Quantity is a line refusal, and the subtype does not change.

A refusal leaves Draft, not Ordered#

PlaceOrder calls ValidateReceiveAsync before leaving Draft. The user pressed one "place and receive" button: a failed receipt check must not leave the order in Ordered, a state they did not choose.

The check is repeated inside ReceiveAsync: between the first check and the transition the document was saved, and the receipt must judge the state in which it is posted.

Off (the default) — only Ordered; receipt is not checked at all.

Trap when writing the test#

Turning on InventorySettings.EnforceWarehouseTasks is not a passive flag. Its OnAfterSave calls PrepareAllYardsAsync(), and that calls InferTypePurposesAsync(), which sets StoreCellType.Purpose by the type NAME when the field is empty (the cell role still lives in the name as a fallback). A type named "Receiving" becomes a receiving type by itself after discipline is enabled.

Therefore the "cell with no assigned purpose" failure scenario cannot be built that way: the purpose must be set explicitly and strictly after discipline is enabled, otherwise the inference from the name will overwrite it back. The test must check its own precondition (IsWarehouseDisciplineOnAsync, GetCellPurposeAsync); otherwise a failure only means "something is wrong" and sends you looking for a bug in the command.

Tests: PlaceOrderCommandTest.AutoReceiveOnOrderReceives (success) and AutoReceiveBlockedKeepsDraft (refusal leaves a draft).

Same topic for other readers: Analyst · User

Where next#

Was this page helpful?