dist/codex/spree-commerce/skills/spree-data-model/SKILL.md
Navigate Spree's canonical data model — the Catalog (Product/Variant/OptionType/Taxon/Property/Metafield), Pricing (Price/PriceList), Order graph (Order/LineItem/Adjustment/Shipment/Payment/PaymentSession/Refund/Reimbursement), Inventory (StockLocation/StockItem/StockMovement), Shipping (ShippingMethod/Zone), Promotions, Identity (User/Role/Address/StoreCredit/GiftCard), Taxes, and the v5.4+ Markets + Store multi-region model. Use when designing a feature that touches Spree models, writing decorators, or building admin/storefront UIs.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins spree-data-modelInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
Store and Market.spree gem source on GitHub for current column names — Spree adds columns between minors.| Model | Purpose | Notes |
|-------|---------|-------|
| Product | Top-level catalog item | Has a master Variant; many Images, Propertys, Taxons |
| Variant | Concrete SKU | Has OptionValues, Prices per currency, StockItems |
| OptionType / OptionValue | Variant axes (size, color) | An OptionType has many OptionValues |
| Property / ProductProperty | Free-form spec table | "Material: cotton", "Weight: 250g" |
| Taxonomy / Taxon | Category trees | Taxon is the nested-set node |
| Image / Asset | Media | Uses ActiveStorage |
| Metafield | Custom data on any model (v5+) | Like Shopify metafields |
| Model | Purpose |
|-------|---------|
| Price | One row per (Variant × Currency) |
| PriceList (v5.3+) | Override prices for a customer group, store, or country |
| Calculator | Polymorphic calculator class for shipping/promotion/tax math |
Order
├── LineItem (one per Variant)
│ └── Adjustment[]
├── Shipment (one per StockLocation involved)
│ ├── InventoryUnit
│ ├── ShippingRate
│ └── selected ShippingRate
├── Payment[]
│ ├── PaymentSession (v5.4+, provider-agnostic envelope)
│ └── source (CreditCard, StoreCredit, etc.)
├── Adjustment[] (order-level)
├── Address (bill_address, ship_address)
└── User (optional — guest orders allowed)
Plus return-flow models: ReturnAuthorization → CustomerReturn → Reimbursement → Refund.
StockLocation — physical / logical warehouseStockItem — count of a Variant in a StockLocationStockMovement — append-only ledger of stock changesStockTransfer — moves stock between locationsShippingMethod — names a way to ship (UPS Ground, Express)ShippingRate — computed cost option on a ShipmentShippingCategory — categorize products by shipping needsZone / ZoneMember — countries/states a method ships toPromotion + PromotionRule + PromotionAction + CouponCode resulting in Adjustments.
| Model | Notes |
|-------|-------|
| User | Spree's customer / admin model (Devise-backed in v5+) |
| Role | Permissions — admin, customer, custom |
| Address | Bill / ship address, optionally tied to a user |
| StoreCredit | Balance on a user, usable as payment |
| GiftCard | Tradeable balance, redeemable as payment method |
| CustomerGroup | Segments for pricing / promotions |
| Invitation | Invite to an account |
| ApiKey | Per-user API key for v3 admin API |
TaxCategory — assignable to products (e.g., "Clothing", "Books")TaxRate — percentage by Zone + TaxCategory| Model | Purpose |
|-------|---------|
| Store | One install → many stores, each with own domain/theme/policies |
| Market (v5.4+) | Bundles currency + locale + payment methods + shipping per region |
| CmsPage | Content pages per store |
| Theme | Storefront theme per store |
Shared across stores: products, inventory, customers, shipping methods, payment gateways, Markets, admin roles. Per-store: orders, shipments, payments, refunds, store credits, gift cards, themes, blogs, pages, integrations.
A flexible custom-data system attached to any model. Replaces ad-hoc decorators for "I just need one extra field." Use this before adding a column.
API v3 exposes prefixed IDs (prod_…, ord_…, var_…, usr_…, pay_…) — these are stable string identifiers separate from the database id. Models gain a prefixed_id method.
Adjustment belongs to an adjustable (Order, LineItem, Shipment) and a source (PromotionAction, TaxRate, manual). When sums change, run order.update_totals or use Spree::OrderUpdater (or its swappable replacement via Spree::Dependencies).
# WRONG — leaks across stores
Spree::Order.complete
# RIGHT — always scope by current_store in customer-facing code
current_store.orders.complete
# Admin-side: admin sees all stores by default, but check role scoping
Spree::Order.where(store_id: current_store.id)
variant = Spree::Variant.find(...)
variant.price_in('USD').amount # raw decimal
variant.price_in('USD').display_price # formatted "$19.99"
variant.amount_in('USD') # alias for price_in(currency).amount
For v5.3+ PriceList overrides:
price = Spree::Pricing::PriceFinder.new(variant: variant, store: store, user: user, currency: 'USD').call
(Verify the live API — pricing service objects are routinely renamed.)
Don't recompute totals manually. Use the order updater service:
Spree::OrderUpdater.new(order).update
# or via Dependencies
Spree::Dependencies.order_updater.call(order)
Prefer Metafield over adding columns:
product.metafields.create!(
namespace: 'my_app',
key: 'launch_date',
value: '2026-06-01',
value_type: 'string'
)
product.metafield('my_app', 'launch_date')
Verify the exact API in the live metafields doc.
StockLocations.Store#default — most APIs default to it, but explicit scoping is safer.delete.Variant.is_master ordering — the master variant is always present; option-variant ordering follows position columns.Always cross-check column names and relationships against the live source — the data model evolves between minor releases.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.