@spree/docs 0.1.182 → 0.1.184

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/developer/core-concepts/addresses.md +106 -198
  2. package/dist/developer/core-concepts/architecture.md +97 -126
  3. package/dist/developer/core-concepts/calculators.md +75 -252
  4. package/dist/developer/core-concepts/carts.md +1 -1
  5. package/dist/developer/core-concepts/catalogs.md +140 -0
  6. package/dist/developer/core-concepts/channels.md +0 -4
  7. package/dist/developer/core-concepts/commissions.md +253 -0
  8. package/dist/developer/core-concepts/companies.md +240 -0
  9. package/dist/developer/core-concepts/customers.md +0 -3
  10. package/dist/developer/core-concepts/discounts.md +133 -0
  11. package/dist/developer/core-concepts/events.md +83 -576
  12. package/dist/developer/core-concepts/fees.md +144 -0
  13. package/dist/developer/core-concepts/imports-exports.md +105 -679
  14. package/dist/developer/core-concepts/inventory.md +114 -248
  15. package/dist/developer/core-concepts/markets.md +9 -12
  16. package/dist/developer/core-concepts/media.md +127 -16
  17. package/dist/developer/core-concepts/metafields.md +123 -200
  18. package/dist/developer/core-concepts/order-totals.md +110 -0
  19. package/dist/developer/core-concepts/orders.md +1 -1
  20. package/dist/developer/core-concepts/payments.md +11 -14
  21. package/dist/developer/core-concepts/pricing.md +11 -13
  22. package/dist/developer/core-concepts/products.md +191 -62
  23. package/dist/developer/core-concepts/promotions.md +12 -11
  24. package/dist/developer/core-concepts/search-filtering.md +2 -4
  25. package/dist/developer/core-concepts/sellers.md +210 -0
  26. package/dist/developer/core-concepts/staff-roles.md +56 -23
  27. package/dist/developer/core-concepts/store-credits-gift-cards.md +0 -3
  28. package/dist/developer/core-concepts/taxes.md +125 -113
  29. package/dist/developer/core-concepts/translations.md +61 -68
  30. package/dist/developer/core-concepts/webhooks.md +25 -59
  31. package/dist/developer/how-to/custom-promotion.md +3 -3
  32. package/package.json +1 -1
  33. package/dist/developer/core-concepts/companies-and-catalogs.md +0 -81
  34. package/dist/developer/core-concepts/taxes-discounts-fees.md +0 -199
@@ -1,325 +1,191 @@
1
1
  ---
2
2
  title: Inventory
3
- description: Stock locations, stock items, stock movements, and inventory tracking
3
+ description: How Spree tracks stock across locations — what's on hand, what's spoken for, and what a shopper is allowed to buy.
4
4
  ---
5
5
 
6
- import { Since } from '/snippets/since.mdx';
7
-
8
6
  ## Overview
9
7
 
10
- Each [Variant](products.md#variants) 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.
11
-
12
- When products are sold or returned, individual `InventoryUnit` records track each unit through the fulfillment process.
13
-
14
- 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.
15
-
16
- During checkout, Spree holds stock with time-limited [Stock Reservations](#stock-reservations) to prevent two customers from buying the same last unit simultaneously.
8
+ Inventory answers one question a shopper cares about *can I buy this?* and several a merchant cares about: where is it, how much is left, and what happened to the rest.
17
9
 
18
- ### Inventory Model Diagram
10
+ Spree tracks stock **per location**. A variant doesn't have "a stock count"; it has a count at each warehouse or shop that carries it. That's what makes it possible to ship from the nearest location, or to let a customer collect in store.
19
11
 
20
12
  ```mermaid
21
13
  erDiagram
22
- StockLocation {
23
- string name
24
- string admin_name
25
- boolean active
26
- boolean default
27
- boolean backorderable_default
28
- boolean propagate_all_variants
29
- }
30
-
31
- StockItem {
14
+ Variant ||--o{ StockLevel : "stocked at each location"
15
+ StockLocation ||--o{ StockLevel : "holds"
16
+ StockLevel ||--o{ StockMovement : "changed by"
17
+ StockLevel ||--o{ StockReservation : "held by checkouts"
18
+ Order ||--o{ Fulfillment : "ships as"
19
+ Fulfillment ||--o{ FulfillmentItem : "contains"
20
+
21
+ StockLevel {
32
22
  integer count_on_hand
23
+ integer allocated_count
33
24
  boolean backorderable
34
- integer stock_location_id
35
- integer variant_id
36
- }
37
-
38
- StockMovement {
39
- integer quantity
40
- string originator_type
41
- integer originator_id
42
- integer stock_item_id
43
- }
44
-
45
- StockTransfer {
46
- string number
47
- string reference
48
- integer source_location_id
49
- integer destination_location_id
50
- }
51
-
52
- InventoryUnit {
53
- string state
54
- integer variant_id
55
- integer order_id
56
- integer shipment_id
57
25
  }
58
-
59
- StockReservation {
60
- integer quantity
61
- datetime expires_at
62
- integer stock_item_id
63
- integer line_item_id
64
- integer order_id
26
+ StockLocation {
27
+ string name
28
+ boolean active
29
+ string country_code
65
30
  }
66
-
67
- StockLocation ||--o{ StockItem : "has many"
68
- StockLocation ||--o{ StockTransfer : "source"
69
- StockLocation ||--o{ StockTransfer : "destination"
70
- Variant ||--o{ StockItem : "has many"
71
- Variant ||--o{ InventoryUnit : "has many"
72
- StockItem ||--o{ StockMovement : "has many"
73
- StockItem ||--o{ StockReservation : "has many"
74
- StockTransfer ||--o{ StockMovement : "has many"
75
- Order ||--o{ InventoryUnit : "has many"
76
- Order ||--o{ StockReservation : "has many"
77
- Shipment ||--o{ InventoryUnit : "has many"
78
31
  ```
79
32
 
80
- **Key relationships:**
81
- - **Stock Location** → Contains Stock Items (inventory per variant) and is the source/destination for Stock Transfers
82
- - **Stock Item** → Tracks quantity (`count_on_hand`) for a specific Variant at a specific Stock Location
83
- - **Stock Movement** → Records changes to Stock Item quantities (purchases, returns, transfers)
84
- - **Stock Transfer** → Moves inventory between Stock Locations, creating Stock Movements at source and destination
85
- - **Inventory Unit** → Represents individual units in [Orders](orders.md) and [Shipments](fulfillments.md)
86
- - **Stock Reservation** → Time-limited soft hold on a Stock Item during checkout, scoped to a specific Order and Line Item
87
-
88
- ## Inventory Management
89
-
90
- ### Stock Locations
91
-
92
- Stock Locations are the physical locations where your inventory is stored and shipped from.
93
-
94
- Stock Locations can be created in the Admin Panel under **Settings → Stock Locations**, or via the Admin API.
33
+ ## The three numbers
95
34
 
96
- Stock Locations have several attributes that define their properties and behavior within the Spree system. Below is a table outlining these attributes:
35
+ A stock level carries two counts, and a third is derived from them. Keeping them apart is what stops a warehouse from disagreeing with a website.
97
36
 
98
- | Attribute | Description | Example Value |
99
- |-------------------|-----------------------------------------------------------------------------|---------------------|
100
- | `name` | The public name of the stock location. This is returned in Store API | Warehouse 1 |
101
- | `admin_name` | The name used internally for the stock location. This is only returned in Admin API. | WH1 Domestic |
102
- | `address1` | The primary address line for the stock location. | 5th avenue |
103
- | `address2` | The secondary address line for the stock location. | Suite 100 |
104
- | `city` | The city where the stock location is based. | New York |
105
- | `state_id` | The ID of the state where the stock location is based. This references the `State` model. | 1 |
106
- | `country_id` | The ID of the country where the stock location is based. This references the `Country` model. | 1 |
107
- | `zipcode` | The postal code for the stock location. | 10001 |
108
- | `phone` | The contact phone number for the stock location. | 555-1234 |
109
- | `active` | A boolean indicating whether the stock location is active. Inactive stock locations will not be used in stock calculations or be available for selection during checkout. | `true` |
110
- | `default` | A boolean indicating whether the stock location is the default one used for new inventory operations. | `false` |
111
- | `backorderable_default` | A boolean indicating whether new stock items in this location are backorderable by default. | `false` |
112
- | `propagate_all_variants` | A boolean indicating whether new stock items should be automatically created for all Store variants when a new stock location is added. | `false` |
37
+ | | What it means |
38
+ |---|---|
39
+ | `count_on_hand` | What is physically on the shelf, right now |
40
+ | `allocated_count` | How much of that is already spoken for by orders not yet shipped |
41
+ | **`available_count`** | `count_on_hand allocated_count` what's actually sellable |
113
42
 
114
- 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.
43
+ > **NOTE:** **Selling an item does not reduce `count_on_hand`.** Placing an order raises `allocated_count`; the physical count only drops when the parcel actually ships.
44
+ >
45
+ > This is deliberate. Until it leaves the building, the stock *is* still on the shelf — and a warehouse worker counting boxes should find the number Spree reports. Overselling shows up honestly as `allocated_count` exceeding what's on hand, rather than as an impossible negative count.
115
46
 
116
- You can easily use them with your Point of Sale (POS) system to track inventory at different locations.
47
+ ## Stock locations
117
48
 
118
- Create and manage stock locations via the [Admin API](../../api-reference/admin-api/introduction.md):
49
+ A stock location is somewhere stock physically sits: a warehouse, a shop, a third-party fulfillment centre.
119
50
 
51
+ | Attribute | Description |
52
+ |---|---|
53
+ | `name` | What staff call it |
54
+ | `active` | Whether it can be used for new orders |
55
+ | `default` | Used first when nothing else decides |
56
+ | `country_code`, `state_code`, `city`, `postal_code` | Where it is — used for shipping rates and for finding the nearest one |
57
+ | `backorderable_default` | Whether new stock levels here allow backorders |
58
+ | `fulfillable` | Whether orders can ship from here, or it's display-only stock |
59
+ | `pickup` | Whether customers can collect here |
120
60
 
121
61
  ```typescript Admin SDK
122
- import { createAdminClient } from '@spree/admin-sdk'
123
-
124
- const client = createAdminClient({
125
- baseUrl: 'https://store.example.com',
126
- secretKey: 'sk_xxx',
127
- })
62
+ const { data: locations } = await adminClient.stockLocations.list()
128
63
 
129
- const location = await client.stockLocations.create({
130
- name: 'Warehouse 1',
131
- admin_name: 'WH1 Domestic',
132
- default: true,
133
- country_iso: 'US',
134
- propagate_all_variants: true,
64
+ await adminClient.stockLocations.create({
65
+ name: 'Berlin Warehouse',
66
+ country_code: 'DE',
67
+ city: 'Berlin',
68
+ active: true,
135
69
  })
136
-
137
- await client.stockLocations.update('sloc_xxx', { active: false })
138
- ```
139
-
140
- ```bash CLI
141
- spree api post /stock_locations -d '{
142
- "name": "Warehouse 1",
143
- "default": true,
144
- "country_iso": "US",
145
- "propagate_all_variants": true
146
- }'
147
70
  ```
148
71
 
72
+ ## Reading and adjusting stock
149
73
 
150
- ### Stock Items
151
-
152
- 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.
153
-
154
- | Attribute | Description | Example Value |
155
- |-------------------|-----------------------------------------------------------------------------|---------------------|
156
- | `stock_location_id` | References the stock location where the stock item belongs. | `1` |
157
- | `variant_id` | References the variant associated with the stock item. | `32` |
158
- | `count_on_hand` | The number of items available on hand. | `150` |
159
- | `backorderable` | Indicates whether the stock item can be backordered. | `true` |
160
-
161
- 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](../../api-reference/admin-api/querying.md) before updating one:
74
+ A stock level is the intersection of a variant and a location. They're created for you — for every variant when a location propagates all variants, or as variants are added.
162
75
 
163
76
 
164
77
  ```typescript Admin SDK
165
- // List a location's stock items, then adjust one
166
- const { data: items } = await client.stockItems.list({ stock_location_id_eq: 'sloc_xxx' })
78
+ // Stock for one location
79
+ const { data: levels } = await adminClient.stockLevels.list({
80
+ filter: { stock_location_id_eq: 'sl_xxx' },
81
+ })
167
82
 
168
- await client.stockItems.update('si_xxx', {
83
+ // Correct a single count
84
+ await adminClient.stockLevels.update('slv_xxx', {
169
85
  count_on_hand: 150,
170
86
  backorderable: true,
171
87
  })
88
+
89
+ // Set many at once — the right call for a nightly sync
90
+ await adminClient.stockLevels.bulkUpsert({
91
+ stock_levels: [
92
+ { variant_id: 'var_xxx', stock_location_id: 'sl_xxx', count_on_hand: 40 },
93
+ { variant_id: 'var_yyy', stock_location_id: 'sl_xxx', count_on_hand: 12 },
94
+ ],
95
+ })
172
96
  ```
173
97
 
174
98
  ```bash CLI
175
- spree api get /stock_items -q stock_location_id_eq=sloc_xxx
176
- spree api patch /stock_items/si_xxx -d '{"count_on_hand": 150, "backorderable": true}'
99
+ spree api get '/stock_levels?q[stock_location_id_eq]=sl_xxx'
100
+ spree api patch /stock_levels/slv_xxx -d '{"count_on_hand": 150}'
177
101
  ```
178
102
 
179
103
 
180
- ### Stock Transfers
181
-
182
- 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.
104
+ > **WARNING:** Use `bulkUpsert` for feeds from a warehouse or ERP rather than a loop of single updates. It's one request instead of thousands, and it won't half-apply if the connection drops.
183
105
 
184
- > **INFO:** Stock Transfers can be created in the Admin dashboard or via the Admin API.
106
+ ## Stock movements
185
107
 
186
- Here's the list of attributes for the Stock Transfer model:
187
-
188
- | Attribute | Description | Example Value |
189
- |--------------------------|-----------------------------------------------------------------------------|---------------------|
190
- | `number` | The unique number identifier for the stock transfer, generated automatically. | `T123456789` |
191
- | `reference` | An optional reference field for the stock transfer. | Transfer for Event |
192
- | `source_location_id` | The ID of the stock location where the stock is transferred from. | `2` |
193
- | `destination_location_id`| The ID of the stock location where the stock is transferred to. | `3` |
194
-
195
- Create a transfer via the [Admin API](../../api-reference/admin-api/introduction.md), listing the variants and quantities to move. Omit `source_location_id` to record an incoming receipt from a vendor:
108
+ Every change to stock is recorded as a movement, so "why is this number what it is" always has an answer.
196
109
 
110
+ | Kind | When it happens |
111
+ |---|---|
112
+ | `received` | New stock arrives |
113
+ | `allocated` | An order claims stock — `allocated_count` goes up |
114
+ | `shipped` | A parcel leaves — `count_on_hand` goes down, the allocation is retired |
115
+ | `released` | An allocation is given back, because an order was cancelled |
116
+ | `adjusted` | A manual correction, such as after a stock count |
197
117
 
198
118
  ```typescript Admin SDK
199
- const transfer = await client.stockTransfers.create({
200
- source_location_id: 'sloc_warehouse',
201
- destination_location_id: 'sloc_store',
202
- reference: 'Transfer for Event',
203
- variants: [
204
- { variant_id: 'variant_xxx', quantity: 20 },
205
- { variant_id: 'variant_yyy', quantity: 5 },
206
- ],
119
+ const { data: movements } = await adminClient.stockMovements.list({
120
+ filter: { variant_id_eq: 'var_xxx' },
207
121
  })
208
122
  ```
209
123
 
210
- ```bash CLI
211
- spree api post /stock_transfers -d '{
212
- "source_location_id": "sloc_warehouse",
213
- "destination_location_id": "sloc_store",
214
- "variants": [{ "variant_id": "variant_xxx", "quantity": 20 }]
215
- }'
216
- ```
217
-
218
-
219
- Stock transfers are crucial for managing inventory across multiple locations, ensuring that stock levels are accurate and up-to-date.
124
+ Each movement points at what caused it — the order, the fulfillment, the transfer — so an audit trail reads as a sequence of business events rather than a list of numbers that changed.
220
125
 
221
- Each Stock Transfer will hold a list of Stock Movements.
126
+ ## What a shopper sees
222
127
 
223
- ### Stock Movements
128
+ For a storefront, all of the above collapses into one question, and the answer is already computed:
224
129
 
225
- Stock Movements track the movement of the inventory:
130
+ ```typescript Store SDK
131
+ const product = await client.products.get('spree-tote')
226
132
 
227
- * when you move inventory between stock locations (via Stock Transfer)
228
- * when you add inventory to a stock location
229
- * when you remove inventory from a stock location
230
- * when customers purchase products
231
- * when customers return products
232
-
233
- Here's the list of attributes for the Stock Movement model:
234
-
235
- | Attribute | Description | Example Value |
236
- |-------------------|-----------------------------------------------------------------------------|---------------------|
237
- | `stock_item_id` | References the stock item that the movement belongs to. | `45` |
238
- | `quantity` | The quantity by which the stock item's count on hand is changed. Positive values indicate stock being added, while negative values indicate stock being removed. | `-10` |
239
- | `originator_type` | The type of the originator of the stock movement. This is a polymorphic association. | `Spree::Shipment` |
240
- | `originator_id` | The ID of the originator of the stock movement. This is used in conjunction with `originator_type`. | `2` |
241
-
242
- Stock Movements are crucial for maintaining accurate inventory levels and for historical tracking of inventory adjustments.
243
-
244
- ## Stock Reservations
133
+ product.variants.forEach((variant) => {
134
+ variant.in_stock // can it be bought right now
135
+ variant.purchasable // in stock, or backorderable
136
+ })
137
+ ```
245
138
 
246
- 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.
139
+ `in_stock` already accounts for what's allocated and what other shoppers are holding in checkout. There is nothing to compute client-side, and no separate inventory request to make.
247
140
 
248
- ### What changes for the storefront
141
+ ## Reservations during checkout
249
142
 
250
- 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.
143
+ Without reservations, two customers can both see the last unit, both start checkout, and one discovers at the payment step that it's gone. That's a bad moment to find out.
251
144
 
252
- ### Lifecycle
145
+ A reservation is a short, time-limited hold placed when a customer enters checkout. Availability drops for everyone else immediately.
253
146
 
254
- | Trigger | Action |
147
+ | Moment | What happens |
255
148
  |---|---|
256
- | Customer enters checkout | A reservation is created for each line item with an expiry timestamp |
257
- | Customer continues mutating the cart while in checkout | The expiry is pushed forward |
258
- | Customer completes the order | The reservation is released; physical stock is decremented as before |
259
- | Customer empties or abandons the cart | The reservation is released or expires automatically |
260
-
261
- Reservations attach to each line item; when a line item or order is removed, the reservation goes with it.
262
-
263
- ### Configuration
149
+ | Customer enters checkout | Their items are held, with an expiry |
150
+ | They keep editing the cart | The expiry is pushed forward |
151
+ | They complete the order | The hold becomes a real allocation |
152
+ | They abandon it | The hold expires and stock returns |
264
153
 
265
- | Setting | Default | Purpose |
266
- |---|---|---|
267
- | `Spree::Config[:stock_reservations_enabled]` | `true` | Global kill switch. When `false`, reservations are not created and availability ignores them — behavior matches pre-5.5. |
268
- | `Spree::Config[:default_stock_reservation_ttl_minutes]` | `10` | Fallback hold duration when a Store doesn't override. |
269
- | `store.preferred_stock_reservation_ttl_minutes` | `10` | Per-Store override. Falls back to the global default only when explicitly unset/blank. |
154
+ Reservations never touch `count_on_hand` they're a separate layer consulted when availability is read. A backorderable item is skipped entirely, since unlimited supply needs no holding.
270
155
 
271
- 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.
156
+ How long the hold lasts is a **store setting**, because it's a decision about the checkout experience rather than a property of a warehouse. Reservations can also be switched off entirely.
272
157
 
273
- ### Insufficient stock during checkout
158
+ > **INFO:** Expired reservations are cleaned up by a background job. Spree ships the job but does not schedule it — your app's job runner should run it every minute or so.
274
159
 
275
- 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.
160
+ ## Backorders
276
161
 
277
- ### Background expiry
162
+ A stock level marked `backorderable` can be sold past zero. Orders are accepted, allocated, and wait for stock.
278
163
 
279
- 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](/v5/developer/upgrades/5.4-to-5.5#schedule-the-stock-reservations-expiry-job) for sidekiq-cron, solid_queue, and good_job snippets.
164
+ When new stock arrives, backorders are filled first, and only the remainder becomes available to new customersso the people who waited longest aren't overtaken by whoever happens to visit the site next.
280
165
 
281
- ### Backorderable items
166
+ ## Moving stock between locations
282
167
 
283
- If a stock item is marked backorderable, it represents unlimited supply, so reservations are skipped entirely for that item. Availability is unaffected.
168
+ A stock transfer moves inventory from one location to another, and reflects that this takes time: stock is booked out of the source, is in transit, and is received at the destination — where a partial receipt is a normal outcome, because sometimes not everything arrives.
284
169
 
285
- ## Inventory Units
286
-
287
- 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.
288
-
289
- We create `InventoryUnit` objects when:
290
-
291
- * a product is sold (they are added to the Shipment)
292
- * a product is returned
293
-
294
- Here's a list of attributes for the Inventory Unit model:
295
-
296
- | Attribute | Description | Example Value |
297
- |-------------------|-----------------------------------------------------------------------------|---------------------|
298
- | `variant_id` | References the variant associated with the inventory unit. | `32` |
299
- | `order_id` | References the order associated with the inventory unit. | `123` |
300
- | `shipment_id` | References the shipment associated with the inventory unit. | `77` |
301
- | `state` | The state of the inventory unit | `on_hand` |
170
+ ```typescript Admin SDK
171
+ const { data: transfers } = await adminClient.stockTransfers.list()
172
+ ```
302
173
 
303
- Inventory Units states are:
174
+ ## Turning tracking off
304
175
 
305
- * `on_hand` - the inventory unit is on hand
306
- * `backordered` - the inventory unit is backordered
307
- * `shipped` - the inventory unit is shipped
308
- * `returned` - the inventory unit has been returned
176
+ Some things don't need counting — a service, a made-to-order item, a digital download. Tracking can be switched off, and those variants are always purchasable.
309
177
 
310
- > **NOTE:** 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.
178
+ This is a store-level setting, so a store that sells only digital goods needn't manage stock at all.
311
179
 
312
- ## Disabling Inventory Tracking
180
+ ## Syncing from an external system
313
181
 
314
- If you don't need to track inventory, you can disable it:
182
+ If your inventory truth lives in an ERP or a warehouse system, Spree can defer to it. The rule that matters: **the read path stays local.** Whether a variant is in stock is answered from Spree's own data, not by calling out to another system while a shopper waits for a product page.
315
183
 
316
- - **Per variant** set `track_inventory` to `false` on a specific variant via the Admin Panel or Admin API
317
- - **Globally** — disable inventory tracking for the entire store in your Spree configuration
184
+ Feeds come in through the bulk update above; live checks happen only at decision moments, like completing an order. See [Providers](../providers/overview.md).
318
185
 
319
- ## Related Documentation
186
+ ## Related
320
187
 
321
- - [Products](products.md) - Product and variant management
322
- - [Shipments](fulfillments.md) - How inventory relates to shipments
323
- - [Orders](orders.md) - How inventory is allocated to orders
324
- - [Admin SDK resources](../sdk/admin/resources.md) - `stockLocations`, `stockItems`, and `stockTransfers` methods used in the examples above
325
- - [Admin API authentication](../../api-reference/admin-api/authentication.md) - How to obtain and scope the secret key (`sk_xxx`) used by these calls
188
+ - [Products](products.md) variants, the things stock is counted for
189
+ - [Fulfillments](fulfillments.md) how stock leaves the building
190
+ - [Orders](orders.md) where allocation happens
191
+ - [Carts](carts.md) checkout and reservations
@@ -3,9 +3,6 @@ title: Markets
3
3
  description: Configure Spree Markets to bundle geography, currency, and locale into distinct selling regions and run multi-region commerce from a single store.
4
4
  ---
5
5
 
6
- import { Since } from '/snippets/since.mdx';
7
-
8
-
9
6
  ## Overview
10
7
 
11
8
  Markets let you segment a single [Store](stores.md) into distinct geographic regions, each with its own currency, locale, and set of countries. For example, an international store might define:
@@ -50,14 +47,14 @@ erDiagram
50
47
  | `supported_locales` | All locales available in this market | `["en", "es"]` |
51
48
  | `tax_inclusive` | Whether prices include tax (affects display and checkout calculation) | `false` |
52
49
  | `default` | Whether this is the fallback market when no country match is found | `true` |
53
- | `countries` | List of countries in this market | `[{ iso: "US" }, { iso: "CA" }]` |
50
+ | `country_codes` | Countries in this market, as ISO codes | `["US", "CA"]` |
54
51
 
55
52
  ## How Markets Work
56
53
 
57
54
  When a customer visits your store, their country determines which market applies. The market then sets the currency, locale, and tax behavior for that session.
58
55
 
59
56
  ```
60
- Customer's Country → Market → Currency + Locale + Tax Zone
57
+ Customer's Country → Market → Currency + Locale + Tax treatment
61
58
  ```
62
59
 
63
60
  The resolution chain:
@@ -65,7 +62,7 @@ The resolution chain:
65
62
  1. Customer's country is detected (from URL, geolocation, `X-Spree-Country` header, or manual selection)
66
63
  2. Spree finds the market containing that country
67
64
  3. The market's currency and locale become the defaults for the session
68
- 4. The market's tax zone determines whether prices are shown with or without tax
65
+ 4. The market's tax setting determines whether prices are shown with or without tax
69
66
 
70
67
  If no market matches the customer's country, the store's **default market** is used.
71
68
 
@@ -215,9 +212,9 @@ The `tax_inclusive` flag on a market controls how prices are displayed and calcu
215
212
  - **`tax_inclusive: true`** (common in Europe) — the price shown to the customer already includes tax
216
213
  - **`tax_inclusive: false`** (common in the US) — tax is added at checkout on top of the displayed price
217
214
 
218
- Each market also resolves a **tax zone** from its default country. This zone determines which tax rates apply when browsing products before the customer enters a shipping address. Once the customer provides an address at checkout, the actual shipping address takes over for tax calculation.
215
+ This matters because prices are shown long before anyone knows where the shopper lives. The market's own country supplies the assumed rate for browsing; once a shipping address is entered at checkout, that address takes over and the real rate applies.
219
216
 
220
- See [Taxes](taxes.md) for details on tax zones and rates.
217
+ A market also chooses **which tax provider works out its numbers** — Spree's own rate tables in a simple market, an external tax service in a complicated one. See [Taxes](taxes.md).
221
218
 
222
219
  ## Pricing Integration
223
220
 
@@ -247,7 +244,7 @@ const northAmerica = await client.markets.create({
247
244
  name: 'North America',
248
245
  currency: 'USD',
249
246
  default_locale: 'en',
250
- country_isos: ['US', 'CA'],
247
+ country_codes: ['US', 'CA'],
251
248
  default: true,
252
249
  })
253
250
 
@@ -258,7 +255,7 @@ const europe = await client.markets.create({
258
255
  default_locale: 'de',
259
256
  supported_locales: ['de', 'en', 'fr'],
260
257
  tax_inclusive: true,
261
- country_isos: ['DE', 'FR', 'AT', 'NL'],
258
+ country_codes: ['DE', 'FR', 'AT', 'NL'],
262
259
  })
263
260
  ```
264
261
 
@@ -267,7 +264,7 @@ spree api post /markets -d '{
267
264
  "name": "North America",
268
265
  "currency": "USD",
269
266
  "default_locale": "en",
270
- "country_isos": ["US", "CA"],
267
+ "country_codes": ["US", "CA"],
271
268
  "default": true
272
269
  }'
273
270
  ```
@@ -291,6 +288,6 @@ spree api delete /markets/market_xxx
291
288
 
292
289
  - [Markets (Store SDK)](../sdk/store/markets.md) — Listing, resolving, and reading markets from the Store SDK
293
290
  - [Pricing](pricing.md) — Price Lists, Price Rules, and the Pricing Context
294
- - [Addresses](addresses.md) — Countries, States, and Zones
291
+ - [Addresses](addresses.md) — Countries, states, and address forms
295
292
  - [Localization](../../api-reference/store-api/localization.md) — Locale, currency, and country headers in API requests
296
293
  - [Translations](translations.md) — Resource and UI translations