Skip to main content

Overview

Each Variant has a StockItem that tracks its inventory at a specific location. A variant can have multiple stock items if it’s available at multiple stock locations. When products are sold or returned, individual InventoryUnit records track each unit through the fulfillment process. Adding new inventory to an out-of-stock product that has backorders will first fill the backorders, then update the available count with the remainder. During checkout, Spree holds stock with time-limited Stock Reservations to prevent two customers from buying the same last unit simultaneously.

Inventory Model Diagram

Key relationships:
  • Stock Location → Contains Stock Items (inventory per variant) and is the source/destination for Stock Transfers
  • Stock Item → Tracks quantity (count_on_hand) for a specific Variant at a specific Stock Location
  • Stock Movement → Records changes to Stock Item quantities (purchases, returns, transfers)
  • Stock Transfer → Moves inventory between Stock Locations, creating Stock Movements at source and destination
  • Inventory Unit → Represents individual units in Orders and Shipments
  • Stock Reservation → Time-limited soft hold on a Stock Item during checkout, scoped to a specific Order and Line Item

Inventory Management

Stock Locations

Stock Locations are the physical locations where your inventory is stored and shipped from. Stock Locations can be created in the Admin Panel under Settings → Stock Locations, or via the Admin API. Stock Locations have several attributes that define their properties and behavior within the Spree system. Below is a table outlining these attributes: Stock Locations can be easily used for tracking warehouses and other physical locations. They can be used to track separate sections of a warehouse (e.g. aisles, shelves, etc.) or to track different warehouses. You can easily use them with your Point of Sale (POS) system to track inventory at different locations. Create and manage stock locations via the Admin API:

Stock Items

Stock Items represent the inventory at a stock location for a specific variant. Stock item count on hand can be increased or decreased by creating stock movements. Stock items are created automatically — for all variants when a location has propagate_all_variants, or via a variant’s stock_items on create. To adjust quantity or backorderable status, update the existing stock item via the Admin API. The example below uses a Ransack predicate (stock_location_id_eq) to list a location’s stock items before updating one:

Stock Transfers

Stock transfers allow you to move inventory in bulk from one stock location to another stock location. This is handy when you want to integrate with a POS system or other inventory management system. Or you can just rely on Spree being the source of truth for your inventory.
Stock Transfers can be created in the Admin dashboard or via the Admin API.
Here’s the list of attributes for the Stock Transfer model: Create a transfer via the Admin API, listing the variants and quantities to move. Omit source_location_id to record an incoming receipt from a vendor:
Stock transfers are crucial for managing inventory across multiple locations, ensuring that stock levels are accurate and up-to-date. Each Stock Transfer will hold a list of Stock Movements.

Stock Movements

Stock Movements track the movement of the inventory:
  • when you move inventory between stock locations (via Stock Transfer)
  • when you add inventory to a stock location
  • when you remove inventory from a stock location
  • when customers purchase products
  • when customers return products
Here’s the list of attributes for the Stock Movement model: Stock Movements are crucial for maintaining accurate inventory levels and for historical tracking of inventory adjustments.

Stock Reservations

Stock Reservations are a time-limited soft hold on stock during checkout. When a customer enters checkout, Spree holds the items in their cart for a limited time so other shoppers see reduced availability immediately. Two customers can no longer both pass the availability check on the same last unit only to have one of them fail at order completion.

What changes for the storefront

Availability now subtracts the units other customers are holding in active checkouts. Whenever you read whether a variant is in stock — whether for a product page, cart line, or checkout summary — Spree returns the post-reservation number automatically. There’s nothing for the storefront to compute and physical stock counts on each StockItem are never modified by reservations; reservations are an independent layer that’s consulted at read time and cleaned up by background jobs.

Lifecycle

Reservations attach to each line item; when a line item or order is removed, the reservation goes with it.

Configuration

TTL is a Store-level setting — it’s a checkout-experience policy, not a warehouse property. A multi-location cart never has to merge conflicting TTLs from different warehouses.

Insufficient stock during checkout

When a cart change in checkout would push the order beyond available stock, the change is rejected up front. The customer sees a validation error immediately, instead of progressing through payment only to fail at the final submit.

Background expiry

Abandoned checkouts leave behind expired reservation rows. Spree provides a job to clean them up but does not auto-schedule it — your application’s job runner needs to invoke it periodically (every minute is typical). See the 5.4 to 5.5 upgrade guide for sidekiq-cron, solid_queue, and good_job snippets.

Backorderable items

If a stock item is marked backorderable, it represents unlimited supply, so reservations are skipped entirely for that item. Availability is unaffected.

Inventory Units

As we mentioned above, back-ordered, sold, or shipped products are stored as individual InventoryUnit objects so they can have relevant information attached to them. We create InventoryUnit objects when:
  • a product is sold (they are added to the Shipment)
  • a product is returned
Here’s a list of attributes for the Inventory Unit model: Inventory Units states are:
  • on_hand - the inventory unit is on hand
  • backordered - the inventory unit is backordered
  • shipped - the inventory unit is shipped
  • returned - the inventory unit has been returned
As we noted before, when you add new Stock Items to a Variant (eg. via Admin Panel or Admin API), the first Inventory Units to fulfill are the backordered ones.

Disabling Inventory Tracking

If you don’t need to track inventory, you can disable it:
  • Per variant — set track_inventory to false on a specific variant via the Admin Panel or Admin API
  • Globally — disable inventory tracking for the entire store in your Spree configuration
  • Products - Product and variant management
  • Shipments - How inventory relates to shipments
  • Orders - How inventory is allocated to orders
  • Admin SDK resources - stockLocations, stockItems, and stockTransfers methods used in the examples above
  • Admin API authentication - How to obtain and scope the secret key (sk_xxx) used by these calls