Quantities and adjustments
This page is the arithmetic. Getting it wrong is the single most likely way for a Goods Received integration to report the wrong stock, so it is worth reading before writing any consumer.
The four quantities
| Quantity | Means | Where it comes from |
|---|---|---|
| Expected | What the delivery note said would arrive | The shipper, via the delivery note |
| Adjusted | The signed difference between what arrived and what was expected | The receiving store |
| Approved | What the store accepted | Entered by staff (or derived from reason-code adjustments); validated by the service |
| Delta | The change in approved since this line was last published | Calculated, only on published events |
The first three are related by one rule, and the service enforces it:
approved = expected + adjusted
Adjusted is signed. A shortage is negative, a surplus is positive:
| Situation | Expected | Adjusted | Approved |
|---|---|---|---|
| Everything arrived | 10 | 0 | 10 |
| Three missing or damaged | 10 | −3 | 7 |
| Two more than the note said | 10 | +2 | 12 |
| Nothing usable arrived | 10 | −10 | 0 |
Approved quantity is what staff enter, by counter, keyboard or by scanning items as they are checked. What the service enforces is that the difference is explained: if the approved quantity does not match the expected quantity, reason codes accounting for exactly the difference must be supplied with it.
The relationship works in both directions, and a receiving app can offer either. Enter an approved quantity of 7 against an expected 10 and a −3 difference has to be explained; or record the reasons first — 3 damaged — and the approved quantity follows as 7. Same three numbers, same rule, whichever end staff start from.
Reason codes explain the adjustment
Every non-zero adjustment must be broken down into one or more reason codes, each carrying its own signed quantity. Their sum must equal the adjustment:
adjusted = Σ reason code quantities
A line short by five units, three damaged in transit and two never loaded:
| Reason code | Quantity |
|---|---|
damaged | −3 |
not-delivered | −2 |
| Adjusted | −5 |
With an expected quantity of 20, the approved quantity is 15.
Reason codes are signed the same way as the adjustment, so an over-delivery uses positive quantities — a code
such as surplus or over-delivery at +2.
⚠ Each reason code carries its own sign in configuration, so a code is not free to be used either way. A code marked as negative-by-default is offered for shortages, one not so marked for surpluses, and one with no marking for both. A store configured only with negative codes therefore cannot record an over-delivery at all — see Configuration.
A reason code can also be used only once per line. To split a difference you use two different codes, not the same code twice.
🛑 Reason codes must exist before they can be used. They are configured per store, and an adjustment quoting a code that is not configured for that store is rejected outright. If staff cannot save a correction, this is almost always why — see Configuration.
⚠ A non-zero adjustment without reason codes is rejected, and so is a set of reason codes that does not add up to the adjustment. Both are deliberate: an unexplained difference is not something the service will record.
Delta versus level
Published events carry both an absolute figure and a change, and they mean different things:
| Field | Kind | Meaning |
|---|---|---|
expectedQuantity | Level | What the note said. Does not change between events for a line |
approvedQuantity | Level | The accepted quantity as it now stands |
deltaQuantity | Delta | The change in approved quantity since this line was last published |
reasonCodes | Level | The line's complete current set of reason codes — not what changed |
🛑 deltaQuantity is a change but reasonCodes is a total. They are not two views of the same thing. A consumer
that accumulates reason code quantities across events the way it accumulates deltaQuantity will over-count every
correction. Replace a line's reason codes with each event's set; add up only deltaQuantity.
Worked example: a correction
A line expects 10 units. Three arrive damaged, so the store records an adjustment of −3 against damaged and
approves the line:
{
"lineId": "1",
"expectedQuantity": 10,
"approvedQuantity": 7,
"deltaQuantity": 7,
"reasonCodes": [{ "id": "damaged", "quantity": -3 }]
}
deltaQuantity equals approvedQuantity here because nothing had been published for this line before — the whole
approved quantity is new information.
The next morning one more unit is found damaged. The store changes the line's adjustment to −4 in total and approves again:
{
"lineId": "1",
"expectedQuantity": 10,
"approvedQuantity": 6,
"deltaQuantity": -1,
"reasonCodes": [{ "id": "damaged", "quantity": -4 }]
}
Read the second event carefully:
approvedQuantityis 6 — the new total, not the change.deltaQuantityis −1 — one fewer unit than last time.reasonCodesis −4 damaged — the running total for the line, not the extra −1.
A stock consumer applies deltaQuantity (−1) to what it already holds. A consumer that rebuilds state from scratch
uses approvedQuantity (6). A consumer reporting on causes of loss uses reasonCodes (−4) and overwrites whatever it
had for that line.
Summing across the two events: 7 + (−1) = 6, which matches approvedQuantity. That identity always holds, and it
is the cheapest way to check a consumer is correct.
Precision
Quantities are numbers, not integers. Items sold by weight or length arrive in fractional quantities, and a delivery
of 12.5 kg is expected, approved and adjusted as 12.5. Do not round on the way in, and do not assume a quantity
fits an integer field.
Prices are per unit, in the currency named on the line. A line's unitPrice may be absent when the source system did
not supply one — that means unpriced, not free.
Serial numbers, batches and dates
A line can carry a batch number, a best-before date, an expiration date and a list of serial numbers. These describe what was expected, and the service passes them through to consumers unchanged; they are not reduced or split when the approved quantity is lower than expected.
⚠ So a line expecting 10 serialised units and approving 7 still carries all 10 serial numbers. Do not infer which individual units were accepted from the serial number list — the service does not record that, and treating the list as the accepted set will overstate what is in stock.