spree-inventory
Spree Inventory
Stock is tracked per variant per location. A variant has no single stock count — it has a Spree::StockLevel (sl_) at each Spree::StockLocation (sloc_) that carries it. Every change is a Spree::StockMovement (sm_) that names its cause.
Coming from 5.x (Spree::StockItem, stock decremented at order completion, Spree::StockMovement with a bare signed quantity)? The alias Spree::StockItem = Spree::StockLevel exists until 6.1 — write new code against StockLevel. See spree-upgrade-5-to-6.
The counters
| Column / method | Meaning | Written by |
|---|---|---|
count_on_hand |
Physically on the shelf | received, adjusted and shipped movements only |
allocated_count |
Promised to placed orders not yet shipped | allocated (+), released / shipped (−) movements |
reserved_count |
Held by carts in checkout | Reservation create/update/destroy (counter cache) |
incoming_count |
On a purchase order marked ordered or a transfer in transit, not yet received | PO / transfer workflows |
available_count (method) |
count_on_hand − allocated_count |
derived |
purchasable_count (method) |
available_count − reserved_count |
derived |
backorderable |
May sell past zero | admin / API |
Placing an order does not reduce count_on_hand. Spree::Orders::Complete allocates each fulfillment's units (allocated movement → allocated_count up). Stock physically leaves only when the fulfillment ships (Spree::Fulfillments::Fulfill → shipped movement: on hand down, allocation retired). Canceling releases the allocation (released) — including units added by editing a placed order, because edits adjust the existing fulfillments in place (Spree::OrderInventory) instead of rebuilding them. Overselling shows up as allocated_count > count_on_hand, never a negative shelf count. So "the website still shows 10 on hand after I sold 3" is correct — look at available_count.