spree_core 5.6.1 → 6.0.0.beta2

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.
Files changed (1602) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +30 -0
  3. data/README.md +2 -3
  4. data/app/finders/spree/line_items/find_by_variant.rb +5 -3
  5. data/app/finders/spree/sellers/panel_url.rb +47 -0
  6. data/app/finders/spree/stores/dashboard_url.rb +76 -0
  7. data/app/helpers/spree/images_helper.rb +1 -1
  8. data/app/jobs/spree/carts/reap_expired_job.rb +43 -0
  9. data/app/jobs/spree/collections/regenerate_time_based_job.rb +18 -0
  10. data/app/jobs/spree/data_requests/process_job.rb +42 -0
  11. data/app/jobs/spree/events/subscriber_job.rb +1 -1
  12. data/app/jobs/spree/images/migrate_product_media_job.rb +80 -0
  13. data/app/jobs/spree/images/save_from_url_job.rb +70 -13
  14. data/app/jobs/spree/imports/create_categories_job.rb +23 -14
  15. data/app/jobs/spree/imports/create_rows_job.rb +3 -62
  16. data/app/jobs/spree/imports/process_group_job.rb +1 -1
  17. data/app/jobs/spree/imports/process_job.rb +164 -0
  18. data/app/jobs/spree/imports/process_rows_job.rb +4 -71
  19. data/app/jobs/spree/orders/finalize_stale_drafts_job.rb +29 -0
  20. data/app/jobs/spree/payments/handle_webhook_job.rb +1 -1
  21. data/app/jobs/spree/price_lists/sync_currencies_job.rb +42 -0
  22. data/app/jobs/spree/product_types/apply_to_products_job.rb +74 -0
  23. data/app/jobs/spree/products/auto_match_collections_job.rb +14 -0
  24. data/app/jobs/spree/products/auto_match_taxons_job.rb +6 -7
  25. data/app/jobs/spree/products/refresh_metrics_job.rb +1 -1
  26. data/app/jobs/spree/products/touch_categories_job.rb +15 -0
  27. data/app/jobs/spree/products/touch_taxons_job.rb +7 -6
  28. data/app/jobs/spree/sample_data/load_job.rb +13 -0
  29. data/app/jobs/spree/seller_payouts/sweep_due_job.rb +57 -0
  30. data/app/jobs/spree/seller_payouts/sweep_seller_job.rb +70 -0
  31. data/app/jobs/spree/seller_transfers/execute_pending_due_job.rb +56 -0
  32. data/app/jobs/spree/seller_transfers/execute_pending_job.rb +52 -0
  33. data/app/jobs/spree/shipping_labels/store_file_job.rb +17 -0
  34. data/app/jobs/spree/stock_locations/stock_items/create_job.rb +7 -5
  35. data/app/jobs/spree/stock_locations/stock_levels/create_job.rb +13 -0
  36. data/app/jobs/spree/stock_reservations/expire_job.rb +1 -1
  37. data/app/jobs/spree/tax_identifiers/validate_job.rb +14 -0
  38. data/app/jobs/spree/variants/remove_line_item_job.rb +29 -1
  39. data/app/mailers/spree/admin_user_mailer.rb +6 -35
  40. data/app/mailers/spree/base_mailer.rb +1 -20
  41. data/app/mailers/spree/invitation_mailer.rb +1 -1
  42. data/app/mailers/spree/seller_user_mailer.rb +47 -0
  43. data/app/models/concerns/spree/account_lockout.rb +38 -0
  44. data/app/models/concerns/spree/acted_by.rb +210 -0
  45. data/app/models/concerns/spree/actor.rb +51 -0
  46. data/app/models/concerns/spree/admin_user_methods.rb +53 -42
  47. data/app/models/concerns/spree/calculated_adjustments.rb +8 -14
  48. data/app/models/concerns/spree/calculator/currency_amounts.rb +43 -0
  49. data/app/models/concerns/spree/capture_method.rb +33 -0
  50. data/app/models/concerns/spree/custom_field_filterable.rb +163 -0
  51. data/app/models/concerns/spree/customer_methods.rb +421 -0
  52. data/app/models/concerns/spree/deprecated_customer_alias.rb +22 -0
  53. data/app/models/concerns/spree/derived_number.rb +66 -0
  54. data/app/models/concerns/spree/has_address_book.rb +89 -0
  55. data/app/models/concerns/spree/has_custom_fields.rb +360 -0
  56. data/app/models/concerns/spree/has_external_references.rb +95 -0
  57. data/app/models/concerns/spree/has_iso_geography.rb +71 -0
  58. data/app/models/concerns/spree/has_library_media.rb +94 -0
  59. data/app/models/concerns/spree/has_list_position.rb +15 -0
  60. data/app/models/concerns/spree/has_number.rb +139 -0
  61. data/app/models/concerns/spree/has_status.rb +76 -0
  62. data/app/models/concerns/spree/has_tracking_carrier.rb +18 -0
  63. data/app/models/concerns/spree/instruments_gateway_calls.rb +20 -0
  64. data/app/models/concerns/spree/integration_backed_provider.rb +89 -0
  65. data/app/models/concerns/spree/integrations_concern.rb +15 -2
  66. data/app/models/concerns/spree/label_translatable.rb +37 -0
  67. data/app/models/concerns/spree/marketplace/order_group.rb +35 -0
  68. data/app/models/concerns/spree/metadata.rb +18 -22
  69. data/app/models/concerns/spree/named_type.rb +11 -1
  70. data/app/models/concerns/spree/parameterizable_name.rb +5 -5
  71. data/app/models/concerns/spree/password_policy.rb +37 -0
  72. data/app/models/concerns/spree/payment_session_transitions.rb +93 -0
  73. data/app/models/concerns/spree/payment_source_concern.rb +1 -1
  74. data/app/models/concerns/spree/personal_data_matching.rb +120 -0
  75. data/app/models/concerns/spree/preference_schema.rb +120 -19
  76. data/app/models/concerns/spree/prefixed_id.rb +25 -3
  77. data/app/models/concerns/spree/presentation_translatable.rb +4 -37
  78. data/app/models/concerns/spree/product_scopes.rb +51 -191
  79. data/app/models/concerns/spree/provider_failure_policy.rb +44 -0
  80. data/app/models/concerns/spree/publishable.rb +7 -7
  81. data/app/models/concerns/spree/purchase/addresses.rb +258 -0
  82. data/app/models/concerns/spree/purchase/channel.rb +19 -0
  83. data/app/models/concerns/spree/purchase/checkout_steps.rb +68 -0
  84. data/app/models/concerns/spree/purchase/company.rb +96 -0
  85. data/app/models/concerns/spree/purchase/currency.rb +29 -0
  86. data/app/models/concerns/spree/purchase/digital_items.rb +60 -0
  87. data/app/models/concerns/spree/purchase/freight.rb +52 -0
  88. data/app/models/concerns/spree/purchase/gift_cards.rb +70 -0
  89. data/app/models/concerns/spree/purchase/lifecycle.rb +53 -0
  90. data/app/models/concerns/spree/purchase/line_item_currencies.rb +38 -0
  91. data/app/models/concerns/spree/purchase/line_item_lookup.rb +25 -0
  92. data/app/models/concerns/spree/purchase/locale.rb +30 -0
  93. data/app/models/concerns/spree/purchase/market.rb +29 -0
  94. data/app/models/concerns/spree/purchase/payment_processing.rb +93 -0
  95. data/app/models/concerns/spree/purchase/purchase_order.rb +94 -0
  96. data/app/models/concerns/spree/purchase/quantity_rules.rb +136 -0
  97. data/app/models/concerns/spree/purchase/store_credits.rb +100 -0
  98. data/app/models/concerns/spree/purchase/taxation.rb +146 -0
  99. data/app/models/concerns/spree/purchase/totals.rb +116 -0
  100. data/app/models/concerns/spree/purchase/validations.rb +45 -0
  101. data/app/models/concerns/spree/receivable.rb +103 -0
  102. data/app/models/concerns/spree/receivable_item.rb +78 -0
  103. data/app/models/concerns/spree/refund_methods.rb +19 -0
  104. data/app/models/concerns/spree/sanitizable_rich_text.rb +109 -0
  105. data/app/models/concerns/spree/search_indexable.rb +5 -2
  106. data/app/models/concerns/spree/single_store_resource.rb +3 -1
  107. data/app/models/concerns/spree/store_data_sources.rb +75 -0
  108. data/app/models/concerns/spree/store_preferences.rb +49 -0
  109. data/app/models/concerns/spree/stores/markets.rb +75 -28
  110. data/app/models/concerns/spree/stores/setup.rb +10 -0
  111. data/app/models/concerns/spree/translatable_resource.rb +25 -6
  112. data/app/models/concerns/spree/typed_adjustment_line.rb +55 -0
  113. data/app/models/concerns/spree/user_address.rb +13 -3
  114. data/app/models/concerns/spree/user_management.rb +18 -6
  115. data/app/models/concerns/spree/user_methods.rb +5 -330
  116. data/app/models/concerns/spree/user_payment_source.rb +1 -1
  117. data/app/models/concerns/spree/user_roles.rb +18 -11
  118. data/app/models/concerns/spree/validates_store_uniqueness.rb +53 -0
  119. data/app/models/concerns/spree/vat_price_calculation.rb +58 -6
  120. data/app/models/concerns/spree/webhook_payload_redaction.rb +1 -1
  121. data/app/models/spree/ability.rb +147 -43
  122. data/app/models/spree/address.rb +387 -118
  123. data/app/models/spree/adjusters/base.rb +26 -0
  124. data/app/models/spree/adjusters/largest_remainder.rb +24 -0
  125. data/app/models/spree/adjusters/promotion.rb +253 -0
  126. data/app/models/spree/admin_user.rb +26 -0
  127. data/app/models/spree/allowed_origin.rb +1 -1
  128. data/app/models/spree/api_key.rb +34 -31
  129. data/app/models/spree/asset.rb +14 -211
  130. data/app/models/spree/authentication/strategies/base_strategy.rb +54 -3
  131. data/app/models/spree/authentication/strategies/email_password_strategy.rb +11 -3
  132. data/app/models/spree/authentication/strategies/oidc_strategy.rb +330 -0
  133. data/app/models/spree/authentication/strategy_registry.rb +83 -0
  134. data/app/models/spree/base.rb +83 -12
  135. data/app/models/spree/base_analytics_event_handler.rb +1 -1
  136. data/app/models/spree/calculator/shipping/digital_delivery.rb +5 -2
  137. data/app/models/spree/calculator/shipping/flat_rate.rb +10 -5
  138. data/app/models/spree/calculator/shipping/flexi_rate.rb +6 -0
  139. data/app/models/spree/calculator/shipping/per_item.rb +8 -3
  140. data/app/models/spree/calculator/shipping/price_sack.rb +6 -0
  141. data/app/models/spree/calculator/tiered_flat_rate.rb +5 -1
  142. data/app/models/spree/calculator.rb +10 -0
  143. data/app/models/spree/cart.rb +285 -0
  144. data/app/models/spree/carts/item_warning.rb +28 -0
  145. data/app/models/spree/carts/resolved_item.rb +37 -0
  146. data/app/models/spree/catalog.rb +503 -0
  147. data/app/models/spree/catalog_assignment.rb +41 -0
  148. data/app/models/spree/catalog_order_minimum.rb +26 -0
  149. data/app/models/spree/catalog_price.rb +100 -0
  150. data/app/models/spree/catalog_price_tier.rb +36 -0
  151. data/app/models/spree/catalog_product.rb +23 -0
  152. data/app/models/spree/catalog_quantity_rule.rb +46 -0
  153. data/app/models/spree/catalogs/product_term.rb +43 -0
  154. data/app/models/spree/category.rb +430 -12
  155. data/app/models/spree/channel/gating.rb +1 -2
  156. data/app/models/spree/channel.rb +38 -4
  157. data/app/models/spree/channel_stock_location.rb +8 -0
  158. data/app/models/spree/claim.rb +69 -0
  159. data/app/models/spree/claim_line_item.rb +46 -0
  160. data/app/models/spree/claim_reason.rb +24 -0
  161. data/app/models/spree/classification.rb +7 -49
  162. data/app/models/spree/collection.rb +269 -0
  163. data/app/models/spree/collection_rule.rb +58 -0
  164. data/app/models/spree/collection_rules/available_on.rb +21 -0
  165. data/app/models/spree/collection_rules/sale.rb +18 -0
  166. data/app/models/spree/collection_rules/tag.rb +21 -0
  167. data/app/models/spree/commission_line.rb +85 -0
  168. data/app/models/spree/commission_rate.rb +370 -0
  169. data/app/models/spree/commission_rate_value.rb +47 -0
  170. data/app/models/spree/commission_rule.rb +77 -0
  171. data/app/models/spree/commission_rule_product.rb +19 -0
  172. data/app/models/spree/commission_rules/category_rule.rb +33 -0
  173. data/app/models/spree/commission_rules/item_total_rule.rb +31 -0
  174. data/app/models/spree/commission_rules/product_rule.rb +55 -0
  175. data/app/models/spree/commission_rules/seller_rule.rb +33 -0
  176. data/app/models/spree/commission_tax.rb +51 -0
  177. data/app/models/spree/commissions/context.rb +71 -0
  178. data/app/models/spree/company.rb +347 -0
  179. data/app/models/spree/company_invitation.rb +81 -0
  180. data/app/models/spree/company_membership.rb +25 -0
  181. data/app/models/spree/consent_record.rb +125 -0
  182. data/app/models/spree/country.rb +128 -52
  183. data/app/models/spree/coupon_code.rb +18 -5
  184. data/app/models/spree/credit_card.rb +10 -9
  185. data/app/models/spree/current.rb +152 -18
  186. data/app/models/spree/custom_field.rb +160 -7
  187. data/app/models/spree/custom_field_definition/search_capabilities.rb +98 -0
  188. data/app/models/spree/custom_field_definition.rb +219 -5
  189. data/app/models/spree/custom_fields/boolean.rb +15 -0
  190. data/app/models/spree/custom_fields/json.rb +38 -0
  191. data/app/models/spree/custom_fields/long_text.rb +11 -0
  192. data/app/models/spree/custom_fields/number.rb +23 -0
  193. data/app/models/spree/custom_fields/rich_text.rb +23 -0
  194. data/app/models/spree/custom_fields/short_text.rb +15 -0
  195. data/app/models/spree/customer.rb +34 -0
  196. data/app/models/spree/customer_group.rb +12 -12
  197. data/app/models/spree/customer_group_user.rb +6 -4
  198. data/app/models/spree/data_feed.rb +1 -1
  199. data/app/models/spree/data_request.rb +98 -0
  200. data/app/models/spree/delivery.rb +169 -0
  201. data/app/models/spree/delivery_method.rb +566 -0
  202. data/app/models/spree/delivery_method_rule.rb +55 -0
  203. data/app/models/spree/delivery_method_rule_product.rb +8 -0
  204. data/app/models/spree/delivery_method_rules/channel_rule.rb +41 -0
  205. data/app/models/spree/delivery_method_rules/company_rule.rb +24 -0
  206. data/app/models/spree/delivery_method_rules/excluded_products_rule.rb +48 -0
  207. data/app/models/spree/delivery_method_rules/item_total_rule.rb +19 -0
  208. data/app/models/spree/delivery_method_rules/volume_rule.rb +35 -0
  209. data/app/models/spree/delivery_method_rules/weight_rule.rb +19 -0
  210. data/app/models/spree/delivery_method_service.rb +24 -0
  211. data/app/models/spree/delivery_method_stock_location.rb +11 -0
  212. data/app/models/spree/delivery_origin_group.rb +73 -0
  213. data/app/models/spree/delivery_origin_group_location.rb +9 -0
  214. data/app/models/spree/delivery_profile.rb +141 -0
  215. data/app/models/spree/delivery_profiles/digital.rb +20 -0
  216. data/app/models/spree/delivery_profiles/shipping.rb +12 -0
  217. data/app/models/spree/delivery_rate.rb +164 -0
  218. data/app/models/spree/delivery_rate_provider/base.rb +120 -0
  219. data/app/models/spree/delivery_rate_provider/estimate.rb +44 -0
  220. data/app/models/spree/delivery_rate_provider/freight.rb +41 -0
  221. data/app/models/spree/delivery_rate_provider/internal.rb +28 -0
  222. data/app/models/spree/delivery_rate_provider/service_catalog.rb +48 -0
  223. data/app/models/spree/delivery_zone.rb +64 -0
  224. data/app/models/spree/delivery_zone_member.rb +98 -0
  225. data/app/models/spree/digital.rb +12 -16
  226. data/app/models/spree/digital_asset.rb +144 -0
  227. data/app/models/spree/digital_asset_provider/base.rb +95 -0
  228. data/app/models/spree/digital_asset_provider/file.rb +20 -0
  229. data/app/models/spree/digital_delivery.rb +28 -0
  230. data/app/models/spree/digital_link.rb +31 -11
  231. data/app/models/spree/discount.rb +51 -0
  232. data/app/models/spree/event.rb +6 -6
  233. data/app/models/spree/exchange.rb +50 -44
  234. data/app/models/spree/exchange_line_item.rb +52 -0
  235. data/app/models/spree/export.rb +128 -21
  236. data/app/models/spree/exports/customers.rb +2 -2
  237. data/app/models/spree/exports/gift_cards.rb +2 -2
  238. data/app/models/spree/exports/newsletter_subscribers.rb +2 -2
  239. data/app/models/spree/exports/orders.rb +35 -5
  240. data/app/models/spree/exports/price_list_prices.rb +107 -0
  241. data/app/models/spree/exports/products.rb +4 -4
  242. data/app/models/spree/exports/purchase_orders.rb +39 -0
  243. data/app/models/spree/exports/report.rb +95 -0
  244. data/app/models/spree/external_reference.rb +77 -0
  245. data/app/models/spree/external_video.rb +117 -0
  246. data/app/models/spree/fee.rb +40 -0
  247. data/app/models/spree/freight_summary/line.rb +257 -0
  248. data/app/models/spree/freight_summary.rb +180 -0
  249. data/app/models/spree/fulfillment/custom_events.rb +96 -0
  250. data/app/models/spree/fulfillment.rb +973 -0
  251. data/app/models/spree/fulfillment_changer.rb +170 -0
  252. data/app/models/spree/fulfillment_item.rb +148 -0
  253. data/app/models/spree/fulfillment_provider/base.rb +130 -0
  254. data/app/models/spree/fulfillment_provider/digital.rb +44 -0
  255. data/app/models/spree/fulfillment_provider/manual.rb +15 -0
  256. data/app/models/spree/fulfillment_provider/pickup.rb +38 -0
  257. data/app/models/spree/fulfillment_provider/pickup_point.rb +24 -0
  258. data/app/models/spree/fulfilment_changer.rb +3 -164
  259. data/app/models/spree/gateway/bogus.rb +5 -4
  260. data/app/models/spree/gateway/custom_payment_source_method.rb +5 -5
  261. data/app/models/spree/gateway.rb +2 -2
  262. data/app/models/spree/gateway_customer.rb +12 -4
  263. data/app/models/spree/gift_card.rb +140 -48
  264. data/app/models/spree/gift_card_batch.rb +2 -2
  265. data/app/models/spree/import.rb +136 -98
  266. data/app/models/spree/import_mapping.rb +1 -1
  267. data/app/models/spree/import_row.rb +35 -31
  268. data/app/models/spree/import_schemas/price_list_prices.rb +13 -0
  269. data/app/models/spree/import_schemas/products.rb +8 -1
  270. data/app/models/spree/import_schemas/purchase_orders.rb +19 -0
  271. data/app/models/spree/imports/price_list_prices.rb +72 -0
  272. data/app/models/spree/imports/purchase_orders.rb +28 -0
  273. data/app/models/spree/integration.rb +128 -5
  274. data/app/models/spree/inventory_provider/base.rb +30 -0
  275. data/app/models/spree/inventory_provider/internal.rb +22 -0
  276. data/app/models/spree/inventory_unit.rb +3 -139
  277. data/app/models/spree/invitation.rb +53 -47
  278. data/app/models/spree/label_purchase.rb +29 -0
  279. data/app/models/spree/line_item.rb +240 -106
  280. data/app/models/spree/market.rb +106 -15
  281. data/app/models/spree/market_country.rb +10 -5
  282. data/app/models/spree/media.rb +497 -0
  283. data/app/models/spree/media_usage_reference.rb +27 -0
  284. data/app/models/spree/metafield.rb +14 -102
  285. data/app/models/spree/metafield_definition.rb +11 -128
  286. data/app/models/spree/metafields.rb +32 -0
  287. data/app/models/spree/newsletter_subscriber.rb +10 -9
  288. data/app/models/spree/number_generators/base.rb +56 -0
  289. data/app/models/spree/number_generators/random.rb +23 -0
  290. data/app/models/spree/number_generators/sequential.rb +88 -0
  291. data/app/models/spree/number_sequence.rb +86 -0
  292. data/app/models/spree/option_type.rb +18 -32
  293. data/app/models/spree/option_type_product_type.rb +8 -0
  294. data/app/models/spree/option_value.rb +19 -13
  295. data/app/models/spree/option_value_variant.rb +0 -1
  296. data/app/models/spree/order.rb +681 -576
  297. data/app/models/spree/order_cancellation_reason.rb +25 -0
  298. data/app/models/spree/order_group.rb +244 -0
  299. data/app/models/spree/order_inventory.rb +42 -33
  300. data/app/models/spree/order_merger.rb +11 -6
  301. data/app/models/spree/order_promotion.rb +32 -6
  302. data/app/models/spree/order_routing/has_strategy_preference.rb +1 -2
  303. data/app/models/spree/order_routing/rules/minimize_splits.rb +6 -6
  304. data/app/models/spree/order_routing/strategy/rules.rb +12 -8
  305. data/app/models/spree/order_routing_rule.rb +5 -15
  306. data/app/models/spree/order_updater.rb +131 -163
  307. data/app/models/spree/package_type.rb +191 -0
  308. data/app/models/spree/payment/custom_events.rb +52 -5
  309. data/app/models/spree/payment/gateway_options.rb +20 -5
  310. data/app/models/spree/payment/processing.rb +95 -116
  311. data/app/models/spree/payment.rb +299 -123
  312. data/app/models/spree/payment_method/check.rb +4 -3
  313. data/app/models/spree/payment_method/session_instrumentation.rb +34 -0
  314. data/app/models/spree/payment_method/store_credit.rb +4 -2
  315. data/app/models/spree/payment_method.rb +106 -9
  316. data/app/models/spree/payment_session.rb +150 -53
  317. data/app/models/spree/payment_setup_session.rb +4 -37
  318. data/app/models/spree/payment_source.rb +5 -4
  319. data/app/models/spree/payment_split.rb +90 -0
  320. data/app/models/spree/payout_provider/base.rb +218 -0
  321. data/app/models/spree/payout_provider/system.rb +36 -0
  322. data/app/models/spree/permission_sets.rb +17 -0
  323. data/app/models/spree/pickup_point_option.rb +28 -0
  324. data/app/models/spree/pickup_point_provider/base.rb +24 -0
  325. data/app/models/spree/policy.rb +12 -11
  326. data/app/models/spree/price.rb +91 -5
  327. data/app/models/spree/price_adjustment_tier.rb +63 -0
  328. data/app/models/spree/price_history.rb +1 -1
  329. data/app/models/spree/price_list.rb +266 -142
  330. data/app/models/spree/price_rule.rb +51 -7
  331. data/app/models/spree/price_rules/channel_rule.rb +3 -0
  332. data/app/models/spree/price_rules/customer_group_rule.rb +10 -1
  333. data/app/models/spree/price_rules/market_rule.rb +6 -0
  334. data/app/models/spree/price_rules/user_rule.rb +12 -3
  335. data/app/models/spree/price_rules/volume_rule.rb +6 -0
  336. data/app/models/spree/price_rules/zone_rule.rb +5 -13
  337. data/app/models/spree/pricing_provider/base.rb +51 -0
  338. data/app/models/spree/pricing_provider/internal.rb +277 -0
  339. data/app/models/spree/pricing_provider.rb +28 -0
  340. data/app/models/spree/product.rb +528 -544
  341. data/app/models/spree/product_category.rb +65 -0
  342. data/app/models/spree/product_collection.rb +20 -0
  343. data/app/models/spree/product_option_type.rb +0 -1
  344. data/app/models/spree/product_promotion_rule.rb +0 -1
  345. data/app/models/spree/product_publication.rb +0 -1
  346. data/app/models/spree/product_submission.rb +72 -0
  347. data/app/models/spree/product_type.rb +116 -0
  348. data/app/models/spree/product_type_category.rb +8 -0
  349. data/app/models/spree/product_type_custom_field_definition.rb +39 -0
  350. data/app/models/spree/promotion/actions/create_adjustment.rb +9 -7
  351. data/app/models/spree/promotion/actions/create_item_adjustments.rb +11 -16
  352. data/app/models/spree/promotion/actions/create_line_items.rb +19 -8
  353. data/app/models/spree/promotion/actions/free_shipping.rb +12 -18
  354. data/app/models/spree/promotion/rules/category.rb +181 -0
  355. data/app/models/spree/promotion/rules/channel.rb +3 -3
  356. data/app/models/spree/promotion/rules/country.rb +43 -17
  357. data/app/models/spree/promotion/rules/currency.rb +2 -2
  358. data/app/models/spree/promotion/rules/customer_group.rb +13 -5
  359. data/app/models/spree/promotion/rules/first_order.rb +3 -3
  360. data/app/models/spree/promotion/rules/item_total.rb +2 -2
  361. data/app/models/spree/promotion/rules/market.rb +3 -3
  362. data/app/models/spree/promotion/rules/one_use_per_user.rb +4 -4
  363. data/app/models/spree/promotion/rules/option_value.rb +34 -11
  364. data/app/models/spree/promotion/rules/product.rb +3 -5
  365. data/app/models/spree/promotion/rules/taxon.rb +10 -131
  366. data/app/models/spree/promotion/rules/user.rb +14 -22
  367. data/app/models/spree/promotion/rules/user_logged_in.rb +3 -3
  368. data/app/models/spree/promotion.rb +59 -21
  369. data/app/models/spree/promotion_action.rb +52 -11
  370. data/app/models/spree/promotion_action_line_item.rb +1 -1
  371. data/app/models/spree/promotion_handler/coupon.rb +44 -16
  372. data/app/models/spree/promotion_handler/free_shipping.rb +1 -1
  373. data/app/models/spree/promotion_handler/promotion_duplicator.rb +1 -1
  374. data/app/models/spree/promotion_rule.rb +2 -9
  375. data/app/models/spree/promotion_rule_category.rb +22 -0
  376. data/app/models/spree/promotion_rule_taxon.rb +7 -6
  377. data/app/models/spree/promotion_rule_user.rb +2 -2
  378. data/app/models/spree/prototype.rb +3 -13
  379. data/app/models/spree/purchase_order.rb +95 -0
  380. data/app/models/spree/purchase_order_item.rb +40 -0
  381. data/app/models/spree/quantity_rule.rb +114 -0
  382. data/app/models/spree/refresh_token.rb +20 -2
  383. data/app/models/spree/refund.rb +96 -42
  384. data/app/models/spree/refund_reason.rb +32 -11
  385. data/app/models/spree/return.rb +151 -0
  386. data/app/models/spree/return_authorization_reason.rb +11 -6
  387. data/app/models/spree/return_line_item.rb +42 -0
  388. data/app/models/spree/return_reason.rb +26 -0
  389. data/app/models/spree/rich_text_sanitizer.rb +99 -0
  390. data/app/models/spree/role.rb +179 -4
  391. data/app/models/spree/role_user.rb +5 -21
  392. data/app/models/spree/saved_report.rb +56 -0
  393. data/app/models/spree/search_provider/base.rb +8 -2
  394. data/app/models/spree/search_provider/database.rb +73 -22
  395. data/app/models/spree/search_provider.rb +29 -0
  396. data/app/models/spree/seller.rb +475 -0
  397. data/app/models/spree/seller_balance.rb +86 -0
  398. data/app/models/spree/seller_payout.rb +116 -0
  399. data/app/models/spree/seller_requirement.rb +274 -0
  400. data/app/models/spree/seller_requirement_custom_field.rb +38 -0
  401. data/app/models/spree/seller_requirement_status.rb +144 -0
  402. data/app/models/spree/seller_requirement_submission.rb +91 -0
  403. data/app/models/spree/seller_requirements/accept_terms.rb +104 -0
  404. data/app/models/spree/seller_requirements/attestation.rb +22 -0
  405. data/app/models/spree/seller_requirements/billing_address.rb +13 -0
  406. data/app/models/spree/seller_requirements/complete_profile.rb +26 -0
  407. data/app/models/spree/seller_requirements/delivery_method.rb +20 -0
  408. data/app/models/spree/seller_requirements/document.rb +62 -0
  409. data/app/models/spree/seller_requirements/minimum_products.rb +20 -0
  410. data/app/models/spree/seller_requirements/operator_review.rb +23 -0
  411. data/app/models/spree/seller_requirements/package_type.rb +40 -0
  412. data/app/models/spree/seller_requirements/payout_account.rb +55 -0
  413. data/app/models/spree/seller_requirements/policy.rb +43 -0
  414. data/app/models/spree/seller_requirements/required_custom_fields.rb +56 -0
  415. data/app/models/spree/seller_requirements/returns_address.rb +19 -0
  416. data/app/models/spree/seller_transfer.rb +166 -0
  417. data/app/models/spree/setup_tasks.rb +3 -6
  418. data/app/models/spree/shipment.rb +3 -560
  419. data/app/models/spree/shipping_calculator.rb +7 -0
  420. data/app/models/spree/shipping_document.rb +32 -0
  421. data/app/models/spree/shipping_label.rb +146 -0
  422. data/app/models/spree/shipping_method.rb +3 -146
  423. data/app/models/spree/shipping_rate.rb +3 -97
  424. data/app/models/spree/state.rb +57 -23
  425. data/app/models/spree/stock/availability_validator.rb +1 -1
  426. data/app/models/spree/stock/content_item.rb +1 -0
  427. data/app/models/spree/stock/coordinator.rb +69 -9
  428. data/app/models/spree/stock/estimator.rb +198 -33
  429. data/app/models/spree/stock/inventory_unit_builder.rb +6 -1
  430. data/app/models/spree/stock/package.rb +145 -14
  431. data/app/models/spree/stock/packer.rb +20 -3
  432. data/app/models/spree/stock/prioritizer.rb +27 -4
  433. data/app/models/spree/stock/quantifier.rb +63 -38
  434. data/app/models/spree/stock/splitter/base.rb +3 -1
  435. data/app/models/spree/stock/splitter/delivery_profile.rb +41 -0
  436. data/app/models/spree/stock_item.rb +3 -147
  437. data/app/models/spree/stock_level/custom_events.rb +48 -0
  438. data/app/models/spree/stock_level.rb +305 -0
  439. data/app/models/spree/stock_location.rb +165 -87
  440. data/app/models/spree/stock_movement/custom_events.rb +1 -1
  441. data/app/models/spree/stock_movement.rb +166 -22
  442. data/app/models/spree/stock_receipt.rb +61 -0
  443. data/app/models/spree/stock_receipt_item.rb +35 -0
  444. data/app/models/spree/stock_reservation.rb +114 -11
  445. data/app/models/spree/stock_transfer.rb +84 -59
  446. data/app/models/spree/stock_transfer_item.rb +25 -0
  447. data/app/models/spree/store.rb +431 -107
  448. data/app/models/spree/store_credit.rb +88 -21
  449. data/app/models/spree/store_credit_category.rb +21 -28
  450. data/app/models/spree/store_credit_event.rb +1 -1
  451. data/app/models/spree/store_credit_type.rb +10 -2
  452. data/app/models/spree/store_payment_method.rb +0 -1
  453. data/app/models/spree/store_product.rb +2 -2
  454. data/app/models/spree/store_promotion.rb +0 -1
  455. data/app/models/spree/storefront/access_denied.rb +22 -0
  456. data/app/models/spree/storefront/access_policy.rb +180 -0
  457. data/app/models/spree/subscriber.rb +6 -6
  458. data/app/models/spree/supplier.rb +76 -0
  459. data/app/models/spree/tax_category.rb +15 -5
  460. data/app/models/spree/tax_exemption/item_override.rb +22 -0
  461. data/app/models/spree/tax_exemption.rb +81 -0
  462. data/app/models/spree/tax_exemption_certificate.rb +95 -0
  463. data/app/models/spree/tax_identifier.rb +209 -0
  464. data/app/models/spree/tax_identifiers/validation_result.rb +41 -0
  465. data/app/models/spree/tax_identifiers/validator/base.rb +53 -0
  466. data/app/models/spree/tax_identifiers/validator/eu_vat.rb +76 -0
  467. data/app/models/spree/tax_line.rb +79 -0
  468. data/app/models/spree/tax_provider/base.rb +150 -0
  469. data/app/models/spree/tax_provider/internal.rb +227 -0
  470. data/app/models/spree/tax_rate.rb +63 -100
  471. data/app/models/spree/taxon.rb +13 -514
  472. data/app/models/spree/taxon_rule.rb +8 -9
  473. data/app/models/spree/taxonomy.rb +8 -42
  474. data/app/models/spree/user_identity.rb +2 -2
  475. data/app/models/spree/variant.rb +629 -150
  476. data/app/models/spree/variant_media.rb +6 -7
  477. data/app/models/spree/webhook_endpoint.rb +2 -1
  478. data/app/models/spree/wished_item.rb +3 -31
  479. data/app/models/spree/wishlist.rb +17 -10
  480. data/app/models/spree/wishlist_item/custom_events.rb +42 -0
  481. data/app/models/spree/wishlist_item.rb +37 -0
  482. data/app/models/spree/zone.rb +24 -182
  483. data/app/models/spree/zone_member.rb +5 -8
  484. data/app/presenters/spree/csv/custom_fields_helper.rb +25 -0
  485. data/app/presenters/spree/csv/customer_presenter.rb +7 -6
  486. data/app/presenters/spree/csv/gift_card_presenter.rb +9 -8
  487. data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +7 -6
  488. data/app/presenters/spree/csv/order_line_item_presenter.rb +53 -21
  489. data/app/presenters/spree/csv/price_list_price_presenter.rb +41 -0
  490. data/app/presenters/spree/csv/product_variant_presenter.rb +22 -21
  491. data/app/presenters/spree/csv/purchase_order_item_presenter.rb +50 -0
  492. data/app/presenters/spree/data_feeds/google_presenter.rb +24 -10
  493. data/app/presenters/spree/filters/options_presenter.rb +2 -2
  494. data/app/services/spree/addresses/create.rb +70 -27
  495. data/app/services/spree/addresses/helper.rb +17 -54
  496. data/app/services/spree/addresses/update.rb +53 -31
  497. data/app/services/spree/carts/associate.rb +46 -0
  498. data/app/services/spree/carts/check_availability.rb +72 -0
  499. data/app/services/spree/carts/create.rb +7 -1
  500. data/app/services/spree/carts/destroy.rb +71 -0
  501. data/app/services/spree/carts/empty.rb +42 -0
  502. data/app/services/spree/carts/partition_by_seller.rb +71 -0
  503. data/app/services/spree/carts/price_items.rb +119 -0
  504. data/app/services/spree/carts/remove_item.rb +42 -0
  505. data/app/services/spree/carts/remove_line_item.rb +42 -0
  506. data/app/services/spree/carts/remove_out_of_stock_items.rb +104 -0
  507. data/app/services/spree/carts/set_quantity.rb +39 -0
  508. data/app/services/spree/carts/split_by_seller.rb +475 -0
  509. data/app/services/spree/carts/update.rb +157 -51
  510. data/app/services/spree/catalogs/resolve_prices.rb +192 -0
  511. data/app/services/spree/catalogs/resolve_quantity_rules.rb +115 -0
  512. data/app/services/spree/catalogs/set_assignments.rb +59 -0
  513. data/app/services/spree/catalogs/set_order_minimums.rb +44 -0
  514. data/app/services/spree/catalogs/set_product_terms.rb +83 -0
  515. data/app/services/spree/categories/add_products.rb +51 -0
  516. data/app/services/spree/categories/remove_products.rb +72 -0
  517. data/app/services/spree/checkout/add_store_credit.rb +11 -79
  518. data/app/services/spree/checkout/advance.rb +38 -38
  519. data/app/services/spree/checkout/remove_store_credit.rb +7 -12
  520. data/app/services/spree/collections/add_products.rb +42 -0
  521. data/app/services/spree/collections/regenerate_products.rb +67 -0
  522. data/app/services/spree/collections/remove_products.rb +65 -0
  523. data/app/services/spree/commissions/calculate_line.rb +132 -0
  524. data/app/services/spree/commissions/commission_order.rb +185 -0
  525. data/app/services/spree/commissions/resolve_rate.rb +114 -0
  526. data/app/services/spree/commissions/resolve_tax_rate.rb +78 -0
  527. data/app/services/spree/companies/add_member.rb +38 -0
  528. data/app/services/spree/company_invitations/accept.rb +60 -0
  529. data/app/services/spree/coupon_codes/coupon_codes_handler.rb +14 -2
  530. data/app/services/spree/customers/data_export.rb +372 -0
  531. data/app/services/spree/data_requests/create.rb +77 -0
  532. data/app/services/spree/deliveries/create.rb +56 -0
  533. data/app/services/spree/deliveries/destroy.rb +39 -0
  534. data/app/services/spree/deliveries/upsert_primary.rb +43 -0
  535. data/app/services/spree/fulfillments/recalculate_delivery.rb +62 -0
  536. data/app/services/spree/fulfillments/stand_down_provider.rb +47 -0
  537. data/app/services/spree/imports/row_processors/customer.rb +8 -8
  538. data/app/services/spree/imports/row_processors/price_list_price.rb +164 -0
  539. data/app/services/spree/imports/row_processors/product_variant.rb +193 -57
  540. data/app/services/spree/imports/row_processors/purchase_order.rb +157 -0
  541. data/app/services/spree/media/destroy.rb +88 -0
  542. data/app/services/spree/media/usage.rb +175 -0
  543. data/app/services/spree/newsletter/link_user.rb +4 -3
  544. data/app/services/spree/newsletter/subscribe.rb +3 -3
  545. data/app/services/spree/newsletter/verify.rb +6 -2
  546. data/app/services/spree/order_groups/allocate_payments.rb +101 -0
  547. data/app/services/spree/orders/approve.rb +11 -24
  548. data/app/services/spree/orders/build_fulfillments.rb +29 -0
  549. data/app/services/spree/orders/create.rb +38 -17
  550. data/app/services/spree/orders/create_from_cart.rb +88 -0
  551. data/app/services/spree/orders/create_user_account.rb +14 -67
  552. data/app/services/spree/orders/discounts/create.rb +84 -0
  553. data/app/services/spree/orders/discounts/destroy.rb +25 -0
  554. data/app/services/spree/orders/discounts/update.rb +29 -0
  555. data/app/services/spree/orders/fees/create.rb +28 -0
  556. data/app/services/spree/orders/fees/destroy.rb +22 -0
  557. data/app/services/spree/orders/fees/update.rb +25 -0
  558. data/app/services/spree/orders/remove_item.rb +13 -0
  559. data/app/services/spree/orders/remove_line_item.rb +13 -0
  560. data/app/services/spree/orders/update.rb +56 -11
  561. data/app/services/spree/orders/update_item.rb +105 -0
  562. data/app/services/spree/orders/update_statuses.rb +109 -0
  563. data/app/services/spree/payments/create.rb +31 -5
  564. data/app/services/spree/prices/bulk_upsert.rb +156 -30
  565. data/app/services/spree/product_submissions/close.rb +46 -0
  566. data/app/services/spree/product_types/apply_to_products.rb +167 -0
  567. data/app/services/spree/products/auto_match_collections.rb +75 -0
  568. data/app/services/spree/products/auto_match_taxons.rb +7 -54
  569. data/app/services/spree/products/duplicator.rb +16 -22
  570. data/app/services/spree/products/for_context.rb +54 -0
  571. data/app/services/spree/products/select_buy_box.rb +98 -0
  572. data/app/services/spree/purchases/amount_due_at_checkout.rb +33 -0
  573. data/app/services/spree/returns/eligibility_validator.rb +53 -0
  574. data/app/services/spree/sample_data/helper.rb +3 -3
  575. data/app/services/spree/sample_data/import_builder.rb +8 -4
  576. data/app/services/spree/sample_data/import_runner.rb +7 -4
  577. data/app/services/spree/sample_data/loader.rb +15 -6
  578. data/app/services/spree/search_provider/custom_field_schema.rb +153 -0
  579. data/app/services/spree/seeds/admin_user.rb +72 -12
  580. data/app/services/spree/seeds/all.rb +24 -13
  581. data/app/services/spree/seeds/commission_rates.rb +53 -0
  582. data/app/services/spree/seeds/digital_delivery.rb +18 -8
  583. data/app/services/spree/seeds/product_types.rb +23 -0
  584. data/app/services/spree/seeds/returns_environment.rb +44 -16
  585. data/app/services/spree/seeds/roles.rb +6 -1
  586. data/app/services/spree/seeds/saved_reports.rb +107 -0
  587. data/app/services/spree/seeds/seller_requirements.rb +19 -0
  588. data/app/services/spree/seeds/stores.rb +1 -1
  589. data/app/services/spree/seeds/tax_categories.rb +9 -2
  590. data/app/services/spree/sellers/requirements.rb +129 -0
  591. data/app/services/spree/shipments/update.rb +4 -33
  592. data/app/services/spree/shipping_labels/confirm_refund.rb +47 -0
  593. data/app/services/spree/shipping_labels/store_file.rb +68 -0
  594. data/app/services/spree/stock_levels/bulk_upsert.rb +67 -0
  595. data/app/services/spree/stock_levels/correct.rb +69 -0
  596. data/app/services/spree/stock_levels/recount.rb +103 -0
  597. data/app/services/spree/stock_locations/stock_items/create.rb +8 -13
  598. data/app/services/spree/stock_locations/stock_levels/create.rb +25 -0
  599. data/app/services/spree/stock_reservations/extend.rb +9 -5
  600. data/app/services/spree/stock_reservations/release.rb +4 -3
  601. data/app/services/spree/stock_reservations/reserve.rb +43 -32
  602. data/app/services/spree/store_credits/apply.rb +93 -0
  603. data/app/services/spree/store_credits/remove.rb +17 -0
  604. data/app/services/spree/stores/provision_defaults.rb +269 -0
  605. data/app/services/spree/tax/resolve_exemptions.rb +44 -0
  606. data/app/services/spree/tax/unusable_exemption_error.rb +8 -0
  607. data/app/services/spree/tax_identifiers/validate.rb +77 -0
  608. data/app/services/spree/taxons/add_products.rb +6 -39
  609. data/app/services/spree/taxons/regenerate_products.rb +8 -53
  610. data/app/services/spree/taxons/remove_products.rb +6 -60
  611. data/app/services/spree/variants/remove_line_items.rb +6 -1
  612. data/app/subscribers/spree/event_log_subscriber.rb +1 -1
  613. data/app/subscribers/spree/order_commission_subscriber.rb +23 -0
  614. data/app/subscribers/spree/order_placed_subscriber.rb +58 -0
  615. data/app/subscribers/spree/order_status_subscriber.rb +82 -0
  616. data/app/subscribers/spree/payment_split_subscriber.rb +71 -0
  617. data/app/subscribers/spree/product_metrics_subscriber.rb +2 -2
  618. data/app/subscribers/spree/seller_onboarding_subscriber.rb +32 -0
  619. data/app/subscribers/spree/seller_transfer_reversal_subscriber.rb +26 -0
  620. data/app/subscribers/spree/seller_transfer_subscriber.rb +26 -0
  621. data/app/subscribers/spree/seller_user_email_subscriber.rb +27 -0
  622. data/app/subscribers/spree/tax_identifier_validation_subscriber.rb +26 -0
  623. data/app/validators/spree/addresses/phone_validator.rb +2 -2
  624. data/app/validators/spree/attachment_content_type_validator.rb +183 -0
  625. data/app/validators/spree/password_length_validator.rb +23 -0
  626. data/app/views/spree/seller_user_mailer/password_reset_email.html.erb +8 -0
  627. data/app/views/spree/shared/_purchased_items_styles.html.erb +7 -0
  628. data/app/workflows/spree/carts/add_item.rb +234 -0
  629. data/app/workflows/spree/carts/complete.rb +516 -0
  630. data/app/workflows/spree/carts/merge.rb +90 -0
  631. data/app/workflows/spree/carts/recalculate.rb +54 -0
  632. data/app/workflows/spree/carts/recalculate_totals.rb +233 -0
  633. data/app/workflows/spree/carts/upsert_items.rb +368 -0
  634. data/app/workflows/spree/catalogs/activate.rb +51 -0
  635. data/app/workflows/spree/catalogs/create.rb +83 -0
  636. data/app/workflows/spree/catalogs/deactivate.rb +35 -0
  637. data/app/workflows/spree/catalogs/set_price_list.rb +130 -0
  638. data/app/workflows/spree/catalogs/update.rb +85 -0
  639. data/app/workflows/spree/claims/approve.rb +38 -0
  640. data/app/workflows/spree/claims/cancel.rb +37 -0
  641. data/app/workflows/spree/claims/create.rb +82 -0
  642. data/app/workflows/spree/claims/deny.rb +35 -0
  643. data/app/workflows/spree/claims/resolve.rb +166 -0
  644. data/app/workflows/spree/customers/anonymize.rb +502 -0
  645. data/app/workflows/spree/customers/create.rb +247 -0
  646. data/app/workflows/spree/data_requests/fulfill.rb +165 -0
  647. data/app/workflows/spree/deliveries/update_tracking.rb +96 -0
  648. data/app/workflows/spree/exchanges/approve.rb +38 -0
  649. data/app/workflows/spree/exchanges/cancel.rb +40 -0
  650. data/app/workflows/spree/exchanges/create.rb +90 -0
  651. data/app/workflows/spree/exchanges/fulfill.rb +146 -0
  652. data/app/workflows/spree/exchanges/receive.rb +86 -0
  653. data/app/workflows/spree/fulfillments/cancel.rb +87 -0
  654. data/app/workflows/spree/fulfillments/create.rb +346 -0
  655. data/app/workflows/spree/fulfillments/fulfill.rb +354 -0
  656. data/app/workflows/spree/fulfillments/mark_delivered.rb +67 -0
  657. data/app/workflows/spree/fulfillments/purchase_label.rb +40 -0
  658. data/app/workflows/spree/fulfillments/update.rb +123 -0
  659. data/app/workflows/spree/gift_cards/apply.rb +235 -0
  660. data/app/workflows/spree/gift_cards/cancel.rb +43 -0
  661. data/app/workflows/spree/gift_cards/redeem.rb +59 -0
  662. data/app/workflows/spree/gift_cards/remove.rb +78 -0
  663. data/app/workflows/spree/imports/complete.rb +45 -0
  664. data/app/workflows/spree/imports/complete_mapping.rb +50 -0
  665. data/app/workflows/spree/imports/retry_failed_rows.rb +46 -0
  666. data/app/workflows/spree/imports/start_mapping.rb +48 -0
  667. data/app/workflows/spree/imports/start_processing.rb +36 -0
  668. data/app/workflows/spree/invitations/accept.rb +50 -0
  669. data/app/workflows/spree/orders/add_item.rb +12 -0
  670. data/app/workflows/spree/orders/cancel.rb +261 -0
  671. data/app/workflows/spree/orders/complete.rb +169 -0
  672. data/app/workflows/spree/orders/recalculate.rb +11 -0
  673. data/app/workflows/spree/orders/recalculate_totals.rb +12 -0
  674. data/app/workflows/spree/orders/upsert_items.rb +28 -0
  675. data/app/workflows/spree/payment_sessions/complete.rb +44 -0
  676. data/app/workflows/spree/payments/capture.rb +109 -0
  677. data/app/workflows/spree/payments/handle_webhook.rb +100 -0
  678. data/app/workflows/spree/payments/process.rb +73 -0
  679. data/app/workflows/spree/payments/void.rb +72 -0
  680. data/app/workflows/spree/price_lists/activate.rb +49 -0
  681. data/app/workflows/spree/price_lists/create.rb +57 -0
  682. data/app/workflows/spree/price_lists/deactivate.rb +30 -0
  683. data/app/workflows/spree/price_lists/update.rb +117 -0
  684. data/app/workflows/spree/products/activate.rb +33 -0
  685. data/app/workflows/spree/products/approve.rb +52 -0
  686. data/app/workflows/spree/products/archive.rb +44 -0
  687. data/app/workflows/spree/products/create.rb +82 -0
  688. data/app/workflows/spree/products/destroy.rb +35 -0
  689. data/app/workflows/spree/products/draft.rb +49 -0
  690. data/app/workflows/spree/products/nested_attributes.rb +242 -0
  691. data/app/workflows/spree/products/propose.rb +62 -0
  692. data/app/workflows/spree/products/reject.rb +54 -0
  693. data/app/workflows/spree/products/update.rb +83 -0
  694. data/app/workflows/spree/purchase_orders/cancel.rb +57 -0
  695. data/app/workflows/spree/purchase_orders/close.rb +57 -0
  696. data/app/workflows/spree/purchase_orders/create.rb +68 -0
  697. data/app/workflows/spree/purchase_orders/mark_draft.rb +49 -0
  698. data/app/workflows/spree/purchase_orders/mark_ordered.rb +50 -0
  699. data/app/workflows/spree/purchase_orders/receive.rb +65 -0
  700. data/app/workflows/spree/purchase_orders/update.rb +56 -0
  701. data/app/workflows/spree/receivables/incoming_counter.rb +50 -0
  702. data/app/workflows/spree/refunds/create.rb +136 -0
  703. data/app/workflows/spree/refunds/order_payments.rb +129 -0
  704. data/app/workflows/spree/returns/approve.rb +47 -0
  705. data/app/workflows/spree/returns/cancel.rb +52 -0
  706. data/app/workflows/spree/returns/create.rb +135 -0
  707. data/app/workflows/spree/returns/purchase_label.rb +32 -0
  708. data/app/workflows/spree/returns/receive.rb +94 -0
  709. data/app/workflows/spree/returns/refund.rb +134 -0
  710. data/app/workflows/spree/seller_payouts/complete.rb +80 -0
  711. data/app/workflows/spree/seller_payouts/sweep.rb +192 -0
  712. data/app/workflows/spree/seller_requirement_submissions/accept.rb +40 -0
  713. data/app/workflows/spree/seller_requirement_submissions/create.rb +104 -0
  714. data/app/workflows/spree/seller_requirement_submissions/reject.rb +45 -0
  715. data/app/workflows/spree/seller_requirement_submissions/reviewable.rb +19 -0
  716. data/app/workflows/spree/seller_requirement_submissions/waive.rb +64 -0
  717. data/app/workflows/spree/seller_transfers/create.rb +131 -0
  718. data/app/workflows/spree/seller_transfers/reverse.rb +241 -0
  719. data/app/workflows/spree/sellers/approve.rb +88 -0
  720. data/app/workflows/spree/sellers/create.rb +72 -0
  721. data/app/workflows/spree/sellers/invite.rb +61 -0
  722. data/app/workflows/spree/sellers/reject.rb +52 -0
  723. data/app/workflows/spree/sellers/reopen_onboarding.rb +55 -0
  724. data/app/workflows/spree/sellers/start_onboarding.rb +46 -0
  725. data/app/workflows/spree/sellers/submit_for_review.rb +76 -0
  726. data/app/workflows/spree/sellers/suspend.rb +55 -0
  727. data/app/workflows/spree/shipping_labels/purchase.rb +154 -0
  728. data/app/workflows/spree/shipping_labels/record.rb +102 -0
  729. data/app/workflows/spree/shipping_labels/refund.rb +77 -0
  730. data/app/workflows/spree/stock_receipts/recording.rb +165 -0
  731. data/app/workflows/spree/stock_transfers/cancel.rb +103 -0
  732. data/app/workflows/spree/stock_transfers/close.rb +56 -0
  733. data/app/workflows/spree/stock_transfers/create.rb +63 -0
  734. data/app/workflows/spree/stock_transfers/mark_draft.rb +41 -0
  735. data/app/workflows/spree/stock_transfers/mark_in_transit.rb +91 -0
  736. data/app/workflows/spree/stock_transfers/mark_ready.rb +36 -0
  737. data/app/workflows/spree/stock_transfers/receive.rb +62 -0
  738. data/app/workflows/spree/stock_transfers/update.rb +55 -0
  739. data/app/workflows/spree/tax_exemption_certificates/verify.rb +48 -0
  740. data/app/workflows/spree/variants/create.rb +66 -0
  741. data/app/workflows/spree/variants/nested_attributes.rb +90 -0
  742. data/app/workflows/spree/variants/update.rb +55 -0
  743. data/config/brakeman.ignore +79 -79
  744. data/config/i18n-tasks.yml +4 -3
  745. data/config/initializers/mobility.rb +0 -4
  746. data/config/initializers/spree_store_setup_tasks.rb +4 -0
  747. data/config/locales/en.yml +952 -189
  748. data/config/routes.rb +11 -4
  749. data/db/migrate/20220201103922_add_first_name_and_last_name_to_spree_users.rb +7 -5
  750. data/db/migrate/20221215151408_add_selected_locale_to_spree_users.rb +6 -4
  751. data/db/migrate/20250127151258_add_phone_to_spree_users.rb +3 -1
  752. data/db/migrate/20250207084000_add_accepts_email_marketing_field_to_spree_users_table.rb +4 -2
  753. data/db/migrate/20250410061306_create_spree_invitations.rb +1 -1
  754. data/db/migrate/20250605131334_add_missing_fields_to_users.rb +24 -20
  755. data/db/migrate/20260315100000_add_product_media_support.rb +4 -1
  756. data/db/migrate/20260613000001_add_store_id_to_spree_role_users.rb +6 -3
  757. data/db/migrate/20260630000001_add_default_variant_id_to_spree_products.rb +9 -0
  758. data/db/migrate/20260708160054_create_spree_collections.rb +69 -0
  759. data/db/migrate/20260710000001_rename_taxons_to_categories.rb +28 -0
  760. data/db/migrate/20260727000001_add_unique_country_abbr_index_to_spree_states.rb +27 -4
  761. data/db/migrate/20260728000000_create_spree_customers_and_admin_users.rb +59 -0
  762. data/db/migrate/20260728000001_rename_prototypes_to_product_types.rb +59 -0
  763. data/db/migrate/20260728000002_create_spree_delivery_zones.rb +29 -0
  764. data/db/migrate/20260728000003_create_spree_carts.rb +58 -0
  765. data/db/migrate/20260728000003_rename_user_id_to_customer_id.rb +30 -0
  766. data/db/migrate/20260728000004_rename_shipping_to_fulfillment_and_delivery.rb +83 -0
  767. data/db/migrate/20260728000005_add_fulfillment_type_columns.rb +33 -0
  768. data/db/migrate/20260729000001_create_typed_adjustment_tables.rb +87 -0
  769. data/db/migrate/20260729100001_add_checkout_columns_to_spree_carts.rb +15 -0
  770. data/db/migrate/20260729100002_relax_owner_null_constraints.rb +7 -0
  771. data/db/migrate/20260729120001_rename_order_totals_to_standard_names.rb +7 -0
  772. data/db/migrate/20260729130001_create_spree_delivery_method_rules.rb +19 -0
  773. data/db/migrate/20260729140001_add_coupon_code_to_spree_orders.rb +6 -0
  774. data/db/migrate/20260729150001_add_store_to_delivery_methods_and_stock_locations.rb +6 -0
  775. data/db/migrate/20260802190001_create_spree_returns.rb +49 -0
  776. data/db/migrate/20260802210001_create_spree_exchanges_and_claims.rb +80 -0
  777. data/db/migrate/20260805100001_rename_return_authorization_reasons_to_return_reasons.rb +109 -0
  778. data/db/migrate/20260806000001_add_store_id_to_spree_tax_rates_and_categories.rb +16 -0
  779. data/db/migrate/20260806000001_create_spree_delivery_method_rule_products.rb +22 -0
  780. data/db/migrate/20260806000002_add_treatment_columns_to_spree_tax_lines.rb +19 -0
  781. data/db/migrate/20260806000003_create_spree_tax_identifiers.rb +41 -0
  782. data/db/migrate/20260806000004_add_tax_provider_to_spree_markets.rb +9 -0
  783. data/db/migrate/20260806000005_add_country_and_state_to_spree_tax_rates.rb +20 -0
  784. data/db/migrate/20260806100001_create_spree_product_type_custom_field_definitions.rb +17 -0
  785. data/db/migrate/20260806120001_add_rate_provider_to_delivery_methods.rb +7 -0
  786. data/db/migrate/20260806120002_add_carrier_columns_to_delivery_rates.rb +13 -0
  787. data/db/migrate/20260806130001_add_unique_store_type_index_to_spree_integrations.rb +9 -0
  788. data/db/migrate/20260807000001_add_permissions_to_spree_roles.rb +14 -0
  789. data/db/migrate/20260807000001_create_spree_companies.rb +66 -0
  790. data/db/migrate/20260807000003_create_spree_tax_exemption_certificates.rb +31 -0
  791. data/db/migrate/20260807120001_remove_claim_type_from_spree_claims.rb +5 -0
  792. data/db/migrate/20260807130001_add_country_state_codes_to_consumers.rb +28 -0
  793. data/db/migrate/20260807150001_reindex_spree_categories_by_store.rb +42 -0
  794. data/db/migrate/20260809000001_rename_metafields_to_custom_fields.rb +126 -0
  795. data/db/migrate/20260809100001_support_multi_rate_delivery_methods.rb +35 -0
  796. data/db/migrate/20260809120001_consolidate_metadata_columns.rb +120 -0
  797. data/db/migrate/20260809150001_promote_shipping_categories_to_delivery_profiles.rb +153 -0
  798. data/db/migrate/20260809170001_create_spree_channel_stock_locations.rb +17 -0
  799. data/db/migrate/20260809200001_add_rich_text_columns_for_action_text_removal.rb +11 -0
  800. data/db/migrate/20260810000001_rename_digitals_to_digital_assets.rb +38 -0
  801. data/db/migrate/20260811000001_add_delivery_tracking_to_fulfillments.rb +17 -0
  802. data/db/migrate/20260812000001_add_provider_type_to_spree_digital_assets.rb +7 -0
  803. data/db/migrate/20260812000002_add_metadata_to_spree_digital_assets.rb +11 -0
  804. data/db/migrate/20260813000001_create_spree_number_sequences.rb +13 -0
  805. data/db/migrate/20260813120001_add_setup_token_to_spree_stores.rb +11 -0
  806. data/db/migrate/20260813130001_add_capture_method_to_payment_methods.rb +17 -0
  807. data/db/migrate/20260813130001_add_unique_indexes_to_spree_data_feeds.rb +10 -0
  808. data/db/migrate/20260814000001_add_customs_fields_to_spree_variants.rb +7 -0
  809. data/db/migrate/20260814000001_rename_stock_items_to_stock_levels.rb +72 -0
  810. data/db/migrate/20260814000002_add_typed_stock_movements.rb +25 -0
  811. data/db/migrate/20260815000001_add_audience_to_spree_refresh_tokens.rb +14 -0
  812. data/db/migrate/20260815000002_add_resource_to_spree_roles.rb +190 -0
  813. data/db/migrate/20260816000001_create_spree_sellers.rb +59 -0
  814. data/db/migrate/20260816000002_add_seller_to_spree_products.rb +10 -0
  815. data/db/migrate/20260816210001_create_spree_commission_rates.rb +229 -0
  816. data/db/migrate/20260816210002_create_spree_commission_lines.rb +72 -0
  817. data/db/migrate/20260817000001_add_seller_to_spree_line_items.rb +15 -0
  818. data/db/migrate/20260817000002_add_seller_and_delivery_profile_to_spree_variants.rb +17 -0
  819. data/db/migrate/20260818000001_create_spree_external_references.rb +33 -0
  820. data/db/migrate/20260818000001_create_spree_seller_requirements.rb +60 -0
  821. data/db/migrate/20260818000001_rename_spree_payments_state_to_status.rb +5 -0
  822. data/db/migrate/20260818000002_add_price_source_to_spree_line_items.rb +9 -0
  823. data/db/migrate/20260818090001_rename_assets_to_media.rb +10 -0
  824. data/db/migrate/20260819000001_create_spree_seller_requirement_custom_fields.rb +15 -0
  825. data/db/migrate/20260819000001_replace_payment_method_display_on_with_storefront_visible.rb +25 -0
  826. data/db/migrate/20260819000002_rename_spree_gift_cards_state_to_status.rb +5 -0
  827. data/db/migrate/20260820000001_add_seller_business_identity_and_stock_locations.rb +17 -0
  828. data/db/migrate/20260820000001_create_spree_order_groups.rb +33 -0
  829. data/db/migrate/20260820000002_add_order_group_to_spree_orders_and_payments.rb +14 -0
  830. data/db/migrate/20260820000003_create_spree_payment_splits.rb +21 -0
  831. data/db/migrate/20260820000004_add_order_to_spree_refunds.rb +32 -0
  832. data/db/migrate/20260821000001_add_claimed_amount_to_spree_payment_splits.rb +14 -0
  833. data/db/migrate/20260822000001_create_spree_seller_transfers.rb +94 -0
  834. data/db/migrate/20260822000002_create_spree_seller_payouts.rb +48 -0
  835. data/db/migrate/20260822000003_add_payout_account_to_spree_sellers.rb +11 -0
  836. data/db/migrate/20260824000001_add_store_to_spree_media.rb +16 -0
  837. data/db/migrate/20260825000001_create_spree_catalogs.rb +40 -0
  838. data/db/migrate/20260826000001_add_owner_to_spree_addresses.rb +30 -0
  839. data/db/migrate/20260826000002_create_spree_product_submissions.rb +28 -0
  840. data/db/migrate/20260827000001_add_seller_to_spree_exports.rb +5 -0
  841. data/db/migrate/20260828100001_unify_import_and_export_tenancy.rb +79 -0
  842. data/db/migrate/20260830000001_add_catalog_to_spree_price_lists.rb +47 -0
  843. data/db/migrate/20260830000002_add_po_number_to_purchases.rb +16 -0
  844. data/db/migrate/20260830000002_add_price_adjustment_to_spree_price_lists.rb +14 -0
  845. data/db/migrate/20260831000001_add_description_to_spree_catalogs.rb +7 -0
  846. data/db/migrate/20260831000002_add_quantity_rules_to_spree_variants.rb +18 -0
  847. data/db/migrate/20260831000003_create_spree_catalog_terms.rb +35 -0
  848. data/db/migrate/20260831100001_create_spree_package_types.rb +38 -0
  849. data/db/migrate/20260831100002_add_carton_details_to_spree_variants.rb +10 -0
  850. data/db/migrate/20260831100003_add_unpriced_to_spree_delivery_rates.rb +8 -0
  851. data/db/migrate/20260901000001_add_seller_to_spree_delivery_methods.rb +11 -0
  852. data/db/migrate/20260902000001_add_store_and_filter_key_to_custom_field_definitions.rb +143 -0
  853. data/db/migrate/20260902000002_add_eu_legal_compliance.rb +68 -0
  854. data/db/migrate/20260902000002_replace_order_cancellation_tables_with_cancel_columns.rb +88 -0
  855. data/db/migrate/20260902100001_rename_wished_items_to_wishlist_items.rb +8 -0
  856. data/db/migrate/20260902100002_rename_promo_total_to_discount_total.rb +9 -0
  857. data/db/migrate/20260902100003_rename_address_name_and_postal_code_columns.rb +24 -0
  858. data/db/migrate/20260902100004_rename_option_presentation_to_label.rb +13 -0
  859. data/db/migrate/20260903000001_create_spree_shipping_labels_and_deliveries.rb +73 -0
  860. data/db/migrate/20260903120000_create_spree_saved_reports.rb +57 -0
  861. data/db/migrate/20260904000001_add_returns_enabled_to_stock_locations.rb +14 -0
  862. data/db/migrate/20260906000001_add_commission_total_to_orders.rb +22 -0
  863. data/db/migrate/20260907000001_add_inventory_operations.rb +113 -0
  864. data/db/migrate/20260907000001_add_min_quantity_to_spree_prices.rb +28 -0
  865. data/db/migrate/20260907000002_create_spree_price_adjustment_tiers.rb +19 -0
  866. data/db/migrate/20260908000001_add_seller_to_spree_package_types.rb +78 -0
  867. data/db/migrate/20260910000001_add_stock_receipts.rb +44 -0
  868. data/db/migrate/20260910000002_add_counters_to_spree_stock_levels.rb +6 -0
  869. data/db/migrate/20260910120000_index_reporting_time_columns.rb +12 -0
  870. data/db/migrate/20260912120000_index_reporting_lifetime_and_promotion_columns.rb +19 -0
  871. data/db/migrate/20260914000001_add_settlement_to_spree_seller_transfers.rb +18 -0
  872. data/db/migrate/20260914230000_add_store_to_spree_option_types.rb +46 -0
  873. data/db/migrate/20260916000001_order_operation_actors_polymorphic.rb +30 -0
  874. data/db/sample_data/collections.rb +58 -0
  875. data/db/sample_data/custom_field_definitions.rb +9 -0
  876. data/db/sample_data/digital_products.rb +26 -0
  877. data/db/sample_data/fulfillment.rb +88 -0
  878. data/db/sample_data/options.rb +25 -1
  879. data/db/sample_data/orders.rb +53 -30
  880. data/db/sample_data/payment_methods.rb +2 -2
  881. data/db/sample_data/price_list_prices.csv +25 -0
  882. data/db/sample_data/product_type_categories.rb +27 -0
  883. data/db/sample_data/product_types.rb +83 -0
  884. data/db/sample_data/products.csv +39 -38
  885. data/db/sample_data/wholesale.rb +42 -3
  886. data/lib/generators/spree/api_resource/api_resource_generator.rb +64 -6
  887. data/lib/generators/spree/api_resource/templates/admin_controller.rb.tt +5 -0
  888. data/lib/generators/spree/api_resource/templates/admin_controller_spec.rb.tt +3 -0
  889. data/lib/generators/spree/api_resource/templates/store_controller_spec.rb.tt +4 -1
  890. data/lib/generators/spree/authentication/dummy/templates/create_spree_admin_users.rb.tt +1 -0
  891. data/lib/generators/spree/controller_decorator/controller_decorator_generator.rb +4 -4
  892. data/lib/generators/spree/dummy/dummy_generator.rb +5 -12
  893. data/lib/generators/spree/dummy/templates/rails/test.rb +5 -0
  894. data/lib/generators/spree/model/model_generator.rb +46 -7
  895. data/lib/generators/spree/model/templates/create_table_migration.rb.tt +9 -0
  896. data/lib/generators/spree/model/templates/model.rb.tt +17 -7
  897. data/lib/spree/category_permalink_deduplicator.rb +328 -0
  898. data/lib/spree/checkout/default_requirements.rb +134 -0
  899. data/lib/spree/checkout/registry.rb +164 -0
  900. data/lib/spree/checkout/requirement.rb +49 -0
  901. data/lib/spree/checkout/requirements.rb +61 -0
  902. data/lib/spree/checkout/step.rb +52 -0
  903. data/lib/spree/core/configuration.rb +112 -63
  904. data/lib/spree/core/controller_helpers/store.rb +8 -9
  905. data/lib/spree/core/dependencies.rb +324 -71
  906. data/lib/spree/core/dependencies_helper.rb +4 -0
  907. data/lib/spree/core/engine.rb +360 -75
  908. data/lib/spree/core/number_generator.rb +37 -33
  909. data/lib/spree/core/permission_configuration.rb +183 -91
  910. data/lib/spree/core/permissions/default_catalog.rb +236 -0
  911. data/lib/spree/core/permissions/entry.rb +15 -0
  912. data/lib/spree/core/permissions/scope.rb +76 -0
  913. data/lib/spree/core/preferences/masking.rb +24 -2
  914. data/lib/spree/core/preferences/preferable.rb +52 -2
  915. data/lib/spree/core/preferences/preferable_class_methods.rb +41 -2
  916. data/lib/spree/core/preferences/runtime_configuration.rb +142 -9
  917. data/lib/spree/core/previews/admin_user_preview.rb +0 -8
  918. data/lib/spree/core/previews/invitation_preview.rb +1 -1
  919. data/lib/spree/core/previews/preview_data.rb +2 -2
  920. data/lib/spree/core/previews/seller_user_preview.rb +14 -0
  921. data/lib/spree/core/pricing/context.rb +28 -8
  922. data/lib/spree/core/pricing/price_resolution.rb +150 -0
  923. data/lib/spree/core/pricing/resolver.rb +17 -107
  924. data/lib/spree/core/ransack_configuration.rb +3 -3
  925. data/lib/spree/core/routes.rb +4 -6
  926. data/lib/spree/core/version.rb +1 -1
  927. data/lib/spree/core.rb +446 -59
  928. data/lib/spree/events/adapters/base.rb +42 -15
  929. data/lib/spree/hooks.rb +146 -0
  930. data/lib/spree/iso_data.rb +188 -0
  931. data/lib/spree/measurement.rb +113 -0
  932. data/lib/spree/money.rb +70 -0
  933. data/lib/spree/number_generators/registry.rb +80 -0
  934. data/lib/spree/reporting/adapters/base.rb +71 -0
  935. data/lib/spree/reporting/adapters/live.rb +677 -0
  936. data/lib/spree/reporting/counter_result.rb +19 -0
  937. data/lib/spree/reporting/counters.rb +33 -0
  938. data/lib/spree/reporting/default_vocabulary.rb +705 -0
  939. data/lib/spree/reporting/hydration.rb +64 -0
  940. data/lib/spree/reporting/query.rb +487 -0
  941. data/lib/spree/reporting/registry.rb +294 -0
  942. data/lib/spree/reporting/result.rb +10 -0
  943. data/lib/spree/reporting/schema.rb +124 -0
  944. data/lib/spree/reporting.rb +49 -0
  945. data/lib/spree/service_module.rb +6 -0
  946. data/lib/spree/store_scope_guard.rb +180 -0
  947. data/lib/spree/testing_support/action_text.rb +33 -0
  948. data/lib/spree/testing_support/common_rake.rb +4 -66
  949. data/lib/spree/testing_support/country_pool.rb +50 -0
  950. data/lib/spree/testing_support/extension_rake.rb +2 -6
  951. data/lib/spree/testing_support/factories/address_factory.rb +45 -15
  952. data/lib/spree/testing_support/factories/api_key_factory.rb +1 -1
  953. data/lib/spree/testing_support/factories/calculator_factory.rb +0 -3
  954. data/lib/spree/testing_support/factories/cart_factory.rb +60 -0
  955. data/lib/spree/testing_support/factories/catalog_factory.rb +37 -0
  956. data/lib/spree/testing_support/factories/category_factory.rb +21 -0
  957. data/lib/spree/testing_support/factories/claim_factory.rb +33 -0
  958. data/lib/spree/testing_support/factories/classification_factory.rb +3 -2
  959. data/lib/spree/testing_support/factories/collection_factory.rb +25 -0
  960. data/lib/spree/testing_support/factories/collection_rule_factory.rb +47 -0
  961. data/lib/spree/testing_support/factories/commission_rate_factory.rb +75 -0
  962. data/lib/spree/testing_support/factories/company_factory.rb +31 -0
  963. data/lib/spree/testing_support/factories/consent_record_factory.rb +10 -0
  964. data/lib/spree/testing_support/factories/country_factory.rb +28 -9
  965. data/lib/spree/testing_support/factories/custom_field_definition_factory.rb +79 -0
  966. data/lib/spree/testing_support/factories/custom_field_factory.rb +64 -0
  967. data/lib/spree/testing_support/factories/customer_group_user_factory.rb +1 -1
  968. data/lib/spree/testing_support/factories/data_request_factory.rb +17 -0
  969. data/lib/spree/testing_support/factories/delivery_factory.rb +13 -0
  970. data/lib/spree/testing_support/factories/delivery_method_factory.rb +47 -0
  971. data/lib/spree/testing_support/factories/delivery_origin_group_factory.rb +5 -0
  972. data/lib/spree/testing_support/factories/delivery_profile_factory.rb +9 -0
  973. data/lib/spree/testing_support/factories/delivery_rate_factory.rb +7 -0
  974. data/lib/spree/testing_support/factories/delivery_zone_factory.rb +34 -0
  975. data/lib/spree/testing_support/factories/digital_asset_factory.rb +13 -0
  976. data/lib/spree/testing_support/factories/digital_link_factory.rb +1 -1
  977. data/lib/spree/testing_support/factories/discount_factory.rb +9 -0
  978. data/lib/spree/testing_support/factories/exchange_factory.rb +50 -0
  979. data/lib/spree/testing_support/factories/external_reference_factory.rb +8 -0
  980. data/lib/spree/testing_support/factories/fee_factory.rb +8 -0
  981. data/lib/spree/testing_support/factories/fulfillment_factory.rb +32 -0
  982. data/lib/spree/testing_support/factories/fulfillment_item_factory.rb +17 -0
  983. data/lib/spree/testing_support/factories/gateway_customer_factory.rb +1 -1
  984. data/lib/spree/testing_support/factories/gift_card_factory.rb +2 -2
  985. data/lib/spree/testing_support/factories/import_factory.rb +33 -1
  986. data/lib/spree/testing_support/factories/line_item_factory.rb +10 -5
  987. data/lib/spree/testing_support/factories/market_factory.rb +5 -12
  988. data/lib/spree/testing_support/factories/media_factory.rb +36 -0
  989. data/lib/spree/testing_support/factories/newsletter_subscriber_factory.rb +1 -1
  990. data/lib/spree/testing_support/factories/number_sequence_factory.rb +7 -0
  991. data/lib/spree/testing_support/factories/options_factory.rb +5 -5
  992. data/lib/spree/testing_support/factories/order_factory.rb +39 -23
  993. data/lib/spree/testing_support/factories/order_group_factory.rb +29 -0
  994. data/lib/spree/testing_support/factories/package_type_factory.rb +36 -0
  995. data/lib/spree/testing_support/factories/payment_factory.rb +7 -7
  996. data/lib/spree/testing_support/factories/payment_method_factory.rb +5 -5
  997. data/lib/spree/testing_support/factories/payment_session_factory.rb +10 -6
  998. data/lib/spree/testing_support/factories/payment_setup_session_factory.rb +2 -2
  999. data/lib/spree/testing_support/factories/payment_split_factory.rb +10 -0
  1000. data/lib/spree/testing_support/factories/price_adjustment_tier_factory.rb +7 -0
  1001. data/lib/spree/testing_support/factories/price_factory.rb +6 -0
  1002. data/lib/spree/testing_support/factories/price_rule_factory.rb +0 -10
  1003. data/lib/spree/testing_support/factories/product_collection_factory.rb +8 -0
  1004. data/lib/spree/testing_support/factories/product_factory.rb +35 -7
  1005. data/lib/spree/testing_support/factories/product_type_custom_field_definition_factory.rb +10 -0
  1006. data/lib/spree/testing_support/factories/product_type_factory.rb +12 -0
  1007. data/lib/spree/testing_support/factories/promotion_factory.rb +4 -0
  1008. data/lib/spree/testing_support/factories/promotion_rule_factory.rb +1 -1
  1009. data/lib/spree/testing_support/factories/purchase_order_factory.rb +49 -0
  1010. data/lib/spree/testing_support/factories/refresh_token_factory.rb +2 -0
  1011. data/lib/spree/testing_support/factories/refund_factory.rb +1 -2
  1012. data/lib/spree/testing_support/factories/return_factory.rb +52 -0
  1013. data/lib/spree/testing_support/factories/return_reason_factory.rb +19 -0
  1014. data/lib/spree/testing_support/factories/role_factory.rb +1 -0
  1015. data/lib/spree/testing_support/factories/saved_report_factory.rb +7 -0
  1016. data/lib/spree/testing_support/factories/seller_factory.rb +34 -0
  1017. data/lib/spree/testing_support/factories/seller_payout_factory.rb +14 -0
  1018. data/lib/spree/testing_support/factories/seller_requirement_factory.rb +61 -0
  1019. data/lib/spree/testing_support/factories/seller_requirement_submission_factory.rb +32 -0
  1020. data/lib/spree/testing_support/factories/seller_transfer_factory.rb +21 -0
  1021. data/lib/spree/testing_support/factories/shipping_label_factory.rb +44 -0
  1022. data/lib/spree/testing_support/factories/state_factory.rb +18 -5
  1023. data/lib/spree/testing_support/factories/stock_level_factory.rb +24 -0
  1024. data/lib/spree/testing_support/factories/stock_location_factory.rb +12 -6
  1025. data/lib/spree/testing_support/factories/stock_movement_factory.rb +20 -3
  1026. data/lib/spree/testing_support/factories/stock_receipt_factory.rb +37 -0
  1027. data/lib/spree/testing_support/factories/stock_reservation_factory.rb +11 -8
  1028. data/lib/spree/testing_support/factories/stock_transfer_factory.rb +53 -6
  1029. data/lib/spree/testing_support/factories/store_credit_factory.rb +1 -3
  1030. data/lib/spree/testing_support/factories/supplier_factory.rb +17 -0
  1031. data/lib/spree/testing_support/factories/tax_exemption_certificate_factory.rb +18 -0
  1032. data/lib/spree/testing_support/factories/tax_identifier_factory.rb +33 -0
  1033. data/lib/spree/testing_support/factories/tax_line_factory.rb +19 -0
  1034. data/lib/spree/testing_support/factories/tax_rate_factory.rb +5 -2
  1035. data/lib/spree/testing_support/factories/taxon_factory.rb +6 -23
  1036. data/lib/spree/testing_support/factories/taxonomy_factory.rb +8 -0
  1037. data/lib/spree/testing_support/factories/user_factory.rb +8 -13
  1038. data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
  1039. data/lib/spree/testing_support/factories/wishlist_factory.rb +1 -1
  1040. data/lib/spree/testing_support/factories/wishlist_item_factory.rb +10 -0
  1041. data/lib/spree/testing_support/factories/zone_factory.rb +2 -25
  1042. data/lib/spree/testing_support/factories/zone_member_factory.rb +2 -1
  1043. data/lib/spree/testing_support/factories.rb +1 -6
  1044. data/lib/spree/testing_support/imports.rb +16 -0
  1045. data/lib/spree/testing_support/label_provider.rb +49 -0
  1046. data/lib/spree/testing_support/lifecycle_events.rb +6 -1
  1047. data/lib/spree/testing_support/metadata.rb +38 -36
  1048. data/lib/spree/testing_support/order_walkthrough.rb +46 -36
  1049. data/lib/spree/testing_support/preferences.rb +39 -0
  1050. data/lib/spree/testing_support/purchase_order.rb +111 -0
  1051. data/lib/spree/testing_support/store.rb +6 -2
  1052. data/lib/spree/testing_support/tax_identifier_validators.rb +68 -0
  1053. data/lib/spree/translations.rb +139 -5
  1054. data/lib/spree/upgrades/5_4_to_5_5/manifest.yml +1 -1
  1055. data/lib/spree/upgrades/5_5_to_5_6/manifest.yml +17 -9
  1056. data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +683 -0
  1057. data/lib/spree/validators.rb +74 -0
  1058. data/lib/spree/workflow.rb +335 -0
  1059. data/lib/tasks/backfill_actor_types.rake +35 -0
  1060. data/lib/tasks/backfill_custom_field_definition_stores.rake +82 -0
  1061. data/lib/tasks/backfill_import_export_tenancy.rake +93 -0
  1062. data/lib/tasks/backfill_library_media_placements.rake +29 -0
  1063. data/lib/tasks/backfill_media_store_ids.rake +70 -0
  1064. data/lib/tasks/backfill_reason_store_ids.rake +28 -0
  1065. data/lib/tasks/capture_methods_migration.rake +30 -0
  1066. data/lib/tasks/carts_migration.rake +113 -0
  1067. data/lib/tasks/categories_and_collections.rake +188 -0
  1068. data/lib/tasks/cli.rake +22 -3
  1069. data/lib/tasks/consolidate_metadata.rake +73 -0
  1070. data/lib/tasks/core.rake +1 -11
  1071. data/lib/tasks/deliveries_migration.rake +52 -0
  1072. data/lib/tasks/delivery_method_rules_migration.rake +51 -0
  1073. data/lib/tasks/delivery_migration.rake +213 -0
  1074. data/lib/tasks/delivery_profiles_migration.rake +109 -0
  1075. data/lib/tasks/fold_store_credit_categories.rake +40 -0
  1076. data/lib/tasks/fulfillment_statuses_migration.rake +59 -0
  1077. data/lib/tasks/inventory_operations_migration.rake +223 -0
  1078. data/lib/tasks/markets.rake +7 -2
  1079. data/lib/tasks/media.rake +2 -2
  1080. data/lib/tasks/media_class_rename.rake +56 -0
  1081. data/lib/tasks/migrate_country_state_codes.rake +228 -0
  1082. data/lib/tasks/migrate_promotion_option_value_rules.rake +124 -0
  1083. data/lib/tasks/migrate_returns.rake +322 -0
  1084. data/lib/tasks/migrate_rich_text_to_columns.rake +128 -0
  1085. data/lib/tasks/migrate_users_to_customers.rake +161 -0
  1086. data/lib/tasks/order_coupon_code_backfill.rake +32 -0
  1087. data/lib/tasks/order_market_backfill.rake +27 -0
  1088. data/lib/tasks/package_types.rake +62 -0
  1089. data/lib/tasks/price_history.rake +49 -6
  1090. data/lib/tasks/products.rake +21 -2
  1091. data/lib/tasks/remove_master_variant.rake +58 -0
  1092. data/lib/tasks/rich_text.rake +44 -0
  1093. data/lib/tasks/seller_sample_data.rake +142 -0
  1094. data/lib/tasks/setup.rake +18 -0
  1095. data/lib/tasks/single_store_associations.rake +11 -0
  1096. data/lib/tasks/stock.rake +15 -0
  1097. data/lib/tasks/store_binding_migration.rake +33 -0
  1098. data/lib/tasks/store_settings.rake +105 -0
  1099. data/lib/tasks/tax_zones_migration.rake +147 -0
  1100. data/lib/tasks/taxons.rake +9 -10
  1101. data/lib/tasks/typed_adjustments_migration.rake +189 -0
  1102. data/lib/tasks/typed_stock_movements_migration.rake +250 -0
  1103. data/lib/tasks/upgrade.rake +13 -8
  1104. data/spec/fixtures/files/eu_vat_numbers.txt +56 -0
  1105. data/spec/fixtures/files/price_list_prices_import.csv +4 -0
  1106. data/spec/fixtures/files/products_import.csv +1 -1
  1107. data/spec/fixtures/tiny-video.mp4 +0 -0
  1108. metadata +798 -546
  1109. data/app/assets/config/spree_core_manifest.js +0 -4
  1110. data/app/assets/images/google_on_white_hdpi.png +0 -0
  1111. data/app/assets/images/logo.png +0 -0
  1112. data/app/assets/images/payment_icons/ach.svg +0 -1
  1113. data/app/assets/images/payment_icons/achdirectdebit.svg +0 -1
  1114. data/app/assets/images/payment_icons/ada.svg +0 -1
  1115. data/app/assets/images/payment_icons/adyen.svg +0 -1
  1116. data/app/assets/images/payment_icons/affinbank.svg +0 -1
  1117. data/app/assets/images/payment_icons/affirm.svg +0 -1
  1118. data/app/assets/images/payment_icons/afterpay.svg +0 -1
  1119. data/app/assets/images/payment_icons/airtel_money.svg +0 -1
  1120. data/app/assets/images/payment_icons/airteltigo_mobile_money.svg +0 -1
  1121. data/app/assets/images/payment_icons/alandsbanken.svg +0 -1
  1122. data/app/assets/images/payment_icons/alipay.svg +0 -1
  1123. data/app/assets/images/payment_icons/alipay_hk.svg +0 -1
  1124. data/app/assets/images/payment_icons/alliancebank.svg +0 -1
  1125. data/app/assets/images/payment_icons/alma.svg +0 -1
  1126. data/app/assets/images/payment_icons/amazon.svg +0 -1
  1127. data/app/assets/images/payment_icons/ambank.svg +0 -1
  1128. data/app/assets/images/payment_icons/american_express.svg +0 -1
  1129. data/app/assets/images/payment_icons/apecoin.svg +0 -1
  1130. data/app/assets/images/payment_icons/apple_pay.svg +0 -1
  1131. data/app/assets/images/payment_icons/arbitrum.svg +0 -1
  1132. data/app/assets/images/payment_icons/atome.svg +0 -1
  1133. data/app/assets/images/payment_icons/authorizenet.svg +0 -1
  1134. data/app/assets/images/payment_icons/avalanche.svg +0 -1
  1135. data/app/assets/images/payment_icons/bacs.svg +0 -1
  1136. data/app/assets/images/payment_icons/bancontact.svg +0 -1
  1137. data/app/assets/images/payment_icons/bangkokbank.svg +0 -1
  1138. data/app/assets/images/payment_icons/bankislam.svg +0 -1
  1139. data/app/assets/images/payment_icons/bankmuamalat.svg +0 -1
  1140. data/app/assets/images/payment_icons/bankofamerica.svg +0 -1
  1141. data/app/assets/images/payment_icons/bankrakyat.svg +0 -1
  1142. data/app/assets/images/payment_icons/banktransfer.svg +0 -1
  1143. data/app/assets/images/payment_icons/base.svg +0 -1
  1144. data/app/assets/images/payment_icons/becs.svg +0 -1
  1145. data/app/assets/images/payment_icons/benefit.svg +0 -1
  1146. data/app/assets/images/payment_icons/billie.svg +0 -1
  1147. data/app/assets/images/payment_icons/bitcoin.svg +0 -1
  1148. data/app/assets/images/payment_icons/bitcoin_cash.svg +0 -1
  1149. data/app/assets/images/payment_icons/bizum.svg +0 -1
  1150. data/app/assets/images/payment_icons/blik.svg +0 -1
  1151. data/app/assets/images/payment_icons/bnbchain.svg +0 -1
  1152. data/app/assets/images/payment_icons/bogus.svg +0 -1
  1153. data/app/assets/images/payment_icons/bogus_app_coin.svg +0 -1
  1154. data/app/assets/images/payment_icons/boleto.svg +0 -1
  1155. data/app/assets/images/payment_icons/boost.svg +0 -1
  1156. data/app/assets/images/payment_icons/braintree.svg +0 -1
  1157. data/app/assets/images/payment_icons/buckaroopaybybank.svg +0 -1
  1158. data/app/assets/images/payment_icons/busd.svg +0 -1
  1159. data/app/assets/images/payment_icons/cartes_bancaires.svg +0 -1
  1160. data/app/assets/images/payment_icons/cash.svg +0 -1
  1161. data/app/assets/images/payment_icons/cashappafterpay.svg +0 -1
  1162. data/app/assets/images/payment_icons/cashapppay.svg +0 -1
  1163. data/app/assets/images/payment_icons/checkout_finance.svg +0 -1
  1164. data/app/assets/images/payment_icons/chinabank.svg +0 -1
  1165. data/app/assets/images/payment_icons/clearpay.svg +0 -1
  1166. data/app/assets/images/payment_icons/collector_bank.svg +0 -1
  1167. data/app/assets/images/payment_icons/dai.svg +0 -1
  1168. data/app/assets/images/payment_icons/dana.svg +0 -1
  1169. data/app/assets/images/payment_icons/dankort.svg +0 -1
  1170. data/app/assets/images/payment_icons/danske_bank.svg +0 -1
  1171. data/app/assets/images/payment_icons/deutschebank.svg +0 -1
  1172. data/app/assets/images/payment_icons/diners_club.svg +0 -1
  1173. data/app/assets/images/payment_icons/directbanktransferlatinamerica.svg +0 -1
  1174. data/app/assets/images/payment_icons/discover.svg +0 -1
  1175. data/app/assets/images/payment_icons/dogecoin.svg +0 -1
  1176. data/app/assets/images/payment_icons/duitnow.svg +0 -1
  1177. data/app/assets/images/payment_icons/eft_secure.svg +0 -1
  1178. data/app/assets/images/payment_icons/eftpos_au.svg +0 -1
  1179. data/app/assets/images/payment_icons/eftposau.svg +0 -1
  1180. data/app/assets/images/payment_icons/elo.svg +0 -1
  1181. data/app/assets/images/payment_icons/eos.svg +0 -1
  1182. data/app/assets/images/payment_icons/eps.svg +0 -1
  1183. data/app/assets/images/payment_icons/ethereum.svg +0 -1
  1184. data/app/assets/images/payment_icons/eurobonuscheckout.svg +0 -1
  1185. data/app/assets/images/payment_icons/ezcash.svg +0 -1
  1186. data/app/assets/images/payment_icons/fantom.svg +0 -1
  1187. data/app/assets/images/payment_icons/fpx.svg +0 -1
  1188. data/app/assets/images/payment_icons/gcash.svg +0 -1
  1189. data/app/assets/images/payment_icons/generic.svg +0 -1
  1190. data/app/assets/images/payment_icons/genericbank.svg +0 -1
  1191. data/app/assets/images/payment_icons/gift-card.svg +0 -1
  1192. data/app/assets/images/payment_icons/giropay.svg +0 -1
  1193. data/app/assets/images/payment_icons/glbepaypal.svg +0 -1
  1194. data/app/assets/images/payment_icons/gmobanktransfer.svg +0 -1
  1195. data/app/assets/images/payment_icons/gnosis.svg +0 -1
  1196. data/app/assets/images/payment_icons/google_pay.svg +0 -1
  1197. data/app/assets/images/payment_icons/google_wallet.svg +0 -1
  1198. data/app/assets/images/payment_icons/gopay.svg +0 -1
  1199. data/app/assets/images/payment_icons/grabpay.svg +0 -1
  1200. data/app/assets/images/payment_icons/gusd.svg +0 -1
  1201. data/app/assets/images/payment_icons/handelsbanken.svg +0 -1
  1202. data/app/assets/images/payment_icons/hongleongbank.svg +0 -1
  1203. data/app/assets/images/payment_icons/hypercard.svg +0 -1
  1204. data/app/assets/images/payment_icons/hypercash.svg +0 -1
  1205. data/app/assets/images/payment_icons/ideal.svg +0 -1
  1206. data/app/assets/images/payment_icons/in3viaideal.svg +0 -1
  1207. data/app/assets/images/payment_icons/inbank.svg +0 -1
  1208. data/app/assets/images/payment_icons/jcb.svg +0 -1
  1209. data/app/assets/images/payment_icons/kakao_pay.svg +0 -1
  1210. data/app/assets/images/payment_icons/kasikornbank.svg +0 -1
  1211. data/app/assets/images/payment_icons/klarna-pay-later.svg +0 -1
  1212. data/app/assets/images/payment_icons/klarna-pay-now.svg +0 -1
  1213. data/app/assets/images/payment_icons/klarna-slice-it.svg +0 -1
  1214. data/app/assets/images/payment_icons/klarna.svg +0 -1
  1215. data/app/assets/images/payment_icons/knet.svg +0 -1
  1216. data/app/assets/images/payment_icons/krungthaibank.svg +0 -1
  1217. data/app/assets/images/payment_icons/landbank.svg +0 -1
  1218. data/app/assets/images/payment_icons/laybuybyklarna.svg +0 -1
  1219. data/app/assets/images/payment_icons/litecoin.svg +0 -1
  1220. data/app/assets/images/payment_icons/lunchcheck.svg +0 -1
  1221. data/app/assets/images/payment_icons/mach.svg +0 -1
  1222. data/app/assets/images/payment_icons/mada.svg +0 -1
  1223. data/app/assets/images/payment_icons/maestro.svg +0 -1
  1224. data/app/assets/images/payment_icons/master.svg +0 -1
  1225. data/app/assets/images/payment_icons/mayabank.svg +0 -1
  1226. data/app/assets/images/payment_icons/maybank.svg +0 -1
  1227. data/app/assets/images/payment_icons/maybankm2u.svg +0 -1
  1228. data/app/assets/images/payment_icons/maybankqrpay.svg +0 -1
  1229. data/app/assets/images/payment_icons/mbway.svg +0 -1
  1230. data/app/assets/images/payment_icons/mcash.svg +0 -1
  1231. data/app/assets/images/payment_icons/medicinosbankas.svg +0 -1
  1232. data/app/assets/images/payment_icons/mercadopago.svg +0 -1
  1233. data/app/assets/images/payment_icons/metrobank.svg +0 -1
  1234. data/app/assets/images/payment_icons/mobilepay.svg +0 -1
  1235. data/app/assets/images/payment_icons/momopay.svg +0 -1
  1236. data/app/assets/images/payment_icons/monero.svg +0 -1
  1237. data/app/assets/images/payment_icons/mtn_mobile_money.svg +0 -1
  1238. data/app/assets/images/payment_icons/mybank.svg +0 -1
  1239. data/app/assets/images/payment_icons/naver_pay.svg +0 -1
  1240. data/app/assets/images/payment_icons/nelo.svg +0 -1
  1241. data/app/assets/images/payment_icons/netbanking.svg +0 -1
  1242. data/app/assets/images/payment_icons/novalnetdirectdebitach.svg +0 -1
  1243. data/app/assets/images/payment_icons/nubank.svg +0 -1
  1244. data/app/assets/images/payment_icons/ocbcbank.svg +0 -1
  1245. data/app/assets/images/payment_icons/offlinebanktransferlatinamerica.svg +0 -1
  1246. data/app/assets/images/payment_icons/ola_money.svg +0 -1
  1247. data/app/assets/images/payment_icons/oney.svg +0 -1
  1248. data/app/assets/images/payment_icons/onlinebanking.svg +0 -1
  1249. data/app/assets/images/payment_icons/onlinebanktransfer.svg +0 -1
  1250. data/app/assets/images/payment_icons/orangemobilemoney.svg +0 -1
  1251. data/app/assets/images/payment_icons/otpbank.svg +0 -1
  1252. data/app/assets/images/payment_icons/oxxo.svg +0 -1
  1253. data/app/assets/images/payment_icons/paybybank.svg +0 -1
  1254. data/app/assets/images/payment_icons/paybybankmollie.svg +0 -1
  1255. data/app/assets/images/payment_icons/paybybankus.svg +0 -1
  1256. data/app/assets/images/payment_icons/paycash.svg +0 -1
  1257. data/app/assets/images/payment_icons/payco.svg +0 -1
  1258. data/app/assets/images/payment_icons/payeverpaybybank.svg +0 -1
  1259. data/app/assets/images/payment_icons/payme.svg +0 -1
  1260. data/app/assets/images/payment_icons/paynow.svg +0 -1
  1261. data/app/assets/images/payment_icons/paynowmbank.svg +0 -1
  1262. data/app/assets/images/payment_icons/paypal.svg +0 -1
  1263. data/app/assets/images/payment_icons/paypo.svg +0 -1
  1264. data/app/assets/images/payment_icons/payrexxbanktransfer.svg +0 -1
  1265. data/app/assets/images/payment_icons/payrexxpaybybank.svg +0 -1
  1266. data/app/assets/images/payment_icons/paysafecash.svg +0 -1
  1267. data/app/assets/images/payment_icons/pix.svg +0 -1
  1268. data/app/assets/images/payment_icons/polygon.svg +0 -1
  1269. data/app/assets/images/payment_icons/przelewy24.svg +0 -1
  1270. data/app/assets/images/payment_icons/publicbank.svg +0 -1
  1271. data/app/assets/images/payment_icons/publicbank_pbe.svg +0 -1
  1272. data/app/assets/images/payment_icons/qr_promptpay.svg +0 -1
  1273. data/app/assets/images/payment_icons/rabobank.svg +0 -1
  1274. data/app/assets/images/payment_icons/revolut.svg +0 -1
  1275. data/app/assets/images/payment_icons/rhbbank.svg +0 -1
  1276. data/app/assets/images/payment_icons/riverty.svg +0 -1
  1277. data/app/assets/images/payment_icons/safetypaybanktransfer.svg +0 -1
  1278. data/app/assets/images/payment_icons/samsung_pay.svg +0 -1
  1279. data/app/assets/images/payment_icons/satispay.svg +0 -1
  1280. data/app/assets/images/payment_icons/scalapay.svg +0 -1
  1281. data/app/assets/images/payment_icons/scotiabank.svg +0 -1
  1282. data/app/assets/images/payment_icons/seabankid.svg +0 -1
  1283. data/app/assets/images/payment_icons/seabankph.svg +0 -1
  1284. data/app/assets/images/payment_icons/sepa_bank_transfer.svg +0 -1
  1285. data/app/assets/images/payment_icons/sepadebit.svg +0 -22
  1286. data/app/assets/images/payment_icons/sepadirectdebit.svg +0 -1
  1287. data/app/assets/images/payment_icons/seveneleven.svg +0 -1
  1288. data/app/assets/images/payment_icons/shib.svg +0 -1
  1289. data/app/assets/images/payment_icons/shopcash.svg +0 -1
  1290. data/app/assets/images/payment_icons/siauliubankas.svg +0 -1
  1291. data/app/assets/images/payment_icons/skeps.svg +0 -1
  1292. data/app/assets/images/payment_icons/snap_checkout.svg +0 -1
  1293. data/app/assets/images/payment_icons/sofort.svg +0 -1
  1294. data/app/assets/images/payment_icons/softbank.svg +0 -1
  1295. data/app/assets/images/payment_icons/spei.svg +0 -1
  1296. data/app/assets/images/payment_icons/storecredit.svg +0 -1
  1297. data/app/assets/images/payment_icons/stripe.svg +0 -1
  1298. data/app/assets/images/payment_icons/sveacheckout.svg +0 -1
  1299. data/app/assets/images/payment_icons/swedbank.svg +0 -1
  1300. data/app/assets/images/payment_icons/swish.svg +0 -1
  1301. data/app/assets/images/payment_icons/tbibank.svg +0 -1
  1302. data/app/assets/images/payment_icons/thanachartbank.svg +0 -1
  1303. data/app/assets/images/payment_icons/tnmmoney.svg +0 -1
  1304. data/app/assets/images/payment_icons/touchngo.svg +0 -1
  1305. data/app/assets/images/payment_icons/trustly.svg +0 -1
  1306. data/app/assets/images/payment_icons/twint.svg +0 -1
  1307. data/app/assets/images/payment_icons/uaevisa.svg +0 -1
  1308. data/app/assets/images/payment_icons/unionpay.svg +0 -1
  1309. data/app/assets/images/payment_icons/unzerbanktransfer.svg +0 -1
  1310. data/app/assets/images/payment_icons/upi.svg +0 -1
  1311. data/app/assets/images/payment_icons/usbank.svg +0 -1
  1312. data/app/assets/images/payment_icons/usdc.svg +0 -1
  1313. data/app/assets/images/payment_icons/usdt.svg +0 -1
  1314. data/app/assets/images/payment_icons/venmo.svg +0 -1
  1315. data/app/assets/images/payment_icons/vipps.svg +0 -1
  1316. data/app/assets/images/payment_icons/visa.svg +0 -1
  1317. data/app/assets/images/payment_icons/volksbank.svg +0 -1
  1318. data/app/assets/images/payment_icons/waavepaybybank.svg +0 -1
  1319. data/app/assets/images/payment_icons/walley.svg +0 -1
  1320. data/app/assets/images/payment_icons/wbtc.svg +0 -1
  1321. data/app/assets/images/payment_icons/wechatpay.svg +0 -1
  1322. data/app/assets/images/payment_icons/whishcheckout.svg +0 -1
  1323. data/app/assets/images/payment_icons/wingbank.svg +0 -1
  1324. data/app/assets/images/payment_icons/xrp.svg +0 -1
  1325. data/app/assets/images/payment_icons/zip.svg +0 -1
  1326. data/app/controllers/spree/base_controller.rb +0 -11
  1327. data/app/finders/concerns/spree/product_filterable.rb +0 -9
  1328. data/app/finders/spree/addresses/find.rb +0 -27
  1329. data/app/finders/spree/base_finder.rb +0 -14
  1330. data/app/finders/spree/countries/find.rb +0 -31
  1331. data/app/finders/spree/credit_cards/find.rb +0 -12
  1332. data/app/finders/spree/option_values/find_available.rb +0 -28
  1333. data/app/finders/spree/orders/find_complete.rb +0 -114
  1334. data/app/finders/spree/orders/find_current.rb +0 -24
  1335. data/app/finders/spree/orders/finder_helper.rb +0 -17
  1336. data/app/finders/spree/products/find.rb +0 -328
  1337. data/app/finders/spree/taxons/find.rb +0 -99
  1338. data/app/finders/spree/variants/find.rb +0 -30
  1339. data/app/finders/spree/variants/option_types_finder.rb +0 -21
  1340. data/app/helpers/spree/addresses_helper.rb +0 -97
  1341. data/app/helpers/spree/base_helper.rb +0 -132
  1342. data/app/helpers/spree/currency_helper.rb +0 -46
  1343. data/app/helpers/spree/integrations_helper.rb +0 -15
  1344. data/app/helpers/spree/locale_helper.rb +0 -44
  1345. data/app/helpers/spree/localized_names_helper.rb +0 -25
  1346. data/app/helpers/spree/products_helper.rb +0 -147
  1347. data/app/helpers/spree/shipment_helper.rb +0 -12
  1348. data/app/javascript/spree/core/controllers/address_autocomplete_controller.js +0 -230
  1349. data/app/javascript/spree/core/controllers/address_form_controller.js +0 -98
  1350. data/app/javascript/spree/core/controllers/disable_submit_button_controller.js +0 -19
  1351. data/app/javascript/spree/core/controllers/enable_button_controller.js +0 -29
  1352. data/app/javascript/spree/core/helpers/address_autocomplete/google_places_suggestions_provider.js +0 -161
  1353. data/app/javascript/spree/core/helpers/debounce.js +0 -9
  1354. data/app/javascript/spree/core/helpers/scroll_lock.js +0 -25
  1355. data/app/jobs/spree/media/migrate_product_assets_job.rb +0 -83
  1356. data/app/jobs/spree/reports/generate_job.rb +0 -23
  1357. data/app/mailers/spree/report_mailer.rb +0 -20
  1358. data/app/models/concerns/spree/adjustment_source.rb +0 -58
  1359. data/app/models/concerns/spree/default_price.rb +0 -138
  1360. data/app/models/concerns/spree/display_on.rb +0 -50
  1361. data/app/models/concerns/spree/image_methods.rb +0 -40
  1362. data/app/models/concerns/spree/legacy_multi_store_support.rb +0 -48
  1363. data/app/models/concerns/spree/metafield_filterable.rb +0 -162
  1364. data/app/models/concerns/spree/metafields.rb +0 -256
  1365. data/app/models/concerns/spree/number_as_param.rb +0 -13
  1366. data/app/models/concerns/spree/store_scoped_resource.rb +0 -37
  1367. data/app/models/concerns/spree/unique_name.rb +0 -12
  1368. data/app/models/spree/adjustable/adjuster/base.rb +0 -25
  1369. data/app/models/spree/adjustable/adjuster/promotion.rb +0 -41
  1370. data/app/models/spree/adjustable/adjuster/tax.rb +0 -26
  1371. data/app/models/spree/adjustable/adjustments_updater.rb +0 -74
  1372. data/app/models/spree/adjustable/promotion_accumulator.rb +0 -78
  1373. data/app/models/spree/adjustment.rb +0 -130
  1374. data/app/models/spree/asset/support/active_storage.rb +0 -26
  1375. data/app/models/spree/calculator/default_tax.rb +0 -87
  1376. data/app/models/spree/calculator/returns/default_refund_amount.rb +0 -40
  1377. data/app/models/spree/checkout/default_requirements.rb +0 -51
  1378. data/app/models/spree/checkout/registry.rb +0 -112
  1379. data/app/models/spree/checkout/requirement.rb +0 -49
  1380. data/app/models/spree/checkout/requirements.rb +0 -56
  1381. data/app/models/spree/checkout/step.rb +0 -52
  1382. data/app/models/spree/customer_return.rb +0 -94
  1383. data/app/models/spree/image/configuration/active_storage.rb +0 -11
  1384. data/app/models/spree/image.rb +0 -6
  1385. data/app/models/spree/legacy_admin_user.rb +0 -31
  1386. data/app/models/spree/legacy_user.rb +0 -33
  1387. data/app/models/spree/log_entry.rb +0 -30
  1388. data/app/models/spree/metafield_definition/search_capabilities.rb +0 -104
  1389. data/app/models/spree/metafields/boolean.rb +0 -15
  1390. data/app/models/spree/metafields/json.rb +0 -37
  1391. data/app/models/spree/metafields/long_text.rb +0 -11
  1392. data/app/models/spree/metafields/number.rb +0 -23
  1393. data/app/models/spree/metafields/rich_text.rb +0 -18
  1394. data/app/models/spree/metafields/short_text.rb +0 -15
  1395. data/app/models/spree/option_type_prototype.rb +0 -9
  1396. data/app/models/spree/order/address_book.rb +0 -100
  1397. data/app/models/spree/order/checkout.rb +0 -350
  1398. data/app/models/spree/order/currency_updater.rb +0 -37
  1399. data/app/models/spree/order/digital.rb +0 -60
  1400. data/app/models/spree/order/gift_card.rb +0 -71
  1401. data/app/models/spree/order/payments.rb +0 -64
  1402. data/app/models/spree/order/store_credit.rb +0 -110
  1403. data/app/models/spree/order/webhooks.rb +0 -19
  1404. data/app/models/spree/order_approval.rb +0 -19
  1405. data/app/models/spree/order_cancellation.rb +0 -19
  1406. data/app/models/spree/order_contents.rb +0 -31
  1407. data/app/models/spree/order_routing/strategy/legacy.rb +0 -33
  1408. data/app/models/spree/payment/webhooks.rb +0 -15
  1409. data/app/models/spree/permission_sets/base.rb +0 -81
  1410. data/app/models/spree/permission_sets/configuration_management.rb +0 -64
  1411. data/app/models/spree/permission_sets/dashboard_display.rb +0 -17
  1412. data/app/models/spree/permission_sets/default_customer.rb +0 -83
  1413. data/app/models/spree/permission_sets/order_display.rb +0 -27
  1414. data/app/models/spree/permission_sets/order_management.rb +0 -33
  1415. data/app/models/spree/permission_sets/product_display.rb +0 -27
  1416. data/app/models/spree/permission_sets/product_management.rb +0 -28
  1417. data/app/models/spree/permission_sets/promotion_management.rb +0 -22
  1418. data/app/models/spree/permission_sets/role_management.rb +0 -21
  1419. data/app/models/spree/permission_sets/stock_display.rb +0 -19
  1420. data/app/models/spree/permission_sets/stock_management.rb +0 -19
  1421. data/app/models/spree/permission_sets/super_user.rb +0 -28
  1422. data/app/models/spree/permission_sets/user_display.rb +0 -19
  1423. data/app/models/spree/permission_sets/user_management.rb +0 -20
  1424. data/app/models/spree/product/webhooks.rb +0 -17
  1425. data/app/models/spree/prototype_taxon.rb +0 -9
  1426. data/app/models/spree/reimbursement/credit.rb +0 -27
  1427. data/app/models/spree/reimbursement/reimbursement_type_engine.rb +0 -44
  1428. data/app/models/spree/reimbursement/reimbursement_type_validator.rb +0 -15
  1429. data/app/models/spree/reimbursement.rb +0 -167
  1430. data/app/models/spree/reimbursement_performer.rb +0 -34
  1431. data/app/models/spree/reimbursement_tax_calculator.rb +0 -37
  1432. data/app/models/spree/reimbursement_type/credit.rb +0 -13
  1433. data/app/models/spree/reimbursement_type/exchange.rb +0 -9
  1434. data/app/models/spree/reimbursement_type/original_payment.rb +0 -13
  1435. data/app/models/spree/reimbursement_type/reimbursement_helpers.rb +0 -64
  1436. data/app/models/spree/reimbursement_type/store_credit.rb +0 -20
  1437. data/app/models/spree/reimbursement_type.rb +0 -22
  1438. data/app/models/spree/report.rb +0 -140
  1439. data/app/models/spree/report_line_item.rb +0 -27
  1440. data/app/models/spree/report_line_items/products_performance.rb +0 -76
  1441. data/app/models/spree/report_line_items/sales_total.rb +0 -49
  1442. data/app/models/spree/reports/products_performance.rb +0 -44
  1443. data/app/models/spree/reports/sales_total.rb +0 -18
  1444. data/app/models/spree/return_authorization.rb +0 -109
  1445. data/app/models/spree/return_item/eligibility_validator/base_validator.rb +0 -24
  1446. data/app/models/spree/return_item/eligibility_validator/default.rb +0 -30
  1447. data/app/models/spree/return_item/eligibility_validator/inventory_shipped.rb +0 -16
  1448. data/app/models/spree/return_item/eligibility_validator/no_reimbursements.rb +0 -16
  1449. data/app/models/spree/return_item/eligibility_validator/order_completed.rb +0 -16
  1450. data/app/models/spree/return_item/eligibility_validator/rma_required.rb +0 -16
  1451. data/app/models/spree/return_item/eligibility_validator/time_since_purchase.rb +0 -16
  1452. data/app/models/spree/return_item/exchange_variant_eligibility/same_option_value.rb +0 -34
  1453. data/app/models/spree/return_item/exchange_variant_eligibility/same_product.rb +0 -9
  1454. data/app/models/spree/return_item.rb +0 -287
  1455. data/app/models/spree/returns_calculator.rb +0 -7
  1456. data/app/models/spree/search_provider/meilisearch.rb +0 -551
  1457. data/app/models/spree/shipment/custom_events.rb +0 -47
  1458. data/app/models/spree/shipment/webhooks.rb +0 -11
  1459. data/app/models/spree/shipment_handler.rb +0 -38
  1460. data/app/models/spree/shipping_category.rb +0 -25
  1461. data/app/models/spree/shipping_method_category.rb +0 -8
  1462. data/app/models/spree/shipping_method_zone.rb +0 -8
  1463. data/app/models/spree/state_change.rb +0 -12
  1464. data/app/models/spree/stock/splitter/digital.rb +0 -35
  1465. data/app/models/spree/stock/splitter/shipping_category.rb +0 -31
  1466. data/app/models/spree/stock_item/webhooks.rb +0 -6
  1467. data/app/models/spree/stock_movement/webhooks.rb +0 -6
  1468. data/app/models/spree/variant/webhooks.rb +0 -6
  1469. data/app/presenters/spree/csv/metafields_helper.rb +0 -17
  1470. data/app/presenters/spree/search_provider/product_presenter.rb +0 -158
  1471. data/app/presenters/spree/variant_presenter.rb +0 -81
  1472. data/app/presenters/spree/variants/option_types_presenter.rb +0 -87
  1473. data/app/services/spree/account/addresses/create.rb +0 -8
  1474. data/app/services/spree/account/addresses/update.rb +0 -8
  1475. data/app/services/spree/account/create.rb +0 -17
  1476. data/app/services/spree/account/update.rb +0 -15
  1477. data/app/services/spree/cart/add_item.rb +0 -63
  1478. data/app/services/spree/cart/associate.rb +0 -29
  1479. data/app/services/spree/cart/change_currency.rb +0 -27
  1480. data/app/services/spree/cart/create.rb +0 -26
  1481. data/app/services/spree/cart/destroy.rb +0 -50
  1482. data/app/services/spree/cart/empty.rb +0 -38
  1483. data/app/services/spree/cart/estimate_shipping_rates.rb +0 -39
  1484. data/app/services/spree/cart/recalculate.rb +0 -35
  1485. data/app/services/spree/cart/remove_item.rb +0 -39
  1486. data/app/services/spree/cart/remove_line_item.rb +0 -28
  1487. data/app/services/spree/cart/remove_out_of_stock_items.rb +0 -65
  1488. data/app/services/spree/cart/set_quantity.rb +0 -32
  1489. data/app/services/spree/cart/update.rb +0 -37
  1490. data/app/services/spree/carts/complete.rb +0 -50
  1491. data/app/services/spree/carts/upsert_items.rb +0 -76
  1492. data/app/services/spree/checkout/complete.rb +0 -23
  1493. data/app/services/spree/checkout/get_shipping_rates.rb +0 -52
  1494. data/app/services/spree/checkout/next.rb +0 -13
  1495. data/app/services/spree/checkout/select_shipping_method.rb +0 -63
  1496. data/app/services/spree/checkout/update.rb +0 -72
  1497. data/app/services/spree/credit_cards/destroy.rb +0 -25
  1498. data/app/services/spree/fulfillments/create.rb +0 -259
  1499. data/app/services/spree/gift_cards/apply.rb +0 -78
  1500. data/app/services/spree/gift_cards/redeem.rb +0 -17
  1501. data/app/services/spree/gift_cards/remove.rb +0 -39
  1502. data/app/services/spree/line_items/create.rb +0 -20
  1503. data/app/services/spree/line_items/destroy.rb +0 -18
  1504. data/app/services/spree/line_items/helper.rb +0 -11
  1505. data/app/services/spree/line_items/update.rb +0 -17
  1506. data/app/services/spree/orders/build_shipments.rb +0 -29
  1507. data/app/services/spree/orders/cancel.rb +0 -54
  1508. data/app/services/spree/orders/complete.rb +0 -53
  1509. data/app/services/spree/orders/update_contact_information.rb +0 -19
  1510. data/app/services/spree/orders/upsert_items.rb +0 -70
  1511. data/app/services/spree/payments/handle_webhook.rb +0 -57
  1512. data/app/services/spree/search_provider/metafield_schema.rb +0 -144
  1513. data/app/services/spree/seeds/countries.rb +0 -35
  1514. data/app/services/spree/seeds/default_reimbursement_types.rb +0 -12
  1515. data/app/services/spree/seeds/shipping_categories.rb +0 -12
  1516. data/app/services/spree/seeds/states.rb +0 -82
  1517. data/app/services/spree/seeds/stock_locations.rb +0 -18
  1518. data/app/services/spree/seeds/store_credit_categories.rb +0 -14
  1519. data/app/services/spree/seeds/zones.rb +0 -52
  1520. data/app/services/spree/shipments/add_item.rb +0 -32
  1521. data/app/services/spree/shipments/change_state.rb +0 -14
  1522. data/app/services/spree/shipments/create.rb +0 -48
  1523. data/app/services/spree/shipments/helper.rb +0 -23
  1524. data/app/services/spree/shipments/remove_item.rb +0 -55
  1525. data/app/services/spree/stores/settings_defaults_by_country.rb +0 -38
  1526. data/app/services/spree/themes/duplicator.rb +0 -40
  1527. data/app/sorters/spree/base_sorter.rb +0 -47
  1528. data/app/sorters/spree/classifications/sort.rb +0 -68
  1529. data/app/sorters/spree/products/sort.rb +0 -73
  1530. data/app/subscribers/spree/report_subscriber.rb +0 -26
  1531. data/app/views/spree/addresses/_form.html.erb +0 -59
  1532. data/app/views/spree/addresses/_suggestions_box.erb +0 -14
  1533. data/app/views/spree/admin_user_mailer/confirmation_email.html.erb +0 -7
  1534. data/app/views/spree/report_mailer/report_done.html.erb +0 -7
  1535. data/app/views/spree/shared/_google_places_api_loader.html.erb +0 -7
  1536. data/app/views/spree/shared/_mailer_line_item.html.erb +0 -12
  1537. data/app/views/spree/shared/_payment.html.erb +0 -52
  1538. data/config/importmap.rb +0 -15
  1539. data/config/initializers/carmen.rb +0 -23
  1540. data/config/initializers/rails61_fixes.rb +0 -1
  1541. data/db/migrate/20250120152208_add_default_locale_to_action_text_rich_texts.rb +0 -7
  1542. data/db/migrate/20250122113708_add_first_and_last_name_to_spree_admin_class.rb +0 -9
  1543. data/db/migrate/20250204162453_create_spree_reports.rb +0 -13
  1544. data/db/migrate/20250217171018_create_action_text_video_embeds.rb +0 -11
  1545. data/db/migrate/20250508060800_add_selected_locale_to_spree_admin_users.rb +0 -8
  1546. data/db/migrate/20260429000001_create_spree_order_cancellations.rb +0 -25
  1547. data/db/migrate/20260429000002_create_spree_order_approvals.rb +0 -22
  1548. data/db/sample_data/markets.rb +0 -64
  1549. data/db/sample_data/metafield_definitions.rb +0 -7
  1550. data/db/sample_data/shipping_methods.rb +0 -61
  1551. data/lib/generators/spree/authentication/custom/custom_generator.rb +0 -49
  1552. data/lib/generators/spree/authentication/custom/templates/authentication_helpers.rb.tt +0 -50
  1553. data/lib/generators/spree/authentication/devise/devise_generator.rb +0 -84
  1554. data/lib/generators/spree/authentication/devise/templates/authentication_helpers.rb.tt +0 -50
  1555. data/lib/generators/spree/dummy/templates/app/assets/config/manifest.js +0 -0
  1556. data/lib/spree/core/components.rb +0 -31
  1557. data/lib/spree/core/controller_helpers/auth.rb +0 -52
  1558. data/lib/spree/core/controller_helpers/currency.rb +0 -59
  1559. data/lib/spree/core/controller_helpers/locale.rb +0 -99
  1560. data/lib/spree/core/controller_helpers/order.rb +0 -172
  1561. data/lib/spree/core/controller_helpers/strong_parameters.rb +0 -48
  1562. data/lib/spree/core/controller_helpers/turbo.rb +0 -19
  1563. data/lib/spree/core/partials.rb +0 -42
  1564. data/lib/spree/core/previews/report_preview.rb +0 -40
  1565. data/lib/spree/permitted_attributes.rb +0 -337
  1566. data/lib/spree/testing_support/authorization_helpers.rb +0 -72
  1567. data/lib/spree/testing_support/caching.rb +0 -47
  1568. data/lib/spree/testing_support/capybara_config.rb +0 -45
  1569. data/lib/spree/testing_support/capybara_ext.rb +0 -83
  1570. data/lib/spree/testing_support/controller_requests.rb +0 -11
  1571. data/lib/spree/testing_support/factories/adjustment_factory.rb +0 -28
  1572. data/lib/spree/testing_support/factories/asset_factory.rb +0 -12
  1573. data/lib/spree/testing_support/factories/customer_return_factory.rb +0 -36
  1574. data/lib/spree/testing_support/factories/digital_factory.rb +0 -10
  1575. data/lib/spree/testing_support/factories/image_factory.rb +0 -11
  1576. data/lib/spree/testing_support/factories/inventory_unit_factory.rb +0 -18
  1577. data/lib/spree/testing_support/factories/log_entry_factory.rb +0 -6
  1578. data/lib/spree/testing_support/factories/metafield_definition_factory.rb +0 -82
  1579. data/lib/spree/testing_support/factories/metafield_factory.rb +0 -68
  1580. data/lib/spree/testing_support/factories/prototype_factory.rb +0 -9
  1581. data/lib/spree/testing_support/factories/reimbursement_credit_factory.rb +0 -7
  1582. data/lib/spree/testing_support/factories/reimbursement_factory.rb +0 -16
  1583. data/lib/spree/testing_support/factories/reimbursement_type_factory.rb +0 -7
  1584. data/lib/spree/testing_support/factories/report_factory.rb +0 -33
  1585. data/lib/spree/testing_support/factories/return_authorization_factory.rb +0 -21
  1586. data/lib/spree/testing_support/factories/return_item_factory.rb +0 -14
  1587. data/lib/spree/testing_support/factories/shipment_factory.rb +0 -21
  1588. data/lib/spree/testing_support/factories/shipping_category_factory.rb +0 -9
  1589. data/lib/spree/testing_support/factories/shipping_method_factory.rb +0 -31
  1590. data/lib/spree/testing_support/factories/shipping_rate_factory.rb +0 -7
  1591. data/lib/spree/testing_support/factories/state_change_factory.rb +0 -8
  1592. data/lib/spree/testing_support/factories/stock_item_factory.rb +0 -22
  1593. data/lib/spree/testing_support/factories/store_credit_category_factory.rb +0 -9
  1594. data/lib/spree/testing_support/factories/store_credit_type_factory.rb +0 -16
  1595. data/lib/spree/testing_support/factories/wished_variant_factory.rb +0 -6
  1596. data/lib/spree/webhooks.rb +0 -22
  1597. data/lib/tasks/role_users.rake +0 -21
  1598. data/vendor/javascript/@rails--request.js.js +0 -4
  1599. data/vendor/javascript/@stimulus-components--auto-submit.js +0 -4
  1600. data/vendor/javascript/stimulus-reveal-controller.js +0 -4
  1601. data/vendor/javascript/stimulus-textarea-autogrow.js +0 -4
  1602. data/vendor/javascript/tailwindcss-stimulus-components.js +0 -4
@@ -4,22 +4,18 @@
4
4
  # Products properties include description, permalink, availability,
5
5
  # shipping category, etc. that do not change by variant.
6
6
  #
7
- # MASTER VARIANT
8
- # Every product has one master variant, which stores master price and sku, size and weight, etc.
9
- # The master variant does not have option values associated with it.
10
- # Price, SKU, size, weight, etc. are all delegated to the master variant.
11
- # Contains on_hand inventory levels only when there are no variants for the product.
12
- #
13
7
  # VARIANTS
14
- # All variants can access the product properties directly (via reverse delegation).
15
- # Inventory units are tied to Variant.
16
- # The master variant can have inventory units, but not option values.
17
- # All other variants have option values and may have inventory units.
18
- # Sum of on_hand each variant's inventory level determine "on_hand" level for the product.
8
+ # Every product has at least one variant. Simple products have a single variant
9
+ # with no option values; products with options have one variant per combination.
10
+ # `default_variant` is the "face" of the product — price, SKU, size, weight, etc.
11
+ # are delegated to it. Inventory units are tied to Variant; the sum of each
12
+ # variant's on_hand determines the product's "on_hand" level.
19
13
  #
20
14
 
21
15
  module Spree
22
16
  class Product < Spree.base_class
17
+ include Spree::CSV::CustomFieldsHelper
18
+
23
19
  has_prefix_id :prod # Stripe: prod_
24
20
 
25
21
  acts_as_paranoid
@@ -30,40 +26,52 @@ module Spree
30
26
  include Spree::ProductScopes
31
27
  include Spree::TranslatableResource
32
28
  include Spree::MemoizedData
33
- include Spree::Metafields
34
- include Spree::MetafieldFilterable
29
+ include Spree::SanitizableRichText
30
+ include Spree::HasCustomFields
31
+ include Spree::CustomFieldFilterable
35
32
  include Spree::Metadata
33
+ include Spree::HasExternalReferences
36
34
  include Spree::Searchable
37
- include Spree::Product::Webhooks
38
35
  include Spree::Product::Slugs
39
36
  include Spree::Product::Channels
40
- include Spree::LegacyMultiStoreSupport unless defined?(SpreeMultiStore)
41
37
  include Spree::SearchIndexable
42
- if defined?(Spree::VendorConcern)
43
- include Spree::VendorConcern
44
- end
38
+ include Spree::StorePreferences
45
39
 
46
40
  publishes_lifecycle_events
47
41
 
48
- MEMOIZED_METHODS = %w[total_on_hand taxonomy_ids taxon_and_ancestors
42
+ MEMOIZED_METHODS = %w[total_on_hand category_and_ancestors
49
43
  default_variant_id tax_category default_variant variant_for_images
50
- brand_taxon main_taxon
44
+ primary_category buy_box_variants resolved_delivery_profile
51
45
  purchasable? in_stock? backorderable? digital?]
52
46
 
47
+ # Statuses an operator sets directly. `bulk_status_update` validates
48
+ # against this, which is why the review statuses are not in it: reaching
49
+ # `proposed` is a seller submitting and reaching `rejected` is an operator
50
+ # deciding, and neither is a value to assign in bulk.
53
51
  STATUSES = %w[draft active archived].freeze
54
52
 
55
- STATUS_TO_WEBHOOK_EVENT = {
56
- 'active' => 'activated',
57
- 'draft' => 'drafted',
58
- 'archived' => 'archived'
59
- }.freeze
53
+ # Where a product sits in the marketplace review lifecycle. Only the
54
+ # workflows (Products::Propose, ::Approve, ::Reject) move a product in or
55
+ # out of these; see docs/plans/6.0-seller-product-submission.md.
56
+ REVIEW_STATUSES = %w[proposed rejected].freeze
57
+
58
+ ALL_STATUSES = (STATUSES + REVIEW_STATUSES).freeze
60
59
 
61
60
  TRANSLATABLE_FIELDS = %i[name description slug meta_description meta_title].freeze
62
61
  RICH_TEXT_TRANSLATABLE_FIELDS = %i[description].freeze
63
62
  translates(*TRANSLATABLE_FIELDS, column_fallback: Spree.mobility_column_fallback)
64
63
 
64
+ has_spree_rich_text :description
65
+
66
+ # Translated descriptions live on the Mobility translation table, so writes
67
+ # through locale accessors or +upsert_translations+ never touch the base
68
+ # record's +description+ attribute — the translation model needs its own
69
+ # sanitization pass.
65
70
  self::Translation.class_eval do
71
+ include Spree::SanitizableRichText
72
+
66
73
  normalizes :name, :meta_title, with: ->(value) { value&.to_s&.squish&.presence }
74
+ sanitizes_rich_text :description
67
75
  end
68
76
 
69
77
  # we need to have this callback before any dependent: :destroy associations
@@ -72,10 +80,76 @@ module Spree
72
80
 
73
81
  has_many :product_option_types, -> { order(:position) }, dependent: :destroy, inverse_of: :product
74
82
  has_many :option_types, through: :product_option_types
75
- has_many :classifications, -> { order(created_at: :asc) }, dependent: :delete_all, inverse_of: :product
76
- has_many :taxons, through: :classifications, before_remove: :remove_taxon
77
- has_many :categories, through: :classifications, class_name: 'Spree::Category', source: :taxon
78
- has_many :taxonomies, through: :taxons
83
+ has_many :product_categories, -> { order(created_at: :asc) }, class_name: 'Spree::ProductCategory', dependent: :delete_all, inverse_of: :product
84
+ has_many :categories, through: :product_categories, class_name: 'Spree::Category', source: :category, before_remove: :remove_category
85
+ has_many :taxonomies, through: :categories, deprecated: true
86
+
87
+ # @deprecated The classification_count column was renamed to categories_count in 6.0.
88
+ alias_attribute :classification_count, :categories_count
89
+
90
+ # @deprecated Use #product_categories; removed in 6.1.
91
+ # The live row in the review trail, or nil for a product nobody submitted.
92
+ #
93
+ # @return [Spree::ProductSubmission, nil]
94
+ def latest_submission
95
+ submissions.latest_first.first
96
+ end
97
+
98
+ # Why the marketplace turned this listing down, for the seller to read.
99
+ # Answers nil once the product moves on, so a stale note never shows
100
+ # against a product that is no longer rejected.
101
+ #
102
+ # @return [String, nil]
103
+ def rejection_reason
104
+ return unless rejected?
105
+
106
+ latest_submission&.review_note
107
+ end
108
+
109
+ # Whether this product is waiting on, or has had, a marketplace decision.
110
+ #
111
+ # @return [Boolean]
112
+ def in_review?
113
+ REVIEW_STATUSES.include?(status)
114
+ end
115
+
116
+ def classifications
117
+ Spree::Deprecation.warn('Spree::Product#classifications is deprecated and will be removed in Spree 6.1. Use #product_categories instead.')
118
+ product_categories
119
+ end
120
+
121
+ # @deprecated Use #categories / #category_ids; removed in 6.1.
122
+ def taxons
123
+ Spree::Deprecation.warn('Spree::Product#taxons is deprecated and will be removed in Spree 6.1. Use #categories instead.')
124
+ categories
125
+ end
126
+
127
+ def taxons=(value)
128
+ Spree::Deprecation.warn('Spree::Product#taxons= is deprecated and will be removed in Spree 6.1. Use #categories= instead.')
129
+ self.categories = value
130
+ end
131
+
132
+ def taxon_ids
133
+ Spree::Deprecation.warn('Spree::Product#taxon_ids is deprecated and will be removed in Spree 6.1. Use #category_ids instead.')
134
+ category_ids
135
+ end
136
+
137
+ def taxon_ids=(ids)
138
+ Spree::Deprecation.warn('Spree::Product#taxon_ids= is deprecated and will be removed in Spree 6.1. Use #category_ids= instead.')
139
+ self.category_ids = ids
140
+ end
141
+
142
+ has_many :product_collections, class_name: 'Spree::ProductCollection', dependent: :destroy_async, inverse_of: :product
143
+ has_many :collections, through: :product_collections
144
+ has_many :catalog_products, class_name: 'Spree::CatalogProduct', dependent: :destroy_async, inverse_of: :product
145
+ has_many :catalogs, through: :catalog_products
146
+
147
+ # Dropped with the product so a deleted product stops excluding itself from
148
+ # delivery methods. Destroyed inline rather than async — a product sits in
149
+ # at most a handful of exclusion rules, and deferring it would leave the
150
+ # rule reporting an id the API can no longer resolve.
151
+ has_many :delivery_method_rule_products, class_name: 'Spree::DeliveryMethodRuleProduct',
152
+ dependent: :destroy, inverse_of: :product
79
153
 
80
154
  has_many :product_promotion_rules, class_name: 'Spree::ProductPromotionRule'
81
155
  has_many :promotion_rules, through: :product_promotion_rules, class_name: 'Spree::PromotionRule'
@@ -86,65 +160,92 @@ module Spree
86
160
  class_name: 'Spree::Promotion',
87
161
  source: :promotion
88
162
 
89
- belongs_to :tax_category, class_name: 'Spree::TaxCategory'
90
- belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :products
91
- has_many :shipping_methods, through: :shipping_category, class_name: 'Spree::ShippingMethod'
163
+ belongs_to :tax_category, class_name: 'Spree::TaxCategory', optional: true
164
+ belongs_to :product_type, class_name: 'Spree::ProductType', optional: true, counter_cache: :products_count
165
+ # The seller this product belongs to on a marketplace. Nil is the
166
+ # operator's own catalog — which is everything, on a store selling only
167
+ # its own goods.
168
+ belongs_to :seller, class_name: 'Spree::Seller', optional: true, inverse_of: :products
169
+
170
+ # How this product ships: origins, zones and methods all hang off the
171
+ # profile. Required — a product without one could not be fulfilled at
172
+ # all — and auto-assigned on create (type template, else the store's
173
+ # default profile), so callers never have to think about it. The FK is
174
+ # the renamed 5.x shipping_category_id, so migrated catalogs arrive
175
+ # already assigned.
176
+ belongs_to :delivery_profile, class_name: 'Spree::DeliveryProfile', inverse_of: :products
177
+
178
+ # Every one of the product's variants now resolves its profile through
179
+ # +resolved_delivery_profile+, which memoizes — so a reassignment has to
180
+ # drop the memo, or the whole fleet keeps shipping on the old profile
181
+ # until the product is saved.
182
+ %i[delivery_profile delivery_profile_id].each do |name|
183
+ define_method(:"#{name}=") do |value|
184
+ @resolved_delivery_profile = nil
185
+ @digital = nil
186
+ super(value)
187
+ end
188
+ end
92
189
 
93
- has_one :master,
94
- -> { where is_master: true },
95
- inverse_of: :product,
96
- class_name: 'Spree::Variant'
190
+ # Guards every write path (including raw prefixed-id assignment) against
191
+ # linking another store's profile.
192
+ validate :delivery_profile_must_belong_to_store, if: :delivery_profile_id_changed?
193
+ # Either side moving breaks the pair, and a product's store is not frozen
194
+ # after create, so watching only the seller would let a move carry the old
195
+ # store's seller along.
196
+ validate :seller_must_belong_to_store, if: -> { seller_id_changed? || store_id_changed? }
97
197
 
198
+ # Every product has at least one variant. `default_variant` is the "face" of
199
+ # the product (price display, default add-to-cart, property delegation).
98
200
  has_many :variants,
99
- -> { where(is_master: false).order(:position) },
100
- inverse_of: :product,
101
- class_name: 'Spree::Variant'
102
-
103
- has_many :variants_including_master,
104
201
  -> { order(:position) },
105
202
  inverse_of: :product,
106
203
  class_name: 'Spree::Variant',
107
204
  dependent: :destroy
108
205
 
109
- has_many :prices, -> { order('spree_variants.position, spree_variants.id, currency') }, through: :variants
206
+ belongs_to :default_variant, class_name: 'Spree::Variant', optional: true, autosave: true
207
+
208
+ has_many :prices, through: :variants
110
209
 
111
- has_many :stock_items, through: :variants_including_master
210
+ has_many :stock_levels, through: :variants
112
211
 
113
- has_many :line_items, through: :variants_including_master
212
+ has_many :line_items, through: :variants
114
213
  has_many :orders, through: :line_items
115
214
  has_many :completed_orders, -> { reorder(nil).distinct.complete }, through: :line_items, source: :order
116
215
 
117
- has_many :media, -> { order(:position) }, as: :viewable, dependent: :destroy, class_name: 'Spree::Asset'
216
+ has_many :media, -> { order(:position) }, as: :viewable, dependent: :destroy, class_name: 'Spree::Media'
118
217
 
119
- has_many :variant_images, -> { order(:position) }, source: :images, through: :variants_including_master
120
- has_many :variant_images_without_master, -> { order(:position) }, source: :images, through: :variants
218
+ # The review trail for a seller's listing. Append-only: the latest row is
219
+ # the live one (docs/plans/6.0-seller-product-submission.md).
220
+ has_many :submissions, class_name: 'Spree::ProductSubmission', dependent: :destroy, inverse_of: :product
121
221
 
122
- belongs_to :primary_media, class_name: 'Spree::Asset', optional: true, foreign_key: :primary_media_id
222
+ has_many :variant_images, -> { order(:position) }, source: :images, through: :variants
223
+
224
+ belongs_to :primary_media, class_name: 'Spree::Media', optional: true, foreign_key: :primary_media_id
123
225
 
124
226
  has_many :option_value_variants, class_name: 'Spree::OptionValueVariant', through: :variants
125
227
  has_many :option_values, class_name: 'Spree::OptionValue', through: :variants
126
228
 
127
- has_many :prices_including_master, -> { non_zero }, through: :variants_including_master, source: :prices
128
-
129
- has_many :digitals, through: :variants_including_master
229
+ has_many :digital_assets, through: :variants, class_name: 'Spree::DigitalAsset'
230
+ has_many :digitals, through: :variants, class_name: 'Spree::DigitalAsset', source: :digital_assets, deprecated: true
130
231
 
131
- after_initialize :ensure_master
132
232
  after_initialize :assign_default_tax_category
233
+ # The type's profile is a creation-time template: stamped here, never
234
+ # synced afterwards — reassigning the type later leaves the product's
235
+ # profile alone. Falls back to the store's default profile, keeping the
236
+ # required association satisfiable without the caller naming one.
237
+ before_validation :stamp_template_delivery_profile, on: :create
133
238
 
134
- before_validation :validate_master
135
- before_validation :ensure_default_shipping_category
239
+ after_create :sync_associations_from_product_type
240
+ after_update :sync_associations_from_product_type, if: :saved_change_to_product_type_id?
241
+ after_create :ensure_default_variant
242
+ after_create :set_default_variant
136
243
 
137
- after_create :add_associations_from_prototype
138
- after_create :build_variants_from_option_values_hash, if: :option_values_hash
139
- after_create :apply_pending_variants, if: :pending_variants?
140
- after_save :apply_pending_media, if: :pending_media?
244
+ after_save :auto_promote_default_variant
245
+ after_save :run_touch_callbacks, if: :saved_changes?
246
+ after_touch :touch_categories
141
247
 
142
- after_save :save_master
143
- after_save :run_touch_callbacks, if: :anything_changed?
144
- after_save :reset_nested_changes
145
- after_touch :touch_taxons
146
-
147
- after_commit :auto_match_taxons, if: :eligible_for_taxon_matching?
248
+ after_commit :auto_match_collections, if: :eligible_for_collection_matching?
148
249
 
149
250
  with_options length: { maximum: 255 }, allow_blank: true do
150
251
  validates :meta_keywords
@@ -152,44 +253,53 @@ module Spree
152
253
  end
153
254
  with_options presence: true do
154
255
  validates :name
155
- validates :shipping_category, if: :requires_shipping_category?
156
- validates :price, if: :requires_price?
157
256
  end
158
257
 
258
+ # Every persisted product must keep a default variant. It's assigned on
259
+ # create by +set_default_variant+, so only enforce it on update.
260
+ validates :default_variant, presence: true, on: :update
261
+
159
262
  validate :discontinue_on_must_be_later_than_make_active_at, if: -> { make_active_at && discontinue_on }
160
263
 
161
264
  scope :for_store, ->(store) { where(store_id: store.id) }
162
- scope :draft, -> { where(status: 'draft') }
163
- scope :archived, -> { where(status: 'archived') }
265
+ # Products belonging to the given seller. Passing nil selects the
266
+ # operator's own catalog, which on a store with no sellers is everything.
267
+ # The same answer `Seller#products` gives, as a scope a bulk read can chain.
268
+ scope :for_seller, ->(seller) { where(seller_id: seller.respond_to?(:id) ? seller.id : seller) }
164
269
  scope :not_archived, -> { where.not(status: 'archived') }
165
270
  scope :on_sale, lambda { |currency = nil|
166
271
  currency ||= Spree::Store.default.default_currency
167
- joins(:prices_including_master).with_currency(currency).
272
+ with_currency(currency).
273
+ where.not(spree_prices: { amount: 0 }).
168
274
  where.not(spree_prices: { compare_at_amount: [nil, 0] }).
169
275
  where("#{Spree::Price.table_name}.compare_at_amount > #{Spree::Price.table_name}.amount")
170
276
  }
171
277
 
172
- scope :search, ->(query) {
278
+ # @param store [Spree::Store] whose custom-field definitions take part in
279
+ # the search. Definitions are store-owned, so the caller says which store
280
+ # is searching; `Spree::Current.store` is the request's answer and a
281
+ # background caller (an export, say) passes its own.
282
+ scope :search, ->(query, store = Spree::Current.store) {
173
283
  next none if query.blank?
174
284
 
175
285
  product_ids = Spree::Variant.search_by_product_name_or_sku(query).pluck(:product_id)
176
286
 
177
- searchable_definitions = Spree::MetafieldDefinition.
287
+ searchable_definitions = store.custom_field_definitions.
178
288
  for_resource_type('Spree::Product').
179
289
  searchable
180
290
 
181
- # Only pay for the metafield scan when some definition opts into search;
182
- # the leading-wildcard LIKE over spree_metafields can't use an index.
183
- metafield_ids = if searchable_definitions.exists?
184
- joins(metafields: :metafield_definition).
291
+ # Only pay for the custom_field scan when some definition opts into search;
292
+ # the leading-wildcard LIKE over spree_custom_fields can't use an index.
293
+ custom_field_ids = if searchable_definitions.exists?
294
+ joins(custom_fields: :custom_field_definition).
185
295
  merge(searchable_definitions).
186
- where(search_condition(Spree::Metafield, :value, query)).
296
+ where(search_condition(Spree::CustomField, :value, query)).
187
297
  unscope(:order).distinct.pluck(:id)
188
298
  else
189
299
  []
190
300
  end
191
301
 
192
- where(id: (product_ids + metafield_ids).uniq.compact)
302
+ where(id: (product_ids + custom_field_ids).uniq.compact)
193
303
  }
194
304
 
195
305
  # Backward compatibility alias — remove in Spree 6.0
@@ -197,19 +307,15 @@ module Spree
197
307
 
198
308
  scope :archivable, -> { where(status: %w[active draft]) }
199
309
  scope :by_source, ->(source) { send(source) }
200
- scope :paused, -> { where(status: 'paused') }
201
310
  scope :published, -> { where(status: 'active') }
202
311
 
203
- attr_accessor :option_values_hash
204
-
205
312
  accepts_nested_attributes_for(
206
313
  :variants,
207
314
  allow_destroy: true,
208
315
  reject_if: lambda do |v|
209
- v[:option_value_variants_attributes].blank? && v[:stock_items_attributes].blank? && v[:prices_attributes].blank?
316
+ v[:option_value_variants_attributes].blank? && v[:stock_levels_attributes].blank? && v[:prices_attributes].blank?
210
317
  end
211
318
  )
212
- accepts_nested_attributes_for :master, reject_if: :all_blank
213
319
  accepts_nested_attributes_for(
214
320
  :product_option_types,
215
321
  allow_destroy: true,
@@ -224,178 +330,202 @@ module Spree
224
330
  self.tag_list = tags
225
331
  end
226
332
 
227
- # Sets prices on the master variant.
333
+ # Sets prices on the default variant.
228
334
  # Accepts array of { currency:, amount:, compare_at_amount: } hashes.
229
335
  def prices=(prices_params)
230
- find_or_build_master.prices = prices_params
336
+ find_or_build_default_variant.prices = prices_params
231
337
  end
232
338
 
233
- # Maps 6.0 API name (category_ids) to model column (taxon_ids).
234
- # Accepts both prefixed IDs and raw integer IDs. Only taxons belonging to
235
- # the product's own store are assigned ids from another store's
236
- # taxonomies are dropped, preventing cross-store category attachment.
339
+ # Accepts both prefixed IDs and raw integer IDs. Only categories belonging to
340
+ # the product's own store are assigned ids from another store are dropped,
341
+ # preventing cross-store category attachment. Decodes prefixed IDs, then calls
342
+ # the generated setter via super.
237
343
  def category_ids=(ids)
238
344
  decoded_ids = Array(ids).filter_map do |id|
239
- id.to_s.include?('_') ? Spree::Taxon.decode_prefixed_id(id) : id
345
+ id.to_s.include?('_') ? Spree::Category.decode_prefixed_id(id) : id
240
346
  end
241
- self.taxon_ids = Spree::Taxon.for_store(assignable_store).where(id: decoded_ids).ids
347
+ super(Spree::Category.for_store(assignable_store).where(id: decoded_ids).ids)
242
348
  end
243
349
 
244
- # Sync media inline. Entries with `id` patch the existing asset
245
- # (alt/position/variant_ids); entries with `signed_id` create + attach a
246
- # fresh upload; missing items are left alone (delete still goes through
247
- # the dedicated DELETE /media endpoint to avoid accidental data loss when
248
- # a form ships stale state).
350
+ # Accepts both prefixed IDs and raw integer IDs. Only collections belonging
351
+ # to the product's own store are assigned; this overrides the generated
352
+ # setter and calls super.
249
353
  #
250
- # Deferred: ActiveStorage attaches require a persisted record, so on new
251
- # records we stash the params and replay them in `after_create`.
252
- # @param media_params [Array<Hash>]
253
- # @return [void]
254
- def media=(media_params)
255
- # Blank input is a no-op never call `super` with an empty array,
256
- # because the ActiveRecord collection setter would replace media with
257
- # `[]` and trigger `dependent: :destroy` on every persisted asset.
258
- # Explicit deletes go through the dedicated DELETE /media endpoint.
259
- return if media_params.blank?
260
- return super if media_params.first.is_a?(Spree::Asset)
261
-
262
- if new_record?
263
- @pending_media_params = media_params
264
- return
265
- end
266
-
267
- apply_media(media_params)
268
- end
269
-
270
- # Syncs variants from an array of hashes.
271
- # Creates new variants, updates existing ones (matched by :id), and removes unlisted ones.
272
- # Must be called on a persisted product (use after_save or call explicitly after create).
273
- # @param variants_params [Array<Hash>] array of variant attribute hashes
274
- # @return [void]
275
- def variants=(variants_params)
276
- return super if variants_params.blank? || variants_params.first.is_a?(Spree::Variant)
277
-
278
- # Store for deferred processing if product is not yet persisted
279
- if new_record?
280
- @pending_variants_params = variants_params
281
- return
354
+ # This is a replace setter, so it governs manual membership only. Automatic
355
+ # collections are materialized from their rules, which means they must
356
+ # survive a write that doesn't mention them (otherwise saving a product
357
+ # would wipe rule-derived membership until the next regeneration) and must
358
+ # not be assignable by hand (a hand-set member would vanish at that
359
+ # regeneration). So incoming automatic ids are ignored, and the product's
360
+ # existing automatic memberships are carried over untouched.
361
+ def collection_ids=(ids)
362
+ decoded_ids = Array(ids).filter_map do |id|
363
+ id.to_s.include?('_') ? Spree::Collection.decode_prefixed_id(id) : id
282
364
  end
283
-
284
- apply_variants(variants_params)
365
+ manual_ids = Spree::Collection.for_store(assignable_store).manual.where(id: decoded_ids).ids
366
+ super(manual_ids | collections.automatic.ids)
285
367
  end
286
368
 
287
- self.whitelisted_ransackable_attributes = %w[description name slug discontinue_on status available_on created_at updated_at]
288
- self.whitelisted_ransackable_associations = %w[taxons categories store channels variants_including_master master variants tags labels
289
- shipping_category classifications option_types]
290
- self.whitelisted_ransackable_scopes = %w[not_discontinued search_by_name in_taxon in_category in_categories price_between
369
+ self.whitelisted_ransackable_attributes = %w[description name slug discontinue_on status available_on created_at updated_at seller_id]
370
+ self.whitelisted_ransackable_associations = %w[categories collections store channels variants default_variant tags labels
371
+ product_type product_categories option_types seller]
372
+ self.whitelisted_ransackable_scopes = %w[not_discontinued search_by_name in_taxon in_category in_categories in_collection price_between
291
373
  price_lte price_gte
292
374
  search multi_search in_stock out_of_stock with_option_value_ids
293
375
 
294
376
  ascend_by_price descend_by_price]
295
377
 
378
+ # All product-level convenience attributes delegate to the default variant —
379
+ # one target, no branching. Reads resolve the existing default variant
380
+ # (nil-safe) and never build one as a side effect; only setters build on
381
+ # demand, so `product.sku = ...` still works before the product is persisted.
296
382
  [
297
- :sku, :barcode, :weight, :height, :width, :depth, :is_master, :dimensions_unit, :weight_unit
383
+ :sku, :barcode, :weight, :height, :width, :depth, :dimensions_unit, :weight_unit,
384
+ :price_in, :amount_in, :compare_at_amount_in,
385
+ :cost_currency, :cost_price, :track_inventory
298
386
  ].each do |method_name|
299
- delegate method_name, :"#{method_name}=", to: :find_or_build_master
387
+ delegate method_name, to: :default_variant, allow_nil: true
388
+ delegate :"#{method_name}=", to: :find_or_build_default_variant
300
389
  end
301
390
 
302
- [
303
- :price, :price_in, :amount_in, :compare_at_price, :compare_at_amount_in,
304
- :currency, :cost_currency, :cost_price, :track_inventory
305
- ].each do |method_name|
306
- delegate method_name, :"#{method_name}=", to: :default_variant
391
+ # Read-only delegations: these have no product-level writer. Prices are
392
+ # written per currency through the default variant's +set_price+.
393
+ delegate :compare_at_price, :track_inventory?, :images, to: :default_variant, allow_nil: true
394
+
395
+ # No state machine — status moves through the Spree::Products workflows
396
+ # (docs/plans/6.0-service-workflows.md). has_status generates the `draft`
397
+ # and `archived` scopes and every predicate, but never redefines what the
398
+ # model already owns: `Product.active` stays the currency-aware scope from
399
+ # Spree::ProductScopes rather than a plain status lookup.
400
+ include Spree::HasStatus
401
+ has_status(*ALL_STATUSES, default: :draft)
402
+
403
+ # @deprecated Call Spree.product_activate_workflow — removed in 6.1.
404
+ def activate!
405
+ Spree::Deprecation.warn('Spree::Product#activate! is deprecated and will be removed in Spree 6.1. Call Spree.product_activate_workflow instead.')
406
+ run_status_workflow(Spree.product_activate_workflow)
307
407
  end
308
408
 
309
- delegate :display_amount, :display_price, :has_default_price?, :track_inventory?,
310
- :display_compare_at_price, :images, to: :default_variant
409
+ # @deprecated Call Spree.product_archive_workflow — removed in 6.1.
410
+ def archive!
411
+ Spree::Deprecation.warn('Spree::Product#archive! is deprecated and will be removed in Spree 6.1. Call Spree.product_archive_workflow instead.')
412
+ run_status_workflow(Spree.product_archive_workflow)
413
+ end
311
414
 
312
- # Rails doesn't provide _id methods for has_one associations by default
313
- delegate :id, to: :master, prefix: true, allow_nil: true
415
+ # @deprecated Call Spree.product_draft_workflow removed in 6.1.
416
+ def draft!
417
+ Spree::Deprecation.warn('Spree::Product#draft! is deprecated and will be removed in Spree 6.1. Call Spree.product_draft_workflow instead.')
418
+ run_status_workflow(Spree.product_draft_workflow)
419
+ end
314
420
 
315
- alias master_images images
421
+ def self.bulk_auto_match_collections(store, product_ids)
422
+ return if store.collections.automatic.none?
316
423
 
317
- state_machine :status, initial: :draft do
318
- event :activate do
319
- transition to: :active
320
- end
321
- after_transition to: :active, do: [:after_activate, :send_product_activated_webhook, :publish_product_activated_event]
424
+ products_to_auto_match_ids = store.products.not_deleted.not_archived.where(id: product_ids).ids
322
425
 
323
- event :archive do
324
- transition to: :archived
426
+ auto_match_collections_jobs = products_to_auto_match_ids.map do |product_id|
427
+ Spree::Products::AutoMatchCollectionsJob.new(product_id).tap { |job| job.scheduled_at = 30.seconds.from_now }
325
428
  end
326
- after_transition to: :archived, do: [:after_archive, :send_product_archived_webhook, :publish_product_archived_event]
327
429
 
328
- event :draft do
329
- transition to: :draft
330
- end
331
- after_transition to: :draft, do: [:after_draft, :send_product_drafted_webhook]
430
+ ActiveJob.perform_all_later(auto_match_collections_jobs)
332
431
  end
333
432
 
334
- def self.bulk_auto_match_taxons(store, product_ids)
335
- return if store.taxons.automatic.none?
336
-
337
- products_to_auto_match_ids = store.products.not_deleted.not_archived.where(id: product_ids).ids
433
+ # The variant this product leads with: its price, its stock, and what the
434
+ # add-to-cart button buys. On a product only the operator sells this is
435
+ # simply the cheapest available variant; once sellers share the listing it
436
+ # is whichever of their offers wins the buy box.
437
+ #
438
+ # Computed per read and never stored — it turns on price, stock and who is
439
+ # currently selling, none of which hold still
440
+ # (docs/plans/6.0-multi-vendor-marketplace.md, Decision 11).
441
+ #
442
+ # @param currency [String, nil]
443
+ # @param option_value_ids [Array<String, Integer>, nil] narrows to one option
444
+ # combination, so a `Condition` option gives a new buy box and a used one
445
+ # @return [Spree::Variant, nil]
446
+ def buy_box_variant(currency: nil, option_value_ids: nil)
447
+ currency ||= Spree::Current.currency
448
+ key = [currency, option_value_ids]
449
+ @buy_box_variants ||= {}
450
+ return @buy_box_variants[key] if @buy_box_variants.key?(key)
338
451
 
339
- auto_match_taxons_jobs = products_to_auto_match_ids.map do |product_id|
340
- Spree::Products::AutoMatchTaxonsJob.new(product_id).tap { |job| job.scheduled_at = 30.seconds.from_now }
341
- end
452
+ @buy_box_variants[key] = Spree::Dependencies.product_buy_box_service.constantize.call(
453
+ product: self, currency: currency, option_value_ids: option_value_ids
454
+ ).value
455
+ end
342
456
 
343
- ActiveJob.perform_all_later(auto_match_taxons_jobs)
457
+ # The variant this product leads with in a currency: the buy-box winner,
458
+ # else the default. One definition, because a caller that prices a page and
459
+ # a caller that preloads for it must land on the same variant — otherwise
460
+ # the preload misses and every row falls back to a query of its own.
461
+ #
462
+ # @param currency [String, nil]
463
+ # @return [Spree::Variant, nil]
464
+ def featured_variant(currency: nil)
465
+ buy_box_variant(currency: currency) || default_variant
344
466
  end
345
467
 
346
- # Can't use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
468
+ # Availability asks whether *anyone who is selling today* has this on the
469
+ # shelf. It deliberately does not ask the buy box: the winner is picked
470
+ # by price in one currency, and stock is a fact about the shelf, not about
471
+ # pricing — a variant stocked but priced only in another currency is still
472
+ # in stock. What it does share with the buy box is the seller test: a
473
+ # variant from a seller who is suspended, still onboarding or away is not
474
+ # for sale, so it cannot make the product available.
347
475
  def purchasable?
348
- @purchasable ||= default_variant.purchasable? || variants.any?(&:purchasable?)
476
+ return @purchasable unless @purchasable.nil?
477
+
478
+ @purchasable = sellable_variants.any?(&:purchasable?)
349
479
  end
350
480
 
351
- # Can't use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
352
481
  def in_stock?
353
- @in_stock ||= default_variant.in_stock? || variants.any?(&:in_stock?)
354
- end
482
+ return @in_stock unless @in_stock.nil?
355
483
 
356
- # Can't use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
357
- def backorderable?
358
- default_variant.backorderable? || variants.any?(&:backorderable?)
484
+ @in_stock = sellable_variants.any?(&:in_stock?)
359
485
  end
360
486
 
361
- def on_sale?(currency)
362
- prices_including_master.find_all { |p| p.currency == currency }.any?(&:discounted?)
487
+ def backorderable?
488
+ sellable_variants.any?(&:backorderable?)
363
489
  end
364
490
 
365
- def find_or_build_master
366
- master || build_master
491
+ # The variants a shopper could actually buy from: every one whose seller
492
+ # is selling today, first-party included. Reads the loaded association so
493
+ # a serialized list resolves it from variants it already holds.
494
+ #
495
+ # @return [Array<Spree::Variant>]
496
+ def sellable_variants
497
+ variants.select do |variant|
498
+ seller = variant.resolved_seller
499
+ seller.nil? || seller.sellable?
500
+ end
367
501
  end
368
502
 
369
- # Checks if product has variants (non-master variants)
370
- # Uses variant_count counter cache for performance
371
- # @return [Boolean]
372
- def has_variants?
373
- return variants.size.positive? if variants.loaded?
374
-
375
- variant_count.positive?
503
+ def on_sale?(currency)
504
+ prices.find_all { |p| p.currency == currency && p.non_zero? }.any?(&:discounted?)
376
505
  end
377
506
 
378
- # Returns default Variant for Product
379
- # If `track_inventory_levels` is enabled it will try to find the first Variant
380
- # in stock or backorderable, if there's none it will return first Variant sorted
381
- # by `position` attribute
382
- # If `track_inventory_levels` is disabled it will return first Variant sorted
383
- # by `position` attribute
384
- #
507
+ # The default variant is a persisted FK, but before the product is saved (or
508
+ # if it somehow has no default set) fall back to the first variant —
509
+ # including one built in memory via the delegated setters.
385
510
  # @return [Spree::Variant]
386
511
  def default_variant
387
- @default_variant ||= if Spree::Config[:track_inventory_levels] && has_variants? && available_variant = variants.detect(&:purchasable?)
388
- available_variant
389
- else
390
- has_variants? ? variants.first : find_or_build_master
391
- end
512
+ super || variants.first
513
+ end
514
+
515
+ def find_or_build_default_variant
516
+ default_variant || variants.build
392
517
  end
393
518
 
394
- # Returns default Variant ID for Product
395
- # @return [Integer]
396
- def default_variant_id
397
- @default_variant_id ||= default_variant.id
519
+ # "Does this product have more than one variant (i.e. options to choose from)?"
520
+ # Uses the variant_count counter cache for performance.
521
+ # @return [Boolean]
522
+ def has_multiple_variants?
523
+ return variants.size > 1 if variants.loaded?
524
+
525
+ variant_count > 1
398
526
  end
527
+ # @deprecated Use #has_multiple_variants? — will be removed in Spree 6.1.
528
+ alias has_variants? has_multiple_variants?
399
529
 
400
530
  # Returns the product's media gallery.
401
531
  # Uses product-level media if present, otherwise falls back to variant images.
@@ -419,34 +549,19 @@ module Spree
419
549
  alias has_variant_images? has_media?
420
550
 
421
551
  # Returns the variant that should be used for displaying images.
422
- # Priority: master > default_variant > first variant with images
552
+ # Priority: default_variant > first variant with images
423
553
  # @return [Spree::Variant, nil]
424
554
  def variant_for_images
425
555
  @variant_for_images ||= find_variant_for_images
426
556
  end
427
557
 
428
- # @deprecated Use #primary_media instead.
429
- def default_image
430
- Spree::Deprecation.warn('Spree::Product#default_image is deprecated and will be removed in Spree 6.0. Please use Spree::Product#primary_media instead.')
431
- primary_media
432
- end
433
-
434
- # @deprecated Use #primary_media instead.
435
- def featured_image
436
- Spree::Deprecation.warn('Spree::Product#featured_image is deprecated and will be removed in Spree 6.0. Please use Spree::Product#primary_media instead.')
437
- primary_media
438
- end
439
-
440
- # @deprecated Use #primary_media instead.
441
- def primary_image
442
- Spree::Deprecation.warn('Spree::Product#primary_image is deprecated and will be removed in Spree 6.0. Please use Spree::Product#primary_media instead.')
443
- primary_media
444
- end
445
-
446
- # Returns secondary media for Product (for hover effects).
447
- # @return [Spree::Asset, nil]
558
+ # @deprecated Read #gallery_media directly; removed in 6.1. Nothing in
559
+ # Spree calls this — a hover image is a storefront presentation choice,
560
+ # not something core should name.
561
+ # @return [Spree::Media, nil]
448
562
  def secondary_image
449
- variant_for_images&.secondary_image
563
+ Spree::Deprecation.warn('Spree::Product#secondary_image is deprecated and will be removed in Spree 6.1. Use #gallery_media instead.')
564
+ gallery_media.second
450
565
  end
451
566
 
452
567
  # @deprecated Use media_count instead
@@ -458,7 +573,14 @@ module Spree
458
573
  # Checks product-level media first, then falls back to variant images.
459
574
  # Called when media is added, removed, or reordered.
460
575
  def update_thumbnail!
461
- first_media = media.order(:position).first || variant_images.order(:position).first
576
+ # Fresh scopes, never the cached associations — this runs from a media
577
+ # row's after_commit, where a loaded association is missing its siblings.
578
+ candidates = media.reload.order(:position).presence || variant_images.reload.order(:position)
579
+ # Only media that can draw as an image: `thumbnail_url` is rendered in an
580
+ # <img> everywhere it's used, so a video with no still leaves no
581
+ # thumbnail at all rather than a broken one.
582
+ first_media = candidates.find(&:renderable_as_image?)
583
+
462
584
  update_column(:primary_media_id, first_media&.id)
463
585
  end
464
586
 
@@ -479,12 +601,9 @@ module Spree
479
601
  # Returns tax category for Product
480
602
  # @return [Spree::TaxCategory, nil]
481
603
  def tax_category
482
- @tax_category ||= super || TaxCategory.default
604
+ @tax_category ||= super || TaxCategory.default(assignable_store)
483
605
  end
484
606
 
485
- # Adding properties and option types on creation based on a chosen prototype
486
- attr_accessor :prototype_id
487
-
488
607
  def first_or_default_variant(currency)
489
608
  if !has_variants?
490
609
  default_variant
@@ -495,38 +614,50 @@ module Spree
495
614
  end
496
615
  end
497
616
 
617
+ # The first variant a shopper can actually buy, in this currency.
618
+ #
619
+ # Position order, deliberately — this is "the first one that works", not
620
+ # the buy box's ranked winner. What it does borrow from the buy box is the
621
+ # seller check: a variant from a seller who is suspended, still onboarding
622
+ # or away is not for sale, so it cannot be what the product displays.
498
623
  def first_available_variant(currency)
499
- variants.find { |v| v.purchasable? && v.price_in(currency).amount.present? }
624
+ sellable_variants.find do |variant|
625
+ variant.purchasable? && variant.price_in(currency).amount.present?
626
+ end
500
627
  end
501
628
 
629
+ # Whether the shop price differs between this product's variants. Reads
630
+ # base prices only: a price list's rows are what one audience pays under an
631
+ # agreement, and a ladder holds several rows for one variant, so counting
632
+ # them would report a product as varying when it does not
633
+ # (docs/plans/6.0-volume-pricing.md).
502
634
  def price_varies?(currency)
503
- prices_including_master.find_all { |p| p.currency == currency && p.amount.present? }.map(&:amount).uniq.count > 1
635
+ shop_prices(currency).map(&:amount).uniq.count > 1
504
636
  end
505
637
 
506
638
  def any_variant_available?(currency)
507
- if has_variants?
508
- first_available_variant(currency).present?
509
- else
510
- master.purchasable? && master.price_in(currency).amount.present?
511
- end
639
+ first_available_variant(currency).present?
512
640
  end
513
641
 
514
642
  # returns the lowest price for the product in the given currency
515
- # prices_including_master are usually already loaded, so this should not trigger an extra query
643
+ # prices are usually already loaded, so this should not trigger an extra query
516
644
  def lowest_price(currency)
517
- prices_including_master.find_all { |p| p.currency == currency }.min_by(&:amount)
645
+ shop_prices(currency).min_by(&:amount)
518
646
  end
519
647
 
520
- # Ensures option_types and product_option_types exist for keys in option_values_hash
521
- def ensure_option_types_exist_for_values_hash
522
- return if option_values_hash.nil?
523
-
524
- # we need to convert the keys to string to make it work with UUIDs
525
- required_option_type_ids = option_values_hash.keys.map(&:to_s)
526
- missing_option_type_ids = required_option_type_ids - option_type_ids.map(&:to_s)
527
- missing_option_type_ids.each do |id|
528
- product_option_types.create(option_type_id: id)
529
- end
648
+ # The prices this product is offered at publicly — one per variant. A price
649
+ # list's rows are excluded: they are what one audience pays under an
650
+ # agreement, so surfacing the cheapest of them as the product's "from"
651
+ # price would quote every shopper a contract they are not party to.
652
+ #
653
+ # Filtered in memory rather than through a scope because callers have
654
+ # already loaded the association, and a scope would re-query per product
655
+ # in a listing.
656
+ #
657
+ # @param currency [String]
658
+ # @return [Array<Spree::Price>]
659
+ def shop_prices(currency)
660
+ prices.find_all { |p| p.currency == currency && p.price_list_id.nil? && p.non_zero? }
530
661
  end
531
662
 
532
663
  # for adding products which are closely related to existing ones
@@ -572,21 +703,14 @@ module Spree
572
703
  !!discontinue_on && discontinue_on <= Time.current
573
704
  end
574
705
 
575
- # determine if any variant (including master) can be supplied
706
+ # determine if any variant can be supplied
576
707
  def can_supply?
577
- variants_including_master.any?(&:can_supply?)
708
+ variants.any?(&:can_supply?)
578
709
  end
579
710
 
580
- # determine if any variant (including master) is out of stock and backorderable
711
+ # determine if any variant is out of stock and backorderable
581
712
  def backordered?
582
- variants_including_master.any?(&:backordered?)
583
- end
584
-
585
- def self.like_any(fields, values)
586
- conditions = fields.product(values).map do |(field, value)|
587
- arel_table[field].matches("%#{value}%")
588
- end
589
- where conditions.inject(:or)
713
+ variants.any?(&:backordered?)
590
714
  end
591
715
 
592
716
  # Suitable for displaying only variants that has at least one option value.
@@ -602,78 +726,91 @@ module Spree
602
726
  @total_on_hand ||= if any_variants_not_track_inventory?
603
727
  BigDecimal::INFINITY
604
728
  else
605
- if variants_including_master.loaded?
606
- variants_including_master.sum(&:total_on_hand)
729
+ if variants.loaded?
730
+ variants.sum(&:total_on_hand)
607
731
  else
608
- stock_items.loaded? ? stock_items.sum(&:count_on_hand) : stock_items.sum(:count_on_hand)
732
+ stock_levels.loaded? ? stock_levels.sum(&:available_count) : stock_levels.sum('count_on_hand - allocated_count')
609
733
  end
610
734
  end
611
735
  end
612
736
 
613
- # Master variant may be deleted (i.e. when the product is deleted)
614
- # which would make AR's default finder return nil.
615
- # This is a stopgap for that little problem.
737
+ # @deprecated Use {#stock_levels}; removed in 6.1.
738
+ def stock_items
739
+ Spree::Deprecation.warn('Spree::Product#stock_items is deprecated and will be removed in Spree 6.1. Use #stock_levels instead.')
740
+ stock_levels
741
+ end
742
+
743
+ # @deprecated Use #default_variant — the master variant is being removed in Spree 6.1.
616
744
  def master
617
- super || variants_including_master.with_deleted.find_by(is_master: true)
618
- end
619
-
620
- # Returns the brand taxon for the product
621
- # @return [Spree::Taxon]
622
- def brand_taxon
623
- @brand_taxon ||= if classification_count.zero?
624
- nil
625
- elsif Spree.use_translations?
626
- taxons.joins(:taxonomy).
627
- join_translation_table(Taxonomy).
628
- find_by(Taxonomy.translation_table_alias => { name: Spree.t(:taxonomy_brands_name) })
629
- elsif taxons.loaded?
630
- taxons.find { |taxon| taxon.taxonomy.name == Spree.t(:taxonomy_brands_name) }
631
- else
632
- taxons.joins(:taxonomy).find_by(Taxonomy.table_name => { name: Spree.t(:taxonomy_brands_name) })
633
- end
634
- end
635
-
636
- # Returns the brand name for the product
637
- # @return [String]
638
- def brand_name
639
- brand_taxon&.name
745
+ Spree::Deprecation.warn('Spree::Product#master is deprecated and will be removed in Spree 6.1. Use #default_variant instead.')
746
+ default_variant
640
747
  end
641
748
 
642
- def main_taxon
643
- return if classification_count.zero?
749
+ # @deprecated Use #variants — will be removed in Spree 6.1.
750
+ def variants_including_master
751
+ Spree::Deprecation.warn('Spree::Product#variants_including_master is deprecated and will be removed in Spree 6.1. Use #variants instead.')
752
+ variants
753
+ end
644
754
 
645
- @main_taxon ||= taxons.first
755
+ # @deprecated Use #default_variant_id — will be removed in Spree 6.1.
756
+ def master_id
757
+ default_variant_id
646
758
  end
647
759
 
648
- def taxons_for_store(store)
649
- return if classification_count.zero?
760
+ # The product's primary category — the first manually-assigned category.
761
+ # Renamed from #main_taxon in 6.0.
762
+ def primary_category
763
+ return if categories_count.zero?
764
+
765
+ @primary_category ||= categories.first
766
+ end
650
767
 
651
- taxons.loaded? ? taxons.find_all { |taxon| taxon.taxonomy.store_id == store.id } : taxons.for_store(store)
768
+ # @deprecated Use {#primary_category}.
769
+ def main_taxon
770
+ Spree::Deprecation.warn('Spree::Product#main_taxon is deprecated and will be removed in Spree 6.1. Use #primary_category instead.')
771
+ primary_category
652
772
  end
653
773
 
654
774
  def any_variant_in_stock_or_backorderable?
655
775
  if has_variants?
656
- variants_including_master.in_stock_or_backorderable.exists?
776
+ variants.in_stock_or_backorderable.not_discontinued.exists?
657
777
  else
658
- master.in_stock_or_backorderable?
778
+ default_variant.in_stock_or_backorderable?
659
779
  end
660
780
  end
661
781
 
662
782
  # Check if the product is digital by checking if any of its shipping methods are digital delivery
663
783
  # This is used to determine if the product is digital and should have a digital delivery price
664
- # instead of a physical shipping price
784
+ # instead of a physical shipping price. The profile kind declares it —
785
+ # capabilities are never stated twice.
665
786
  #
666
787
  # @return [Boolean]
667
788
  def digital?
668
- @digital ||= shipping_category&.includes_digital_shipping_method?
789
+ # Memoized via nil-check: MemoizedData resets caches by assigning nil.
790
+ return @digital unless @digital.nil?
791
+
792
+ @digital = !!resolved_delivery_profile&.digital?
669
793
  end
670
794
 
671
- def auto_match_taxons
795
+ # The profile that governs this product. The association is required for
796
+ # new records; the store-default fallback covers rows predating the
797
+ # backfill (never expected in practice, cheap to be safe about).
798
+ #
799
+ # @return [Spree::DeliveryProfile, nil]
800
+ def resolved_delivery_profile
801
+ # Memoized via nil-check, like +digital?+ above: the store-default branch
802
+ # is an uncached lookup, and every variant on the product asks.
803
+ return @resolved_delivery_profile unless @resolved_delivery_profile.nil?
804
+
805
+ @resolved_delivery_profile = delivery_profile || (store && Spree::DeliveryProfile.default_for(store))
806
+ end
807
+
808
+ def auto_match_collections
672
809
  return if deleted?
673
810
  return if archived?
674
- return if store.nil? || store.taxons.automatic.none?
811
+ return if store.nil? || store.collections.automatic.none?
675
812
 
676
- Spree::Products::AutoMatchTaxonsJob.set(wait: 30.seconds).perform_later(id)
813
+ Spree::Products::AutoMatchCollectionsJob.set(wait: 30.seconds).perform_later(id)
677
814
  end
678
815
 
679
816
  def to_csv(store = nil)
@@ -688,21 +825,22 @@ module Spree
688
825
  else
689
826
  []
690
827
  end
691
- metafields_for_csv ||= Spree::MetafieldDefinition.for_resource_type('Spree::Product').order(:namespace, :key).map do |mf_def|
692
- metafields.find { |mf| mf.metafield_definition_id == mf_def.id }&.csv_value
693
- end
694
- taxons_for_csv ||= taxons.manual.reorder(depth: :desc).first(3).pluck(:pretty_name)
695
- taxons_for_csv.fill(nil, taxons_for_csv.size...3)
828
+ # Through the shared helper so the value row is built from exactly the
829
+ # definitions, in exactly the order, that Export#custom_fields_headers
830
+ # used for the header row — a mismatch shifts every value column.
831
+ custom_field_values = custom_fields_for_csv(self, store)
832
+ categories_for_csv ||= categories.reorder(depth: :desc).first(3).pluck(:pretty_name)
833
+ categories_for_csv.fill(nil, categories_for_csv.size...3)
696
834
 
697
835
  csv_lines = []
698
- all_variants = has_variants? ? variants_including_master.to_a : [master]
836
+ all_variants = variants.to_a
699
837
  default_currency = store.default_currency
700
838
  additional_currencies = store.supported_currencies_list.map(&:iso_code) - [default_currency]
701
839
 
702
840
  # Primary rows in the store's default currency
703
841
  all_variants.each_with_index do |variant, index|
704
- csv_lines << Spree::CSV::ProductVariantPresenter.new(self, variant, index, properties_for_csv, taxons_for_csv, store,
705
- metafields_for_csv).call
842
+ csv_lines << Spree::CSV::ProductVariantPresenter.new(self, variant, index, properties_for_csv, categories_for_csv, store,
843
+ custom_field_values).call
706
844
  end
707
845
 
708
846
  # Price-only rows for each additional currency
@@ -732,257 +870,118 @@ module Spree
732
870
  private
733
871
 
734
872
  # Determines which variant should be used for displaying media.
735
- # Priority: master > default_variant > first variant with media
873
+ # Priority: default_variant > first variant with media
736
874
  def find_variant_for_images
737
- return master if master.has_media?
738
- return default_variant if has_variants? && default_variant.has_media?
875
+ return default_variant if default_variant&.has_media?
739
876
  return find_variant_with_images if has_media?
740
877
 
741
878
  nil
742
879
  end
743
880
 
744
- def pending_variants?
745
- @pending_variants_params.present?
746
- end
747
-
748
- def apply_pending_variants
749
- return unless @pending_variants_params
750
-
751
- apply_variants(@pending_variants_params)
752
- @pending_variants_params = nil
753
- end
754
-
755
- def pending_media?
756
- @pending_media_params.present?
757
- end
881
+ # Additive, unlike the legacy prototype callback which replaced both sets —
882
+ # the type seeds the product when attached (creation, later assignment,
883
+ # reassignment) and never removes anything; detaching seeds nothing.
884
+ # The already-linked sets come from the join tables in one query each: the
885
+ # cached associations can be stale here (pending variants insert option-type
886
+ # joins through product_option_types within the same save).
887
+ def sync_associations_from_product_type
888
+ return unless product_type
758
889
 
759
- def apply_pending_media
760
- return unless @pending_media_params
890
+ missing_option_type_ids = product_type.option_type_ids - product_option_types.pluck(:option_type_id)
891
+ Spree::OptionType.where(id: missing_option_type_ids).each { |option_type| option_types << option_type }
761
892
 
762
- apply_media(@pending_media_params)
763
- @pending_media_params = nil
764
- end
765
-
766
- def apply_media(media_params)
767
- # Eager-load Asset descendants once so the type allowlist is stable across
768
- # the loop (and across requests once subclasses are referenced). Computed
769
- # per-call rather than at class-load to avoid forcing autoload of every
770
- # Asset subclass during boot.
771
- allowed_types = [Spree::Asset, *Spree::Asset.descendants].map(&:name).to_set
772
- media_params.each do |raw|
773
- attrs = raw.respond_to?(:to_h) ? raw.to_h : raw
774
- attrs = attrs.with_indifferent_access
775
-
776
- # Upsert path: entries with an `id` patch an existing asset (alt,
777
- # position, variant_ids). Entries with a `signed_id` create+attach.
778
- # Omitting an entry leaves it alone — explicit DELETE on the dedicated
779
- # media endpoint is still the only way to remove an asset.
780
- asset_id = attrs.delete(:id)
781
- if asset_id.present?
782
- asset = media.find_by_param(asset_id) || next
783
- asset.update!(attrs.except(:signed_id, :type))
784
- next
785
- end
786
-
787
- signed_id = attrs.delete(:signed_id)
788
- next if signed_id.blank?
789
-
790
- media_type = attrs.delete(:type) || 'Spree::Image'
791
- next unless allowed_types.include?(media_type)
792
-
793
- asset = media.build(attrs.except(:id))
794
- asset.type = media_type
795
- asset.attachment.attach(signed_id)
796
- asset.save!
797
- end
798
- end
799
-
800
- def apply_variants(variants_params)
801
- variant_ids_in_payload = []
802
- master_touched = false
803
- mutated = false
804
-
805
- variants_params.each do |variant_data|
806
- variant_data = variant_data.to_h.with_indifferent_access
807
- variant_id = variant_data.delete(:id)
808
- options = variant_data[:options]
809
-
810
- if variant_id.present?
811
- variant = variants_including_master.find_by_param!(variant_id)
812
- variant.update!(variant_data)
813
- variant_ids_in_payload << variant.id
814
- mutated = true
815
- elsif options.blank? || (options.is_a?(Array) && options.empty?)
816
- # An entry with no options addresses the master variant. Building a
817
- # non-master here would create a phantom duplicate (the auto-built
818
- # master already exists, and `variants` excludes it). Upsert onto
819
- # the master instead — the merchant-visible "default variant" on a
820
- # simple product IS the master.
821
- variant_data = variant_data.except(:options)
822
- target = find_or_build_master
823
- target.assign_attributes(variant_data)
824
- target.save!
825
- master_touched = true
826
- mutated = true
827
- else
828
- variant = variants.build
829
- variant.assign_attributes(variant_data)
830
- variant.save!
831
- variant_ids_in_payload << variant.id
832
- mutated = true
833
- end
834
- end
835
-
836
- # Remove variants not in the payload (only non-master). If only the
837
- # master was touched (simple product), leave existing non-master
838
- # variants alone — the payload is partial, not a full replacement.
839
- if variant_ids_in_payload.any? && !master_touched
840
- removed = variants.where.not(id: variant_ids_in_payload).destroy_all
841
- mutated ||= removed.any?
842
- end
843
-
844
- sync_variant_state! if mutated
845
- end
846
-
847
- # Re-syncs the in-memory derived variant state after `apply_variants`
848
- # mutates variants out-of-band (the variant counter is bumped via
849
- # `Spree::Product.increment_counter` in a Variant callback, and
850
- # `default_variant` is memoized). Without this, a freshly built/updated
851
- # product serialized in the same request returns a stale `variant_count`
852
- # and a stale `price` (delegated to the memoized `default_variant`).
853
- #
854
- # The `default_variant` memo reset here changes shape once master is
855
- # removed (it becomes a `belongs_to` FK). See
856
- # docs/plans/6.0-remove-master-variant.md (Phase 3).
857
- # @return [void]
858
- def sync_variant_state!
859
- # `variant_count` is maintained by a direct counter update in a Variant
860
- # callback, so the in-memory attribute is stale here — re-read it from the
861
- # row without reloading the whole record.
862
- self[:variant_count] = self.class.where(id: id).pick(:variant_count)
863
- variants.reset
864
- @default_variant = nil
865
- @default_variant_id = nil
866
- end
867
-
868
- def add_associations_from_prototype
869
- if prototype_id && prototype = Spree::Prototype.find_by(id: prototype_id)
870
- self.option_types = prototype.option_types
871
- self.taxons = prototype.taxons
872
- end
893
+ # Scoped to the product's own store, matching `category_ids=`: a type
894
+ # pointing at another store's category must not drag it onto the product.
895
+ missing_category_ids = product_type.category_ids - product_categories.pluck(:category_id)
896
+ Spree::Category.for_store(assignable_store).where(id: missing_category_ids).
897
+ each { |category| categories << category }
873
898
  end
874
899
 
875
900
  def any_variants_not_track_inventory?
876
- return true unless Spree::Config.track_inventory_levels
901
+ return true unless store_preference(:track_inventory_levels)
877
902
 
878
- if variants_including_master.loaded?
879
- variants_including_master.any? { |v| !v.track_inventory? }
903
+ if variants.loaded?
904
+ variants.any? { |v| !v.track_inventory? }
880
905
  else
881
- variants_including_master.where(track_inventory: false).exists?
906
+ variants.where(track_inventory: false).exists?
882
907
  end
883
908
  end
884
909
 
885
- # Builds variants from a hash of option types & values
886
- def build_variants_from_option_values_hash
887
- Spree::Deprecation.warn('Spree::Product#build_variants_from_option_values_hash is deprecated and will be removed in Spree 6.0.')
888
- ensure_option_types_exist_for_values_hash
889
- values = option_values_hash.values
890
- values = values.inject(values.shift) { |memo, value| memo.product(value).map(&:flatten) }
891
-
892
- default_currency = store&.default_currency || Spree::Store.default.default_currency
893
- master_price = master.price_in(default_currency).amount
894
-
895
- values.each do |ids|
896
- variant = variants.create!(option_value_ids: ids)
897
- variant.set_price(default_currency, master_price) if master_price.present?
898
- end
899
- save
910
+ # Every product must have at least one variant. If none was built via the
911
+ # delegated setters or supplied as nested attributes, create an empty one.
912
+ def ensure_default_variant
913
+ variants.create! if variants.empty?
900
914
  end
901
915
 
902
- def ensure_master
903
- return unless new_record?
916
+ def set_default_variant
917
+ return if default_variant_id.present?
904
918
 
905
- self.master ||= build_master
919
+ first_variant = preferred_default_variant
920
+ update_column(:default_variant_id, first_variant.id) if first_variant
906
921
  end
907
922
 
908
- def assign_default_tax_category
909
- self.tax_category = Spree::TaxCategory.default if new_record? && self[:tax_category_id].blank?
910
- end
923
+ # Re-point the default variant when the current one has been removed. Sticky
924
+ # otherwise an in-stock change never re-points (see the plan's open question).
925
+ def auto_promote_default_variant
926
+ current = association(:default_variant).reader
927
+ return if current && current.deleted_at.nil?
911
928
 
912
- def anything_changed?
913
- saved_changes? || @nested_changes
929
+ next_variant = preferred_default_variant(reload: true)
930
+ update_column(:default_variant_id, next_variant&.id) if next_variant && next_variant.id != default_variant_id
914
931
  end
915
932
 
916
- def reset_nested_changes
917
- @nested_changes = false
933
+ # The product's public identity — its headline price, its SKU, and the
934
+ # Omnibus `prior_price`, which is a compliance claim about this shop's own
935
+ # pricing history. Where the operator sells the item themselves that is the
936
+ # listing that should speak for the product, not whichever seller happens
937
+ # to sort first.
938
+ def preferred_default_variant(reload: false)
939
+ candidates = reload ? variants.reload : variants
940
+ candidates.detect { |variant| variant.resolved_seller_id.nil? } || candidates.first
918
941
  end
919
942
 
920
- def master_updated?
921
- master && (
922
- master.new_record? ||
923
- master.changed? ||
924
- (
925
- Spree::Config.enable_legacy_default_price &&
926
- master.default_price &&
927
- (
928
- master.default_price.new_record? ||
929
- master.default_price.changed?
930
- )
931
- )
932
- )
943
+ def assign_default_tax_category
944
+ self.tax_category = Spree::TaxCategory.default(assignable_store) if new_record? && self[:tax_category_id].blank?
933
945
  end
934
946
 
935
- # there's a weird quirk with the delegate stuff that does not automatically save the delegate object
936
- # when saving so we force a save using a hook
937
- # Fix for issue #5306
938
- def save_master
939
- if master_updated?
940
- master.save!
941
- @nested_changes = true
942
- end
943
- end
947
+ def stamp_template_delivery_profile
948
+ return if delivery_profile_id.present?
944
949
 
945
- # If the master cannot be saved, the Product object will get its errors
946
- # and will be destroyed
947
- def validate_master
948
- if Spree::Config.enable_legacy_default_price
949
- # We call master.default_price here to ensure price is initialized.
950
- # Required to avoid Variant#check_price validation failing on create.
951
- master.default_price
952
- end
950
+ self.delivery_profile_id = product_type&.delivery_profile_id ||
951
+ (store && Spree::DeliveryProfile.default_for(store)&.id)
952
+ end
953
953
 
954
- unless master.valid?
955
- master.errors.map { |error| { field: error.attribute, message: error&.message } }.each do |err|
956
- next if err[:field].blank? || err[:message].blank?
954
+ def delivery_profile_must_belong_to_store
955
+ return if delivery_profile.nil? || store.nil?
956
+ return if delivery_profile.store_id == store_id
957
957
 
958
- errors.add(err[:field], err[:message])
959
- end
960
- end
958
+ errors.add(:delivery_profile, :invalid)
961
959
  end
962
960
 
963
- def ensure_default_shipping_category
964
- return if shipping_category.present?
961
+ # A seller from another store would put one store's catalog inside another
962
+ # store's seller — and every seller-scoped read is rooted in exactly that
963
+ # association.
964
+ def seller_must_belong_to_store
965
+ return if seller.nil? || store.nil?
966
+ return if seller.store_id == store_id
965
967
 
966
- if new_record?
967
- name = I18n.t('spree.seed.shipping.categories.default')
968
- self.shipping_category = Spree::ShippingCategory.find_or_create_by!(name: name)
969
- end
968
+ errors.add(:seller, :invalid)
970
969
  end
971
970
 
972
971
  def run_touch_callbacks
973
972
  run_callbacks(:touch)
974
973
  end
975
974
 
976
- def taxon_and_ancestors
977
- @taxon_and_ancestors ||= taxons.map(&:self_and_ancestors).flatten.uniq
975
+ def category_and_ancestors
976
+ @category_and_ancestors ||= categories.map(&:self_and_ancestors).flatten.uniq
978
977
  end
979
978
 
980
- # Iterate through this products taxons and taxonomies and touch their timestamps in a batch
981
- def touch_taxons
982
- if taxons.any?
983
- Spree::Products::TouchTaxonsJob.
979
+ # Iterate through this product's categories and touch their timestamps in a batch
980
+ def touch_categories
981
+ if categories.any?
982
+ Spree::Products::TouchCategoriesJob.
984
983
  set(wait: 5.seconds).
985
- perform_later(taxon_and_ancestors.map(&:id), taxonomy_ids.uniq)
984
+ perform_later(category_and_ancestors.map(&:id))
986
985
  end
987
986
  end
988
987
 
@@ -993,48 +992,33 @@ module Spree
993
992
  end
994
993
  end
995
994
 
996
- def remove_taxon(taxon)
997
- removed_classifications = classifications.where(taxon: taxon)
998
- removed_classifications.each(&:remove_from_list)
995
+ def remove_category(category)
996
+ removed_product_categories = product_categories.where(category: category)
997
+ removed_product_categories.each(&:remove_from_list)
999
998
  end
1000
999
 
1001
1000
  def discontinue_on_must_be_later_than_make_active_at
1002
- Spree::Deprecation.warn('Spree::Product#discontinue_on_must_be_later_than_make_active_at is deprecated and will be removed in Spree 6.0.')
1003
1001
  if discontinue_on < make_active_at
1004
1002
  errors.add(:discontinue_on, :invalid_date_range)
1005
1003
  end
1006
1004
  end
1007
1005
 
1008
- def requires_price?
1009
- Spree::Config[:require_master_price]
1010
- end
1006
+ # Mirrors the machine's bang events: an illegal move raised.
1007
+ def run_status_workflow(workflow)
1008
+ result = workflow.call(product: self)
1009
+
1010
+ if result.failure?
1011
+ # ResultError#to_s unwraps an ActiveModel::Errors into its full
1012
+ # messages; its `value` would inspect the object into the message.
1013
+ errors.add(:base, result.error.to_s)
1014
+ raise ActiveRecord::RecordInvalid, self
1015
+ end
1011
1016
 
1012
- def requires_shipping_category?
1013
1017
  true
1014
1018
  end
1015
1019
 
1016
- def eligible_for_taxon_matching?
1020
+ def eligible_for_collection_matching?
1017
1021
  previously_new_record? || tag_list_previously_changed? || available_on_previously_changed?
1018
1022
  end
1019
-
1020
- def after_activate
1021
- # Implement your logic here
1022
- end
1023
-
1024
- def after_archive
1025
- # Implement your logic here
1026
- end
1027
-
1028
- def after_draft
1029
- # Implement your logic here
1030
- end
1031
-
1032
- def publish_product_activated_event
1033
- publish_event('product.activated')
1034
- end
1035
-
1036
- def publish_product_archived_event
1037
- publish_event('product.archived')
1038
- end
1039
1023
  end
1040
1024
  end