@spree/docs 0.1.298 → 0.1.300

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.
@@ -8461,7 +8461,7 @@ paths:
8461
8461
  client_secret: secret_123
8462
8462
  payment_method_id: pm_UkLWZg9DAJ
8463
8463
  payment_source_id: card_UkLWZg9DAJ
8464
- payment_source_type: Spree::CreditCard
8464
+ payment_source_type: credit_card
8465
8465
  customer_id: cust_UkLWZg9DAJ
8466
8466
  payment_method:
8467
8467
  id: pm_UkLWZg9DAJ
@@ -11738,7 +11738,7 @@ components:
11738
11738
  type: array
11739
11739
  items:
11740
11740
  type: string
11741
- description: Subject class names, e.g. ["Spree::Product"] or ["all"]
11741
+ description: Subject short names, e.g. ["product"] or ["all"]
11742
11742
  has_conditions:
11743
11743
  type: boolean
11744
11744
  description: True if the server-side rule has per-record conditions. The
@@ -12746,9 +12746,6 @@ components:
12746
12746
  type: string
12747
12747
  label:
12748
12748
  type: string
12749
- type:
12750
- type: string
12751
- deprecated: true
12752
12749
  field_type:
12753
12750
  type: string
12754
12751
  enum:
@@ -12765,7 +12762,6 @@ components:
12765
12762
  required:
12766
12763
  - id
12767
12764
  - label
12768
- - type
12769
12765
  - field_type
12770
12766
  - key
12771
12767
  - value
@@ -38,7 +38,7 @@ erDiagram
38
38
  | `default` | Exactly one channel per store is the default. Used as a fallback when no channel header is present and as the auto-publish target for new products | `true` |
39
39
  | `storefront_access` | Controls what an anonymous visitor may see: `public`, `prices_hidden`, or `login_required`. Unset inherits the store's setting. See [Storefront Access Gating](#storefront-access-gating) | `login_required` |
40
40
  | `guest_checkout` | Whether an order may be placed without an account on this channel. Unset inherits the store's setting | `false` |
41
- | `preferred_order_routing_strategy` | Optional per-channel override of the store's [Order Routing](../how-to/custom-order-routing.md) strategy | `Spree::OrderRouting::Strategy::Rules` |
41
+ | `preferred_order_routing_strategy` | Optional per-channel override of the store's [Order Routing](../how-to/custom-order-routing.md) strategy | `rules` |
42
42
 
43
43
  `code` is normalized to a URL-safe slug on save — `POS` becomes `pos`, `Point of Sale!` becomes `point-of-sale`. Leaving `code` blank derives it from `name`.
44
44
 
@@ -243,7 +243,7 @@ split.
243
243
  ```typescript Admin SDK
244
244
  await adminClient.deliveryMethods.create({
245
245
  name: 'Pallet freight',
246
- rate_provider: 'Spree::DeliveryRateProvider::Freight',
246
+ rate_provider: 'freight',
247
247
  rules: [
248
248
  { type: 'volume_rule', preferences: { minimum_volume: 1, maximum_volume: 15 } },
249
249
  { type: 'company_rule', preferences: { company_orders_only: true } },
@@ -257,7 +257,7 @@ curl -X POST 'https://api.mystore.com/api/v3/admin/delivery_methods' \
257
257
  -H 'Content-Type: application/json' \
258
258
  -d '{
259
259
  "name": "Pallet freight",
260
- "rate_provider": "Spree::DeliveryRateProvider::Freight",
260
+ "rate_provider": "freight",
261
261
  "rules": [
262
262
  { "type": "volume_rule", "preferences": { "minimum_volume": 1, "maximum_volume": 15 } },
263
263
  { "type": "company_rule", "preferences": { "company_orders_only": true } }
@@ -257,6 +257,16 @@ curl 'https://api.mystore.com/api/v3/store/delivery_methods/dm_xxx/pickup_locati
257
257
  ```
258
258
 
259
259
 
260
+ Third-party pickup points come from a pickup point provider — a locker network or partner-shop integration. Spree ships none; an extension registers its provider so a delivery method can select it by its short name (for example `inpost` for `SpreeInpost::PickupPointProvider`):
261
+
262
+ ```ruby server/config/initializers/spree.rb
263
+ Rails.application.config.after_initialize do
264
+ Spree.pickup_point_providers << SpreeInpost::PickupPointProvider
265
+ end
266
+ ```
267
+
268
+ A delivery method can only use a registered provider.
269
+
260
270
  ## Managing fulfillments
261
271
 
262
272
 
@@ -71,7 +71,7 @@ Because the definition carries the type and the label, the dashboard can build a
71
71
 
72
72
  ```typescript Admin SDK
73
73
  const definition = await adminClient.customFieldDefinitions.create({
74
- resource_type: 'Spree::Product',
74
+ resource_type: 'product',
75
75
  namespace: 'properties',
76
76
  key: 'material',
77
77
  label: 'Material',
@@ -83,7 +83,7 @@ const definition = await adminClient.customFieldDefinitions.create({
83
83
 
84
84
  ```bash CLI
85
85
  spree api post /custom_field_definitions -d '{
86
- "resource_type": "Spree::Product",
86
+ "resource_type": "product",
87
87
  "namespace": "properties",
88
88
  "key": "material",
89
89
  "label": "Material",
@@ -686,7 +686,7 @@ sequenceDiagram
686
686
  | `external_client_secret` | Client secret for the frontend SDK | `seti_ABC123_secret_xyz` |
687
687
  | `external_data` | Provider-specific data | `{}` |
688
688
  | `payment_source_id` | The saved payment source created after completion | `card_xyz789` |
689
- | `payment_source_type` | The type of saved payment source | `Spree::CreditCard` |
689
+ | `payment_source_type` | The type of saved payment source | `credit_card` |
690
690
 
691
691
  ### Payment Setup Session API
692
692
 
@@ -22,7 +22,7 @@ nav.add({
22
22
  path: '/analytics', // prefixed with /$storeId at render time
23
23
  icon: BarChartIcon,
24
24
  position: 650,
25
- subject: 'Spree::Order', // optional permission subject — hides item without read permission
25
+ subject: 'order', // optional permission subject — hides item without read permission
26
26
  })
27
27
  ```
28
28
 
@@ -70,7 +70,7 @@ nav.addChild('products', {
70
70
  key: 'products.brands',
71
71
  label: 'Brands',
72
72
  path: '/products/brands',
73
- subject: 'Spree::Brand',
73
+ subject: 'brand',
74
74
  })
75
75
  ```
76
76
 
@@ -177,7 +177,7 @@ settingsNav.add({
177
177
  path: '/integrations/stripe-tax', // prefixed with /$storeId/settings
178
178
  group: 'integrations',
179
179
  position: 100,
180
- subject: 'Spree::TaxRate',
180
+ subject: 'tax_rate',
181
181
  })
182
182
  ```
183
183
 
@@ -33,10 +33,10 @@ interface Permissions {
33
33
  It mirrors the backend ability at the **class level**:
34
34
 
35
35
  ```ts
36
- permissions.can('read', 'Spree::Order')
36
+ permissions.can('read', 'order')
37
37
  ```
38
38
 
39
- Subjects are strings (`'Spree::Order'`, `'Spree::Product'`, or your own `'MyApp::Report'`). There is no client-side record-level check — when a rule is conditional on record attributes, `isConditional` returns `true` and the API is the arbiter: render the control and handle a possible 403.
39
+ Subjects are short names: the model's class name without its namespace, underscored (`'order'`, `'product'`, or `'report'` for your own `MyApp::Report`). Use the `Subject` constants from `@spree/dashboard-core` for Spree's own models. There is no client-side record-level check — when a rule is conditional on record attributes, `isConditional` returns `true` and the API is the arbiter: render the control and handle a possible 403.
40
40
 
41
41
  ## `subject` shortcut
42
42
 
@@ -47,7 +47,7 @@ nav.add({
47
47
  key: 'reports',
48
48
  label: 'Reports',
49
49
  path: '/reports',
50
- subject: 'Spree::Order', // hides nav item without read:Spree::Order
50
+ subject: 'order', // hides nav item without read:order
51
51
  })
52
52
  ```
53
53
 
@@ -58,7 +58,7 @@ routes: [{
58
58
  key: 'reports',
59
59
  path: '/reports',
60
60
  component: ReportsPage,
61
- subject: 'Spree::Order',
61
+ subject: 'order',
62
62
  }],
63
63
  ```
64
64
 
@@ -76,7 +76,7 @@ nav.add({
76
76
  label: 'Reports',
77
77
  path: '/reports',
78
78
  if: ({ permissions, store }) =>
79
- permissions.can('read', 'Spree::Order') &&
79
+ permissions.can('read', 'order') &&
80
80
  !!(store as Store | null)?.setup_tasks?.every((task) => task.done),
81
81
  })
82
82
  ```
@@ -94,18 +94,18 @@ import { usePermissions } from '@spree/dashboard'
94
94
 
95
95
  function ReportsPage() {
96
96
  const { permissions } = usePermissions()
97
- if (!permissions.can('read', 'Spree::Order')) {
97
+ if (!permissions.can('read', 'order')) {
98
98
  return <Forbidden />
99
99
  }
100
100
  return <ReportsList />
101
101
  }
102
102
  ```
103
103
 
104
- The same `permissions` object backs the nav registry's `if` predicate, so you can move logic between the two without changing behaviour. For declarative gating, `<Can I="update" a="Spree::Order">…</Can>` (also from `@spree/dashboard-core`) renders children only when the check passes.
104
+ The same `permissions` object backs the nav registry's `if` predicate, so you can move logic between the two without changing behaviour. For declarative gating, `<Can I="update" a="order">…</Can>` (also from `@spree/dashboard-core`) renders children only when the check passes.
105
105
 
106
106
  ## Custom permissions
107
107
 
108
- If your customization introduces a new model on the backend, register it as a permission catalog scope there (`Spree.permissions.register_scope` in your engine's initializer). Its keys then appear in the role editor and the API-key scope picker, and any role granted them makes `permissions.can('read', 'MyApp::Report')` resolve in the dashboard exactly like a first-party check — the abilities ship with the current-user response (`GET /api/v3/admin/me`) at sign-in, alongside `permission_keys`, the flat key list the role editor grants from.
108
+ If your customization introduces a new model on the backend, register it as a permission catalog scope there (`Spree.permissions.register_scope` in your engine's initializer). Its keys then appear in the role editor and the API-key scope picker, and any role granted them makes `permissions.can('read', 'report')` resolve in the dashboard exactly like a first-party check — the abilities ship with the current-user response (`GET /api/v3/admin/me`) at sign-in, alongside `permission_keys`, the flat key list the role editor grants from.
109
109
 
110
110
  ## Reference
111
111
 
@@ -135,7 +135,7 @@ routes: [{
135
135
  key: 'reports',
136
136
  path: '/reports',
137
137
  component: ReportsPage,
138
- subject: 'Spree::Order',
138
+ subject: 'order',
139
139
  }],
140
140
  ```
141
141
 
@@ -68,7 +68,7 @@ import { usePermissions } from '@spree/dashboard'
68
68
 
69
69
  function AdminOnlyMenuItem() {
70
70
  const { permissions } = usePermissions()
71
- if (!permissions.can('manage', 'Spree::Customer')) return null
71
+ if (!permissions.can('manage', 'customer')) return null
72
72
  return <DropdownMenuItem>…</DropdownMenuItem>
73
73
  }
74
74
  ```
@@ -15,7 +15,7 @@ Create a definition for products (Settings → Custom fields, the in-place "Set
15
15
 
16
16
  ```bash
17
17
  npx spree api post custom_field_definitions --data '{
18
- "resource_type": "Spree::Product",
18
+ "resource_type": "product",
19
19
  "namespace": "specs",
20
20
  "key": "tech_specs",
21
21
  "label": "Technical specifications",
@@ -33,7 +33,7 @@ export function SendInvoiceButton({ resource }: Props) {
33
33
  })
34
34
 
35
35
  // Only show to users who can act, and only on completed orders.
36
- if (!permissions.can('update', 'Spree::Order')) return null
36
+ if (!permissions.can('update', 'order')) return null
37
37
  if (resource.status !== 'placed') return null
38
38
 
39
39
  return (
@@ -95,7 +95,7 @@ export function SyncToErpItem({ resource }: Props) {
95
95
  successMessage: 'Synced',
96
96
  })
97
97
 
98
- if (!permissions.can('manage', 'Spree::Order')) return null
98
+ if (!permissions.can('manage', 'order')) return null
99
99
 
100
100
  return (
101
101
  <DropdownMenuItem
@@ -42,7 +42,7 @@ interface Props {
42
42
  export function LoyaltyStatusCard({ customer }: Props) {
43
43
  const { storeId } = useStore()
44
44
  const { permissions } = usePermissions()
45
- const canRead = permissions.can('read', 'MyApp::LoyaltyRecord')
45
+ const canRead = permissions.can('read', 'loyalty_record')
46
46
  const { data, isLoading } = useQuery({
47
47
  queryKey: ['loyalty', storeId, customer.id],
48
48
  queryFn: () =>
@@ -44,7 +44,7 @@ import { usePermissions, useStore } from '@spree/dashboard'
44
44
  function MyWidget({ product }: { product: Product }) {
45
45
  const { permissions } = usePermissions()
46
46
  const { store } = useStore()
47
- if (!permissions.can('read', 'MyApp::LoyaltyRecord')) return null
47
+ if (!permissions.can('read', 'loyalty_record')) return null
48
48
  // ...
49
49
  }
50
50
  ```
@@ -808,7 +808,7 @@ A method priced by the **Freight** rate provider returns an unpriced rate. It re
808
808
  ```typescript Admin SDK
809
809
  await adminClient.deliveryMethods.create({
810
810
  name: 'Pallet freight',
811
- rate_provider: 'Spree::DeliveryRateProvider::Freight',
811
+ rate_provider: 'freight',
812
812
  rules: [
813
813
  { type: 'volume_rule', preferences: { minimum_volume: 1, maximum_volume: 15 } },
814
814
  { type: 'company_rule', preferences: { company_orders_only: true } },
@@ -822,7 +822,7 @@ curl -X POST 'https://api.mystore.com/api/v3/admin/delivery_methods' \
822
822
  -H 'Content-Type: application/json' \
823
823
  -d '{
824
824
  "name": "Pallet freight",
825
- "rate_provider": "Spree::DeliveryRateProvider::Freight",
825
+ "rate_provider": "freight",
826
826
  "rules": [
827
827
  { "type": "volume_rule", "preferences": { "minimum_volume": 1, "maximum_volume": 15 } },
828
828
  { "type": "company_rule", "preferences": { "company_orders_only": true } }
@@ -94,7 +94,7 @@ end
94
94
 
95
95
  Once registered, the provider appears as a source on the product's **Digital files** card: the **Add** button becomes a menu offering **Upload a file** alongside each registered provider. Picking your provider creates an asset with its `provider_type` set and no file attached.
96
96
 
97
- > **NOTE:** Registration is what makes a `provider_type` valid. An asset validates that its `provider_type` names a registered provider, so an unregistered or misspelled class name is rejected with a 422 rather than failing at download time. A blank `provider_type` is always the built-in `File` provider.
97
+ > **NOTE:** Registration is what makes a `provider_type` valid. The Admin API names a provider by its shorthand — the class name's last segment, underscored, so `license_key` for the provider above and `file` for the built-in one — and an asset validates that its `provider_type` names a registered provider, so an unregistered or misspelled value is rejected with a 422 rather than failing at download time. A blank `provider_type` is always the built-in `File` provider.
98
98
 
99
99
  ## The download contract
100
100
 
@@ -236,7 +236,11 @@ First register the class so it's selectable (this is the allowlist the model val
236
236
  Spree.order_routing.strategies << 'Acme::Oms::Strategy'.constantize
237
237
  ```
238
238
 
239
- Then select it by class name string — set on `Spree::Store` (default) or `Spree::Channel` (override). Setting an unregistered class fails validation.
239
+ Then select it — set on `Spree::Store` (default) or `Spree::Channel` (override). Setting an unregistered strategy fails validation. Ruby code may name the class; the Admin API and the dashboard name a strategy by its shorthand, `api_type` (the class name's last segment, underscored — `rules` for the built-in one). Override `self.api_type` on your strategy to give it a distinct, stable shorthand:
240
+
241
+ ```ruby
242
+ def self.api_type = 'acme_oms'
243
+ ```
240
244
 
241
245
  Activate on the whole store:
242
246
 
@@ -122,7 +122,7 @@ await client.products.customFields.create('prod_xxx', {
122
122
  The generic escape hatch covers any owner type:
123
123
 
124
124
  ```typescript
125
- await client.customFields('Spree::Product', 'prod_xxx').list()
125
+ await client.customFields('product', 'prod_xxx').list()
126
126
  ```
127
127
 
128
128
  ## Adding your own resources
@@ -495,6 +495,19 @@ now returns `nil` for an ID with another model's prefix, the same as
495
495
  `find_by_prefix_id`. Call `Spree::PrefixedId.decode_prefixed_id` if you
496
496
  really need to decode any prefix.
497
497
 
498
+ ## Type values are short names, not class names
499
+
500
+ The API no longer sends or accepts Ruby class names. Every value that used to name a class is now a short name (`Spree::CreditCard` becomes `credit_card`), so the API keeps the same shape whatever the server is built with. Records in the database are not touched.
501
+
502
+ If your storefront or webhook receiver reads any of these fields, compare against the short name instead:
503
+
504
+ | Field | Where | Before | After |
505
+ |---|---|---|---|
506
+ | `payment_source_type` | Store API payment setup sessions, `payment_setup_session.*` webhooks | `Spree::CreditCard` | `credit_card` |
507
+ | `type` | Custom fields on products, variants, categories and collections | `Spree::CustomFields::ShortText` | removed — read `field_type` (`short_text`) |
508
+
509
+ The Admin API follows the same rule everywhere: providers and strategies (`fulfillment_provider: 'manual'`, `preferred_order_routing_strategy: 'rules'`), custom field definition `resource_type` (`product`, `category`), tag `taggable_type`, the owner and originator types on records, the permission subjects `/me` returns (`product`, `category`), and filters such as `q[type_eq]=orders`. An integration written against the Admin API preview sends and expects these short names; the [Admin SDK](../sdk/admin/resources.md) types list them.
510
+
498
511
  ## Every event is declared
499
512
 
500
513
  Events are now declared on the model that publishes them (`publishes_event`, `publishes_events`), and `publish_event` raises in development and test for a name nobody declared. Extensions that publish their own events need one line each — see [Publishing your own events](../core-concepts/events.md#publishing-your-own-events). In production an undeclared event is logged and still delivered.
@@ -21,10 +21,10 @@ The same setting through the Admin API:
21
21
 
22
22
  ```typescript Admin SDK
23
23
  const { data: providers } = await adminClient.payoutProviders.list()
24
- // find the row with id 'SpreeStripe::PayoutProvider' — `available` says whether this store can use it
24
+ // find the row with id 'stripe' — `available` says whether this store can use it
25
25
 
26
26
  await adminClient.store.update({
27
- preferred_payout_provider: 'SpreeStripe::PayoutProvider',
27
+ preferred_payout_provider: 'stripe',
28
28
  preferred_default_payouts_schedule_interval: 'weekly',
29
29
  preferred_default_minimum_payout_amount: 50,
30
30
  })
@@ -34,7 +34,7 @@ await adminClient.store.update({
34
34
  curl -X PATCH https://your-store.com/api/v3/admin/store \
35
35
  -H "X-Spree-API-Key: sk_xxx" \
36
36
  -H "Content-Type: application/json" \
37
- -d '{"preferred_payout_provider": "SpreeStripe::PayoutProvider", "preferred_default_payouts_schedule_interval": "weekly", "preferred_default_minimum_payout_amount": 50}'
37
+ -d '{"preferred_payout_provider": "stripe", "preferred_default_payouts_schedule_interval": "weekly", "preferred_default_minimum_payout_amount": 50}'
38
38
  ```
39
39
 
40
40
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spree/docs",
3
- "version": "0.1.298",
3
+ "version": "0.1.300",
4
4
  "description": "Spree Commerce developer documentation for AI agents and local reference",
5
5
  "type": "module",
6
6
  "license": "CC-BY-4.0",