Why we chose ledger-based stock over running balances
Running balances break silently. Ledger-based stock — append-only moves with immutable unit costs — gives you an audit trail that survives price changes, reversals and multi-location complexity.
The problem with running balances
Most inventory systems store a single number per item per location: the current quantity on hand. When a purchase arrives, the system adds to it. When a recipe consumes stock, it subtracts. Simple, fast, easy to query.
Until it breaks.
Running balances break in ways that are hard to detect and harder to correct. A background job crashes halfway through posting an invoice. A user double-submits a form. A developer runs a migration script that inadvertently adjusts quantities. A supplier return is partially processed. The number on screen becomes fiction, but there is no history to tell you when it diverged from reality or by how much.
What ledger-based stock looks like
A ledger-based system never updates a quantity. It only appends records. Every stock movement — purchase receipt, recipe consumption, waste event, transfer, stock count — becomes a new row in the StockMove table. The current quantity on hand is derived by summing all moves for a given item and location.
SELECT SUM(qty) FROM stock_moves
WHERE item_id = $1 AND location_id = $2
That query is the source of truth. There is no separate "current quantity" field that can drift out of sync. If a move was recorded incorrectly, you correct it by posting a correcting move — never by editing the original.
The Orion mark-to-market problem
We learned from a specific failure mode we observed in a competitor's platform (internal reference: "Orion mark-to-market bug", versions 4.1.19 to 6.1.20). Their system recalculated the unit cost of all historical consumption moves whenever a supplier price updated.
On the surface this seems reasonable: if the ingredient now costs more, shouldn't historical COGS reflect that? No. It destroys the integrity of closed periods. A P&L you signed off on in January should not change in March because a supplier raised prices.
The Orion bug caused reported food cost percentages for closed months to silently shift by 2–4 points every time a bulk price update ran. Finance teams were reconciling against numbers that had already moved.
How EYP Ops solves it: immutable unit costs
In EYP Ops, every StockMove row carries a unitCost field that is written at posting time and never changed. The cost of a recipe consumption is the weighted average cost of the ingredients at the moment the production was posted — not today's price, not the price at month-end.
This means:
- Closed periods stay closed. A February P&L will show the same food cost in April as it did in February.
- Price changes are prospective. A new delivery at a higher price affects moves from that delivery forward, not backward.
- Audit is trivial. Every COGS line can be traced to the specific stock moves that created it, each with its recorded unit cost.
Why this matters for multi-location operations
Restaurants with multiple outlets face a compounding version of this problem. A transfer from the main store to the kitchen moves inventory between locations. If you are storing running balances, a transfer requires updating two rows — and a crash between those two updates leaves the books unbalanced.
With ledger-based stock, a transfer is two moves: a TRANSFER_OUT from the source and a TRANSFER_IN to the destination. They are written in a single database transaction. Either both succeed or neither does. The books are always consistent.
For a group operating across three outlets — main store, kitchen, bar — the ledger approach is the only viable path to accurate outlet-level COGS that can be reconciled to the group P&L.
The trade-off: query complexity
The trade-off is real. Summing all moves on every balance query is slower than reading a single field. For an operation with years of history and hundreds of items, a naive implementation will become slow.
The solution is not to abandon the ledger model — it is to build appropriate materialized views or snapshot tables that cache period totals without sacrificing the audit integrity of the underlying moves. The cache can be wrong; the ledger cannot.
Starting without the infrastructure
You do not need a purpose-built ledger system to benefit from these principles today. Even in a spreadsheet, the practice of recording every movement (rather than updating a total) and preserving the unit cost at time of posting will give you a recoverable audit trail. The discipline comes first; the automation follows.
More posts
A practical food cost framework for Dubai restaurants
Why 28–32% food cost targeting matters, how to categorise variance, and the review cadence that separates profitable operators from the rest.
How to set up purchase orders that actually get reconciled
Three-way matching, tolerance rules and exception handling — the process discipline that turns your PO workflow from a paper trail into a control system.
Enjoyed this post?
Get practical operations content in your inbox monthly.