Per-product inventory history
The Inventory History page is the per-product audit trail. Every restock, every sale, every adjustment, every return — every event that touched this product’s stock — shows up as a single row, in chronological order, with the user and the source attached. It’s the page you open when you ask “how did we get to 7 on hand?” and the page that backs up everything the FIFO costing engine claims about cost layers.
Reaching the page
From the Inventory page or the Products page, open any product row’s overflow menu and pick View History. The URL is /inventory/history/:productId. For variable products, the same page shows every variant’s transactions interleaved in one stream, with the variant attributes badged on each row.
What’s on each row
Every row is one record from inventory_transactions — the same table the FIFO engine reads from. Columns:
- Type badge —
ingress(green up-arrow),egress(red down-arrow), oradjustment(orange dot). - Date — when the row was written, formatted in the store’s timezone.
- Quantity delta — colour-coded: green positive for ingress, red negative for egress.
- Source — what triggered the move:
- Restock — ad-hoc restock dialog or PO receive.
- Sale — invoice number, click-through to the invoice detail.
- Return — refund wizard, click-through to the return.
- Adjustment — stock count fix, defect routing, write-off, dispose.
- Cancel — invoice cancelled, units flowed back into stock.
- User — who did it. Always populated from the active session.
- Cost per unit — the per-unit cost stamped on the row at the time it was written. Frozen at write-time — reports read this column, not the live
products.cost_price. The column is gated behind thecost:viewpermission since v1.6.105 and won’t render for users who don’t have it. - Notes — optional free-text from the action that wrote the row (e.g. “physical count adjustment Jan 2026”, “customer return — defect”).
- Variant badge — for variable products, the variant attributes (e.g.
Red / Large). - Lot / serial badge — for batched or serialized products, the lot number or identifier the row references.
A filter bar above the table narrows by transaction type — useful when you’re looking specifically for adjustments, or only care about restocks.
How this maps to FIFO layers
Every ingress row IS a FIFO cost layer. Look at any ingress and you’ll see two extra columns in the detail expander:
- Initial qty — the quantity the layer opened with.
- Qty remaining — what’s still open. Decrements as later sales drain it; hits zero when the layer is closed.
Egress rows show the cost they pulled from layers and (for FIFO-walking sales) the layer ID they drew from. This is the page you open when an Inventory Valuation report doesn’t match your gut — every open layer is right there with its remaining quantity and unit cost.
Adjustments
Three kinds of adjustment row land on this timeline:
- Stock count fix — manual quantity adjustment (e.g. you counted 47 on the shelf but the system said 50). Posts a 3-unit egress with the chosen reason and a note.
- Defect routing — when a return’s wizard sets disposition to
defect, the unit comes off regular stock and lands on the Defect Inventory page. The egress shows up here withsource='defect'. - Dispose / write-off — for spoiled or damaged stock you’ve physically thrown out. Posts to disposal expense (5040).
All three are reachable from the Remove Stock action on the product row, with a reason picker that picks which one applies.
Variant and serialized history
For variable products, the timeline interleaves every variant. Use the variant filter to drill into one. The variant attributes are stamped on every row.
For serialized products, click into a unit from the serialized tracking page to see that unit’s full history — restock, the sale that took it out, any return, any defect routing. Same data shape as the per-product timeline, just scoped to one identifier.
What this page does not show
- Journal entries — the JE block that posted on each move lives on the Reports → General Ledger page, scoped by
source_typeandsource_id. The history page links to the relevant invoice or PO from each row, but the JE itself is one click further away. - Cash drawer transactions — when a restock was paid from cash, the cash leg is on the Cash Drawer Report for that shift, not here.
- Other products’ moves — this page is per-product. For a cross-product view of every move in a date range, use the Inventory Moves report.
Related
FIFO costing
Every ingress is a layer; every egress drains layers oldest-first.
Inventory Moves report
The cross-product view of every move over a date range.
Restock a product
Where ingress rows come from on this timeline.
Defect inventory
Where defect-disposition egresses land for follow-up.