Skip to main content
All posts
January 30, 2026Β·2 min readΒ·Diogo Hudson

Every stock movement tells a story

RESERVE, RELEASE, SHIP, RETURN, RECEIVE, ADJUSTMENT. Six kinds, one ledger, zero ambiguity about why anything moved.

Every stock movement tells a story

Most inventory systems have one number that changes. Quotery has a typed movement for every change β€” so 'why did this product go down by 3?' is not a mystery but a join.

The single-number approach is seductive in its simplicity. UPDATE stock_item SET quantity = quantity - 3 WHERE sku = 'AB-001'. One query, done. But that query tells you nothing about why 3 units moved. Was it a sale? A return? A warehouse adjustment? A correction of a miscount from last month? The number changed, the reason evaporated. Six months later, when the auditor asks, the answer is 'I don't know β€” the system says we shipped it.' The system is correct but useless for understanding.

The six kinds RESERVE (quote closed), RELEASE (quote cancelled from closed), SHIP (delivery note posted), RETURN (return note posted), RECEIVE (stock receipt posted), ADJUSTMENT (admin-only manual correction). Each kind has an expected sign and an expected source document.

The type system is what gives the ledger its explanatory power. A RESERVE movement means something different from a SHIP movement, even if they move the same quantity. The type tells you the business event that caused the movement. The source document FK tells you exactly which quote, delivery, return, or receipt triggered it. The timestamp tells you when. The user who initiated the transaction tells you who. Every dimension of the 'why' is captured in the row.

Signed deltas A RESERVE writes on_hand=0, reserved=+N. A SHIP writes on_hand=-N, reserved=-N. The ledger stores the delta; the cache stores the running total.

The separation of delta from total is what makes the ledger reconstructable. Each row is self-contained: it records the exact change to on_hand and reserved at that moment. To compute the state at any point in time, sum all deltas up to that point. There's no need to trust the cache β€” the cache is just a materialized view of the ledger, and any discrepancy is resolved by re-summing, never by editing the ledger.

One source document, one movement Posting a DeliveryNote creates a SHIP movement with the delivery's id. Cancelling a closed Quote creates a RELEASE with the quote's id. The FK is optional only for ADJUSTMENT β€” and even there we require a non-blank note.

The mandatory note on ADJUSTMENT is the safety valve. Manual corrections happen β€” inventory counts drift, products get damaged, samples get taken for quality testing. When an admin adjusts stock manually, the note field captures the human explanation: 'Physical count found 2 fewer units than system recorded β€” likely damaged during June heat wave.' That note is visible in the ledger and in the audit trail. It's not a substitute for a proper process, but it's a durable record of an exceptional event.

How the stock ledger tracks your inventory.

All posts
Short pieces on quoting, inventory, AI, and how small distributors ship a lot of stuff without the fuss.