1 Overview
Spree uses a hybrid approach for tracking inventory: On-hand inventory is stored as a count on the product Variant. This gives good performance for stores with large inventories. Back-ordered, sold or shipped products are stored as individual InventoryUnits so the can have relevant information attached to them.
What if you don’t need to track inventory? We have come up with a design that basically shields the simple store user from much of this complexity. Simply leave the inventory fields blank and you never have to worry about it.
New products created in the system can be given a starting “on hand” inventory level. You can subsequently set new inventory levels and the correct things will happen, e.g. adding new on-hand inventory to an out-of-stock product that has some backorders will first fill the backorders then update the product with the remaining inventory count.
We’ve also done a great deal of improvement with variants. Every line item in an order tracks the exact variant of the product sold. Things work fine even if the product has no meaningful variant information (say you are just selling CD’s and you don’t need to worry about “size”, “color”, etc.) A “master” variant is created behind the scenes and your inventory and SKU information will be associated with this variant. If you do end up using variants though, you now have fine grained control over the SKU and inventory information.
2 Design and functionality
On-hand inventory is stored as attribute on_hand in the Variant model. The on-hand inventory level is adjusted using Variant#on_hand=(). If on_hand= is given a new stock level that is below the current on_hand inventory count, it will just store this new level. When given a new on-hand stock level for a product that was out of stock in a store that allows backorders, on_hand= will first create new InventoryItems for the backordered product and mark it “sold”. The remaining delta in inventory is then stored in the on_hand column.
2.1 Relevant settings
Behaviour of the inventory system is governed by these settings:
- show_zero_stock_products – usually, Spree shows products in listings if it has some stock (which means at least one of its variants has stock), and this setting disregards the result of that test and so allows such products’ details to be viewed.
- allow_backorders – similarly, Spree usually only accepts orders for a variant if there is some stock, and this setting overrides that condition. The default behavior, when the setting is false and there is no stock in any variant, is to show an “Out of stock” message; or when just some of the variants are unavailable, to disable the relevant radio buttons.
One other flag is relevant:
- allow_backorder_shipping – allows inventory in the backorder state to be marked as shipped. You may need to do this in certain cases, eg if your backorders are handled by a 3rd party fulfillment house and they have agreed to ship an item once stock is available again.
2.2 What the customer sees
Generally, the settings above are the most significant. Customizations can also access details of stock levels for each variant via the on_hand method, e.g. to show the current level on the cart page.
stock levels will of course change over time, and there is no locking of requested inventory to orders until the very last stage of checkout. If insufficient stock is available, the deficit is marked up as being in a back-ordered state, irrespective of the allow_backorders flag
2.3 What the orders administrator sees
As mentioned above, the admin interface hides some of the complexity of the full system. When adding or editing a variants of a product, the merchant can set or alter the stock level for that variant through a single number. If the product has no variants (rather, it has an “empty” variant which Spree doesn’t show explicitly), then this stock level can be set on the product details page.
the admin interface currently doesn’t explicitly highlight when a shipment contains back-orders. However, if you try to mark such a shipment as being shipped, an error message will appear – unless you have allow_backorder_shipping set, that is.
3 Site Customizations
3.1 Programming interface
Here’s a summary of methods you might use in a customization (v is a variant, p is a product):
- v.on_hand – returns how many inventory items a variant has with the “on_hand” state
- v.on_hand= – sets the inventory level, filling back-orders first if there are any
- v.in_stock? – returns true if at least one item is “on_hand”
- p.has_stock? – returns true if any variant of the product is “on_hand”
3.2 More sophisticated inventory management
It’s straightforward to add new fields to inventory units, eg to record a unit’s location or some tracking information