@spree/docs 0.1.177 → 0.1.178

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 (117) hide show
  1. package/dist/developer/cli/quickstart.md +1 -1
  2. package/dist/developer/contributing/creating-an-extension.md +2 -2
  3. package/dist/developer/core-concepts/addresses.md +3 -3
  4. package/dist/developer/core-concepts/architecture.md +4 -4
  5. package/dist/developer/core-concepts/calculators.md +2 -2
  6. package/dist/developer/core-concepts/carts.md +207 -0
  7. package/dist/developer/core-concepts/channels.md +3 -3
  8. package/dist/developer/core-concepts/customers.md +1 -1
  9. package/dist/developer/core-concepts/events.md +1 -1
  10. package/dist/developer/core-concepts/fulfillments.md +278 -0
  11. package/dist/developer/core-concepts/imports-exports.md +6 -18
  12. package/dist/developer/core-concepts/inventory.md +3 -3
  13. package/dist/developer/core-concepts/media.md +1 -1
  14. package/dist/developer/core-concepts/metafields.md +1 -1
  15. package/dist/developer/core-concepts/orders.md +92 -339
  16. package/dist/developer/core-concepts/payments.md +1 -1
  17. package/dist/developer/core-concepts/promotions.md +68 -195
  18. package/dist/developer/core-concepts/returns-exchanges-claims.md +173 -0
  19. package/dist/developer/core-concepts/store-credits-gift-cards.md +16 -18
  20. package/dist/developer/core-concepts/stores.md +1 -1
  21. package/dist/developer/core-concepts/taxes-discounts-fees.md +199 -0
  22. package/dist/developer/core-concepts/taxes.md +3 -3
  23. package/dist/developer/create-spree-app/quickstart.md +1 -1
  24. package/dist/developer/customization/api.md +36 -11
  25. package/dist/developer/customization/configuration.md +207 -37
  26. package/dist/developer/customization/decorators.md +8 -8
  27. package/dist/developer/customization/permissions.md +34 -229
  28. package/dist/developer/customization/quickstart.md +159 -108
  29. package/dist/developer/customization/validations.md +1 -1
  30. package/dist/developer/dashboard/overview.md +1 -1
  31. package/dist/developer/how-to/build-a-b2b-store.md +21 -0
  32. package/dist/developer/how-to/build-a-marketplace.md +25 -0
  33. package/dist/developer/how-to/custom-api-authentication.md +1 -1
  34. package/dist/developer/how-to/custom-delivery-rate-provider.md +242 -0
  35. package/dist/developer/how-to/custom-document-numbers.md +154 -0
  36. package/dist/developer/how-to/custom-order-routing.md +1 -1
  37. package/dist/developer/how-to/custom-payment-method.md +1 -1
  38. package/dist/developer/how-to/custom-promotion.md +76 -115
  39. package/dist/developer/how-to/custom-report.md +2 -2
  40. package/dist/developer/how-to/custom-stock-splitter.md +5 -5
  41. package/dist/developer/how-to/sell-digital-products.md +20 -0
  42. package/dist/developer/multi-tenant/quickstart.md +1 -1
  43. package/dist/developer/providers/dam.md +14 -0
  44. package/dist/developer/providers/erp.md +31 -0
  45. package/dist/developer/providers/fulfillment.md +25 -0
  46. package/dist/developer/{deployment/telemetry.md → providers/observability.md} +1 -1
  47. package/dist/developer/providers/overview.md +44 -0
  48. package/dist/developer/providers/pim.md +25 -0
  49. package/dist/developer/providers/sso.md +20 -0
  50. package/dist/developer/sdk/admin/extending.md +1 -1
  51. package/dist/developer/sdk/admin/quickstart.md +1 -1
  52. package/dist/developer/sdk/admin/resources.md +1 -1
  53. package/dist/developer/sdk/extending.md +1 -1
  54. package/dist/developer/tutorial/admin-api.md +13 -0
  55. package/dist/developer/tutorial/dashboard-plugin.md +14 -0
  56. package/dist/developer/tutorial/events.md +7 -161
  57. package/dist/developer/tutorial/introduction.md +15 -35
  58. package/dist/developer/tutorial/model.md +7 -98
  59. package/dist/developer/tutorial/store-api.md +13 -0
  60. package/dist/developer/tutorial/storefront.md +12 -0
  61. package/dist/developer/tutorial/testing.md +7 -711
  62. package/dist/developer/upgrades/5.6-to-6.0.md +2 -2
  63. package/package.json +1 -1
  64. package/dist/developer/admin/admin.md +0 -214
  65. package/dist/developer/admin/authentication.md +0 -59
  66. package/dist/developer/admin/components.md +0 -711
  67. package/dist/developer/admin/custom-css.md +0 -256
  68. package/dist/developer/admin/custom-javascript.md +0 -116
  69. package/dist/developer/admin/extending-ui.md +0 -1839
  70. package/dist/developer/admin/form-builder.md +0 -444
  71. package/dist/developer/admin/helper-methods.md +0 -531
  72. package/dist/developer/admin/navigation.md +0 -805
  73. package/dist/developer/admin/tables.md +0 -490
  74. package/dist/developer/advanced/adding_spree_to_rails_app.md +0 -92
  75. package/dist/developer/core-concepts/adjustments.md +0 -113
  76. package/dist/developer/core-concepts/reports.md +0 -208
  77. package/dist/developer/core-concepts/shipments.md +0 -307
  78. package/dist/developer/core-concepts/users.md +0 -303
  79. package/dist/developer/customization/authentication.md +0 -100
  80. package/dist/developer/customization/checkout.md +0 -202
  81. package/dist/developer/customization/emails.md +0 -18
  82. package/dist/developer/customization/routes.md +0 -24
  83. package/dist/developer/multi-vendor/installation.md +0 -61
  84. package/dist/developer/multi-vendor/quickstart.md +0 -17
  85. package/dist/developer/tutorial/admin.md +0 -206
  86. package/dist/developer/tutorial/api.md +0 -606
  87. package/dist/developer/tutorial/extending-models.md +0 -393
  88. package/dist/developer/tutorial/sdk.md +0 -170
  89. package/dist/developer/upgrades/2.0-to-2.1.md +0 -46
  90. package/dist/developer/upgrades/2.1-to-2.2.md +0 -59
  91. package/dist/developer/upgrades/2.2-to-2.3.md +0 -44
  92. package/dist/developer/upgrades/2.3-to-2.4.md +0 -42
  93. package/dist/developer/upgrades/3.0-to-3.1.md +0 -47
  94. package/dist/developer/upgrades/3.1-to-3.2.md +0 -34
  95. package/dist/developer/upgrades/3.2-to-3.3.md +0 -70
  96. package/dist/developer/upgrades/3.3-to-3.4.md +0 -36
  97. package/dist/developer/upgrades/3.4-to-3.5.md +0 -44
  98. package/dist/developer/upgrades/3.5-to-3.6.md +0 -40
  99. package/dist/developer/upgrades/3.6-to-3.7.md +0 -62
  100. package/dist/developer/upgrades/3.7-to-4.0.md +0 -152
  101. package/dist/developer/upgrades/4.0-to-4.1.md +0 -92
  102. package/dist/developer/upgrades/4.1-to-4.2.md +0 -109
  103. package/dist/developer/upgrades/4.10-to-5.0.md +0 -131
  104. package/dist/developer/upgrades/4.2-to-4.3.md +0 -100
  105. package/dist/developer/upgrades/4.3-to-4.4.md +0 -125
  106. package/dist/developer/upgrades/4.4-to-4.5.md +0 -94
  107. package/dist/developer/upgrades/4.5-to-4.6.md +0 -119
  108. package/dist/developer/upgrades/4.6-to-4.7.md +0 -39
  109. package/dist/developer/upgrades/4.8-to-4.9.md +0 -24
  110. package/dist/developer/upgrades/4.9-to-4.10.md +0 -24
  111. package/dist/developer/upgrades/4.x-to-4.8.md +0 -52
  112. package/dist/developer/upgrades/5.0-to-5.1.md +0 -28
  113. package/dist/developer/upgrades/5.1-to-5.2.md +0 -131
  114. package/dist/developer/upgrades/5.2-to-5.3.md +0 -338
  115. package/dist/developer/upgrades/5.3-to-5.4.md +0 -277
  116. package/dist/developer/upgrades/5.4-to-5.5.md +0 -301
  117. package/dist/developer/upgrades/5.5-to-5.6.md +0 -207
@@ -0,0 +1,199 @@
1
+ ---
2
+ title: Taxes, Discounts & Fees
3
+ description: How Spree records money added to or taken off an order — separate tax lines, discounts and fees, and the order totals you render in a storefront.
4
+ ---
5
+
6
+ ## Overview
7
+
8
+ An order total is rarely just the sum of item prices. Tax gets added, a promo code takes something off, gift wrapping costs extra. Spree records each of those as its own row, of one of three kinds:
9
+
10
+ | Kind | Effect | What it is |
11
+ |---|---|---|
12
+ | **Tax line** | adds | Tax charged on an item, on delivery, or on a fee |
13
+ | **Discount** | subtracts | A promotion or a manual reduction |
14
+ | **Fee** | adds | A surcharge — handling, gift wrap, cash on delivery |
15
+
16
+ Keeping them apart means a direct question gets a direct answer. "What tax did we charge on this order?" is one request against tax lines, rather than filtering a mixed list and hoping you caught every case.
17
+
18
+ ```mermaid
19
+ erDiagram
20
+ Order ||--o{ TaxLine : "has many"
21
+ Order ||--o{ Discount : "has many"
22
+ Order ||--o{ Fee : "has many"
23
+ LineItem ||--o{ TaxLine : "taxed by"
24
+ LineItem ||--o{ Discount : "reduced by"
25
+ Fulfillment ||--o{ TaxLine : "taxed by"
26
+ Fee ||--o{ TaxLine : "taxed by"
27
+
28
+ TaxLine {
29
+ string label
30
+ string rate
31
+ boolean included
32
+ string amount
33
+ }
34
+ Discount {
35
+ string label
36
+ string kind
37
+ string code
38
+ string amount
39
+ }
40
+ Fee {
41
+ string label
42
+ string kind
43
+ string amount
44
+ }
45
+ ```
46
+
47
+ > **NOTE:** Money is sent as a **string** — `"15.99"`, not `15.99`. JavaScript numbers can't represent every decimal exactly, and `0.1 + 0.2` gives `0.30000000000000004`, which is not something you want inside a total. If you need to do arithmetic, use a decimal library or work in whole cents.
48
+
49
+ ## Building an order summary
50
+
51
+ Most storefronts never need the individual rows. The order already carries a total for each kind, so you can render a summary directly:
52
+
53
+ ```typescript
54
+ const order = await client.orders.get('or_xxx')
55
+
56
+ order.display_item_total // "$120.00" items before tax and discounts
57
+ order.display_discount_total // "-$12.00" everything taken off
58
+ order.display_delivery_total // "$5.00" delivery
59
+ order.display_tax_total // "$22.60" all tax
60
+ order.display_total // "$135.60" what the customer pays
61
+ ```
62
+
63
+ Every total comes in two forms: `total` is the raw value (`"135.60"`) and `display_total` is formatted for the order's currency (`"$135.60"`). Render the `display_` one — it handles the symbol, decimal separator and where the symbol goes.
64
+
65
+ | Attribute | What it sums |
66
+ |---|---|
67
+ | `item_total` | Line item prices before tax and discounts |
68
+ | `discount_total` | All discounts |
69
+ | `tax_total` | All tax |
70
+ | `included_tax_total` | Tax already inside displayed prices |
71
+ | `additional_tax_total` | Tax added on top |
72
+ | `delivery_total` | Delivery charges |
73
+ | `total` | What the customer pays |
74
+
75
+ > **WARNING:** If you add up a subtotal yourself, include `additional_tax_total` only. `included_tax_total` is **already inside** the item prices — adding it again counts the tax twice. This is the most common mistake when building a European storefront.
76
+
77
+ ## Tax lines
78
+
79
+ A tax line records one tax charge and what it was charged on.
80
+
81
+ | Attribute | Description |
82
+ |---|---|
83
+ | `label` | What the customer sees, e.g. `VAT 20%` |
84
+ | `rate` | The rate applied, e.g. `"0.2"` |
85
+ | `included` | Whether the tax was already inside the displayed price |
86
+ | `amount` / `display_amount` | Tax charged |
87
+ | `line_item_id` / `fulfillment_id` / `fee_id` | What was taxed — exactly one is set |
88
+ | `tax_rate_id` | The configured rate, when tax came from Spree rather than an outside service |
89
+
90
+ `included` is the field that trips people up. In most of Europe a €120 price already contains €20 of VAT, so the tax line is there for information — the customer pays €120, not €140. In the US, tax is added at checkout, so the same field is `false` and the amount really is extra.
91
+
92
+ ```typescript Admin SDK
93
+ const taxLines = await adminClient.orders.taxLines.list('or_xxx')
94
+ ```
95
+
96
+ You never create tax lines yourself. They're written whenever the order total is worked out — see [Taxes](taxes.md).
97
+
98
+ ## Discounts
99
+
100
+ | Attribute | Description |
101
+ |---|---|
102
+ | `label` | What the customer sees, e.g. `Summer Sale` |
103
+ | `kind` | `promotion` or `manual` |
104
+ | `code` | The coupon code used, if any |
105
+ | `value` / `value_type` | The rule behind it, e.g. `"10"` / `percent`. Accepts a string or a number on write. |
106
+ | `amount` / `display_amount` | The reduction, as a negative amount |
107
+ | `promotion_id` | The promotion it came from |
108
+ | `line_item_id` / `fulfillment_id` | What it reduced |
109
+
110
+ Most discounts come from [Promotions](promotions.md) — a customer applies a code and the rows appear. A **manual** discount is one an admin adds directly, for a goodwill gesture or a price match:
111
+
112
+
113
+ ```typescript Admin SDK
114
+ // 10% off a single line item
115
+ await adminClient.orders.discounts.create('or_xxx', {
116
+ label: 'Goodwill discount',
117
+ value: '10',
118
+ value_type: 'percent',
119
+ line_item_id: 'li_xxx',
120
+ })
121
+
122
+ // A flat amount across the order
123
+ await adminClient.orders.discounts.create('or_xxx', {
124
+ label: 'Price match',
125
+ value: '15.00',
126
+ value_type: 'flat',
127
+ })
128
+ ```
129
+
130
+ ```bash cURL
131
+ curl -X POST 'https://api.mystore.com/api/v3/admin/orders/or_xxx/discounts' \
132
+ -H 'X-Spree-API-Key: sk_xxx' \
133
+ -H 'Content-Type: application/json' \
134
+ -d '{ "label": "Goodwill discount", "value": 10, "value_type": "percent" }'
135
+ ```
136
+
137
+
138
+ Only manual discounts can be edited or deleted. Changing one that came from a promotion returns a `422` — those belong to the promotion that created them, and letting an order disagree with its own promotion is how reporting stops adding up.
139
+
140
+ A discount on the whole order is shared out across the items rather than kept as one lump sum. So if the customer later returns one item, Spree knows how much of the discount belonged to it.
141
+
142
+ ## Fees
143
+
144
+ | Attribute | Description |
145
+ |---|---|
146
+ | `label` | What the customer sees, e.g. `Gift wrapping` |
147
+ | `kind` | `surcharge`, `handling`, `gift_wrap`, `cod`, `payment`, `duty`, or your own |
148
+ | `amount` / `display_amount` | The charge |
149
+ | `line_item_id` / `fulfillment_id` | What it applies to — both empty means the whole order |
150
+
151
+ ```typescript Admin SDK
152
+ await adminClient.orders.fees.create('or_xxx', {
153
+ label: 'Gift wrapping',
154
+ kind: 'gift_wrap',
155
+ amount: '5.00',
156
+ line_item_id: 'li_xxx',
157
+ })
158
+ ```
159
+
160
+ Fees are always positive, and they're taxable by default — a fee can have its own tax lines. Customs duties are the one exception, described below. To take money off, create a discount instead. That way a refund never has to work out whether a negative fee was a charge or a credit.
161
+
162
+ ### Customs duties
163
+
164
+ A `duty` fee is the one kind tax is **not** applied to automatically. A customs duty is an import charge levied by the destination country, not a sale that domestic tax applies to, so charging tax on top of it would invent tax the merchant never owed. Where import VAT is charged on a duty, whoever calculates the duty records that tax itself.
165
+
166
+ A duty also keeps a copy of what it was calculated from — the HS code, the country of origin, the rate — in its `metadata`. Classification on a product can change next month; what the customer was charged must not.
167
+
168
+ ## Where rows attach
169
+
170
+ Each row points at exactly the thing it applies to:
171
+
172
+ - **Tax lines** → a line item, a [fulfillment](fulfillments.md), or a fee, so item tax and delivery tax stay separately visible
173
+ - **Discounts** → a line item or a fulfillment
174
+ - **Fees** → a line item, a fulfillment, or the order itself
175
+
176
+ Rows also keep a copy of where they came from — the rate and label on a tax line, the code and promotion on a discount. If someone deletes that promotion next month, the order still says what the customer was actually given.
177
+
178
+ ## When totals change
179
+
180
+ Totals are worked out again whenever something changes on a cart — an item added, an address entered, a code applied. The response to every cart change already includes the new totals, so a follow-up request is never needed:
181
+
182
+ ```typescript
183
+ // The returned cart already has updated totals
184
+ const cart = await client.carts.items.create(cartId, {
185
+ variant_id: 'var_xxx',
186
+ quantity: 2,
187
+ })
188
+
189
+ cart.display_total // already correct
190
+ ```
191
+
192
+ Once an order is placed, the rows stop being regenerated. Editing a placed order re-adds its existing rows rather than starting from scratch, so today's promotions can't quietly rewrite what a customer agreed to last week.
193
+
194
+ ## Related
195
+
196
+ - [Taxes](taxes.md) — how tax is worked out
197
+ - [Promotions](promotions.md) — where most discounts come from
198
+ - [Fulfillments](fulfillments.md) — delivery charges
199
+ - [Orders](orders.md) — order totals and statuses
@@ -37,7 +37,7 @@ erDiagram
37
37
  - **Tax Category** groups products for tax purposes (e.g., "Clothing", "Food", "Digital")
38
38
  - **Tax Rate** defines the percentage and rules for a Tax Category within a [Zone](addresses.md#zones)
39
39
  - **Zone** defines geographic regions (countries or states) where taxes apply
40
- - **[Adjustments](adjustments.md)** are created on orders/line items to apply taxes
40
+ - **[Adjustments](taxes-discounts-fees.md)** are created on orders/line items to apply taxes
41
41
 
42
42
  ## Tax Categories
43
43
 
@@ -127,7 +127,7 @@ During checkout, taxes are calculated based on the shipping address:
127
127
 
128
128
  1. The customer's shipping address determines their [Zone](addresses.md#zones)
129
129
  2. Spree finds matching tax rates for that zone and the product's tax category
130
- 3. Tax [Adjustments](adjustments.md) are created on line items
130
+ 3. Tax [Adjustments](taxes-discounts-fees.md) are created on line items
131
131
  4. The order total is updated
132
132
 
133
133
  > **NOTE:** For complex tax requirements (interstate US sales, international VAT), consider using automated tax calculation. The [Stripe Tax integration](../../integrations/payments/stripe.md) handles this automatically.
@@ -158,6 +158,6 @@ Tax categories and rates are managed in the Admin Panel under **Settings → Tax
158
158
 
159
159
  - [Markets](markets.md) — Tax-inclusive pricing per market
160
160
  - [Addresses](addresses.md) — Zones and address-based taxation
161
- - [Adjustments](adjustments.md) — Tax adjustments
161
+ - [Adjustments](taxes-discounts-fees.md) — Tax adjustments
162
162
  - [Orders](orders.md) — How taxes affect order totals
163
163
  - [Avalara integration](../../integrations/tax/avalara.md) — Automated tax calculation for complex jurisdictions
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: "create-spree-app"
3
- sidebarTitle: Overview
3
+ sidebarTitle: create-spree-app
4
4
  description: "Scaffold a new Spree Commerce project with a single command — bootstraps a full Rails backend, Docker dev stack, and the @spree/cli for customization."
5
5
  ---
6
6
 
@@ -5,7 +5,7 @@ description: Add new Store API endpoints, customize existing JSON responses with
5
5
 
6
6
  Before you start customizing Spree API endpoints, make sure you reviewed all existing API endpoints in the [Spree API docs](../../api-reference/admin-api/endpoints.md).
7
7
 
8
- For a step-by-step walkthrough of adding a complete new resource (model, serializer, controller, routes), see the [API tutorial](../tutorial/api.md).
8
+ For a step-by-step walkthrough of adding a complete new resource (model, serializer, controller, routes), see the [API tutorial](../tutorial/admin-api.md).
9
9
 
10
10
  ## Customizing JSON Responses
11
11
 
@@ -35,7 +35,7 @@ Restart the server and the Product API will include your new attribute.
35
35
 
36
36
  ### Adding an Association
37
37
 
38
- Let's say you've created a `Spree::Brand` model that belongs to `Product` (see the [tutorial](../tutorial/api.md) for the full example).
38
+ Let's say you've created a `Spree::Brand` model that belongs to `Product` (see the [tutorial](../tutorial/admin-api.md) for the full example).
39
39
 
40
40
  Create a serializer for the new model:
41
41
 
@@ -173,20 +173,45 @@ end
173
173
 
174
174
  ### Permitted Attributes
175
175
 
176
- For endpoints that accept writes (create/update), add your attributes:
176
+ For endpoints that accept writes (create/update), declare the attributes in your
177
+ controller:
177
178
 
178
- ```ruby config/initializers/spree.rb
179
- Spree::PermittedAttributes.brand_attributes = [:name, :slug, :description, :logo]
179
+ ```ruby
180
+ def resource_permitted_attributes
181
+ [:name, :slug, :description, :logo]
182
+ end
180
183
  ```
181
184
 
182
- Or override `permitted_params` in the controller:
185
+ Entries are `params.permit` fragments, so collections and nested structures use
186
+ the shapes you would pass there directly — `[:name, { region_ids: [] }]`.
183
187
 
184
- ```ruby
185
- def permitted_params
186
- params.permit(:name, :slug, :description, :logo)
187
- end
188
+ > **NOTE:** Declare `resource_permitted_attributes`, not `permitted_attributes`. The
189
+ > latter is what appends extension-contributed attributes to your list, so
190
+ > overriding it replaces them.
191
+
192
+ ### Adding an Attribute to a Core Resource
193
+
194
+ To make a new column writable on a Spree model without decorating the
195
+ controllers that write it, append to the model's permitted attributes:
196
+
197
+ ```ruby config/initializers/spree.rb
198
+ Spree::Product.additional_permitted_attributes += [:brand_id]
188
199
  ```
189
200
 
201
+ Standard resource endpoints append these to their own allowlist, so one
202
+ declaration covers the model's create and update endpoints.
203
+
204
+ Use `+=`, never `=` — assigning replaces what another extension added — and
205
+ declare only attributes of your own, since redeclaring a key the controller
206
+ already permits replaces its filter rather than widening it.
207
+
208
+ > **NOTE:** For merchant-managed data, prefer [Custom Fields](../core-concepts/metafields.md),
209
+ > which need no code and are filterable and sortable. This is for extensions
210
+ > that add real database columns.
211
+
212
+ Two endpoints deliberately ignore this hook because their parameters are
213
+ authorization data rather than resource data: API keys and invitations.
214
+
190
215
  ## Consuming Custom Endpoints with the SDK
191
216
 
192
- See the [SDK tutorial](../tutorial/sdk.md) for how to call custom endpoints from TypeScript and extend the generated types.
217
+ See the [SDK tutorial](../tutorial/storefront.md) for how to call custom endpoints from TypeScript and extend the generated types.
@@ -1,55 +1,225 @@
1
1
  ---
2
- title: "Configuration"
2
+ title: Configuration
3
+ description: Every global Spree setting that is actually read by Spree 6 — grouped by area, with defaults and what each one changes.
3
4
  ---
4
5
 
5
- Here is a list of all the configuration options that are available in Spree.
6
-
7
- | Configuration Key | Description | Default Value |
8
- |------------------------------------|-------------------------------------------------------------------------------------------------------|---------------------|
9
- | `allow_checkout_on_gateway_error` | Continues the checkout process even if the payment gateway error failed. | `false` |
10
- | `address_requires_phone` | Determines whether a phone number is required for Addresses. | `false` |
11
- | `alternative_shipping_phone` | Determines if an alternative phone number should be present for the shipping address on the checkout page. | `false` |
12
- | `always_include_confirm_step` | Determines if the confirmation step is always included in the checkout process, regardless of the payment method. | `false` |
13
- | `auto_capture` | Depending on whether or not Spree is configured to "auto capture" the credit card, either a purchase or an authorize operation will be performed on the card (via the current credit card gateway). | `true` |
14
- | `auto_capture_on_dispatch` | Captures payment for each shipment in Shipment#after_ship callback, and makes Shipment.ready when payment authorized. | `false` |
15
- | `company` | Determines whether or not a field for "Company" displays on the address form. | `false` |
16
- | `credit_to_new_allocation` | Determines if a new allocation is created anytime store credit is added. If not set, it will update the store credit's amount in place. | `false` |
17
- | `disable_sku_validation` | Determines if the built-in SKU uniqueness validation is disabled. | `false` |
18
- | `disable_store_presence_validation` | Determines if Store presence validation for Products and Payment Methods is disabled. | `false` |
19
- | `expedited_exchanges` | Determines if an exchange shipment is kicked off upon return authorization save. Requires payment profiles to be supported on your gateway and a configured delayed job handler. | `false` |
20
- | `expedited_exchanges_days_window` | The number of days the customer has to return their item after the expedited exchange is shipped to avoid being charged. | `14` |
21
- | `restock_inventory` | Determines if inventory should be restocked when an order is canceled or returned | `true` |
22
- | `return_eligibility_number_of_days` | The number of days after purchase within which a return can be initiated. | `365` |
23
- | `show_products_without_price` | Determines if products without a price are shown in the storefront and Storefront API | `false` |
24
- | `tax_using_ship_address` | Determines if tax information should be based on shipping address, rather than the billing address. | `true` |
25
- | `track_inventory_levels` | Determines if inventory levels should be tracked when products are purchased at checkout. This option causes new `InventoryUnit` objects to be created when a product is bought. | `true` |
26
-
27
- ## Spree Initializer
28
-
29
- To change values for these preferences, you need to edit your `config/initializers/spree.rb` file.
30
-
31
- For example, to disable the `expedited_exchanges` feature, you would add the following line:
6
+ Global settings live in `config/initializers/spree.rb` and apply to the whole application. Anything that varies per store — currency, markets, delivery zones, languages — is data you change in the dashboard instead.
32
7
 
33
- ```ruby
8
+ ```ruby config/initializers/spree.rb
34
9
  Spree.config do |config|
35
- config.expedited_exchanges = false
10
+ config.minimum_password_length = 10
11
+ config.allow_empty_price_amount = true
36
12
  end
37
13
  ```
38
14
 
39
- > **NOTE:** Remember to restart your Rails server after making changes to the `config/initializers/spree.rb` file.
15
+ > **NOTE:** Restart your Rails server after editing the initializer.
16
+
17
+ ## Deployment settings read the environment
18
+
19
+ Settings that describe the deployment rather than commerce behavior can be set with an environment variable — no Ruby, no code change, no restart-after-edit dance. An explicitly configured value always wins over the environment.
20
+
21
+ | Setting | Environment variable | What it configures |
22
+ |---|---|---|
23
+ | `dashboard_url` | `SPREE_DASHBOARD_URL` | Origin where the admin dashboard is hosted (e.g. `https://admin.my-shop.com`). Used for every link that sends someone into the dashboard: invitation emails, SSO redirects, and the first-run setup link printed by `db:seed`. |
24
+
25
+ This page lists only settings Spree 6 actually reads. Settings that exist for backwards compatibility are listed under [Deprecated settings](#deprecated-settings) rather than mixed in with the rest.
26
+
27
+ ## Settings that belong to a store
28
+
29
+ Settings that shape how a shop sells — when customers are charged, whether stock is counted, which products appear without a price — are properties of a store, not of the installation, so they live on `Spree::Store` and are edited in the dashboard under **Settings → Store**:
40
30
 
41
- ## Accessing Configuration
31
+ `capture_method`, `track_inventory_levels`, `stock_reservations_enabled`, `stock_reservation_ttl_minutes`, `track_price_history`, `show_products_without_price`, `disable_sku_validation`, `address_requires_phone`, `company_field_enabled`, `tax_using_ship_address`, plus `guest_checkout` and `storefront_access` — those last two are owned by the channel, and the store's value is what a channel falls back to.
42
32
 
43
- To access these preferences in your application, you can use the `Spree::Config` module. For example, to access the `expedited_exchanges` preference, you can do the following:
33
+ They were global before Spree 6. The global versions still exist for one release but are no longer read, so setting one in the initializer changes nothing — run `bin/rake spree:store_settings:backfill_from_config` during the upgrade to copy your values onto every store.
44
34
 
45
35
  ```ruby
46
- Spree::Config.expedited_exchanges
36
+ store = Spree::Current.store
37
+ store.preferred_capture_method # => "checkout"
38
+ store.update!(preferred_track_inventory_levels: false)
47
39
  ```
48
40
 
49
- This will return the current value of the `expedited_exchanges` preference. You can also set the value of a preference using the `Spree::Config` module. For example, to set the `expedited_exchanges` preference to `true`, you can do the following:
41
+ ### When customers are charged
42
+
43
+ `capture_method` says when a customer is charged rather than only authorized. It applies to every payment method, not just card gateways. It takes one of three values:
44
+
45
+ | Value | What happens |
46
+ |---|---|
47
+ | `checkout` | The money is taken as soon as the order is placed. This is the default. |
48
+ | `on_dispatch` | The amount is authorized at checkout and captured when the goods are dispatched. |
49
+ | `manual` | The amount is authorized at checkout and staff capture it when they choose. |
50
+
51
+ A payment method can override the store for its own payments by setting the same three values; leaving it blank inherits the store's choice. Ask a payment method what applies with `resolved_capture_method`, or use the `capture_at_checkout?`, `capture_on_dispatch?` and `capture_manually?` questions.
50
52
 
51
53
  ```ruby
52
- Spree::Config.expedited_exchanges = true
54
+ payment_method.update!(capture_method: 'on_dispatch')
55
+ payment_method.resolved_capture_method # => "on_dispatch"
56
+
57
+ payment_method.update!(capture_method: nil) # back to inheriting
58
+ payment_method.resolved_capture_method # => "checkout"
53
59
  ```
54
60
 
55
- This will set the `expedited_exchanges` preference to `true` for the current process. After restarting the Rails server, the preference will return to the default value or the one set in the initializer.
61
+ Before Spree 6 this was two separate booleans, `auto_capture` and `auto_capture_on_dispatch`, which could contradict each other. Two upgrade tasks carry existing installs over: `spree:store_settings:backfill_from_config` maps the old globals onto each store's `capture_method`, and `spree:migrate_capture_methods` converts the per-payment-method `auto_capture` column. A method left without a value inherits its store.
62
+
63
+ ## Checkout and payments
64
+
65
+ | Setting | Default | What it does |
66
+ |---|---|---|
67
+ | `always_include_confirm_step` | `false` | Always shows a confirmation step, even when the payment method doesn't need one. |
68
+ | `credit_to_new_allocation` | `false` | Records refunded store credit as a new entry each time instead of topping the original credit back up. |
69
+
70
+ ## Carts and stock reservations
71
+
72
+ | Setting | Default | What it does |
73
+ |---|---|---|
74
+ | `guest_cart_expiry_days` | `30` | How long an abandoned guest cart is kept before deletion. |
75
+ | `customer_cart_expiry_days` | `90` | How long a signed-in customer's abandoned cart is kept. |
76
+ | `empty_cart_expiry_hours` | `48` | How long a cart with no items is kept. |
77
+
78
+ ## Products and pricing
79
+
80
+ | Setting | Default | What it does |
81
+ |---|---|---|
82
+ | `allow_empty_price_amount` | `false` | Allows a price to be left blank instead of requiring an amount. Prices in a price list already allow blank. |
83
+ | `price_history_retention_days` | `30` | How many days of price history the `spree:price_history:prune` task keeps. |
84
+ | `always_use_translations` | `false` | Always read product, category and collection names from the translation tables, even in the store's own default language. |
85
+
86
+ ## Addresses
87
+
88
+ | Setting | Default | What it does |
89
+ |---|---|---|
90
+ | `geocode_addresses` | `true` | Looks up latitude and longitude for saved addresses. |
91
+
92
+ ## Media
93
+
94
+ | Setting | Default | What it does |
95
+ |---|---|---|
96
+ | `max_image_download_size` | `20971520` | Largest file size, in bytes, accepted when downloading an image from an external URL. |
97
+ | `images_save_from_url_job_attempts` | `5` | How many times to retry fetching an external image before giving up. |
98
+
99
+ ## Promotions and gift cards
100
+
101
+ | Setting | Default | What it does |
102
+ |---|---|---|
103
+ | `coupon_codes_web_limit` | `500` | How many coupon codes are created immediately when saving a promotion. Larger batches move to a background job so saving stays fast. |
104
+ | `coupon_codes_total_limit` | `5000` | Maximum coupon codes a single promotion may generate. |
105
+ | `gift_card_batch_web_limit` | `500` | How many gift card codes a batch creates immediately before switching to a background job. |
106
+ | `gift_card_batch_limit` | `50000` | Maximum gift card codes in a single batch. |
107
+
108
+ ## Accounts and passwords
109
+
110
+ | Setting | Default | What it does |
111
+ |---|---|---|
112
+ | `minimum_password_length` | `8` | Shortest password a customer or staff member may set. |
113
+ | `maximum_password_length` | `72` | Longest password allowed. |
114
+ | `max_failed_login_attempts` | `5` | Failed sign-ins before an account is temporarily locked. |
115
+ | `lockout_duration` | `1800` | How long an account stays locked, in seconds. |
116
+ | `customer_password_reset_expires_in` | `15` | How long a customer's password reset link stays valid, in minutes. |
117
+ | `admin_password_reset_expires_in` | `15` | How long a staff password reset link stays valid, in minutes. |
118
+
119
+ ## URLs
120
+
121
+ | Setting | Default | What it does |
122
+ |---|---|---|
123
+ | `dashboard_url` | `nil` | Where the React dashboard lives, so single sign-on can send staff back to it after they authenticate. |
124
+ | `admin_url` | `nil` | Base address used to build staff invitation links in email. Invitation links are relative when this is unset. |
125
+
126
+ ## Imports and events
127
+
128
+ | Setting | Default | What it does |
129
+ |---|---|---|
130
+ | `large_import_threshold` | `500` | Row count above which a CSV import switches to bulk processing — faster, with less live progress detail. |
131
+ | `events_log_enabled` | `true` | Writes every Spree event to the Rails log. |
132
+ | `disable_migration_check` | `false` | Silences the boot warning about pending engine migrations. |
133
+
134
+ ## API settings
135
+
136
+ API settings live on their own configuration object:
137
+
138
+ ```ruby config/initializers/spree.rb
139
+ Spree::Api::Config.jwt_expiration = 1800
140
+ ```
141
+
142
+ ### Authentication
143
+
144
+ | Setting | Default | What it does |
145
+ |---|---|---|
146
+ | `jwt_expiration` | `3600` | How long a customer or store access token stays valid, in seconds. |
147
+ | `admin_jwt_expiration` | `300` | How long a staff access token stays valid. Deliberately short — admin tokens carry far more privilege. |
148
+ | `refresh_token_expiry` | `2592000` | How long a refresh token stays valid, in seconds (30 days). |
149
+ | `jwt_secret_key` | `nil` | Signing key for tokens. Falls back to the Rails secret key base. |
150
+
151
+ ### Rate limits
152
+
153
+ | Setting | Default | What it does |
154
+ |---|---|---|
155
+ | `rate_limit_window` | `60` | Length of the rate-limit window, in seconds. |
156
+ | `rate_limit_per_key` | `300` | Requests allowed per window for each publishable API key + client IP pair (per visitor), and for keyless traffic per IP. Applies across the whole API, not per endpoint. |
157
+ | `rate_limit_per_secret_key` | `600` | Requests allowed per secret API key per window, across the whole API. |
158
+ | `rate_limit_login` | `5` | Sign-in attempts allowed per IP per window. |
159
+ | `rate_limit_register` | `3` | Registration attempts per IP. |
160
+ | `rate_limit_refresh` | `10` | Token refresh attempts per IP. |
161
+ | `rate_limit_password_reset` | `3` | Password reset requests per IP. |
162
+ | `max_request_body_size` | `102400` | Largest accepted request body, in bytes (100 KB). |
163
+
164
+ ### Webhooks
165
+
166
+ | Setting | Default | What it does |
167
+ |---|---|---|
168
+ | `webhooks_enabled` | `true` | Whether webhooks fire at all. |
169
+ | `webhooks_verify_ssl` | `true` | Verifies SSL certificates on webhook endpoints. Off in development. |
170
+
171
+ ## Reading a setting
172
+
173
+ ```ruby
174
+ Spree::Config.minimum_password_length # => 8
175
+ Spree::Api::Config.jwt_expiration # => 3600
176
+ ```
177
+
178
+ Assigning at runtime affects only the current process and is lost on restart. Set values in the initializer to make them stick.
179
+
180
+ ## Deprecated settings
181
+
182
+ These still exist so upgrades don't break, but Spree 6 either ignores them or reads them only from subsystems it has replaced. Don't use them in new code.
183
+
184
+ | Setting | Use instead |
185
+ |---|---|
186
+ | `return_eligibility_number_of_days` | The `return_window_days` preference on [Market](../core-concepts/markets.md), or a `returns.create.validate` [hook](workflows.md) |
187
+ | `restock_inventory` | Restocking is decided per return line item by its resellable flag — see [Returns](../core-concepts/returns-exchanges-claims.md#processing-a-return) |
188
+ | `expedited_exchanges` | [Exchanges](../core-concepts/returns-exchanges-claims.md) |
189
+ | `expedited_exchanges_days_window` | [Exchanges](../core-concepts/returns-exchanges-claims.md) |
190
+ | `enable_legacy_default_price` | Compatibility switch for the old default-price behavior; leave off |
191
+ | `use_user_locale` | Locale comes from the `x-spree-locale` header or the `locale` parameter |
192
+ | `company` | The `company_field_enabled` setting on the store |
193
+ | `address_requires_phone` | The `address_requires_phone` setting on the store |
194
+ | `default_stock_reservation_ttl_minutes` | The `stock_reservation_ttl_minutes` setting on the store |
195
+ | `auto_capture` | The `capture_method` setting on the store |
196
+ | `auto_capture_on_dispatch` | The `capture_method` setting on the store |
197
+ | `stock_reservations_enabled` | The `stock_reservations_enabled` setting on the store |
198
+ | `track_inventory_levels` | The `track_inventory_levels` setting on the store |
199
+ | `show_products_without_price` | The `show_products_without_price` setting on the store |
200
+ | `disable_sku_validation` | The `disable_sku_validation` setting on the store |
201
+ | `track_price_history` | The `track_price_history` setting on the store |
202
+ | `tax_using_ship_address` | The `tax_using_ship_address` setting on the store |
203
+ | `disable_store_presence_validation` | Every store-owned record now requires a store |
204
+
205
+ > **WARNING:** These settings have no effect at all in Spree 6, and are scheduled for removal: `allow_checkout_on_gateway_error` (completion checks whether payments cover the total, so a failed gateway call never completes an order), `products_per_page`, `alternative_shipping_phone`, `show_variant_full_price`, `reserve_stock_on`, `storefront_products_path`, `storefront_taxons_path`, `storefront_pages_path`. Setting them changes nothing.
206
+
207
+ ## Settings removed in Spree 6
208
+
209
+ These were deprecated in Spree 5 and are gone. Assigning one in your initializer now raises `NoMethodError`, so delete the line.
210
+
211
+ | Setting | What to do instead |
212
+ |---|---|
213
+ | `send_core_emails` | The `send_consumer_transactional_emails` setting on the store, which turns a shop's customer emails on and off. |
214
+ | `currency` | Currency belongs to the store and the market. |
215
+ | `address_requires_state` | State requirements come from the address country. |
216
+ | `checkout_zone` | Use [delivery zones](../core-concepts/fulfillments.md#delivery-methods-and-zones). |
217
+ | `allow_guest_checkout` | Use the `guest_checkout` setting on the store or channel. |
218
+ | `binary_inventory_cache` | A stock change always touches its variant. |
219
+ | `logo`, `mailer_logo` | The store's own `logo` and `mailer_logo` images, uploaded in the dashboard. Only the global settings are gone; the images themselves are unchanged. |
220
+ | `layout`, `always_put_site_name_in_title`, `title_site_name_separator`, `max_level_in_taxons_menu`, `show_raw_product_description`, `shipping_instructions`, `show_only_complete_orders_by_default` | These configured the removed Rails storefront and admin. A storefront owns its own presentation. |
221
+
222
+ ## Related
223
+
224
+ - [Model preferences](model-preferences.md) — per-record settings
225
+ - [Services & Workflows](workflows.md) — changing behavior rather than settings
@@ -8,9 +8,9 @@ description: Use Spree decorators to add or modify behavior on core models, cont
8
8
  > - **[Events & Subscribers](../core-concepts/events.md)** - For reacting to model changes (after save, create, update, delete)
9
9
  > - **[Webhooks](../core-concepts/webhooks.md)** - For notifying external services when events occur
10
10
  > - **[Dependencies](dependencies.md)** - For swapping out services, serializers, and abilities
11
- > - **[Admin Navigation](../admin/navigation.md)** - For adding menu items without controller decorators
12
- > - **[Admin Partials](../admin/extending-ui.md)** - For extending admin UI without view decorators
13
- > - **[Admin Tables](../admin/tables.md)** - For customizing admin list views
11
+ > - **[Admin Navigation](../dashboard/customization/navigation.md)** - For adding menu items without controller decorators
12
+ > - **[Admin Partials](../dashboard/customization/quickstart.md)** - For extending admin UI without view decorators
13
+ > - **[Admin Tables](../dashboard/customization/tables.md)** - For customizing admin list views
14
14
  > - **[Ransack Configuration](../core-concepts/search-filtering.md#extending-ransackable-configuration)** - For adding searchable/sortable fields without model decorators
15
15
 
16
16
  ## When to Use Decorators vs Modern Alternatives
@@ -23,8 +23,8 @@ Before reaching for a decorator, check if your use case is better served by a mo
23
23
  | Notify external service on changes | Model decorator with callbacks | [Webhooks](../core-concepts/webhooks.md) |
24
24
  | Custom add-to-cart logic | Service decorator | [Dependencies injection](dependencies.md) |
25
25
  | Custom API responses | Serializer decorator | [Dependencies injection](dependencies.md) |
26
- | Add admin menu item | Controller decorator | [Admin Navigation API](../admin/navigation.md) |
27
- | Add section to admin form | View decorator/override | [Admin Partials injection](../admin/extending-ui.md) |
26
+ | Add admin menu item | Controller decorator | [Admin Navigation API](../dashboard/customization/navigation.md) |
27
+ | Add section to admin form | View decorator/override | [Admin Partials injection](../dashboard/customization/quickstart.md) |
28
28
  | Add searchable/filterable field | Model decorator with `ransackable_attributes` | [Ransack configuration](../core-concepts/search-filtering.md#extending-ransackable-configuration) |
29
29
  | Add association to core model | - | Decorator (still appropriate) |
30
30
  | Add validation to core model | - | Decorator (still appropriate) |
@@ -528,7 +528,7 @@ end
528
528
  - [Events](../core-concepts/events.md) - Learn about Spree's event system
529
529
  - [Webhooks](../core-concepts/webhooks.md) - HTTP callbacks for external integrations
530
530
  - [Dependencies](dependencies.md) - Swap core services with your own
531
- - [Admin Navigation](../admin/navigation.md) - Extend admin menu without decorators
532
- - [Admin Partials](../admin/extending-ui.md) - Extend admin UI without view decorators
533
- - [Extending Core Models Tutorial](../tutorial/extending-models.md) - Step-by-step guide to connecting custom models with Spree core
531
+ - [Admin Navigation](../dashboard/customization/navigation.md) - Extend admin menu without decorators
532
+ - [Admin Partials](../dashboard/customization/quickstart.md) - Extend admin UI without view decorators
533
+ - [Extending Core Models Tutorial](../tutorial/model.md) - Step-by-step guide to connecting custom models with Spree core
534
534
  - [Customization Overview](quickstart.md) - General customization patterns