← All notes

Inventory

Stock is a ledger, not a number

A quantity column tells you what you think you have. A movement log tells you how you got there, which is the only thing that helps when it is wrong.

  • Invexa Technologies
  • 2 min read

Every inventory system starts with products.quantity. Sell one, decrement. Receive ten, increment. It is the obvious model and it is the same mistake as a mutable account balance.

The question you cannot answer

The count says 47. The shelf says 44. Where did three go?

With a quantity column there is no answer. You cannot tell whether it was a miscount at receiving, a sale that decremented twice, a return that never incremented, or theft. You can only correct the number to 44 and lose the evidence that anything happened.

Do that a few times and nobody trusts the system, so people keep their own spreadsheets, and now you have two wrong numbers.

Movements, append-only

stock_movements
  sku
  location_id
  delta            -- signed: +10 receipt, -1 sale
  reason           -- receipt | sale | return | adjustment
                   -- | transfer_in | transfer_out | damage
  reference        -- order id, transfer id, count sheet id
  batch_id         -- null unless tracked
  occurred_at
  actor

Quantity on hand is SUM(delta) filtered by sku and location. Never stored, never updated, only derived.

Every row is immutable. A mistake is corrected by writing the opposite movement with reason = 'adjustment' and a reference to the count that found it — so the correction is as visible as the error.

The performance objection, answered

Summing millions of rows per lookup is genuinely slow, and this is where teams retreat to a quantity column. The right answer is a periodic snapshot:

stock_snapshots
  sku, location_id, as_of, quantity

Current stock is the latest snapshot plus movements since. Snapshot nightly and the sum covers a day, not a decade. Crucially the snapshot is a cache: delete every row and it rebuilds from movements. A quantity column cannot be rebuilt from anything, which is the whole difference.

What it costs and what it buys

One extra table and a nightly job. In return: “why is this wrong” becomes a query, cycle counts write adjustments instead of overwriting truth, and stock at any past date is answerable — which matters the first time a supplier disputes a delivery from six weeks ago.

Next step

Have a project in mind?

A 30-minute call is usually enough to know whether we are the right team for it. If we are not, we will say so.

Start a project

Replies within one working day