Inventory · advanced
Move stock, trace everything.
Two workflows that pay off as soon as you have more than one branch — branch-to-branch transfers (HQ → outlet, rebalancing between stores), and the per-SKU lifetime history feed.
Why transfers matter
Most multi-branch optical chains run a central warehouse where supplier deliveries land first. The warehouse then dispatches sub-batches to each retail outlet. The retail manager confirms the boxes arrived, with any short-shipment edits — and the stock + cost basis carry across cleanly.
Doing this manually (spreadsheet of who sent what to whom) is the #1 source of inventory-vs-system mismatch in the chains we've talked to. The dedicated transfer flow eliminates it.
Dispatching from the source
- Sidebar → Transfers, then + New transfer.
- Pick source branch (where the stock is right now) and destination branch. Cross-org transfers (e.g. parent brand → franchisee) aren't allowed here — they need the inter-org accounting flow.
- Optional but recommended: type a reason(“new outlet opening”, “rebalance after spring sale”, “customer request”) and an expected delivery date so the destination team knows when to expect the boxes.
- Search and add SKUs. Search is bound to the source branch — you can only dispatch what's actually in stock there. Each line caps at the source's on-hand quantity.
- Hit Dispatch →. The source's stock decrements immediately. The transfer enters in_transit status. You get a transfer number (TRF-2026-NNNNN) for tracking.
Confirming receipt at the destination
The destination branch sees the inbound transfer on their /inventory/transfers list with status in_transit. Open it, verify the line items match what physically arrived (quantity adjustments come in a future release; for now you receive as-dispatched), and click Confirm received →.
On confirm:
- Destination's
branch_inventory.quantity_on_handincrements by the line quantity. - Destination's weighted-average cost recomputes using the snapshotted cost from dispatch.
- Two
inventory_movementsrows are written — onetransfer_outat source (on dispatch), onetransfer_inat destination (on confirm). - Status flips to received. Audit columns capture who confirmed and when.
Cancelling a transfer
If a transfer was created in error and hasn't been received yet, the source branch manager can cancel it. This restores the dispatched units back to the source's on-hand count and writes a reversal movement. Once a transfer is received, it's closed — handle corrections via a new transfer in the other direction.
If you have only one branch
/inventory/transfers/new shows a friendly empty-state instead of a broken dropdown: “One branch, no transfers needed. Once you add a second branch this flow becomes available.” The list page (/inventory/transfers) still works — it just stays empty.
Per-SKU history
The where did this product come from, where did it go view. Open any SKU's detail card and click History →. Three sections, top to bottom:
1 · Stock by branch
Current quantity at every branch that has (or has ever had) this SKU. Columns: on-hand, reserved, last movement timestamp. Sorted by qty desc.
2 · Cost over time
Every receipt's unit cost as a small card with the date and a percent-delta from the prior receipt. Anything more than +10% colours red (supplier price hike — review your selling price); -10% or worse colours green (better deal, margin opportunity).
3 · Lifetime feed
Every event that ever touched this SKU, across every branch, reverse-chronological:
| Icon | Event | Reference |
|---|---|---|
| 📦 | Received from supplier | Receipt number (clickable later) |
| 🛒 | Sold to customer | Order number — clicks through to the order page |
| ↗ | Transferred out | Transfer number |
| ↘ | Transferred in | Transfer number |
| ↩ | Returned by customer | Return number |
| ✎ | Manual adjustment | Audit note |
Each row shows quantity delta (+5, -1), branch name, the counterparty (supplier, customer, source/destination branch), and the per-unit price at the time of the event.