Skip to content
Vince.
Lab

Product · Inventory · Billing

Sari-sari store

Stock as a ledger, so a wrong count can be explained

Inventory, costing and billing for a neighbourhood shop. Quantities are derived from stock movements rather than stored, costs are weighted-average, and receipts extract VAT from prices that already include it.

  • TypeScript
  • React
  • Vitest
  • 33 tests

Stock

1 item at or below reorder point: Eggs, tray of 6

stock at cost ₱3,945.00

Receipt

Tap an item to ring it up.

Stock book

reconciles ✓
MovementQtyUnit costBalance
receiptOpening+20₱48.0020
receiptRestock+10₱54.0030

The quantity above is derived from these movements, not stored alongside them. That is what makes the difference between a count that is wrong and a count you can explain — and the reconciliation flag is the invariant being checked as you use it.

Rice arrived twice at different prices, so its average cost is neither of them. Ring some up and watch the stock book below: the quantity is derived from those movements, not stored next to them.

Try to buy more eggs than there are. The sale is refused rather than letting the count go negative — which would quietly corrupt the stock value, the margin and the reorder list all at once.

What this is, and what it is not

This is the part of the product that is worth building carefully: the rules that decide what can be sold, for how much, and what happens when the answer is no. It is a real engine with tests beside it, running in your browser with no server involved.

It is not a whole product. There is no login, no database and no payment provider — those are known quantities, and building them here would demonstrate nothing that the interesting half does not already demonstrate better. State lives in the page and resets when you reload it.

Read the full case study