Delivery lifecycle
The states
A delivery and each of its lines carry the same three statuses:
| Status | Meaning |
|---|---|
created | The delivery note exists. Nothing has been verified |
receiving | Verification has started — some lines are counted, others are not |
submitted | Approved. The approved quantities have been published |
created ──────► receiving ─── ───► submitted
│ │
│ │ corrections
└──────────────────────────────────────┘ re-publish
(approve everything at once)
A delivery is submitted when its lines are. Approving one line of a five-line delivery moves that line to
submitted and the delivery to receiving; the delivery reaches submitted when the last line does.
⚠ There is no cancelled or rejected state. A delivery that should not have existed is not withdrawn — it is
approved with a zero quantity and a reason code, which is what leaves an auditable record of why nothing was
accepted.
Approving a delivery
Approval is what publishes data. Until a line is approved, nothing has left the service. There are three granularities, and they exist because goods do not always arrive in one piece:
| Approve | When to use it | What is published |
|---|---|---|
| The whole delivery | Everything arrived and was checked together | Every not-yet-approved line |
| One container | A pallet or cage has been unpacked and checked, others have not | Every not-yet-approved line in that container |
| One line | Progressive receiving — staff check items as they are put away | That line |
Each approval publishes an event containing only the lines that approval covered. A five-line delivery approved line by line produces five events, not one; a consumer must therefore treat each event as a partial statement about a delivery, never as the delivery's final content. See External Events.
Approving something already approved does not re-publish it. Lines that are already submitted are skipped, so
re-approving a container after adding one line to it publishes only the new line.
Corrections after approval
A delivery is not frozen when it is approved. A miscount found the next morning, a damaged case discovered during put-away, or a late correction from the supplier can all still be recorded: change the line's adjustment, then approve it again.
The correction publishes a new event for that line. Because consumers have already acted on the first event, the event carries both the new absolute figure and the change since last time — see Quantities and adjustments. Using the wrong one is the classic way to double-count a correction.
Asynchronous writes
🛑 Every write returns 202 Accepted, not 200 OK. Creating a delivery, adding lines, setting an adjustment and
approving are all accepted for processing. The service records them a moment later. If you write and then
immediately read, you can legitimately get the old value back.
This is deliberate: it is what keeps receiving usable when the store's connection is poor, and it is why approvals survive a mid-shift restart. It does mean a client has to be built for it.
Each delivery and each line therefore also carries a processing status:
processingStatus | Meaning | What a UI should do |
|---|---|---|
IDLE | Nothing in flight. The values you can read are the current ones | Normal display |
PENDING | A write for this delivery or line has been accepted and not yet applied | Show a spinner or a "saving" state; do not offer a conflicting action |
FAILED | A write was accepted and could not be applied | Surface it. The service retries; if it clears to IDLE the write succeeded after all |
The scope is precise, and it is worth knowing: approving a whole delivery marks the delivery; approving a line or a container, or setting an adjustment, marks the lines. So a line-level save never makes the delivery header look busy, and two staff members working on different lines of the same delivery do not block each other.
FAILED is not final. The failed write is retried automatically, and a retry that succeeds clears the status back to
IDLE. Treat FAILED as "this has not landed yet and may need attention", not as "this has been rejected".
What this means for an integration
⚠ A 202 does not mean the delivery was created. Validation that can be done on the request itself — a missing
field, an unparseable file, a quantity that does not match its adjustments, an unknown reason code — is done
immediately and returns 400. Validation that needs other data is not: an item that cannot be resolved, or a date
that cannot be stored, fails after the caller has been given 202.
For an interactive client this is invisible, because the user sees the delivery appear or not. For a
machine-to-machine integration it matters: after a successful import, confirm the delivery exists by reading it back
rather than assuming the 202 was the end of the story. See
GS1 import.
Identity and re-sending
A delivery's identity is derived from the receiving store, the delivery note number and the shipper — not from a generated key. Sending the same delivery note for the same store from the same shipper twice therefore refers to the same delivery, and the second import is ignored rather than creating a duplicate.
That makes a retry safe: if a network error leaves you unsure whether an import was received, send it again.
⚠ It also means the delivery note number must be stable and unique per shipper. A sending system that reuses delivery note numbers across shipments, or renumbers on retry, will either collide with an old delivery or create duplicates. Deliveries created from a completed store transfer are keyed on the transfer instead, and are equally safe to reprocess.