@spree/docs 0.1.144 → 0.1.146
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.
|
@@ -233,9 +233,6 @@ First register the class so it's selectable (this is the allowlist the model val
|
|
|
233
233
|
```ruby
|
|
234
234
|
# config/initializers/spree.rb
|
|
235
235
|
Spree.order_routing.strategies << 'Acme::Oms::Strategy'.constantize
|
|
236
|
-
|
|
237
|
-
# Optionally drop the legacy escape hatch:
|
|
238
|
-
# Spree.order_routing.strategies.delete(Spree::OrderRouting::Strategy::Legacy)
|
|
239
236
|
```
|
|
240
237
|
|
|
241
238
|
Then select it by class name string — set on `Spree::Store` (default) or `Spree::Channel` (override). Setting an unregistered class fails validation.
|
|
@@ -241,7 +241,7 @@ store.update!(preferred_order_routing_strategy: 'Spree::OrderRouting::Strategy::
|
|
|
241
241
|
|
|
242
242
|
The Legacy strategy delegates to `Spree::Stock::Coordinator`, which is the exact pre-5.5 packing pipeline — every active stock location is packed, the Prioritizer distributes inventory units across the resulting packages, and no merchant routing rules are consulted. Your existing customizations on `Coordinator`, `Packer`, `Prioritizer`, and the splitters keep working unchanged.
|
|
243
243
|
|
|
244
|
-
> **WARNING:** `Spree::OrderRouting::Strategy::Legacy`
|
|
244
|
+
> **WARNING:** `Spree::OrderRouting::Strategy::Legacy` **was removed in Spree 6.0**. It exists only on the 5.5/5.6 line, as a temporary escape hatch while you evaluate the Rules strategy. Stores still carrying this value fall back to the default Rules strategy on 6.0 (with a logged warning), so clear the preference before upgrading.
|
|
245
245
|
|
|
246
246
|
## Behavior changes to review
|
|
247
247
|
|
|
@@ -182,6 +182,74 @@ The full address surface is shared by Cart and Order through `Spree::Purchase::A
|
|
|
182
182
|
- A signed-in customer entering checkout gets blank address slots **auto-filled from their saved defaults**.
|
|
183
183
|
- **`use_billing` is deprecated** (removed in 6.1): the shipping address is canonical — use `use_shipping` to copy ship → bill.
|
|
184
184
|
|
|
185
|
+
## Returns, exchanges and claims
|
|
186
|
+
|
|
187
|
+
The `ReturnAuthorization → CustomerReturn → Reimbursement` chain is replaced by three first-class records that each belong directly to an order: **`Spree::Return`** (items come back, money goes back), **`Spree::Exchange`** (items come back, different items go out), and **`Spree::Claim`** (something went wrong in delivery — no items required back).
|
|
188
|
+
|
|
189
|
+
The old classes are **gone with no bridge**, so calls raise `NameError`:
|
|
190
|
+
|
|
191
|
+
| Removed | Use instead |
|
|
192
|
+
|---|---|
|
|
193
|
+
| `Spree::ReturnAuthorization` | `Spree::Return` or `Spree::Exchange` |
|
|
194
|
+
| `Spree::CustomerReturn` | `Spree::Return` — receiving is a status on the same record |
|
|
195
|
+
| `Spree::Reimbursement`, `Spree::ReimbursementType` | `Spree::Returns::Refund` (a workflow, not a model) |
|
|
196
|
+
| `Spree::ReturnItem` | `Spree::ReturnLineItem` / `Spree::ExchangeLineItem` / `Spree::ClaimLineItem` |
|
|
197
|
+
| `Spree::ReturnItem::EligibilityValidator::*` | the `validate` hook — see below |
|
|
198
|
+
| `Spree::ReturnAuthorizationReason` | `Spree::ReturnReason` (constant alias kept until 6.1, with a warning) |
|
|
199
|
+
|
|
200
|
+
> **NOTE:** **The legacy tables are not dropped.** They stay through 6.1 as the data migration's source and rollback path. Run `spree:upgrade:migrate_returns` to copy the history onto the new models — it's resumable, and it aborts if any row fails so an upgrade can't silently proceed on partial history.
|
|
201
|
+
|
|
202
|
+
### No state machines
|
|
203
|
+
|
|
204
|
+
New records carry a plain `status` string with an inclusion validation. Every transition is a workflow — `Spree::Returns::Approve`, `Returns::Receive`, `Returns::Refund`, `Exchanges::Fulfill`, `Claims::Resolve`, and so on. **Nothing happens in a model callback or transition callback**, so code that hooked `before_transition` on the old machines has no equivalent; move it to a workflow hook or an event subscriber.
|
|
205
|
+
|
|
206
|
+
Statuses are extensible but additive only, through `Spree::HasStatus`:
|
|
207
|
+
|
|
208
|
+
```ruby
|
|
209
|
+
Spree::Return.add_status('inspecting', after: 'received')
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Eligibility is a hook, not a validator chain
|
|
213
|
+
|
|
214
|
+
`ReturnItem::EligibilityValidator::Default` chained five validators. Only one was policy; the rest were invariants, so they moved to different places:
|
|
215
|
+
|
|
216
|
+
| Old validator | Where the rule lives now |
|
|
217
|
+
|---|---|
|
|
218
|
+
| `TimeSincePurchase` | `Spree::Returns::EligibilityValidator`, registered on the `validate` hook |
|
|
219
|
+
| `OrderCompleted` | inline guard in `Returns::Create` |
|
|
220
|
+
| `InventoryShipped` | structural — only fulfillment items can be returned, and quantities are checked against what shipped |
|
|
221
|
+
| `NoReimbursements` | quantity math — prior return line items are subtracted, so units can't come back twice |
|
|
222
|
+
| `RMARequired` | gone: the `Return` **is** the request, so there is nothing to require |
|
|
223
|
+
|
|
224
|
+
Core ships exactly one policy rule — a return window read from `market.preferred_return_window_days` (default 30), per market because return windows are a regional legal matter. **Staff can override it**: when `created_by` is present the window is advisory, so a supervisor can accept a late return without a code change.
|
|
225
|
+
|
|
226
|
+
> **WARNING:** Four returns settings are **deprecated and read by nothing** — they still exist so applications don't crash at boot, but they have no effect and now emit a deprecation warning when read or written:
|
|
227
|
+
>
|
|
228
|
+
> | Deprecated setting | Use instead |
|
|
229
|
+
> |---|---|
|
|
230
|
+
> | `return_eligibility_number_of_days` | `preferred_return_window_days` on `Spree::Market`, or a `returns.create.validate` hook |
|
|
231
|
+
> | `restock_inventory` | `Spree::ReturnLineItem#resellable`, decided per line item at receiving |
|
|
232
|
+
> | `expedited_exchanges`, `expedited_exchanges_days_window` | `Spree::Exchange` and the `Exchanges::Fulfill` workflow |
|
|
233
|
+
|
|
234
|
+
Replace it by swapping the handler:
|
|
235
|
+
|
|
236
|
+
```ruby
|
|
237
|
+
Spree.hooks.unregister('returns.create.validate', 'Spree::Returns::EligibilityValidator')
|
|
238
|
+
Spree.hooks.register('returns.create.validate', 'MyStore::ReturnPolicy')
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
A handler receives the workflow (so it can read `order`, `items`, `created_by`, `order.market`) and calls `workflow.reject!(message)` to veto. Every one of the fifteen transitions has a leading `validate` hook, so the same seam gates approving, receiving and refunding.
|
|
242
|
+
|
|
243
|
+
> **WARNING:** `requires_manual_intervention?` has no equivalent. The old validators could mark an item eligible-but-flagged for manual review; a handler now either accepts or vetoes. If you relied on that middle state, model it explicitly — an added status, or a metafield your handler sets.
|
|
244
|
+
|
|
245
|
+
### Other behavioral changes
|
|
246
|
+
|
|
247
|
+
- **`Return#refunded_total` counts store credits.** Store credit is a separate ledger and never creates a `Spree::Refund` row, so the old sum reported zero for a store-credit refund.
|
|
248
|
+
- **`Order#outstanding_balance` dropped its reimbursement term** rather than replacing it — refunds already net out of `payment_total`, so that term was double-counting.
|
|
249
|
+
- **The reimbursement email is now `Spree::ReturnMailer#refunded_email`**, sent on `return.refunded` (in the optional `spree_emails` gem, like the other transactional mail).
|
|
250
|
+
- **`Refund#originator`** points at the new records.
|
|
251
|
+
- Events are `return.requested` / `.approved` / `.received` / `.refunded` / `.canceled`, and the matching `exchange.*` and `claim.*` families.
|
|
252
|
+
|
|
185
253
|
## Dependency injection changes
|
|
186
254
|
|
|
187
255
|
6.0 introduces `*_workflow` keys for the flows that graduated to the workflow tier. The old `*_service` keys **stay settable and readable one release so applications don't crash at boot — but a legacy write is stashed, not applied**: a class written against the old service contract is not interchangeable with the workflow the new call sites consume. Reads return your stashed class (legacy code calling its own override keeps working), falling back to the workflow. Removed in 6.1.
|
|
@@ -209,6 +277,73 @@ Removed keys (their classes no longer exist): `carts_validate_service` (completi
|
|
|
209
277
|
|
|
210
278
|
If you override a workflow seam, subclass the shipped workflow (or implement the same `perform` keyword contract) — workflow arguments are plain Ruby keywords, so a mismatch raises `ArgumentError` at call time, not silently.
|
|
211
279
|
|
|
280
|
+
## Removed in 6.0
|
|
281
|
+
|
|
282
|
+
These were deprecated in 5.x and are **gone now** — there is no bridge, so calls raise `NoMethodError`. Most were one-line delegations to a replacement that already exists.
|
|
283
|
+
|
|
284
|
+
| Removed | Use instead |
|
|
285
|
+
|---|---|
|
|
286
|
+
| `Product#default_image`, `#featured_image`, `#primary_image` | `#primary_media` |
|
|
287
|
+
| `Variant#default_image`, `#primary_image` | `#primary_media` |
|
|
288
|
+
| `Address#user_default_billing?`, `#user_default_shipping?` | `#is_default_billing?`, `#is_default_shipping?` |
|
|
289
|
+
| `OptionType#color?` | `#color_swatch?` |
|
|
290
|
+
| `Category#set_store` | `#ensure_store` |
|
|
291
|
+
| `Category.for_taxonomy` | `.for_store` |
|
|
292
|
+
| `Store#admin_users` | `#users` |
|
|
293
|
+
| `Store#supported_shipping_zones` | `#countries_with_shipping_coverage` |
|
|
294
|
+
| `Spree.searcher_class` | `Spree.search_provider` |
|
|
295
|
+
| `Spree.admin_user_class.spree_admin_created?` | `.spree_admin.exists?` |
|
|
296
|
+
| `BaseMailer#set_email_locale` | wrap the action body in `with_store_locale(store) { ... }` |
|
|
297
|
+
| `Asset#styles`, `ImageMethods#generate_url`, `#original_url` | Active Storage variants with `cdn_image_url` |
|
|
298
|
+
| `Spree::ImageMethods`, `Spree::NumberAsParam` (concerns) | deleted — `NumberAsParam` was already a no-op; prefixed IDs come from `Spree::PrefixedId` |
|
|
299
|
+
| `Product.with_option`, `.with`, `.in_name`, `.in_name_or_keywords`, `.in_name_or_description`, `.with_ids`, `.for_user` | `.with_option_value`, `.search`, `where(id: ids)` |
|
|
300
|
+
| `Product.add_search_scope` | plain `scope :name, -> { ... }` |
|
|
301
|
+
| `Spree::Image::Configuration::ActiveStorage` | deleted — an empty no-op module; all logic lives in `Spree::Asset` |
|
|
302
|
+
| `Spree::OrderRouting::Strategy::Legacy` | `Spree::OrderRouting::Strategy::Rules` (see below) |
|
|
303
|
+
|
|
304
|
+
### The Legacy order-routing strategy is gone
|
|
305
|
+
|
|
306
|
+
`Spree::OrderRouting::Strategy::Legacy` — the pre-5.5 escape hatch that delegated straight to `Spree::Stock::Coordinator` and consulted no routing rules — is removed and no longer registered in `Spree.order_routing.strategies`.
|
|
307
|
+
|
|
308
|
+
A store or channel still carrying `preferred_order_routing_strategy: 'Spree::OrderRouting::Strategy::Legacy'` **keeps working**: `Order#order_routing_strategy` ignores unregistered classes, logs a warning, and falls back to `Strategy::Rules`. Clear the stale preference to silence the warning — note that saving such a record now fails validation, since the value is no longer in the registry.
|
|
309
|
+
|
|
310
|
+
`Spree::Stock::Coordinator` itself stays — cart fulfillment building, exchanges, and claims still use it.
|
|
311
|
+
|
|
312
|
+
### `Store.default` no longer builds a store
|
|
313
|
+
|
|
314
|
+
`Spree::Store.default` returned an **unpersisted** `Store.new(default: true)` when no default store existed. It now returns `nil`.
|
|
315
|
+
|
|
316
|
+
This matters more than it looks: `Spree::Current.store` falls back to `Store.default`, and every `Spree::SingleStoreResource` model resolves its own `store` from `Spree::Current.store`. Without a default store, those records now fail validation with "Store can't be blank" instead of silently attaching to a throwaway store.
|
|
317
|
+
|
|
318
|
+
Make sure a default store exists before creating store-scoped records, and set `Spree::Current.store` in jobs, rake tasks, and tests that run outside a request.
|
|
319
|
+
|
|
320
|
+
### The `DefaultPrice` concern is gone — `price_in` / `set_price` is the interface
|
|
321
|
+
|
|
322
|
+
`Spree::DefaultPrice` and the `enable_legacy_default_price` setting are removed, along with the `has_one :default_price` association on `Variant`. Prices live in `spree_prices`, one row per currency, and a price is always an **amount plus a currency** — there is no longer an implicit "the" price.
|
|
323
|
+
|
|
324
|
+
The single-currency accessors are gone from both `Variant` and `Product`:
|
|
325
|
+
|
|
326
|
+
| Removed | Use instead |
|
|
327
|
+
|---|---|
|
|
328
|
+
| `variant.price`, `product.price` | `variant.price_in(currency).amount` or `amount_in(currency)` |
|
|
329
|
+
| `variant.price = x`, `product.price = x` | `variant.set_price(currency, x)` |
|
|
330
|
+
| `variant.default_price` | `variant.price_in(currency)` |
|
|
331
|
+
| `variant.currency`, `product.currency` | the currency is an argument now — pass the one you mean |
|
|
332
|
+
| `display_price`, `display_amount` | `price_in(currency).display_amount` |
|
|
333
|
+
| `compare_at_price=` | `set_price(currency, amount, compare_at_amount)` |
|
|
334
|
+
| `display_compare_at_price` | `price_in(currency).display_compare_at_amount` |
|
|
335
|
+
| `price_including_vat_for(opts)` | `price_in(currency).price_including_vat_for(opts)` |
|
|
336
|
+
| `has_default_price?` | `prices.base_prices.exists?(currency: currency)` |
|
|
337
|
+
|
|
338
|
+
The `compare_at_price` **reader** (which resolves against `cost_currency`) is unchanged on both `Variant` and `Product` — only the writer is gone. So are the `price_in` / `amount_in` / `compare_at_amount_in` readers.
|
|
339
|
+
|
|
340
|
+
Also note:
|
|
341
|
+
|
|
342
|
+
- **Ransack:** `default_price` is no longer a searchable association on `Variant`; query `prices` instead.
|
|
343
|
+
- **`Spree::PermittedAttributes`:** the dead `:price` and `:compare_at_price` entries are dropped from `product_attributes` and `variant_attributes`. Prices were already written as nested `prices: [{ amount:, currency: }]` under variants — the top-level keys had no writer behind them.
|
|
344
|
+
- Localized number parsing still happens: `Spree::Price#amount=` runs `Spree::LocalizedNumber.parse`, so `set_price(currency, '1,599.99')` works as `price=` did.
|
|
345
|
+
- The variant validation that inferred a missing price from the product's default variant is gone. Set prices explicitly (the product and variant factories already do).
|
|
346
|
+
|
|
212
347
|
## Deprecated in 6.0, removed in 6.1
|
|
213
348
|
|
|
214
349
|
Every rename keeps the legacy name working for one release with a deprecation warning. The notable ones:
|