Skip to content

ADR-011: The inventory ledger is the only stock writer

Status: accepted 13 September 2026 (BRV-05).

The baseline found ten places that changed stock, two stock figures with no link between them, and no record of why a quantity changed. Reservations, consumption, corrections and write-offs all need one trustworthy history.

  • InventoryLedger is the single writer. It locks the flower row, then the batch row, writes an inventory_movements row identified by an action key, and updates the projections (supply_items.quantity_remaining, supply_items.quantity_reserved, flowers.stock_quantity) in the same transaction. A repeated action key returns the existing movement instead of applying twice.
  • Invariants after every action: on-hand ≥ 0, reserved ≥ 0, reserved ≤ on-hand. An action that would break one is refused with a message an operator can read.
  • Movements are append-only. History is corrected with a compensating movement, never by editing a movement.
  • Batches carry history, so they are never hard-deleted. Removing a batch reverses its receipt and soft-deletes the row; it is refused once stems are reserved or consumed. A batch cannot be moved to another flower.
  • Pending orders reserve nothing (rule B01). The old create/cancel/delete decrements are gone; reservation arrives with confirmation (BRV-06) and consumption with assembly (BRV-07).
  • The flower form no longer sets stock. Stock enters through supplies and changes through corrections, write-offs and orders.

At introduction every existing batch received an opening-balance movement documenting its remainder, without inventing pre-cutover receipts or consumption. Flower stock that no batch accounted for was preserved as a legacy batch with unknown cost and expiry in status review, kept on hand and visible but excluded from automatic allocation (rules B06, B13). php artisan inventory:reconcile is read-only and exits non-zero on any disagreement between movements and projections.

  • A reviewer can explain every change in a batch from recorded actions.
  • Between BRV-05 and BRV-07, confirmed orders do not yet reserve; the branch is not for a real shop until BRV-07 lands.
  • Legacy review batches need an operator to set an expiry (and cost) before they can be sold.