spree-inventory

Installation
SKILL.md

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.

Installs
21
GitHub Stars
10
First Seen
Oct 1, 2026