docs/plans/decisions.md
Spree::StockItem → Spree::StockLevel, spree_stock_items → spree_stock_levels.
Prefix ID: si_ → sl_.
Every other platform uses "level" for this concept — Shopify (InventoryLevel),
Medusa (InventoryLevel), Vendure (StockLevel), Saleor (Stock). "Item" sounds
like a physical object; "level" correctly describes "the quantity of a variant at
a location."
Part of the 6.0 model rename wave. Includes renaming the FK columns
(stock_item_id → stock_level_id) on StockMovement, StockReservation, and
any other referencing tables.
As part of the User → Customer rename (6.0-platform-auth.md), rename user_id
foreign key columns to customer_id on all models where the FK references a
storefront customer (not an admin user).
Rename to customer_id (11 models — FK references Spree.customer_class):
spree_orders.user_id → customer_idspree_addresses.user_id → customer_idspree_credit_cards.user_id → customer_idspree_store_credits.user_id → customer_idspree_wishlists.user_id → customer_idspree_gift_cards.user_id → customer_idspree_gateway_customers.user_id → customer_idspree_payment_sources.user_id → customer_idspree_newsletter_subscribers.user_id → customer_idspree_promotion_rule_users.user_id → customer_idspree_customer_group_users.user_id → customer_idKeep as user_id (5 models — FK references Spree.admin_user_class or is polymorphic):
spree_imports.user_id — admin who ran the importspree_exports.user_id — admin who ran the exportspree_reports.user_id — admin who generated the reportspree_state_changes.user_id — admin who triggered the changespree_user_identities.user_id — polymorphic (Customer or AdminUser)Single migration renames all 11 columns. Model associations updated:
belongs_to :user → belongs_to :customer with class_name: Spree.customer_class.
Both PaymentMethod and DeliveryMethod (renamed from ShippingMethod) switch from
multi-store join tables (StorePaymentMethod, StoreShippingMethod) to
SingleStoreResource with direct belongs_to :store.
In practice, different stores have different currencies, zones, and provider accounts — sharing the same payment/delivery config across stores is rare. If a merchant wants the same config on two stores, they create two records.
Changes:
store_id column to spree_payment_methods and spree_delivery_methodsstore_id; duplicate methods linked
to multiple storesspree_store_payment_methods and spree_store_shipping_methods join tablesSpree::SingleStoreResource concernRename Spree::Promotion::Rules::* → Spree::PromotionRules::* and
Spree::Promotion::Actions::* → Spree::PromotionActions::*.
The convention for STI subtypes is Spree::{BaseClass}s::{Subtype} — pluralized
base class as the namespace. Every other hierarchy follows this already:
Spree::PriceRules::VolumeRuleSpree::Metafields::ShortTextSpree::CollectionRules::Tag (from categories plan)Spree::ReimbursementType::CreditPromotion was the only one nesting under the parent model (Spree::Promotion::Rules)
instead of the base class (Spree::PromotionRules).
Changes:
app/models/spree/promotion/rules/ → app/models/spree/promotion_rules/app/models/spree/promotion/actions/ → app/models/spree/promotion_actions/type column in spree_promotion_rules and spree_promotion_actions
(e.g., Spree::Promotion::Rules::Product → Spree::PromotionRules::Product)Settle on status as the standard column name for state machines. Newer models
(Product, PriceList, PaymentSession, Import, Invitation) already use status.
Order.state and Adjustment.state are removed entirely by other 6.0 plans
(cart-order-split, split-adjustments). Five remaining models need a column
rename from state → status in 6.0:
state → statusstate → statusstate → statusstate → statusstate → statusSingle migration renaming all five columns. State machine declarations updated
to state_machine :status, initial: .... Deprecation aliases
(alias_attribute :state, :status) for one release.
Models already correct (no change): Product, PriceList, PaymentSession,
PaymentSetupSession, Import, ImportRow, Invitation, ReturnItem
(reception_status/acceptance_status), Reimbursement (reimbursement_status).
Replace three-way display_on (both/front_end/back_end) with storefront_visible
boolean (default: true) on CustomFieldDefinition. front_end-only was already
excluded from MetafieldDefinition::DISPLAY and never made sense.
This makes the two-system boundary razor-sharp:
Matches Vendure (public: boolean) and Saleor (visibleInStorefront: boolean).
Ships with the 6.0 model rename wave. See 5.4-6.0-custom-fields-rename.md.
Drop public_metadata column (never exposed in Store API, unused). Rename
private_metadata → metadata in the database. Simplify the Spree::Metadata
concern to a single metadata JSON column with no alias indirection.
Metadata (JSON column) is a permanent, first-class system — the schemaless
developer escape hatch for integration IDs, sync state, ad-hoc flags. No
definition required, one-step API:
PATCH /product { metadata: { erp_id: "123" } }. Never exposed in Store API
(Stripe convention: write-only). Metadata is here to stay.
Metafields (→ Custom Fields in 6.0, see 5.4-6.0-custom-fields-rename.md)
stay as merchant-defined structured data — typed values (short_text, number,
boolean, json, rich_text, long_text), require a MetafieldDefinition, have
storefront_visible boolean, searchable, CSV importable. With the
ProductType plan (6.0-product-types.md), metafields become schema-enforced
custom attributes driven by ProductType.
Two systems, two purposes, no overlap. No consolidation into one. Metadata for machines, metafields/custom fields for humans.
Considered Action Text. Rejected for API-first performance — serializing rich text adds overhead for every product response. Also in the new Admin UI we will use TipTap for rich text editing.