spree_core 6.0.0.beta3 → 6.0.0.rc1

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 (495) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/app/helpers/spree/images_helper.rb +0 -57
  4. data/app/jobs/spree/base_job.rb +21 -0
  5. data/app/jobs/spree/carts/reap_expired_job.rb +2 -2
  6. data/app/jobs/spree/collections/regenerate_time_based_job.rb +6 -3
  7. data/app/jobs/spree/orders/finalize_stale_drafts_job.rb +8 -5
  8. data/app/jobs/spree/payments/cancel_unused_sessions_job.rb +61 -0
  9. data/app/jobs/spree/seller_payouts/sweep_seller_job.rb +2 -0
  10. data/app/mailers/spree/admin_user_mailer.rb +4 -19
  11. data/app/mailers/spree/base_mailer.rb +96 -13
  12. data/app/mailers/spree/email_template_mailer.rb +27 -0
  13. data/app/mailers/spree/export_mailer.rb +3 -4
  14. data/app/mailers/spree/import_mailer.rb +3 -4
  15. data/app/mailers/spree/invitation_mailer.rb +24 -7
  16. data/app/mailers/spree/seller_user_mailer.rb +4 -16
  17. data/app/mailers/spree/webhook_mailer.rb +2 -2
  18. data/app/models/concerns/spree/acted_by.rb +1 -1
  19. data/app/models/concerns/spree/admin_user_methods.rb +13 -1
  20. data/app/models/concerns/spree/api_typed.rb +91 -0
  21. data/app/models/concerns/spree/calculator/tiers.rb +76 -0
  22. data/app/models/concerns/spree/customer_methods.rb +15 -14
  23. data/app/models/concerns/spree/email_template_identity.rb +52 -0
  24. data/app/models/concerns/spree/parameterizable_name.rb +2 -0
  25. data/app/models/concerns/spree/payment_session_transitions.rb +8 -2
  26. data/app/models/concerns/spree/post_sale/taxation.rb +54 -0
  27. data/app/models/concerns/spree/post_sale/taxed_line.rb +51 -0
  28. data/app/models/concerns/spree/preference_schema.rb +4 -12
  29. data/app/models/concerns/spree/product_scopes.rb +11 -1
  30. data/app/models/concerns/spree/provider_listing.rb +41 -0
  31. data/app/models/concerns/spree/publishable.rb +82 -14
  32. data/app/models/concerns/spree/purchase/addresses.rb +23 -10
  33. data/app/models/concerns/spree/purchase/currency.rb +1 -1
  34. data/app/models/concerns/spree/purchase/deprecated_aliases.rb +87 -0
  35. data/app/models/concerns/spree/purchase/gift_cards.rb +10 -4
  36. data/app/models/concerns/spree/purchase/locale.rb +1 -1
  37. data/app/models/concerns/spree/purchase/payment_processing.rb +7 -3
  38. data/app/models/concerns/spree/purchase/purchase_order.rb +2 -2
  39. data/app/models/concerns/spree/purchase/totals.rb +49 -1
  40. data/app/models/concerns/spree/receivable.rb +1 -1
  41. data/app/models/concerns/spree/searchable.rb +0 -4
  42. data/app/models/concerns/spree/secret_preferences.rb +59 -0
  43. data/app/models/concerns/spree/single_store_resource.rb +1 -1
  44. data/app/models/concerns/spree/stores/email_branding.rb +31 -0
  45. data/app/models/concerns/spree/type_labels.rb +38 -0
  46. data/app/models/concerns/spree/typed_adjustment_line.rb +1 -1
  47. data/app/models/concerns/spree/typed_associations.rb +6 -0
  48. data/app/models/spree/address.rb +11 -1
  49. data/app/models/spree/adjusters/promotion.rb +1 -15
  50. data/app/models/spree/allowed_origin.rb +0 -1
  51. data/app/models/spree/api_key.rb +5 -6
  52. data/app/models/spree/authentication/strategies/oidc_strategy.rb +5 -5
  53. data/app/models/spree/base.rb +70 -22
  54. data/app/models/spree/calculator/flat_percent_item_total.rb +1 -3
  55. data/app/models/spree/calculator/flat_rate.rb +1 -3
  56. data/app/models/spree/calculator/flexi_rate.rb +1 -3
  57. data/app/models/spree/calculator/percent_on_line_item.rb +1 -1
  58. data/app/models/spree/calculator/price_sack.rb +1 -3
  59. data/app/models/spree/calculator/shipping/digital_delivery.rb +1 -3
  60. data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +1 -3
  61. data/app/models/spree/calculator/shipping/flat_rate.rb +1 -3
  62. data/app/models/spree/calculator/shipping/flexi_rate.rb +1 -3
  63. data/app/models/spree/calculator/shipping/per_item.rb +1 -3
  64. data/app/models/spree/calculator/shipping/price_sack.rb +1 -3
  65. data/app/models/spree/calculator/tiered_flat_rate.rb +6 -23
  66. data/app/models/spree/calculator/tiered_percent.rb +7 -25
  67. data/app/models/spree/cart.rb +78 -18
  68. data/app/models/spree/channel/gating.rb +1 -1
  69. data/app/models/spree/channel.rb +4 -4
  70. data/app/models/spree/claim.rb +21 -1
  71. data/app/models/spree/claim_line_item.rb +41 -6
  72. data/app/models/spree/collection.rb +0 -1
  73. data/app/models/spree/collection_rule.rb +5 -21
  74. data/app/models/spree/commission_line.rb +1 -1
  75. data/app/models/spree/commission_rate.rb +2 -3
  76. data/app/models/spree/commission_rate_value.rb +1 -1
  77. data/app/models/spree/commission_rule.rb +4 -11
  78. data/app/models/spree/commission_rules/product_rule.rb +1 -1
  79. data/app/models/spree/company_invitation.rb +1 -0
  80. data/app/models/spree/consent_record.rb +0 -1
  81. data/app/models/spree/credit_card.rb +1 -1
  82. data/app/models/spree/current.rb +23 -0
  83. data/app/models/spree/custom_field.rb +2 -2
  84. data/app/models/spree/custom_field_definition.rb +9 -23
  85. data/app/models/spree/custom_fields/boolean.rb +1 -1
  86. data/app/models/spree/custom_fields/json.rb +1 -1
  87. data/app/models/spree/data_feed/google.rb +0 -2
  88. data/app/models/spree/data_request.rb +1 -1
  89. data/app/models/spree/delivery_method.rb +30 -13
  90. data/app/models/spree/delivery_method_rule.rb +4 -11
  91. data/app/models/spree/delivery_rate.rb +4 -5
  92. data/app/models/spree/delivery_rate_provider/base.rb +1 -0
  93. data/app/models/spree/delivery_zone.rb +0 -1
  94. data/app/models/spree/delivery_zone_member.rb +4 -4
  95. data/app/models/spree/digital_asset.rb +7 -0
  96. data/app/models/spree/digital_asset_provider/base.rb +2 -0
  97. data/app/models/spree/digital_link.rb +2 -5
  98. data/app/models/spree/discount.rb +1 -1
  99. data/app/models/spree/email_template.rb +25 -0
  100. data/app/models/spree/email_template_draft.rb +13 -0
  101. data/app/models/spree/email_template_revision.rb +17 -0
  102. data/app/models/spree/email_templates/entry.rb +118 -0
  103. data/app/models/spree/email_templates/sample_record.rb +16 -0
  104. data/app/models/spree/emails/branding.rb +80 -0
  105. data/app/models/spree/exchange.rb +15 -1
  106. data/app/models/spree/exchange_line_item.rb +121 -5
  107. data/app/models/spree/export.rb +0 -1
  108. data/app/models/spree/exports/products.rb +10 -1
  109. data/app/models/spree/exports/report.rb +1 -1
  110. data/app/models/spree/fee.rb +4 -2
  111. data/app/models/spree/fulfillment.rb +53 -27
  112. data/app/models/spree/fulfillment_changer.rb +2 -2
  113. data/app/models/spree/fulfillment_item.rb +0 -4
  114. data/app/models/spree/fulfillment_provider/base.rb +1 -0
  115. data/app/models/spree/gateway/bogus.rb +4 -0
  116. data/app/models/spree/gateway/custom_payment_source_method.rb +5 -0
  117. data/app/models/spree/gateway.rb +5 -1
  118. data/app/models/spree/gift_card.rb +12 -1
  119. data/app/models/spree/gift_card_batch.rb +0 -1
  120. data/app/models/spree/import.rb +2 -7
  121. data/app/models/spree/import_mapping.rb +1 -2
  122. data/app/models/spree/import_row.rb +2 -2
  123. data/app/models/spree/import_schemas/products.rb +1 -0
  124. data/app/models/spree/integration.rb +8 -18
  125. data/app/models/spree/invitation.rb +5 -5
  126. data/app/models/spree/line_item.rb +13 -2
  127. data/app/models/spree/locale.rb +11 -8
  128. data/app/models/spree/market.rb +1 -0
  129. data/app/models/spree/newsletter_subscriber.rb +3 -0
  130. data/app/models/spree/order.rb +56 -138
  131. data/app/models/spree/order_group.rb +11 -2
  132. data/app/models/spree/order_inventory.rb +10 -5
  133. data/app/models/spree/order_promotion.rb +1 -1
  134. data/app/models/spree/order_routing/has_strategy_preference.rb +1 -1
  135. data/app/models/spree/order_routing/strategy/base.rb +4 -2
  136. data/app/models/spree/order_routing/strategy/rules.rb +1 -1
  137. data/app/models/spree/order_routing_rule.rb +6 -22
  138. data/app/models/spree/package_type.rb +1 -2
  139. data/app/models/spree/payment/gateway_options.rb +14 -52
  140. data/app/models/spree/payment/processing.rb +2 -4
  141. data/app/models/spree/payment.rb +20 -5
  142. data/app/models/spree/payment_method/session_instrumentation.rb +1 -0
  143. data/app/models/spree/payment_method/store_credit.rb +4 -4
  144. data/app/models/spree/payment_method.rb +36 -4
  145. data/app/models/spree/payment_session.rb +5 -1
  146. data/app/models/spree/payment_setup_session.rb +1 -0
  147. data/app/models/spree/payout_provider/base.rb +3 -1
  148. data/app/models/spree/pickup_point_provider/base.rb +2 -0
  149. data/app/models/spree/price_list.rb +1 -2
  150. data/app/models/spree/price_rules/channel_rule.rb +1 -1
  151. data/app/models/spree/price_rules/customer_group_rule.rb +1 -1
  152. data/app/models/spree/product.rb +31 -7
  153. data/app/models/spree/product_submission.rb +0 -12
  154. data/app/models/spree/product_type.rb +0 -1
  155. data/app/models/spree/promotion/actions/create_line_items.rb +27 -17
  156. data/app/models/spree/promotion/rules/category.rb +0 -4
  157. data/app/models/spree/promotion/rules/channel.rb +0 -4
  158. data/app/models/spree/promotion/rules/country.rb +0 -4
  159. data/app/models/spree/promotion/rules/currency.rb +0 -4
  160. data/app/models/spree/promotion/rules/customer_group.rb +0 -4
  161. data/app/models/spree/promotion/rules/first_order.rb +0 -4
  162. data/app/models/spree/promotion/rules/item_total.rb +4 -8
  163. data/app/models/spree/promotion/rules/market.rb +0 -4
  164. data/app/models/spree/promotion/rules/one_use_per_user.rb +0 -4
  165. data/app/models/spree/promotion/rules/option_value.rb +0 -3
  166. data/app/models/spree/promotion/rules/product.rb +0 -4
  167. data/app/models/spree/promotion/rules/user.rb +0 -4
  168. data/app/models/spree/promotion/rules/user_logged_in.rb +0 -4
  169. data/app/models/spree/promotion.rb +35 -6
  170. data/app/models/spree/promotion_action.rb +7 -14
  171. data/app/models/spree/promotion_action_line_item.rb +1 -1
  172. data/app/models/spree/promotion_handler/coupon.rb +59 -27
  173. data/app/models/spree/promotion_rule.rb +7 -14
  174. data/app/models/spree/purchase_order.rb +1 -0
  175. data/app/models/spree/quantity_rule.rb +1 -1
  176. data/app/models/spree/refund.rb +28 -3
  177. data/app/models/spree/return.rb +30 -2
  178. data/app/models/spree/return_line_item.rb +65 -14
  179. data/app/models/spree/role.rb +7 -7
  180. data/app/models/spree/saved_report.rb +1 -1
  181. data/app/models/spree/seller.rb +2 -1
  182. data/app/models/spree/seller_payout.rb +7 -0
  183. data/app/models/spree/seller_requirement.rb +4 -12
  184. data/app/models/spree/seller_requirement_custom_field.rb +1 -2
  185. data/app/models/spree/seller_requirement_submission.rb +2 -2
  186. data/app/models/spree/seller_requirements/accept_terms.rb +2 -3
  187. data/app/models/spree/seller_transfer.rb +5 -0
  188. data/app/models/spree/shipping_label.rb +1 -0
  189. data/app/models/spree/stock/availability_validator.rb +2 -2
  190. data/app/models/spree/stock/inventory_unit_builder.rb +1 -1
  191. data/app/models/spree/stock/packer.rb +2 -2
  192. data/app/models/spree/stock/splitter/weight.rb +1 -1
  193. data/app/models/spree/stock_level/custom_events.rb +6 -0
  194. data/app/models/spree/stock_level.rb +1 -1
  195. data/app/models/spree/stock_movement.rb +1 -1
  196. data/app/models/spree/stock_receipt_item.rb +1 -1
  197. data/app/models/spree/stock_reservation.rb +2 -2
  198. data/app/models/spree/stock_transfer.rb +4 -2
  199. data/app/models/spree/store.rb +33 -7
  200. data/app/models/spree/store_credit.rb +8 -9
  201. data/app/models/spree/store_credit_event.rb +5 -5
  202. data/app/models/spree/tax_exemption_certificate.rb +2 -0
  203. data/app/models/spree/tax_identifier.rb +3 -1
  204. data/app/models/spree/tax_line.rb +53 -3
  205. data/app/models/spree/tax_provider/base.rb +71 -14
  206. data/app/models/spree/tax_provider/internal.rb +59 -33
  207. data/app/models/spree/tax_provider/recorded_share.rb +114 -0
  208. data/app/models/spree/tax_rate.rb +1 -2
  209. data/app/models/spree/taxonomy.rb +0 -1
  210. data/app/models/spree/variant.rb +4 -3
  211. data/app/models/spree/variant_media.rb +1 -1
  212. data/app/models/spree/webhook_endpoint.rb +10 -13
  213. data/app/models/spree/wishlist.rb +1 -6
  214. data/app/models/spree/wishlist_item/custom_events.rb +6 -0
  215. data/app/presenters/spree/csv/customer_presenter.rb +1 -1
  216. data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +1 -1
  217. data/app/presenters/spree/csv/order_line_item_presenter.rb +1 -1
  218. data/app/presenters/spree/csv/product_variant_presenter.rb +42 -12
  219. data/app/serializers/spree/emails/base_serializer.rb +14 -0
  220. data/app/serializers/spree/emails/export_serializer.rb +13 -0
  221. data/app/serializers/spree/emails/import_serializer.rb +19 -0
  222. data/app/serializers/spree/emails/invitation_serializer.rb +28 -0
  223. data/app/serializers/spree/emails/store_serializer.rb +31 -0
  224. data/app/serializers/spree/emails/user_serializer.rb +12 -0
  225. data/app/serializers/spree/emails/webhook_endpoint_serializer.rb +16 -0
  226. data/app/services/spree/carts/create.rb +8 -2
  227. data/app/services/spree/carts/destroy.rb +2 -2
  228. data/app/services/spree/carts/remove_out_of_stock_items.rb +2 -2
  229. data/app/services/spree/carts/split_by_seller.rb +1 -1
  230. data/app/services/spree/carts/update.rb +19 -5
  231. data/app/services/spree/catalogs/set_product_terms.rb +1 -1
  232. data/app/services/spree/categories/add_products.rb +13 -31
  233. data/app/services/spree/categories/remove_products.rb +12 -51
  234. data/app/services/spree/checkout/advance.rb +1 -4
  235. data/app/services/spree/collections/add_products.rb +10 -23
  236. data/app/services/spree/collections/remove_products.rb +7 -43
  237. data/app/services/spree/coupon_codes/bulk_generate.rb +8 -14
  238. data/app/services/spree/data_requests/create.rb +1 -1
  239. data/app/services/spree/deliveries/destroy.rb +2 -2
  240. data/app/services/spree/email_templates/check.rb +58 -0
  241. data/app/services/spree/email_templates/no_sample_record.rb +7 -0
  242. data/app/services/spree/email_templates/preview.rb +99 -0
  243. data/app/services/spree/emails/file_system.rb +23 -0
  244. data/app/services/spree/emails/filters.rb +84 -0
  245. data/app/services/spree/emails/liquid_context.rb +48 -0
  246. data/app/services/spree/emails/rendered_email.rb +14 -0
  247. data/app/services/spree/emails/renderer.rb +125 -0
  248. data/app/services/spree/emails/template.rb +51 -0
  249. data/app/services/spree/emails/template_data.rb +23 -0
  250. data/app/services/spree/emails/template_resolver.rb +109 -0
  251. data/app/services/spree/emails/text_converter.rb +72 -0
  252. data/app/services/spree/emails/translation_inliner.rb +134 -0
  253. data/app/services/spree/imports/row_processors/price_list_price.rb +10 -10
  254. data/app/services/spree/imports/row_processors/product_variant.rb +65 -34
  255. data/app/services/spree/imports/row_processors/purchase_order.rb +13 -13
  256. data/app/services/spree/locales.rb +4 -5
  257. data/app/services/spree/newsletter/subscribe.rb +8 -12
  258. data/app/services/spree/orders/build_fulfillments.rb +3 -2
  259. data/app/services/spree/orders/create.rb +2 -1
  260. data/app/services/spree/orders/discounts/create.rb +7 -4
  261. data/app/services/spree/orders/discounts/destroy.rb +1 -0
  262. data/app/services/spree/orders/discounts/update.rb +1 -0
  263. data/app/services/spree/orders/fees/create.rb +1 -0
  264. data/app/services/spree/orders/fees/destroy.rb +1 -0
  265. data/app/services/spree/orders/fees/update.rb +1 -0
  266. data/app/services/spree/orders/remove_line_item.rb +13 -3
  267. data/app/services/spree/orders/update.rb +10 -6
  268. data/app/services/spree/orders/update_item.rb +6 -3
  269. data/app/services/spree/orders/update_statuses.rb +3 -1
  270. data/app/services/spree/product_memberships.rb +61 -0
  271. data/app/services/spree/returns/eligibility_validator.rb +1 -1
  272. data/app/services/spree/search_provider/custom_field_schema.rb +2 -2
  273. data/app/services/spree/seeds/commission_rates.rb +1 -1
  274. data/app/services/spree/seeds/digital_delivery.rb +1 -1
  275. data/app/services/spree/seeds/payment_methods.rb +2 -2
  276. data/app/services/spree/seeds/saved_reports.rb +3 -3
  277. data/app/services/spree/sellers/requirements.rb +1 -2
  278. data/app/services/spree/shipping_labels/confirm_refund.rb +1 -1
  279. data/app/services/spree/shipping_labels/store_file.rb +3 -3
  280. data/app/services/spree/stock_levels/correct.rb +3 -3
  281. data/app/services/spree/stock_reservations/reserve.rb +2 -3
  282. data/app/services/spree/store_credits/apply.rb +15 -13
  283. data/app/services/spree/stores/provision_defaults.rb +15 -14
  284. data/app/subscribers/spree/seller_onboarding_subscriber.rb +1 -1
  285. data/app/views/layouts/spree/base_mailer.html.erb +2 -43
  286. data/app/views/layouts/spree/base_mailer.liquid +79 -0
  287. data/app/views/spree/admin_user_mailer/password_reset_email.liquid +14 -0
  288. data/app/views/spree/export_mailer/export_done.liquid +12 -0
  289. data/app/views/spree/import_mailer/import_done.liquid +15 -0
  290. data/app/views/spree/invitation_mailer/invitation_accepted.liquid +9 -0
  291. data/app/views/spree/invitation_mailer/invitation_email.liquid +13 -0
  292. data/app/views/spree/seller_user_mailer/password_reset_email.liquid +14 -0
  293. data/app/views/spree/shared/_mailer_button.html.erb +4 -5
  294. data/app/views/spree/shared/_mailer_hero.html.erb +6 -5
  295. data/app/views/spree/shared/_summary_row.liquid +8 -0
  296. data/app/views/spree/webhook_mailer/endpoint_disabled.liquid +21 -0
  297. data/app/workflows/spree/carts/add_item.rb +4 -4
  298. data/app/workflows/spree/carts/complete.rb +13 -3
  299. data/app/workflows/spree/carts/merge.rb +1 -1
  300. data/app/workflows/spree/carts/recalculate_totals.rb +5 -0
  301. data/app/workflows/spree/carts/upsert_items.rb +18 -9
  302. data/app/workflows/spree/claims/approve.rb +5 -22
  303. data/app/workflows/spree/claims/cancel.rb +5 -21
  304. data/app/workflows/spree/claims/create.rb +15 -0
  305. data/app/workflows/spree/claims/deny.rb +7 -0
  306. data/app/workflows/spree/claims/resolve.rb +34 -31
  307. data/app/workflows/spree/customers/anonymize.rb +3 -4
  308. data/app/workflows/spree/customers/create.rb +1 -1
  309. data/app/workflows/spree/data_requests/fulfill.rb +2 -2
  310. data/app/workflows/spree/deliveries/update_tracking.rb +1 -1
  311. data/app/workflows/spree/email_templates/discard_draft.rb +20 -0
  312. data/app/workflows/spree/email_templates/publish.rb +70 -0
  313. data/app/workflows/spree/email_templates/restore_revision.rb +33 -0
  314. data/app/workflows/spree/email_templates/revert.rb +29 -0
  315. data/app/workflows/spree/email_templates/save_draft.rb +85 -0
  316. data/app/workflows/spree/exchanges/approve.rb +5 -22
  317. data/app/workflows/spree/exchanges/cancel.rb +5 -21
  318. data/app/workflows/spree/exchanges/create.rb +6 -0
  319. data/app/workflows/spree/exchanges/fulfill.rb +75 -51
  320. data/app/workflows/spree/exchanges/receive.rb +6 -65
  321. data/app/workflows/spree/fulfillments/cancel.rb +1 -1
  322. data/app/workflows/spree/fulfillments/create.rb +51 -47
  323. data/app/workflows/spree/fulfillments/fulfill.rb +23 -27
  324. data/app/workflows/spree/fulfillments/mark_delivered.rb +1 -1
  325. data/app/workflows/spree/fulfillments/replacements.rb +59 -0
  326. data/app/workflows/spree/gift_cards/apply.rb +2 -2
  327. data/app/workflows/spree/orders/add_item.rb +5 -1
  328. data/app/workflows/spree/orders/cancel.rb +3 -3
  329. data/app/workflows/spree/orders/complete.rb +16 -8
  330. data/app/workflows/spree/payments/process.rb +2 -2
  331. data/app/workflows/spree/post_sale/approval.rb +35 -0
  332. data/app/workflows/spree/post_sale/cancellation.rb +44 -0
  333. data/app/workflows/spree/post_sale/receipt.rb +92 -0
  334. data/app/workflows/spree/products/nested_attributes.rb +1 -1
  335. data/app/workflows/spree/purchase_orders/cancel.rb +1 -1
  336. data/app/workflows/spree/purchase_orders/close.rb +3 -31
  337. data/app/workflows/spree/purchase_orders/mark_draft.rb +2 -2
  338. data/app/workflows/spree/purchase_orders/mark_ordered.rb +2 -2
  339. data/app/workflows/spree/purchase_orders/receive.rb +1 -1
  340. data/app/workflows/spree/purchase_orders/update.rb +5 -34
  341. data/app/workflows/spree/receivables/closing_short.rb +46 -0
  342. data/app/workflows/spree/receivables/draft_editing.rb +48 -0
  343. data/app/workflows/spree/refunds/create.rb +5 -2
  344. data/app/workflows/spree/refunds/order_payments.rb +14 -2
  345. data/app/workflows/spree/refunds/tax_credit.rb +67 -0
  346. data/app/workflows/spree/returns/approve.rb +5 -22
  347. data/app/workflows/spree/returns/cancel.rb +5 -22
  348. data/app/workflows/spree/returns/create.rb +8 -0
  349. data/app/workflows/spree/returns/receive.rb +5 -76
  350. data/app/workflows/spree/returns/refund.rb +31 -8
  351. data/app/workflows/spree/seller_requirement_submissions/create.rb +1 -2
  352. data/app/workflows/spree/seller_transfers/reverse.rb +28 -1
  353. data/app/workflows/spree/shipping_labels/purchase.rb +8 -8
  354. data/app/workflows/spree/shipping_labels/record.rb +5 -5
  355. data/app/workflows/spree/shipping_labels/refund.rb +2 -2
  356. data/app/workflows/spree/stock_receipts/recording.rb +4 -4
  357. data/app/workflows/spree/stock_transfers/cancel.rb +2 -2
  358. data/app/workflows/spree/stock_transfers/close.rb +3 -30
  359. data/app/workflows/spree/stock_transfers/mark_draft.rb +1 -1
  360. data/app/workflows/spree/stock_transfers/mark_in_transit.rb +3 -3
  361. data/app/workflows/spree/stock_transfers/mark_ready.rb +2 -2
  362. data/app/workflows/spree/stock_transfers/receive.rb +1 -1
  363. data/app/workflows/spree/stock_transfers/update.rb +5 -33
  364. data/config/i18n-tasks.yml +32 -18
  365. data/config/initializers/alba.rb +7 -0
  366. data/config/language_names.csv +264 -0
  367. data/config/locales/ar.yml +811 -0
  368. data/config/locales/be.yml +855 -0
  369. data/config/locales/bg.yml +723 -0
  370. data/config/locales/ca.yml +689 -0
  371. data/config/locales/cs.yml +855 -0
  372. data/config/locales/da.yml +771 -0
  373. data/config/locales/de-CH.yml +248 -0
  374. data/config/locales/de.yml +835 -0
  375. data/config/locales/el.yml +769 -0
  376. data/config/locales/en.yml +37 -503
  377. data/config/locales/es-CL.yml +301 -0
  378. data/config/locales/es-EC.yml +186 -0
  379. data/config/locales/es-MX.yml +334 -0
  380. data/config/locales/es.yml +831 -0
  381. data/config/locales/et.yml +644 -0
  382. data/config/locales/fa.yml +709 -0
  383. data/config/locales/fi.yml +771 -0
  384. data/config/locales/fr.yml +830 -0
  385. data/config/locales/hu.yml +751 -0
  386. data/config/locales/id.yml +756 -0
  387. data/config/locales/it.yml +833 -0
  388. data/config/locales/ja.yml +726 -0
  389. data/config/locales/km.yml +759 -0
  390. data/config/locales/ko.yml +575 -0
  391. data/config/locales/ku.yml +753 -0
  392. data/config/locales/lt.yml +821 -0
  393. data/config/locales/lv.yml +685 -0
  394. data/config/locales/nb.yml +824 -0
  395. data/config/locales/ne.yml +823 -0
  396. data/config/locales/nl.yml +826 -0
  397. data/config/locales/pl.yml +835 -0
  398. data/config/locales/pt-BR.yml +547 -0
  399. data/config/locales/pt.yml +636 -0
  400. data/config/locales/ro.yml +750 -0
  401. data/config/locales/ru.yml +834 -0
  402. data/config/locales/sk.yml +680 -0
  403. data/config/locales/sl-SI.yml +600 -0
  404. data/config/locales/sv.yml +827 -0
  405. data/config/locales/th.yml +745 -0
  406. data/config/locales/tr.yml +777 -0
  407. data/config/locales/uk.yml +836 -0
  408. data/config/locales/vi.yml +768 -0
  409. data/config/locales/zh-CN.yml +786 -0
  410. data/config/locales/zh-TW.yml +834 -0
  411. data/db/migrate/20260925000001_add_replacement_to_spree_fulfillment_items.rb +5 -0
  412. data/db/migrate/20261001000001_add_post_sale_tax.rb +30 -0
  413. data/db/migrate/20261001000001_convert_preferences_to_json.rb +166 -0
  414. data/db/migrate/20261001000001_create_spree_email_templates.rb +52 -0
  415. data/lib/generators/spree/dummy/dummy_generator.rb +0 -9
  416. data/lib/generators/spree/dummy/templates/rails/test.rb +7 -0
  417. data/lib/spree/checkout/default_requirements.rb +37 -13
  418. data/lib/spree/core/configuration.rb +1 -1
  419. data/lib/spree/core/controller_helpers/store.rb +1 -1
  420. data/lib/spree/core/dependencies.rb +5 -4
  421. data/lib/spree/core/emails/escaped_output.rb +60 -0
  422. data/lib/spree/core/engine.rb +48 -167
  423. data/lib/spree/core/permissions/default_catalog.rb +5 -0
  424. data/lib/spree/core/preferences/json_conversion.rb +182 -0
  425. data/lib/spree/core/preferences/masking.rb +4 -1
  426. data/lib/spree/core/preferences/preferable.rb +137 -83
  427. data/lib/spree/core/preferences/preferable_class_methods.rb +81 -146
  428. data/lib/spree/core/previews/import_preview.rb +10 -0
  429. data/lib/spree/core/tax/provider_error.rb +37 -0
  430. data/lib/spree/core/version.rb +1 -1
  431. data/lib/spree/core.rb +85 -370
  432. data/lib/spree/emails/editable_templates.rb +82 -0
  433. data/lib/spree/events/catalog.rb +189 -0
  434. data/lib/spree/events.rb +21 -1
  435. data/lib/spree/hooks.rb +0 -17
  436. data/lib/spree/i18n.rb +32 -15
  437. data/lib/spree/reporting/adapters/live.rb +6 -6
  438. data/lib/spree/reporting/default_vocabulary.rb +22 -10
  439. data/lib/spree/reporting/hydration.rb +1 -1
  440. data/lib/spree/reporting/query.rb +8 -0
  441. data/lib/spree/reporting/schema.rb +10 -10
  442. data/lib/spree/testing_support/editable_email_templates.rb +61 -0
  443. data/lib/spree/testing_support/emails.rb +22 -0
  444. data/lib/spree/testing_support/factories/cart_factory.rb +1 -1
  445. data/lib/spree/testing_support/factories/email_template_factory.rb +28 -0
  446. data/lib/spree/testing_support/factories/order_factory.rb +2 -2
  447. data/lib/spree/testing_support/factories/refund_factory.rb +1 -1
  448. data/lib/spree/testing_support/factories/stock_factory.rb +1 -1
  449. data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
  450. data/lib/spree/testing_support/i18n.rb +3 -94
  451. data/lib/spree/testing_support/lifecycle_events.rb +1 -1
  452. data/lib/spree/testing_support/order_walkthrough.rb +1 -1
  453. data/lib/spree/testing_support/preferences.rb +0 -5
  454. data/lib/spree/testing_support/store.rb +9 -1
  455. data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +45 -0
  456. data/lib/tasks/categories_and_collections.rake +3 -3
  457. data/lib/tasks/core.rake +6 -4
  458. data/lib/tasks/media.rake +3 -3
  459. data/lib/tasks/package_types.rake +1 -1
  460. data/lib/tasks/preferences_json.rake +101 -0
  461. data/spec/fixtures/mailer_views/custom_views_probe_mailer/welcome_email.html.erb +4 -0
  462. metadata +188 -44
  463. data/app/models/acts_as_taggable_on/tag_decorator.rb +0 -10
  464. data/app/models/concerns/spree/has_image_alt_text.rb +0 -18
  465. data/app/models/spree/base_analytics_event_handler.rb +0 -54
  466. data/app/models/spree/invitations/store.rb +0 -6
  467. data/app/models/spree/preference.rb +0 -14
  468. data/app/models/spree/promotion_handler/page.rb +0 -25
  469. data/app/models/spree/promotion_handler/promotion_duplicator.rb +0 -60
  470. data/app/models/spree/stock/differentiator.rb +0 -45
  471. data/app/presenters/spree/filters/options_presenter.rb +0 -47
  472. data/app/services/spree/carts/empty.rb +0 -42
  473. data/app/services/spree/country_to_timezone.rb +0 -273
  474. data/app/services/spree/payments/create.rb +0 -92
  475. data/app/validators/spree/url_validator.rb +0 -23
  476. data/app/views/active_storage/blobs/_blob.html.erb +0 -14
  477. data/app/views/spree/admin_user_mailer/password_reset_email.html.erb +0 -8
  478. data/app/views/spree/export_mailer/export_done.html.erb +0 -7
  479. data/app/views/spree/import_mailer/import_done.html.erb +0 -12
  480. data/app/views/spree/invitation_mailer/invitation_accepted.html.erb +0 -4
  481. data/app/views/spree/invitation_mailer/invitation_email.html.erb +0 -12
  482. data/app/views/spree/seller_user_mailer/password_reset_email.html.erb +0 -8
  483. data/app/views/spree/shared/_base_mailer_footer.html.erb +0 -19
  484. data/app/views/spree/shared/_base_mailer_header.html.erb +0 -8
  485. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +0 -399
  486. data/app/views/spree/shared/_mailer_logo.html.erb +0 -41
  487. data/app/views/spree/shared/_purchased_items_styles.html.erb +0 -119
  488. data/app/views/spree/webhook_mailer/endpoint_disabled.html.erb +0 -51
  489. data/app/views/spree/webhook_mailer/endpoint_disabled.text.erb +0 -10
  490. data/app/workflows/spree/fulfillments/purchase_label.rb +0 -40
  491. data/app/workflows/spree/returns/purchase_label.rb +0 -32
  492. data/lib/spree/analytics.rb +0 -11
  493. data/lib/spree/core/preferences/scoped_store.rb +0 -34
  494. data/lib/spree/core/preferences/store.rb +0 -105
  495. data/lib/spree/translation_migrations.rb +0 -52
@@ -2,6 +2,8 @@ module Spree
2
2
  # One problem item on a {Spree::Claim}, and how it is being made right:
3
3
  # money back, a replacement, or both.
4
4
  class ClaimLineItem < Spree.base_class
5
+ include Spree::PostSale::TaxedLine
6
+
5
7
  has_prefix_id :cli
6
8
 
7
9
  belongs_to :claim, class_name: 'Spree::Claim', inverse_of: :claim_line_items
@@ -20,16 +22,45 @@ module Spree
20
22
 
21
23
  delegate :order, :currency, to: :claim
22
24
 
23
- # What the customer paid for the affected units, after discounts — the
24
- # ceiling for a refund on this line.
25
+ extend Spree::DisplayMoney
26
+ money_methods :refund_amount, :paid_amount, :pre_tax_amount, :included_tax_total,
27
+ :additional_tax_total, :tax_total
28
+
29
+ # What the customer paid for the affected units, after discounts and with
30
+ # the tax charged on top of the price — the ceiling for a refund on this
31
+ # line, which is entered tax included. A claim opened before claims
32
+ # carried tax has none, so its ceiling is unchanged.
33
+ #
34
+ # @return [BigDecimal]
25
35
  def paid_amount
26
- return 0 if line_item.nil? || line_item.quantity.to_i.zero?
36
+ discounted_amount + additional_tax_total
37
+ end
27
38
 
28
- (line_item.amount / line_item.quantity) * quantity.to_i
39
+ # @return [BigDecimal] the affected units before tax
40
+ def pre_tax_amount
41
+ discounted_amount - included_tax_total
29
42
  end
30
43
 
31
- def display_refund_amount
32
- Spree::Money.new(refund_amount, currency: currency)
44
+ def tax_total
45
+ included_tax_total + additional_tax_total
46
+ end
47
+
48
+ # Nothing is given back on a claim that was denied or withdrawn.
49
+ #
50
+ # @return [Integer]
51
+ def credited_quantity
52
+ claim.denied? || claim.canceled? ? 0 : quantity.to_i
53
+ end
54
+
55
+ # @return [BigDecimal]
56
+ def credited_worth
57
+ paid_amount
58
+ end
59
+
60
+ # @return [void]
61
+ def fold_tax_lines!
62
+ included, additional = tax_totals_from(tax_lines.credits.reload)
63
+ update_columns(included_tax_total: included, additional_tax_total: additional)
33
64
  end
34
65
 
35
66
  # What actually ships when the claim is resolved with a replacement.
@@ -39,6 +70,10 @@ module Spree
39
70
 
40
71
  private
41
72
 
73
+ def discounted_amount
74
+ line_item&.discounted_amount_for(quantity) || 0
75
+ end
76
+
42
77
  def set_variant_from_line_item
43
78
  self.variant ||= line_item&.variant
44
79
  end
@@ -58,7 +58,6 @@ module Spree
58
58
  #
59
59
  # Associations
60
60
  #
61
- belongs_to :store, class_name: 'Spree::Store'
62
61
 
63
62
  has_many :product_collections, class_name: 'Spree::ProductCollection', dependent: :destroy_async, inverse_of: :collection
64
63
  has_many :products, through: :product_collections
@@ -2,6 +2,10 @@
2
2
 
3
3
  module Spree
4
4
  class CollectionRule < Spree.base_class
5
+ include Spree::TypeLabels
6
+
7
+ self.type_labels_scope = 'spree.collection_rule_types'
8
+
5
9
  has_prefix_id :crule
6
10
 
7
11
  MATCH_POLICIES = %w[is_equal_to is_not_equal_to contains does_not_contain].freeze
@@ -18,25 +22,6 @@ module Spree
18
22
 
19
23
  validate :type_must_be_registered
20
24
 
21
- # @return [String] localized display name for the rule kind, used by admin pickers
22
- def self.human_name
23
- Spree.t("collection_rule_types.#{api_type}.name", default: api_type.titleize)
24
- end
25
-
26
- # @return [String] localized description for the rule kind
27
- def self.human_description
28
- Spree.t("collection_rule_types.#{api_type}.description", default: '')
29
- end
30
-
31
- # Feeds the `description` field of `subclasses_with_preference_schema`
32
- # (the `/types` discovery payload), which only reads `.description`.
33
- def self.description
34
- human_description
35
- end
36
-
37
- def human_name = self.class.human_name
38
- def human_description = self.class.human_description
39
-
40
25
  registers_subclasses_via { Rails.application.config.spree.collection_rules }
41
26
 
42
27
  private
@@ -47,8 +32,7 @@ module Spree
47
32
  return if type.blank?
48
33
  return if Rails.application.config.spree.collection_rules.any? { |rule| rule.to_s == type }
49
34
 
50
- errors.add(:type, :invalid_collection_rule, message: Spree.t(:invalid_collection_rule, scope: [:errors, :messages],
51
- default: 'is not a registered collection rule'))
35
+ errors.add(:type, :invalid_collection_rule, message: I18n.t('spree.errors.messages.invalid_collection_rule'))
52
36
  end
53
37
 
54
38
  def regenerate_collection_products
@@ -81,7 +81,7 @@ module Spree
81
81
  def exactly_one_subject
82
82
  return if [line_item_id, fulfillment_id].compact.one?
83
83
 
84
- errors.add(:base, :exactly_one_of_line_item_or_fulfillment, message: Spree.t('errors.messages.exactly_one_of_line_item_or_fulfillment'))
84
+ errors.add(:base, :exactly_one_of_line_item_or_fulfillment, message: I18n.t('spree.errors.messages.exactly_one_of_line_item_or_fulfillment'))
85
85
  end
86
86
  end
87
87
  end
@@ -43,7 +43,6 @@ module Spree
43
43
  #
44
44
  # Associations
45
45
  #
46
- belongs_to :store, class_name: 'Spree::Store'
47
46
  # Every amount this rate states, one row per currency: what a flat fee
48
47
  # charges, and the floor and cap a percentage charges within. A flat rate
49
48
  # with no amount for the sale's currency does not apply to it (see
@@ -357,14 +356,14 @@ module Spree
357
356
  def flat_fee_does_not_charge_delivery
358
357
  return unless fixed? && include_shipping?
359
358
 
360
- errors.add(:include_shipping, :flat_fee_cannot_charge_delivery, message: Spree.t('errors.messages.flat_fee_cannot_charge_delivery'))
359
+ errors.add(:include_shipping, :flat_fee_cannot_charge_delivery, message: I18n.t('spree.errors.messages.flat_fee_cannot_charge_delivery'))
361
360
  end
362
361
 
363
362
  def fixed_rate_states_an_amount
364
363
  return unless fixed?
365
364
  return if @pending_amounts.present? || commission_rate_values.any?
366
365
 
367
- errors.add(:base, :commission_rate_needs_an_amount, message: Spree.t('errors.messages.commission_rate_needs_an_amount'))
366
+ errors.add(:base, :commission_rate_needs_an_amount, message: I18n.t('spree.errors.messages.commission_rate_needs_an_amount'))
368
367
  end
369
368
  end
370
369
  end
@@ -41,7 +41,7 @@ module Spree
41
41
  return if max_amount.nil? || min_amount.nil?
42
42
  return if max_amount >= min_amount
43
43
 
44
- errors.add(:max_amount, :must_be_greater_than_min_amount, message: Spree.t('errors.messages.must_be_greater_than_min_amount'))
44
+ errors.add(:max_amount, :must_be_greater_than_min_amount, message: I18n.t('spree.errors.messages.must_be_greater_than_min_amount'))
45
45
  end
46
46
  end
47
47
  end
@@ -15,6 +15,9 @@ module Spree
15
15
  # setting. A rate with no rules charges every sale.
16
16
  class CommissionRule < Spree.base_class
17
17
  include Spree::PreferenceSchema
18
+ include Spree::TypeLabels
19
+
20
+ self.type_labels_scope = 'spree.commission_rule_types'
18
21
 
19
22
  has_prefix_id :comrule
20
23
 
@@ -47,16 +50,6 @@ module Spree
47
50
 
48
51
  registers_subclasses_via { Spree.commission_rules }
49
52
 
50
- # @return [String] the name an operator picks this rule kind by
51
- def self.human_name
52
- Spree.t("commission_rule_types.#{api_type}.name", default: name.demodulize.titleize)
53
- end
54
-
55
- # @return [String] what the rule does, shown beside the name in the picker
56
- def self.description
57
- Spree.t("commission_rule_types.#{api_type}.description", default: '')
58
- end
59
-
60
53
  # Whether this rule admits the sale.
61
54
  #
62
55
  # @param context [Spree::Commissions::Context]
@@ -71,7 +64,7 @@ module Spree
71
64
  return if type.blank?
72
65
  return if Spree.commission_rules.any? { |rule| rule.to_s == type }
73
66
 
74
- errors.add(:type, :invalid_commission_rule, message: Spree.t('errors.messages.invalid_commission_rule'))
67
+ errors.add(:type, :invalid_commission_rule, message: I18n.t('spree.errors.messages.invalid_commission_rule'))
75
68
  end
76
69
  end
77
70
  end
@@ -48,7 +48,7 @@ module Spree
48
48
 
49
49
  return if store.products.where(id: chosen_ids).count == chosen_ids.uniq.size
50
50
 
51
- errors.add(:products, :product_not_in_store, message: Spree.t('errors.messages.product_not_in_store'))
51
+ errors.add(:products, :product_not_in_store, message: I18n.t('spree.errors.messages.product_not_in_store'))
52
52
  end
53
53
  end
54
54
  end
@@ -15,6 +15,7 @@ module Spree
15
15
  include Spree::Metadata
16
16
 
17
17
  publishes_lifecycle_events
18
+ publishes_events :accepted, :revoked
18
19
 
19
20
  EXPIRY = 30.days
20
21
 
@@ -42,7 +42,6 @@ module Spree
42
42
  SOURCES = [CHECKOUT, REGISTRATION, ACCOUNT, ADMIN, NEWSLETTER, ANONYMIZATION,
43
43
  UNKNOWN, 'import', 'storefront'].freeze
44
44
 
45
- belongs_to :store, class_name: 'Spree::Store'
46
45
  belongs_to :owner, polymorphic: true
47
46
 
48
47
  validates :purpose, presence: true
@@ -158,7 +158,7 @@ module Spree
158
158
  # Show the card brand, eg. "VISA", "MASTERCARD", etc.
159
159
  # @return [String]
160
160
  def display_brand
161
- brand.present? ? brand.upcase : Spree.t(:no_cc_type)
161
+ brand.present? ? brand.upcase : I18n.t('spree.no_cc_type')
162
162
  end
163
163
 
164
164
  # Returns the first name of the cardholder.
@@ -38,6 +38,29 @@ module Spree
38
38
  super || Spree::Store.default
39
39
  end
40
40
 
41
+ # Runs the block in +store+ from a clean context: nothing a previous store
42
+ # resolved (channel, market, currency, integrations, pricing) carries in,
43
+ # and nothing resolved inside leaks out. For work that walks several
44
+ # stores in one unit of work, such as an installation-wide sweep job; a
45
+ # request or a single-store job assigns #store once instead.
46
+ #
47
+ # The store scope guard stays armed afterwards, like any other store
48
+ # declaration in this unit of work.
49
+ #
50
+ # @param store [Spree::Store]
51
+ # @return [Object] the block's result
52
+ def with_store(store)
53
+ previous = attributes
54
+
55
+ begin
56
+ self.attributes = {}
57
+ self.store = store
58
+ yield
59
+ ensure
60
+ self.attributes = previous.merge(store_scope_guard_armed: store_scope_guard_armed)
61
+ end
62
+ end
63
+
41
64
  def channel
42
65
  super || (self.channel = store&.default_channel)
43
66
  end
@@ -128,7 +128,7 @@ module Spree
128
128
 
129
129
  unless type == custom_field_definition.field_type_class_name
130
130
  errors.add(:type, :custom_field_definition_mismatch,
131
- message: Spree.t('errors.messages.custom_field_definition_mismatch'))
131
+ message: I18n.t('spree.errors.messages.custom_field_definition_mismatch'))
132
132
  end
133
133
  end
134
134
 
@@ -144,7 +144,7 @@ module Spree
144
144
  return if definition_type.safe_constantize&.base_class&.name == resource_type
145
145
 
146
146
  errors.add(:resource_type, :custom_field_definition_mismatch,
147
- message: Spree.t('errors.messages.custom_field_definition_mismatch'))
147
+ message: I18n.t('spree.errors.messages.custom_field_definition_mismatch'))
148
148
  end
149
149
 
150
150
  # Definitions are store-owned, so a record may only carry its own store's.
@@ -39,6 +39,10 @@ module Spree
39
39
  #
40
40
  # Callbacks
41
41
  #
42
+ # The API names resources by shorthand (`product`); the column keeps the
43
+ # class name the custom fields themselves are stored under. Also applies to
44
+ # `where(resource_type:)`, so finders accept either.
45
+ normalizes :resource_type, with: ->(value) { Spree::Base.polymorphic_type_for(value, available_resources) || value }
42
46
  normalizes :key, with: ->(value) { value.to_s.parameterize.underscore.strip }
43
47
  normalizes :namespace, with: ->(value) { value.to_s.parameterize.underscore.strip }
44
48
  before_validation :set_default_type, if: -> { self[:field_type].blank? }, on: :create
@@ -117,25 +121,13 @@ module Spree
117
121
  # as the API names it, so a client renders a picker rather than carrying
118
122
  # its own list that drifts from the registry.
119
123
  #
120
- # `Spree::Category` reports itself as `Spree::Taxon`: existing category
121
- # definitions are stored under the old class name, and offering the new
122
- # one would file a merchant's next definition somewhere the category card
123
- # does not read. Drops with the alias in 6.1.
124
- #
125
- # @return [Array<Hash{Symbol => String}>] `resource_type` + `name`
124
+ # @return [Array<Hash{Symbol => String}>] `resource_type` shorthand + `name`
126
125
  def self.enabled_resource_types
127
126
  # Through `available_resources`, not the registry directly: that is the
128
127
  # accessor the rest of the class reads, so discovery and validation
129
128
  # cannot disagree about what a host app has made available.
130
- Array(available_resources).filter_map do |resource|
131
- next if resource.nil?
132
-
133
- # The label follows the class a merchant knows; the wire value follows
134
- # where the rows are actually stored.
135
- name = resource.to_s.demodulize.titleize.pluralize
136
- resource_type = resource.to_s == 'Spree::Category' ? 'Spree::Taxon' : resource.to_s
137
-
138
- { resource_type: resource_type, name: name }
129
+ Array(available_resources).compact.map do |resource|
130
+ { resource_type: Spree::Base.polymorphic_api_type(resource), name: resource.to_s.demodulize.titleize.pluralize }
139
131
  end.uniq { |entry| entry[:resource_type] }.sort_by { |entry| entry[:name] }
140
132
  end
141
133
 
@@ -174,7 +166,7 @@ module Spree
174
166
 
175
167
  tokens = Spree::CustomField::TYPE_TOKENS.keys.join(', ')
176
168
  errors.add(:field_type, :unknown_custom_field_type, types: tokens,
177
- message: Spree.t('errors.messages.unknown_custom_field_type', types: tokens))
169
+ message: I18n.t('spree.errors.messages.unknown_custom_field_type', types: tokens))
178
170
  end
179
171
 
180
172
  # Validates the stored class name, not the token the reader returns.
@@ -186,14 +178,8 @@ module Spree
186
178
  errors.add(:field_type, :inclusion)
187
179
  end
188
180
 
189
- # Both the registry's own names and the ones discovery offers: a value a
190
- # client was offered has to be accepted, and the two differ where a class
191
- # has been renamed (categories are stored under Spree::Taxon while the
192
- # registry holds Spree::Category). Existing callers name either, so
193
- # narrowing to one would reject definitions that already work.
194
181
  def valid_available_resources
195
- self.class.available_resources.map(&:to_s) |
196
- self.class.enabled_resource_types.map { |resource| resource[:resource_type] }
182
+ self.class.available_resources.map(&:to_s)
197
183
  end
198
184
 
199
185
  def set_default_type
@@ -4,7 +4,7 @@ module Spree
4
4
  normalizes :value, with: ->(value) { value.to_b.to_s }
5
5
 
6
6
  def csv_value
7
- value.to_b ? Spree.t(:say_yes) : Spree.t(:say_no)
7
+ value.to_b ? I18n.t('spree.say_yes') : I18n.t('spree.say_no')
8
8
  end
9
9
 
10
10
  def serialize_value
@@ -31,7 +31,7 @@ module Spree
31
31
  JSON.parse(value)
32
32
  rescue JSON::ParserError => e
33
33
  errors.add(:value, :invalid_json, detail: e.message,
34
- message: Spree.t('errors.messages.invalid_json', detail: e.message))
34
+ message: I18n.t('spree.errors.messages.invalid_json', detail: e.message))
35
35
  end
36
36
  end
37
37
  end
@@ -1,5 +1,3 @@
1
- require_dependency 'spree/data_feed'
2
-
3
1
  module Spree
4
2
  class DataFeed::Google < DataFeed
5
3
  class << self
@@ -19,6 +19,7 @@ module Spree
19
19
  has_spree_number prefix: 'DSR'
20
20
 
21
21
  publishes_lifecycle_events
22
+ publishes_event :completed
22
23
 
23
24
  include Spree::HasStatus
24
25
  has_status :pending, :processing, :completed, :failed, default: :pending
@@ -37,7 +38,6 @@ module Spree
37
38
  # asked, and it stops working when the request expires.
38
39
  has_secure_token :download_token
39
40
 
40
- belongs_to :store, class_name: 'Spree::Store'
41
41
  belongs_to :customer, class_name: Spree.customer_class.to_s
42
42
  # Null when the subject asked for it themselves; set when staff acted on a
43
43
  # request that arrived by email.
@@ -80,6 +80,12 @@ module Spree
80
80
  after_save :apply_pending_services, if: :pending_services?
81
81
  attribute :fulfillment_provider, :string, default: 'Spree::FulfillmentProvider::Manual'
82
82
 
83
+ # The API names providers by their shorthand (`manual`, `easy_post`); the
84
+ # columns keep the class name.
85
+ normalizes :fulfillment_provider, with: ->(value) { Spree::ApiTyped.class_name_for(Spree.fulfillment_providers, value) }
86
+ normalizes :rate_provider, with: ->(value) { Spree::ApiTyped.class_name_for(Spree.delivery_rate_providers, value) }
87
+ normalizes :pickup_point_provider, with: ->(value) { Spree::ApiTyped.class_name_for(Spree.pickup_point_providers, value) }
88
+
83
89
  # Methods whose fulfillment provider satisfies the predicate — behavior
84
90
  # queries route through provider classes, never string vocabularies.
85
91
  scope :with_provider, ->(predicate) {
@@ -138,6 +144,16 @@ module Spree
138
144
  # not save a provider whose integration isn't connected — that breaks
139
145
  # quoting at checkout, not at save time where the admin can see it.
140
146
  validate :rate_provider_must_be_available, if: :rate_provider_changed?
147
+ # Every provider column is constantized and instantiated at checkout or
148
+ # ship time, so only a registered class may be stored.
149
+ validates :fulfillment_provider,
150
+ inclusion: { in: -> (_record) { Spree.fulfillment_providers.map(&:to_s) } },
151
+ allow_blank: true,
152
+ if: :fulfillment_provider_changed?
153
+ validates :pickup_point_provider,
154
+ inclusion: { in: -> (_record) { Spree.pickup_point_providers.map(&:to_s) } },
155
+ allow_blank: true,
156
+ if: :pickup_point_provider_changed?
141
157
  # Providers declare the fulfillment types they handle; a mismatch would
142
158
  # only surface at checkout (no rates) or at ship time (no dispatch),
143
159
  # so reject it where the admin can still see why.
@@ -234,7 +250,7 @@ module Spree
234
250
  return if delivery_zone.delivery_origin_group_id == delivery_origin_group_id
235
251
 
236
252
  errors.add(:delivery_zone, :does_not_belong_to_origin_group,
237
- message: Spree.t('errors.messages.delivery_zone_not_in_origin_group'))
253
+ message: I18n.t('spree.errors.messages.delivery_zone_not_in_origin_group'))
238
254
  end
239
255
 
240
256
  def pickup_point?
@@ -263,7 +279,7 @@ module Spree
263
279
  #
264
280
  # @return [Class]
265
281
  def provider_class
266
- (fulfillment_provider.presence || DEFAULT_FULFILLMENT_PROVIDER).safe_constantize ||
282
+ Spree::ApiTyped.registered_class(Spree.fulfillment_providers, fulfillment_provider.presence || DEFAULT_FULFILLMENT_PROVIDER) ||
267
283
  DEFAULT_FULFILLMENT_PROVIDER.constantize
268
284
  end
269
285
 
@@ -286,7 +302,8 @@ module Spree
286
302
  #
287
303
  # @return [Class]
288
304
  def rate_provider_class
289
- (rate_provider.presence || DEFAULT_RATE_PROVIDER).safe_constantize || DEFAULT_RATE_PROVIDER.constantize
305
+ Spree::ApiTyped.registered_class(Spree.delivery_rate_providers, rate_provider.presence || DEFAULT_RATE_PROVIDER) ||
306
+ DEFAULT_RATE_PROVIDER.constantize
290
307
  end
291
308
 
292
309
 
@@ -363,7 +380,7 @@ module Spree
363
380
  #
364
381
  # @return [Spree::PickupPointProvider::Base, nil]
365
382
  def pickup_point_provider_instance
366
- @pickup_point_provider_instance ||= pickup_point_provider.presence&.constantize&.new
383
+ @pickup_point_provider_instance ||= Spree::ApiTyped.registered_class(Spree.pickup_point_providers, pickup_point_provider)&.new
367
384
  end
368
385
 
369
386
  def self.calculators
@@ -430,14 +447,14 @@ module Spree
430
447
 
431
448
  if calculator.is_a?(Spree::Calculator::Shipping::FlatRate)
432
449
  if calculator.preferred_amount == 0
433
- Spree.t(:free)
450
+ I18n.t('spree.free')
434
451
  else
435
452
  Spree::Money.new(calculator.preferred_amount, { currency: calculator.preferred_currency }).to_s
436
453
  end
437
454
  elsif calculator.is_a?(Spree::Calculator::Shipping::FlexiRate)
438
455
  Spree::Money.new(calculator.preferred_first_item, { currency: calculator.preferred_currency }).to_s
439
456
  elsif calculator.is_a?(Spree::Calculator::Shipping::FlatPercentItemTotal)
440
- ActionController::Base.helpers.number_to_percentage(calculator.preferred_flat_percent, precision: 2)
457
+ ActiveSupport::NumberHelper.number_to_percentage(calculator.preferred_flat_percent, precision: 2)
441
458
  else
442
459
  ''
443
460
  end
@@ -500,7 +517,7 @@ module Spree
500
517
  return if delivery_zone.delivery_profile_id == delivery_profile_id
501
518
 
502
519
  errors.add(:delivery_zone, :does_not_belong_to_profile,
503
- message: Spree.t('errors.messages.delivery_zone_not_in_profile'))
520
+ message: I18n.t('spree.errors.messages.delivery_zone_not_in_profile'))
504
521
  end
505
522
 
506
523
  def profile_must_accept_provider
@@ -513,7 +530,7 @@ module Spree
513
530
  errors.add(
514
531
  :fulfillment_provider, :profile_does_not_accept_provider,
515
532
  provider: provider_class.provider_name, profile: delivery_profile.name,
516
- message: Spree.t('errors.messages.profile_does_not_accept_provider',
533
+ message: I18n.t('spree.errors.messages.profile_does_not_accept_provider',
517
534
  provider: provider_class.provider_name, profile: delivery_profile.name)
518
535
  )
519
536
  end
@@ -527,7 +544,7 @@ module Spree
527
544
  errors.add(
528
545
  :rate_provider, :rate_provider_requires_shipping,
529
546
  provider: rate_provider_class.provider_name,
530
- message: Spree.t('errors.messages.rate_provider_requires_shipping',
547
+ message: I18n.t('spree.errors.messages.rate_provider_requires_shipping',
531
548
  provider: rate_provider_class.provider_name)
532
549
  )
533
550
  end
@@ -537,7 +554,7 @@ module Spree
537
554
  return unless Spree.delivery_rate_providers.map(&:to_s).include?(rate_provider)
538
555
  return if rate_provider_class.available_for_store?(store)
539
556
 
540
- errors.add(:rate_provider, :rate_provider_unavailable, message: Spree.t('errors.messages.rate_provider_unavailable'))
557
+ errors.add(:rate_provider, :rate_provider_unavailable, message: I18n.t('spree.errors.messages.rate_provider_unavailable'))
541
558
  end
542
559
 
543
560
  def seller_must_belong_to_store
@@ -549,18 +566,18 @@ module Spree
549
566
 
550
567
  def seller_methods_use_own_providers
551
568
  unless rate_provider.blank? || rate_provider == DEFAULT_RATE_PROVIDER
552
- errors.add(:rate_provider, :seller_delivery_method_provider, message: Spree.t('errors.messages.seller_delivery_method_provider'))
569
+ errors.add(:rate_provider, :seller_delivery_method_provider, message: I18n.t('spree.errors.messages.seller_delivery_method_provider'))
553
570
  end
554
571
 
555
572
  return if fulfillment_provider.blank? || fulfillment_provider == DEFAULT_FULFILLMENT_PROVIDER
556
573
 
557
- errors.add(:fulfillment_provider, :seller_delivery_method_provider, message: Spree.t('errors.messages.seller_delivery_method_provider'))
574
+ errors.add(:fulfillment_provider, :seller_delivery_method_provider, message: I18n.t('spree.errors.messages.seller_delivery_method_provider'))
558
575
  end
559
576
 
560
577
  def only_marketplace_methods_are_shared
561
578
  return unless available_to_sellers? && seller_owned?
562
579
 
563
- errors.add(:available_to_sellers, :seller_delivery_method_not_shareable, message: Spree.t('errors.messages.seller_delivery_method_not_shareable'))
580
+ errors.add(:available_to_sellers, :seller_delivery_method_not_shareable, message: I18n.t('spree.errors.messages.seller_delivery_method_not_shareable'))
564
581
  end
565
582
  end
566
583
  end
@@ -7,6 +7,9 @@ module Spree
7
7
  # See docs/plans/6.0-delivery-method-rules.md.
8
8
  class DeliveryMethodRule < Spree.base_class
9
9
  include Spree::PreferenceSchema
10
+ include Spree::TypeLabels
11
+
12
+ self.type_labels_scope = 'spree.delivery_method_rule_types'
10
13
 
11
14
  has_prefix_id :dmrule
12
15
 
@@ -27,16 +30,6 @@ module Spree
27
30
 
28
31
  registers_subclasses_via { Spree.delivery_method_rules }
29
32
 
30
- # @return [String] localized display name for the rule kind, used by admin pickers
31
- def self.human_name
32
- Spree.t("delivery_method_rule_types.#{api_type}.name", default: api_type.titleize)
33
- end
34
-
35
- # @return [String] localized description for the rule kind
36
- def self.human_description
37
- Spree.t("delivery_method_rule_types.#{api_type}.description", default: '')
38
- end
39
-
40
33
  # @param package [Spree::Stock::Package]
41
34
  # @return [Boolean]
42
35
  def eligible?(_package)
@@ -49,7 +42,7 @@ module Spree
49
42
  return if type.blank?
50
43
  return if Spree.delivery_method_rules.any? { |rule| rule.to_s == type }
51
44
 
52
- errors.add(:type, :invalid_delivery_method_rule, message: Spree.t(:invalid_delivery_method_rule, scope: [:errors, :messages], default: 'is not a registered delivery method rule'))
45
+ errors.add(:type, :invalid_delivery_method_rule, message: I18n.t('spree.errors.messages.invalid_delivery_method_rule'))
53
46
  end
54
47
  end
55
48
  end
@@ -40,9 +40,8 @@ module Spree
40
40
 
41
41
  return price if tax_rate.nil? || tax_amount.zero? || !tax_rate.show_rate_in_label
42
42
 
43
- Spree.t(
44
- tax_rate.included_in_price? ? :including_tax : :excluding_tax,
45
- scope: 'shipping_rates.display_price',
43
+ I18n.t(
44
+ "spree.shipping_rates.display_price.#{tax_rate.included_in_price? ? :including_tax : :excluding_tax}",
46
45
  price: price,
47
46
  tax_amount: display_tax_amount,
48
47
  tax_rate_name: tax_rate.name
@@ -147,14 +146,14 @@ module Spree
147
146
  def display_delivery_range
148
147
  return unless delivery_range
149
148
 
150
- Spree.t(:display_delivery_range, delivery_range: delivery_range)
149
+ I18n.t('spree.display_delivery_range', delivery_range: delivery_range)
151
150
  end
152
151
 
153
152
  private
154
153
 
155
154
  # What every money display on this rate reads instead of an amount.
156
155
  def quoted_after_review
157
- Spree.t('delivery_rates.quoted_after_review')
156
+ I18n.t('spree.delivery_rates.quoted_after_review')
158
157
  end
159
158
 
160
159
  def discount_amount
@@ -13,6 +13,7 @@ module Spree
13
13
  # store integration — they never store credentials themselves.
14
14
  class Base
15
15
  include Spree::IntegrationBackedProvider
16
+ extend Spree::ApiTyped
16
17
 
17
18
  attr_reader :delivery_method
18
19
 
@@ -8,7 +8,6 @@ module Spree
8
8
  # 2026-03-16 "Consolidate metadata") — write-only developer escape hatch.
9
9
  attribute :metadata, default: -> { {} }
10
10
 
11
- belongs_to :store, class_name: 'Spree::Store'
12
11
  # Zones are destination sets owned by a delivery profile; within it they
13
12
  # hang off an origin group, so the same profile can quote different
14
13
  # tables per warehouse. The profile's methods bind to at most one zone.
@@ -78,21 +78,21 @@ module Spree
78
78
 
79
79
  def postal_definition
80
80
  if postal_code_prefix.present?
81
- errors.add(:postal_code_prefix, :delivery_zone_prefix_and_range_exclusive, message: Spree.t('errors.messages.delivery_zone_prefix_and_range_exclusive')) if postal_range?
81
+ errors.add(:postal_code_prefix, :delivery_zone_prefix_and_range_exclusive, message: I18n.t('spree.errors.messages.delivery_zone_prefix_and_range_exclusive')) if postal_range?
82
82
  return
83
83
  end
84
84
 
85
85
  if postal_code_from.blank? || postal_code_to.blank?
86
- errors.add(:base, :delivery_zone_postal_definition_required, message: Spree.t('errors.messages.delivery_zone_postal_definition_required'))
86
+ errors.add(:base, :delivery_zone_postal_definition_required, message: I18n.t('spree.errors.messages.delivery_zone_postal_definition_required'))
87
87
  return
88
88
  end
89
89
 
90
90
  unless country_code.present? && range_capable_country_codes.include?(country_code)
91
- errors.add(:base, :delivery_zone_range_not_supported, message: Spree.t('errors.messages.delivery_zone_range_not_supported'))
91
+ errors.add(:base, :delivery_zone_range_not_supported, message: I18n.t('spree.errors.messages.delivery_zone_range_not_supported'))
92
92
  return
93
93
  end
94
94
 
95
- errors.add(:postal_code_to, :delivery_zone_range_inverted, message: Spree.t('errors.messages.delivery_zone_range_inverted')) if postal_code_to < postal_code_from
95
+ errors.add(:postal_code_to, :delivery_zone_range_inverted, message: I18n.t('spree.errors.messages.delivery_zone_range_inverted')) if postal_code_to < postal_code_from
96
96
  end
97
97
  end
98
98
  end