@spree/docs 0.1.181 → 0.1.183
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.
- package/dist/api-reference/seller-api/errors.md +1 -1
- package/dist/api-reference/store.yaml +78 -91
- package/dist/developer/core-concepts/addresses.md +106 -198
- package/dist/developer/core-concepts/architecture.md +97 -126
- package/dist/developer/core-concepts/calculators.md +75 -252
- package/dist/developer/core-concepts/carts.md +1 -1
- package/dist/developer/core-concepts/channels.md +0 -4
- package/dist/developer/core-concepts/companies-and-catalogs.md +1 -1
- package/dist/developer/core-concepts/customers.md +0 -3
- package/dist/developer/core-concepts/discounts.md +133 -0
- package/dist/developer/core-concepts/events.md +83 -576
- package/dist/developer/core-concepts/fees.md +144 -0
- package/dist/developer/core-concepts/imports-exports.md +105 -679
- package/dist/developer/core-concepts/inventory.md +114 -248
- package/dist/developer/core-concepts/markets.md +9 -12
- package/dist/developer/core-concepts/media.md +9 -11
- package/dist/developer/core-concepts/metafields.md +123 -200
- package/dist/developer/core-concepts/order-totals.md +110 -0
- package/dist/developer/core-concepts/orders.md +1 -1
- package/dist/developer/core-concepts/payments.md +11 -14
- package/dist/developer/core-concepts/pricing.md +11 -13
- package/dist/developer/core-concepts/products.md +173 -19
- package/dist/developer/core-concepts/promotions.md +12 -11
- package/dist/developer/core-concepts/search-filtering.md +2 -4
- package/dist/developer/core-concepts/store-credits-gift-cards.md +0 -3
- package/dist/developer/core-concepts/taxes.md +125 -113
- package/dist/developer/core-concepts/translations.md +61 -68
- package/dist/developer/core-concepts/webhooks.md +25 -59
- package/dist/developer/how-to/custom-promotion.md +3 -3
- package/package.json +1 -1
- package/dist/developer/core-concepts/taxes-discounts-fees.md +0 -199
|
@@ -3,8 +3,6 @@ title: Pricing
|
|
|
3
3
|
description: Prices, Price Lists, Price Rules, and the Pricing Context — Spree's flexible pricing engine for regional, wholesale, volume, and market-based pricing.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
import { Since } from '/snippets/since.mdx';
|
|
7
|
-
|
|
8
6
|
## Overview
|
|
9
7
|
|
|
10
8
|
Spree's pricing system supports both simple single-currency pricing and advanced multi-currency, rule-based pricing through Price Lists. Every [Variant](products.md#variants) can have multiple prices — a base price per currency, plus additional prices from Price Lists that apply conditionally based on rules like geography, customer segment, or quantity.
|
|
@@ -94,7 +92,7 @@ spree api post /prices/bulk_upsert -d '{
|
|
|
94
92
|
```
|
|
95
93
|
|
|
96
94
|
|
|
97
|
-
## Price Lists
|
|
95
|
+
## Price Lists
|
|
98
96
|
|
|
99
97
|
Price Lists allow you to create different pricing strategies based on various conditions. This enables advanced pricing scenarios like:
|
|
100
98
|
|
|
@@ -165,27 +163,27 @@ spree api patch /price_lists/pl_xxx/activate
|
|
|
165
163
|
```
|
|
166
164
|
|
|
167
165
|
|
|
168
|
-
## Price Rules
|
|
166
|
+
## Price Rules
|
|
169
167
|
|
|
170
168
|
Price Rules define conditions that must be met for a Price List to apply. Spree includes five built-in rule types:
|
|
171
169
|
|
|
172
170
|
| Rule | Description | Use Case |
|
|
173
171
|
|------|-------------|----------|
|
|
174
172
|
| **Market Rule** | Matches based on the current [Market](markets.md) | Regional pricing across markets |
|
|
175
|
-
| **
|
|
173
|
+
| **Channel Rule** | Matches based on the [Channel](channels.md) the customer is buying through | App-only or in-store pricing |
|
|
176
174
|
| **User Rule** | Matches specific customer accounts | VIP customers, wholesale accounts |
|
|
177
175
|
| **Customer Group Rule** | Matches members of customer groups | Loyalty tiers, membership pricing |
|
|
178
176
|
| **Volume Rule** | Matches based on quantity purchased | Bulk discounts, tiered pricing |
|
|
179
177
|
|
|
180
|
-
### Market Rule
|
|
178
|
+
### Market Rule
|
|
181
179
|
|
|
182
|
-
|
|
180
|
+
Regional pricing. Applies the Price List when the customer is in one of the specified markets.
|
|
183
181
|
|
|
184
182
|
**Example:** Price a product at $29.99 in North America and €24.99 in Europe, rather than relying on exchange rate conversion.
|
|
185
183
|
|
|
186
|
-
###
|
|
184
|
+
### Channel Rule
|
|
187
185
|
|
|
188
|
-
Applies
|
|
186
|
+
Applies when the customer is buying through a particular sales channel — a discount that exists only in the mobile app, or a price that only applies at a retail till.
|
|
189
187
|
|
|
190
188
|
### User Rule
|
|
191
189
|
|
|
@@ -207,7 +205,7 @@ Applies based on quantity purchased. Supports `min_quantity` and `max_quantity`
|
|
|
207
205
|
|
|
208
206
|
> **INFO:** Custom Price Rules can be created for specialized pricing logic. See the [Customization Quickstart](../customization/quickstart.md) for details.
|
|
209
207
|
|
|
210
|
-
## Pricing Context
|
|
208
|
+
## Pricing Context
|
|
211
209
|
|
|
212
210
|
When resolving prices, Spree considers the full context of the request:
|
|
213
211
|
|
|
@@ -215,8 +213,8 @@ When resolving prices, Spree considers the full context of the request:
|
|
|
215
213
|
|---------|--------|-------------|
|
|
216
214
|
| Currency | Market or request header | The currency to price in |
|
|
217
215
|
| Market | Customer's country | The [Market](markets.md) for market-based rules |
|
|
218
|
-
|
|
|
219
|
-
| Customer |
|
|
216
|
+
| Channel | The API credential in use | The [Channel](channels.md) for channel-based rules |
|
|
217
|
+
| Customer | Authentication | The signed-in customer for user-based rules |
|
|
220
218
|
| Quantity | Cart line item | The quantity for volume-based rules |
|
|
221
219
|
| Date | Current time | For time-based Price List scheduling |
|
|
222
220
|
|
|
@@ -236,7 +234,7 @@ Price Lists are managed in the Admin Panel under **Products → Price Lists**, o
|
|
|
236
234
|
|
|
237
235
|
Each Price List contains prices for specific variants and currencies. Products can be added to a Price List, and individual variant prices set within it.
|
|
238
236
|
|
|
239
|
-
## Price History (EU Omnibus Directive)
|
|
237
|
+
## Price History (EU Omnibus Directive)
|
|
240
238
|
|
|
241
239
|
Spree automatically records price changes for EU Omnibus Directive compliance. When a product goes on sale, EU regulations require displaying the lowest price in the preceding 30 days alongside the discounted price.
|
|
242
240
|
|
|
@@ -3,8 +3,6 @@ title: Products
|
|
|
3
3
|
description: How Spree models products, variants, option types, images, prices, and categories — the building blocks of every catalog and storefront.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
import { Since } from '/snippets/since.mdx';
|
|
7
|
-
|
|
8
6
|
## Overview
|
|
9
7
|
|
|
10
8
|
A product represents something you sell. Each product has one or more **variants** — the actual purchasable items with their own SKU, price, and inventory. For example, a "T-Shirt" product might have variants for each size and color combination.
|
|
@@ -16,15 +14,16 @@ Products are organized into **categories** — a flexible hierarchy for grouping
|
|
|
16
14
|
```mermaid
|
|
17
15
|
erDiagram
|
|
18
16
|
Product ||--o{ Variant : "has many"
|
|
19
|
-
Product }o--o{ OptionType : "
|
|
20
|
-
Product
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Variant
|
|
17
|
+
Product }o--o{ OptionType : "varies by"
|
|
18
|
+
Product }o--o{ Category : "filed under"
|
|
19
|
+
Product }o--o{ Collection : "grouped into"
|
|
20
|
+
Product ||--o{ Media : "images and video"
|
|
21
|
+
Product }o--|| DeliveryProfile : "ships by"
|
|
22
|
+
Variant ||--o{ Price : "one per currency"
|
|
23
|
+
Variant ||--o{ StockLevel : "stocked per location"
|
|
24
|
+
Variant }o--o{ OptionValue : "identified by"
|
|
25
25
|
OptionType ||--o{ OptionValue : "has many"
|
|
26
|
-
|
|
27
|
-
Taxonomy ||--o{ Taxon : "has many"
|
|
26
|
+
Category ||--o{ Category : "nests under"
|
|
28
27
|
|
|
29
28
|
Product {
|
|
30
29
|
string name
|
|
@@ -36,7 +35,7 @@ erDiagram
|
|
|
36
35
|
|
|
37
36
|
Variant {
|
|
38
37
|
string sku
|
|
39
|
-
|
|
38
|
+
string barcode
|
|
40
39
|
decimal weight
|
|
41
40
|
}
|
|
42
41
|
|
|
@@ -64,7 +63,7 @@ erDiagram
|
|
|
64
63
|
| `name` | Product name | Yes |
|
|
65
64
|
| `description` | Full product description | Yes |
|
|
66
65
|
| `slug` | URL-friendly identifier (e.g., `spree-tote`) | Yes |
|
|
67
|
-
| `status` | `draft`, `active`, or `archived` | No |
|
|
66
|
+
| `status` | `draft`, `active`, or `archived`. A marketplace adds `proposed` and `rejected` — see Seller submissions below | No |
|
|
68
67
|
| `available_on` | Date the product becomes available for sale | No |
|
|
69
68
|
| `discontinue_on` | Date the product is no longer available | No |
|
|
70
69
|
| `meta_title` | Custom SEO title | Yes |
|
|
@@ -221,6 +220,144 @@ spree api delete /products/prod_xxx
|
|
|
221
220
|
|
|
222
221
|
> **TIP:** Operating on many products at once? The Admin API has bulk actions — `bulkStatusUpdate`, `bulkAddToCategories`, `bulkAddTags`, `bulkDestroy`, and more. See the [Admin API endpoint index](../../api-reference/admin-api/endpoints.md).
|
|
223
222
|
|
|
223
|
+
## Seller submissions
|
|
224
|
+
|
|
225
|
+
On a marketplace, a seller lists a product but does not publish one. They submit it, and the marketplace decides. This adds two statuses to the three above — both hidden from the storefront, since only `active` is visible.
|
|
226
|
+
|
|
227
|
+
> **INFO:** This applies to products that belong to a seller. A marketplace's own catalog is unaffected: an operator publishing their own product sets `status` directly and answers to nobody.
|
|
228
|
+
|
|
229
|
+
### The lifecycle
|
|
230
|
+
|
|
231
|
+
```mermaid
|
|
232
|
+
stateDiagram-v2
|
|
233
|
+
[*] --> draft
|
|
234
|
+
draft --> proposed : seller submits
|
|
235
|
+
proposed --> active : operator approves
|
|
236
|
+
proposed --> rejected : operator sends back
|
|
237
|
+
rejected --> proposed : seller revises and resubmits
|
|
238
|
+
proposed --> draft : seller withdraws
|
|
239
|
+
proposed --> archived : seller withdraws
|
|
240
|
+
active --> draft : seller takes it down
|
|
241
|
+
active --> archived : seller withdraws it
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Withdrawing a submission before anyone has ruled on it closes the open row as `withdrawn`, so a `pending` row always means the marketplace still owes an answer. A product that was already rejected keeps that decision at the head of its trail instead.
|
|
245
|
+
|
|
246
|
+
| Status | Meaning | Storefront |
|
|
247
|
+
|---|---|:---:|
|
|
248
|
+
| `draft` | The seller is still working on it | Hidden |
|
|
249
|
+
| `proposed` | Submitted, waiting on the marketplace | Hidden |
|
|
250
|
+
| `rejected` | Sent back with a reason, awaiting changes | Hidden |
|
|
251
|
+
| `active` | Approved and on sale | **Visible** |
|
|
252
|
+
| `archived` | Withdrawn | Hidden |
|
|
253
|
+
|
|
254
|
+
A seller can always take their own listing down — that is not a review decision. Putting one up is.
|
|
255
|
+
|
|
256
|
+
### The submission record
|
|
257
|
+
|
|
258
|
+
Each submission and each decision on it is a `Spree::ProductSubmission` row. The product's `status` stays the operational truth; these rows are how it got there.
|
|
259
|
+
|
|
260
|
+
```mermaid
|
|
261
|
+
erDiagram
|
|
262
|
+
Product ||--o{ ProductSubmission : "has many"
|
|
263
|
+
AdminUser ||--o{ ProductSubmission : "submitted"
|
|
264
|
+
AdminUser ||--o{ ProductSubmission : "reviewed"
|
|
265
|
+
|
|
266
|
+
ProductSubmission {
|
|
267
|
+
string status
|
|
268
|
+
bigint product_id
|
|
269
|
+
bigint submitted_by_id
|
|
270
|
+
bigint reviewed_by_id
|
|
271
|
+
datetime reviewed_at
|
|
272
|
+
text review_note
|
|
273
|
+
json metadata
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
| Column | Description |
|
|
278
|
+
|---|---|
|
|
279
|
+
| `status` | `pending`, `approved`, `rejected`, or `withdrawn` |
|
|
280
|
+
| `submitted_by_id` | The seller's staff member who asked |
|
|
281
|
+
| `reviewed_by_id` | The marketplace's staff member who decided |
|
|
282
|
+
| `reviewed_at` | When the decision was made |
|
|
283
|
+
| `review_note` | Why it was sent back — this is what the seller reads |
|
|
284
|
+
|
|
285
|
+
Rows accumulate rather than overwrite, so a seller sent back three times leaves three rows. The latest row for a product is the live one; the ones before it are the trail.
|
|
286
|
+
|
|
287
|
+
> **WARNING:** Never store a rejection reason on the product itself. A seller can write their own product's `metadata`, so a note kept there is erased the next time they save.
|
|
288
|
+
|
|
289
|
+
An approval with no `reviewed_by_id` and `metadata.auto_approved` set means the store approves listings automatically — never a decision whose author was lost. Turn that on with the `auto_approve_seller_products` store preference.
|
|
290
|
+
|
|
291
|
+
### Submitting, as a seller
|
|
292
|
+
|
|
293
|
+
Status is not writable on the seller branch. A seller moves a product with an explicit action:
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
```typescript Seller SDK
|
|
297
|
+
import { createSellerClient } from '@spree/seller-sdk'
|
|
298
|
+
|
|
299
|
+
const client = createSellerClient({
|
|
300
|
+
baseUrl: 'https://marketplace.example.com',
|
|
301
|
+
sellerId: 'sel_xxx',
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
await client.products.submit('prod_xxx') // draft or rejected → proposed
|
|
305
|
+
await client.products.draft('prod_xxx') // take it back down
|
|
306
|
+
await client.products.archive('prod_xxx') // withdraw it
|
|
307
|
+
|
|
308
|
+
// Why it was sent back
|
|
309
|
+
const product = await client.products.get('prod_xxx', 'submission')
|
|
310
|
+
product.submission?.review_note
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
```bash CLI
|
|
314
|
+
spree api patch /seller/products/prod_xxx/submit
|
|
315
|
+
spree api patch /seller/products/prod_xxx/draft
|
|
316
|
+
spree api patch /seller/products/prod_xxx/archive
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
### Deciding, as the marketplace
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
```typescript Admin SDK
|
|
324
|
+
await client.products.approve('prod_xxx')
|
|
325
|
+
await client.products.reject('prod_xxx', { reason: 'Please add a photo showing scale.' })
|
|
326
|
+
|
|
327
|
+
// The review queue
|
|
328
|
+
const pending = await client.products.list({ filter: { status_eq: 'proposed' } })
|
|
329
|
+
|
|
330
|
+
// Who decided, and when
|
|
331
|
+
const product = await client.products.get('prod_xxx', { expand: ['submission'] })
|
|
332
|
+
product.submission?.reviewed_by_name
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
```bash CLI
|
|
336
|
+
spree api patch /products/prod_xxx/approve
|
|
337
|
+
spree api patch /products/prod_xxx/reject -d '{"reason": "Please add a photo showing scale."}'
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
> **NOTE:** The seller sees the note and when the decision was made, but never who made it.
|
|
342
|
+
|
|
343
|
+
### Leaving review is a decision
|
|
344
|
+
|
|
345
|
+
A product in `proposed` or `rejected` cannot have its status changed by an ordinary update — that would put it on sale with nobody's name against it. The refusal lives in the product update workflow, so every caller inherits it, and bulk status updates skip those products and report how many they left behind.
|
|
346
|
+
|
|
347
|
+
### Events
|
|
348
|
+
|
|
349
|
+
Each transition publishes an event you can subscribe to:
|
|
350
|
+
|
|
351
|
+
| Event | Published when |
|
|
352
|
+
|---|---|
|
|
353
|
+
| `product.proposed` | A seller submits for review |
|
|
354
|
+
| `product.approved` | The marketplace accepts it |
|
|
355
|
+
| `product.rejected` | The marketplace sends it back |
|
|
356
|
+
| `product.drafted` | A seller takes a listing down |
|
|
357
|
+
| `product.archived` | A seller withdraws it |
|
|
358
|
+
|
|
359
|
+
The submission row itself also publishes `product_submission.created` and `product_submission.updated`, carrying the status, the note and the product. See [Events](events.md).
|
|
360
|
+
|
|
224
361
|
## Product Filters
|
|
225
362
|
|
|
226
363
|
Get available filter options for building a faceted search UI. Returns price ranges, option values, and categories with counts:
|
|
@@ -468,14 +605,19 @@ See the [Pricing](pricing.md) guide for details on Price Lists, Price Rules, and
|
|
|
468
605
|
|
|
469
606
|
## Categories
|
|
470
607
|
|
|
471
|
-
|
|
608
|
+
There are two ways to group products, and they answer different questions.
|
|
609
|
+
|
|
610
|
+
**Categories** are a hierarchy — the navigation tree a shopper browses. Clothing contains T-Shirts, which contains Long Sleeve. A product can sit in several categories, and each one has a permalink built from its path.
|
|
611
|
+
|
|
612
|
+
**Collections** are flat groupings — "Summer 2025", "Best Sellers", "Under $50". A collection can be curated by hand, or defined by rules so products join and leave it on their own as their price, tags or stock change.
|
|
472
613
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
614
|
+
| | Categories | Collections |
|
|
615
|
+
|---|---|---|
|
|
616
|
+
| Shape | Nested tree | Flat list |
|
|
617
|
+
| Membership | You assign it | Assigned, or matched by rules |
|
|
618
|
+
| Typical use | Site navigation | Merchandising and campaigns |
|
|
477
619
|
|
|
478
|
-
|
|
620
|
+
A brand is usually best modelled as one or the other rather than as a separate concept — a category if you want it in the navigation tree, a collection if it's a landing page.
|
|
479
621
|
|
|
480
622
|
|
|
481
623
|
```typescript Store SDK
|
|
@@ -512,7 +654,19 @@ curl 'https://api.mystore.com/api/v3/store/categories/clothing/shirts/products?l
|
|
|
512
654
|
|
|
513
655
|
> **INFO:** Category `name` and `description` fields are translatable.
|
|
514
656
|
|
|
515
|
-
|
|
657
|
+
Collections work the same way from a storefront's point of view:
|
|
658
|
+
|
|
659
|
+
```typescript Store SDK
|
|
660
|
+
const { data: collections } = await client.collections.list()
|
|
661
|
+
|
|
662
|
+
const { data: products } = await client.collections.products.list('summer-2025', {
|
|
663
|
+
limit: 12,
|
|
664
|
+
})
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
A rule-based collection is defined once and maintains itself — set it to match everything tagged `sale` and under $50, and products appear and disappear as those facts change. Ordering can be manual or by a rule such as newest first.
|
|
668
|
+
|
|
669
|
+
## Publications and Sales Channels
|
|
516
670
|
|
|
517
671
|
A product is visible on a [Channel](channels.md) only when a `ProductPublication` record joins the two. Publications carry an optional time window so a product can be scheduled to go live and come down without code or manual toggles.
|
|
518
672
|
|
|
@@ -5,7 +5,9 @@ description: Build percentage and fixed-amount discounts, free shipping, BOGO of
|
|
|
5
5
|
|
|
6
6
|
## Overview
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
A promotion is a campaign: a percentage off, free shipping, or free items in the cart. **Rules** decide when it applies, **actions** decide what it does.
|
|
9
|
+
|
|
10
|
+
A promotion is a *rule that exists on its own*, set up in advance and lasting across many orders. What lands on any one order is a [Discount](discounts.md) — the record of money actually coming off. The promotion is the campaign; the discount is what a particular customer got.
|
|
9
11
|
|
|
10
12
|
```mermaid
|
|
11
13
|
erDiagram
|
|
@@ -52,14 +54,13 @@ Promotions can be activated in two ways:
|
|
|
52
54
|
- **Automatic promotions** — applied on their own when the rules are met (e.g. free shipping on orders over $50)
|
|
53
55
|
- **Coupon code promotions** — applied when a customer enters a valid code during checkout
|
|
54
56
|
|
|
55
|
-
###
|
|
57
|
+
### When promotions compete
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
Several promotions can qualify for the same item at once. Spree applies **only the one that saves the customer the most**, and records just that one.
|
|
58
60
|
|
|
59
|
-
|
|
61
|
+
Losing candidates aren't stored or tracked — they're simply reconsidered on every cart change. So a promotion that loses today can win tomorrow, when the basket changes, without any bookkeeping to keep straight.
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
- **A discount on the whole order is shared out across the items.** If a customer later returns one item, Spree knows exactly how much of the discount belonged to it.
|
|
63
|
+
What an applied promotion leaves behind on the order is a [Discount](discounts.md) — a permanent record that survives the promotion being edited or deleted.
|
|
63
64
|
|
|
64
65
|
## Promotion Attributes
|
|
65
66
|
|
|
@@ -112,7 +113,7 @@ You can also [build your own rules](../how-to/custom-promotion.md) for business-
|
|
|
112
113
|
|
|
113
114
|
Actions define what happens when a promotion applies.
|
|
114
115
|
|
|
115
|
-
### CreateAdjustment
|
|
116
|
+
### Order discount (`CreateAdjustment`)
|
|
116
117
|
|
|
117
118
|
A discount on the whole order. The amount is distributed proportionally across the line items — there is no single order-level row, so per-item reporting and partial returns always know their share.
|
|
118
119
|
|
|
@@ -120,7 +121,7 @@ A discount on the whole order. The amount is distributed proportionally across t
|
|
|
120
121
|
|
|
121
122
|
**Use case:** "10% off your order", "$20 off orders over $100".
|
|
122
123
|
|
|
123
|
-
### CreateItemAdjustments
|
|
124
|
+
### Item discount (`CreateItemAdjustments`)
|
|
124
125
|
|
|
125
126
|
A discount on individual line items. Only items that match the promotion's rules receive it — if a Category rule says "Electronics", only electronics are discounted.
|
|
126
127
|
|
|
@@ -128,13 +129,13 @@ A discount on individual line items. Only items that match the promotion's rules
|
|
|
128
129
|
|
|
129
130
|
**Use case:** "15% off shoes", "Buy 2+ shirts get 10% off each".
|
|
130
131
|
|
|
131
|
-
### FreeShipping
|
|
132
|
+
### Free shipping (`FreeShipping`)
|
|
132
133
|
|
|
133
134
|
Writes a discount on each fulfillment covering its delivery cost. The row is kept even when the cost is zero — its presence is what marks the order as having free shipping.
|
|
134
135
|
|
|
135
136
|
**Use case:** "Free shipping on orders over $75", "Free shipping with code FREESHIP".
|
|
136
137
|
|
|
137
|
-
### CreateLineItems
|
|
138
|
+
### Free items (`CreateLineItems`)
|
|
138
139
|
|
|
139
140
|
Adds specified products to the cart when the promotion is eligible, checking stock first. Items are not removed automatically if eligibility is lost — customers remove them manually.
|
|
140
141
|
|
|
@@ -245,7 +246,7 @@ Automatic promotions need no customer action at all — they are evaluated on ev
|
|
|
245
246
|
|
|
246
247
|
## Related Documentation
|
|
247
248
|
|
|
248
|
-
- [
|
|
249
|
+
- [Discounts](discounts.md) — the rows promotions write, and manual discounts
|
|
249
250
|
- [Build Custom Promotion Rules & Actions](../how-to/custom-promotion.md) — step-by-step guide to custom rules, actions and adjusters
|
|
250
251
|
- [Calculators](calculators.md) — promotion calculators
|
|
251
252
|
- [Carts](carts.md) — the cart lifecycle promotions act on
|
|
@@ -3,13 +3,11 @@ title: Search & Filtering
|
|
|
3
3
|
description: "Use Ransack-style query parameters to search, filter, sort, and paginate products, taxons, and other resources through the Spree Store API."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
import { Since } from '/snippets/since.mdx';
|
|
7
|
-
|
|
8
6
|
## Overview
|
|
9
7
|
|
|
10
8
|
Spree provides powerful search, filtering, and sorting capabilities for products and other resources. The Store API supports:
|
|
11
9
|
|
|
12
|
-
- Full-text search across product names, SKUs, and searchable
|
|
10
|
+
- Full-text search across product names, SKUs, and searchable custom fields
|
|
13
11
|
- Attribute-based filtering (price range, availability, stock status)
|
|
14
12
|
- Category and taxon filtering
|
|
15
13
|
- Faceted search with filter counts
|
|
@@ -276,7 +274,7 @@ curl 'https://api.mystore.com/api/v3/store/products?page=1&limit=24' \
|
|
|
276
274
|
|
|
277
275
|
See [Querying](../../api-reference/store-api/querying.md) for the full list of filtering, sorting, and pagination options.
|
|
278
276
|
|
|
279
|
-
## Search Providers
|
|
277
|
+
## Search Providers
|
|
280
278
|
|
|
281
279
|
Spree uses a pluggable search provider architecture. The default provider uses SQL (ILIKE + Ransack). For production catalogs with 1,000+ products, we recommend switching to [Meilisearch](../../integrations/search/meilisearch.md) for typo tolerance, relevance ranking, and faster faceted search.
|
|
282
280
|
|
|
@@ -3,9 +3,6 @@ title: Store Credits & Gift Cards
|
|
|
3
3
|
description: How Spree models store credits and gift cards — stored value balances, redeemable codes, and checkout usage for refunds, loyalty, and gifting.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
import { Since } from '/snippets/since.mdx';
|
|
7
|
-
|
|
8
|
-
|
|
9
6
|
## Overview
|
|
10
7
|
|
|
11
8
|
Spree provides two stored value mechanisms that customers can use at checkout:
|