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
@@ -3,7 +3,11 @@ module Spree
3
3
  #
4
4
  # Named for {Spree::LineItem}, which it mirrors: `quantity` is what the
5
5
  # customer said was coming, `received_quantity` what the warehouse counted.
6
+ # Like a line item it carries its worth before tax in `pre_tax_amount` and
7
+ # the tax on it beside that, both for the announced quantity.
6
8
  class ReturnLineItem < Spree.base_class
9
+ include Spree::PostSale::TaxedLine
10
+
7
11
  has_prefix_id :rli
8
12
 
9
13
  belongs_to :return, class_name: 'Spree::Return', inverse_of: :return_line_items
@@ -19,31 +23,80 @@ module Spree
19
23
 
20
24
  delegate :order, :currency, to: :return
21
25
 
22
- # What this line refunds. Once the warehouse has counted, only the units
23
- # that arrived are paid for — a customer who announced three and sent two
24
- # is owed two — and before the count there is nothing to go on but the
25
- # announced quantity.
26
+ extend Spree::DisplayMoney
27
+ money_methods :pre_tax_amount, :included_tax_total, :additional_tax_total, :tax_total,
28
+ :refund_amount, :refund_tax_amount
29
+
30
+ # What this line refunds, tax included. Once the warehouse has counted,
31
+ # only the units that arrived are paid for — a customer who announced
32
+ # three and sent two is owed two — and before the count there is nothing
33
+ # to go on but the announced quantity.
26
34
  #
27
35
  # Rounded to the currency, because this is the ceiling a refund is checked
28
36
  # against as well as the figure the dialog offers: a third of $29.99 taken
29
37
  # three times is not $29.99 until it is.
30
38
  #
39
+ # A line opened before returns carried tax holds the price it was opened
40
+ # with in `pre_tax_amount` and no tax, so it refunds exactly what it did.
41
+ #
31
42
  # @return [BigDecimal]
32
43
  def refund_amount
33
- return pre_tax_amount unless self.return.counted?
34
- return 0.to_d if quantity.to_i.zero?
44
+ counted_share(total)
45
+ end
35
46
 
36
- Spree::Money::Rounding.to_currency(
37
- (pre_tax_amount / quantity) * received_quantity.to_i, currency
38
- )
47
+ # @return [BigDecimal] the tax inside {#refund_amount}
48
+ def refund_tax_amount
49
+ counted_share(tax_total)
50
+ end
51
+
52
+ # @return [BigDecimal] the announced units, tax included
53
+ def total
54
+ pre_tax_amount + tax_total
55
+ end
56
+
57
+ def tax_total
58
+ included_tax_total + additional_tax_total
59
+ end
60
+
61
+ # The units this line gives back: none once the return is canceled, what
62
+ # arrived once the warehouse has counted, what was announced before.
63
+ #
64
+ # @return [Integer]
65
+ def credited_quantity
66
+ return 0 if self.return.canceled?
67
+
68
+ self.return.counted? ? received_quantity.to_i : quantity.to_i
69
+ end
70
+
71
+ # @return [BigDecimal] what the credited units are worth, tax included
72
+ def credited_worth
73
+ refund_amount
39
74
  end
40
75
 
41
- def display_pre_tax_amount
42
- Spree::Money.new(pre_tax_amount, currency: currency)
76
+ # Copies the credit rows written at opening onto the line. The before-tax
77
+ # figure is what was paid after discounts less the tax inside it, so the
78
+ # three columns add up to the cent.
79
+ #
80
+ # @return [void]
81
+ def fold_tax_lines!
82
+ included, additional = tax_totals_from(tax_lines.credits.reload)
83
+
84
+ update_columns(
85
+ pre_tax_amount: default_pre_tax_amount - included,
86
+ included_tax_total: included,
87
+ additional_tax_total: additional
88
+ )
43
89
  end
44
90
 
45
91
  private
46
92
 
93
+ def counted_share(value)
94
+ return value unless self.return.counted?
95
+ return 0.to_d if quantity.to_i.zero?
96
+
97
+ Spree::Money::Rounding.to_currency((value / quantity) * received_quantity.to_i, currency)
98
+ end
99
+
47
100
  # The refundable amount defaults to this item's share of what the
48
101
  # customer actually paid for the line, after discounts — refunding the
49
102
  # list price would give back more than was taken.
@@ -53,9 +106,7 @@ module Spree
53
106
  end
54
107
 
55
108
  def default_pre_tax_amount
56
- return 0 if line_item.nil? || line_item.quantity.to_i.zero?
57
-
58
- (line_item.amount / line_item.quantity) * quantity.to_i
109
+ line_item&.discounted_amount_for(quantity) || 0
59
110
  end
60
111
  end
61
112
  end
@@ -150,7 +150,7 @@ module Spree
150
150
 
151
151
  def permissions_must_be_known
152
152
  unknown = permissions - Spree.permissions.catalog_keys
153
- errors.add(:permissions, :role_permissions_unknown, message: Spree.t(:role_permissions_unknown, keys: unknown.join(', '))) if unknown.any?
153
+ errors.add(:permissions, :role_permissions_unknown, message: I18n.t('spree.role_permissions_unknown', keys: unknown.join(', '))) if unknown.any?
154
154
  end
155
155
 
156
156
  # Every role is bounded by the catalog: only resources registered as
@@ -164,26 +164,26 @@ module Spree
164
164
  return if ungrantable.none?
165
165
 
166
166
  errors.add(
167
- :permissions, :role_permissions_not_grantable_for_audience, message: Spree.t(:role_permissions_not_grantable_for_audience, audience: audience, keys: ungrantable.join(', '))
167
+ :permissions, :role_permissions_not_grantable_for_audience, message: I18n.t('spree.role_permissions_not_grantable_for_audience', audience: audience, keys: ungrantable.join(', '))
168
168
  )
169
169
  end
170
170
 
171
171
  # Re-pointing the resource would move every existing assignment to another
172
172
  # panel — and, for a store, to another tenant.
173
173
  def resource_immutable
174
- errors.add(:resource, :role_resource_immutable, message: Spree.t(:role_resource_immutable))
174
+ errors.add(:resource, :role_resource_immutable, message: I18n.t('spree.role_resource_immutable'))
175
175
  end
176
176
 
177
177
  def name_immutable
178
- errors.add(:name, :role_immutable, message: Spree.t(:role_immutable)) unless mutable_was_and_not_admin?
178
+ errors.add(:name, :role_immutable, message: I18n.t('spree.role_immutable')) unless mutable_was_and_not_admin?
179
179
  end
180
180
 
181
181
  def permissions_immutable
182
- errors.add(:permissions, :role_immutable, message: Spree.t(:role_immutable)) unless mutable_was_and_not_admin?
182
+ errors.add(:permissions, :role_immutable, message: I18n.t('spree.role_immutable')) unless mutable_was_and_not_admin?
183
183
  end
184
184
 
185
185
  def description_immutable
186
- errors.add(:description, :role_immutable, message: Spree.t(:role_immutable)) unless mutable_was_and_not_admin?
186
+ errors.add(:description, :role_immutable, message: I18n.t('spree.role_immutable')) unless mutable_was_and_not_admin?
187
187
  end
188
188
 
189
189
  # Guards compare against the persisted `mutable` so flipping it in the same
@@ -197,7 +197,7 @@ module Spree
197
197
  def ensure_can_be_deleted
198
198
  return true if can_be_deleted?
199
199
 
200
- errors.add(:base, :role_cannot_be_deleted, message: Spree.t(:role_cannot_be_deleted))
200
+ errors.add(:base, :role_cannot_be_deleted, message: I18n.t('spree.role_cannot_be_deleted'))
201
201
  throw :abort
202
202
  end
203
203
  end
@@ -25,7 +25,7 @@ module Spree
25
25
  self.whitelisted_ransackable_attributes = %w[name seeded created_at updated_at]
26
26
 
27
27
  def event_serializer_class
28
- 'Spree::Api::V3::Admin::SavedReportSerializer'.safe_constantize
28
+ 'Spree::Api::V3::SavedReportEventSerializer'.safe_constantize
29
29
  end
30
30
 
31
31
  # The stored query, normalized against the registry.
@@ -25,6 +25,8 @@ module Spree
25
25
  MEMOIZED_METHODS = %w[onboarding_requirements onboarding_progress products_count returns_location].freeze
26
26
 
27
27
  publishes_lifecycle_events
28
+ publishes_events :approved, :invited, :onboarding_reopened, :onboarding_started, :rejected,
29
+ :submitted_for_review, :suspended
28
30
 
29
31
  # Where a seller is in its life on the marketplace. Transitions belong to
30
32
  # workflows (Spree::Sellers::Approve and friends) rather than a state
@@ -62,7 +64,6 @@ module Spree
62
64
  #
63
65
  # Associations
64
66
  #
65
- belongs_to :store, class_name: 'Spree::Store'
66
67
  # The address knows it is a seller's from its owner, so it is read back
67
68
  # with the business rules — no personal name insisted on, the company line
68
69
  # required. Not `dependent: :destroy`: a seller is paranoid, so destroy is
@@ -16,6 +16,8 @@ module Spree
16
16
  # and leaves the operator to pay by bank, while a provider like Stripe
17
17
  # Connect performs the transfer itself.
18
18
  class SellerPayout < Spree.base_class
19
+ publishes_event :completed
20
+
19
21
  has_prefix_id :vpo
20
22
 
21
23
  include Spree::Metadata
@@ -58,6 +60,11 @@ module Spree
58
60
  scope :owed, -> { where(status: %w[pending processing]) }
59
61
 
60
62
  self.whitelisted_ransackable_attributes = %w[amount currency status provider reference period_start period_end created_at seller_id]
63
+ # Filtered by the provider's API shorthand (`system`); the column holds
64
+ # its class name.
65
+ ransacker :provider, formatter: ->(value) { Spree::ApiTyped.class_name_for(Spree.payout_providers, value) } do |parent|
66
+ parent.table[:provider]
67
+ end
61
68
  # No `transfers`: they lead on to the order and its buyer, which a
62
69
  # payouts-only caller may not read.
63
70
  self.whitelisted_ransackable_associations = %w[seller]
@@ -16,6 +16,9 @@ module Spree
16
16
  include Spree::HasListPosition
17
17
  include Spree::PreferenceSchema
18
18
  include Spree::Metadata
19
+ include Spree::TypeLabels
20
+
21
+ self.type_labels_scope = 'spree.seller_requirement_types'
19
22
 
20
23
  has_prefix_id :selreq
21
24
 
@@ -87,16 +90,6 @@ module Spree
87
90
  end
88
91
  end
89
92
 
90
- # @return [String] the localized name of the kind, used by operator pickers
91
- def self.human_name
92
- Spree.t("seller_requirement_types.#{api_type}.name", default: api_type.titleize)
93
- end
94
-
95
- # @return [String] the localized description of the kind
96
- def self.human_description
97
- Spree.t("seller_requirement_types.#{api_type}.description", default: '')
98
- end
99
-
100
93
  # Whether the operator may configure this kind more than once per store.
101
94
  # True for the kinds whose meaning comes from the operator's own copy.
102
95
  #
@@ -266,8 +259,7 @@ module Spree
266
259
  return if Spree.seller_requirements.any? { |kind| kind.to_s == type }
267
260
 
268
261
  errors.add(
269
- :type, :invalid_seller_requirement, message: Spree.t(:invalid_seller_requirement, scope: [:errors, :messages],
270
- default: 'is not a registered seller requirement')
262
+ :type, :invalid_seller_requirement, message: I18n.t('spree.errors.messages.invalid_seller_requirement')
271
263
  )
272
264
  end
273
265
  end
@@ -22,8 +22,7 @@ module Spree
22
22
  return if custom_field_definition.resource_type == Spree::Seller.to_s
23
23
 
24
24
  errors.add(:custom_field_definition, :invalid,
25
- message: Spree.t('seller_requirements.custom_field_not_for_sellers',
26
- default: 'must be a custom field defined for sellers'))
25
+ message: I18n.t('spree.seller_requirements.custom_field_not_for_sellers'))
27
26
  end
28
27
 
29
28
  # Both sides are store-owned, so a marketplace can only ask its sellers
@@ -16,6 +16,7 @@ module Spree
16
16
  has_prefix_id :selsub
17
17
 
18
18
  publishes_lifecycle_events
19
+ publishes_events :accepted, :created, :rejected, :waived
19
20
 
20
21
  # `waived` is the operator excusing one seller from something the store
21
22
  # asks of everyone — a requirement they have already satisfied off the
@@ -84,8 +85,7 @@ module Spree
84
85
  return if seller.nil? || requirement.nil?
85
86
  return if seller.store_id == requirement.store_id
86
87
 
87
- errors.add(:requirement, :seller_requirement_store_mismatch, message: Spree.t('errors.messages.seller_requirement_store_mismatch',
88
- default: 'must belong to the same store as the seller'))
88
+ errors.add(:requirement, :seller_requirement_store_mismatch, message: I18n.t('spree.errors.messages.seller_requirement_store_mismatch'))
89
89
  end
90
90
  end
91
91
  end
@@ -90,14 +90,13 @@ module Spree
90
90
  # On the preference rather than :base, so the operator's form marks the
91
91
  # field they got wrong instead of only showing a summary line.
92
92
  errors.add(:preferred_terms_effective_from, :invalid_terms_effective_from,
93
- message: Spree.t('seller_requirements.invalid_terms_effective_from',
94
- default: 'Terms effective from is not a date'))
93
+ message: I18n.t('spree.seller_requirements.invalid_terms_effective_from'))
95
94
  end
96
95
 
97
96
  def store_terms_body
98
97
  return if store.nil?
99
98
 
100
- store.policies.with_matching_name(Spree.t(:terms_of_service)).detect(&:with_body?)&.body
99
+ store.policies.with_matching_name(I18n.t('spree.terms_of_service')).detect(&:with_body?)&.body
101
100
  end
102
101
  end
103
102
  end
@@ -112,6 +112,11 @@ module Spree
112
112
  end
113
113
 
114
114
  self.whitelisted_ransackable_attributes = %w[amount currency kind status provider reference created_at seller_id order_id payout_id]
115
+ # Filtered by the provider's API shorthand (`system`); the column holds
116
+ # its class name.
117
+ ransacker :provider, formatter: ->(value) { Spree::ApiTyped.class_name_for(Spree.payout_providers, value) } do |parent|
118
+ parent.table[:provider]
119
+ end
115
120
  # No `order`: filtering through it reaches the buyer's email and address,
116
121
  # which a payouts-only caller may not read. `order_id` covers the lookup.
117
122
  self.whitelisted_ransackable_associations = %w[seller payout refund]
@@ -20,6 +20,7 @@ module Spree
20
20
  include Spree::Metadata
21
21
 
22
22
  publishes_lifecycle_events
23
+ publishes_events :purchased, :refunded
23
24
 
24
25
  has_status :purchased, :refund_requested, :refunded, default: :purchased
25
26
 
@@ -17,11 +17,11 @@ module Spree
17
17
  if variant.available? || variant.preorder?
18
18
  line_item.errors.add(:quantity,
19
19
  :selected_quantity_not_available,
20
- message: Spree.t(:selected_quantity_not_available, item: display_name.inspect))
20
+ message: I18n.t('spree.selected_quantity_not_available', item: display_name.inspect))
21
21
  else
22
22
  line_item.errors.add(:base,
23
23
  :only_active_products_can_be_added_to_cart,
24
- message: Spree.t(:only_active_products_can_be_added_to_cart))
24
+ message: I18n.t('spree.only_active_products_can_be_added_to_cart'))
25
25
  end
26
26
  end
27
27
 
@@ -14,7 +14,7 @@ module Spree
14
14
  @order.line_items.map do |line_item|
15
15
  # They go through multiple splits, avoid loading the
16
16
  # association to order until needed.
17
- Spree::InventoryUnit.new(
17
+ Spree::FulfillmentItem.new(
18
18
  pending: true,
19
19
  line_item_id: line_item.id,
20
20
  variant_id: line_item.variant_id,
@@ -35,8 +35,8 @@ module Spree
35
35
  next unless stock_location.stocks? variant
36
36
 
37
37
  on_hand, backordered = stock_location.fill_status(variant, unit.quantity)
38
- package.add(InventoryUnit.split(unit, backordered), :backordered) if backordered.positive?
39
- package.add(InventoryUnit.split(unit, on_hand), :on_hand) if on_hand.positive?
38
+ package.add(FulfillmentItem.split(unit, backordered), :backordered) if backordered.positive?
39
+ package.add(FulfillmentItem.split(unit, on_hand), :on_hand) if on_hand.positive?
40
40
  else
41
41
  package.add unit
42
42
  end
@@ -73,7 +73,7 @@ module Spree
73
73
  per_content_max_quantity = 1 if per_content_max_quantity.zero?
74
74
  content_items = [content_item]
75
75
  while content_item.quantity > per_content_max_quantity
76
- split_inventory = InventoryUnit.split(content_item.inventory_unit, per_content_max_quantity)
76
+ split_inventory = FulfillmentItem.split(content_item.inventory_unit, per_content_max_quantity)
77
77
  content_items << ContentItem.new(split_inventory, content_item.state)
78
78
  end
79
79
  content_items
@@ -17,6 +17,12 @@ module Spree
17
17
  LEGACY_EVENT_PREFIX = 'stock_item'
18
18
  LEGACY_EVENT_SUFFIXES = %w[created updated deleted].freeze
19
19
 
20
+ def self.included(base)
21
+ LEGACY_EVENT_SUFFIXES.each do |suffix|
22
+ base.publishes_event("#{LEGACY_EVENT_PREFIX}.#{suffix}", deprecated_alias_of: "#{base.event_prefix}.#{suffix}")
23
+ end
24
+ end
25
+
20
26
  # Wraps publishing rather than the three callbacks that call it, so the
21
27
  # decisions Publishable makes — whether events are enabled at all,
22
28
  # whether a touch-only update is worth announcing — are made once and the
@@ -100,7 +100,7 @@ module Spree
100
100
  }
101
101
 
102
102
  def backordered_inventory_units
103
- Spree::InventoryUnit.backordered_for_stock_level(self)
103
+ Spree::FulfillmentItem.backordered_for_stock_level(self)
104
104
  end
105
105
 
106
106
  # @api private
@@ -114,7 +114,7 @@ module Spree
114
114
  return default_adjustment_reason if reason.blank?
115
115
  return reason.to_s unless ADJUSTMENT_REASONS.include?(reason.to_s)
116
116
 
117
- Spree.t("stock_movement.reasons.#{reason}", locale: :en)
117
+ I18n.t("spree.stock_movement.reasons.#{reason}", locale: :en)
118
118
  end
119
119
 
120
120
  # A movement is an immutable audit row: once written, nothing may rewrite
@@ -29,7 +29,7 @@ module Spree
29
29
  def counts_something
30
30
  return if quantity_counted.positive?
31
31
 
32
- errors.add(:base, :nothing_counted, message: Spree.t('stock_receipt.errors.nothing_counted'))
32
+ errors.add(:base, :nothing_counted, message: I18n.t('spree.stock_receipt.errors.nothing_counted'))
33
33
  end
34
34
  end
35
35
  end
@@ -127,7 +127,7 @@ module Spree
127
127
  end
128
128
 
129
129
  def exactly_one_owner
130
- errors.add(:base, :exactly_one_of_cart_or_order, message: Spree.t('errors.messages.exactly_one_of_cart_or_order')) unless [order, cart].compact.one?
130
+ errors.add(:base, :exactly_one_of_cart_or_order, message: I18n.t('spree.errors.messages.exactly_one_of_cart_or_order')) unless [order, cart].compact.one?
131
131
  end
132
132
 
133
133
  # Only a quantity change moves units between counters, so re-pointing a
@@ -135,7 +135,7 @@ module Spree
135
135
  # missing from the new. A hold that belongs elsewhere is released and
136
136
  # taken again, never moved.
137
137
  def stock_level_unchanged
138
- errors.add(:stock_level_id, :immutable, message: Spree.t('errors.messages.stock_level_immutable')) if stock_level_id_changed?
138
+ errors.add(:stock_level_id, :immutable, message: I18n.t('spree.errors.messages.stock_level_immutable')) if stock_level_id_changed?
139
139
  end
140
140
  end
141
141
  end
@@ -36,6 +36,8 @@ module Spree
36
36
  acts_as_paranoid
37
37
 
38
38
  publishes_lifecycle_events
39
+ publishes_events :canceled, :draft, :partially_received, :over_received, :ready_to_ship,
40
+ :received, :shipped
39
41
 
40
42
  has_status :draft, :ready_to_ship, :in_transit, :partially_received, :received, :over_received, :canceled,
41
43
  default: :draft
@@ -97,7 +99,7 @@ module Spree
97
99
  return if source_location_id.blank? || destination_location_id.blank?
98
100
  return if source_location_id != destination_location_id
99
101
 
100
- errors.add(:source_location, :same_location, message: Spree.t('stock_transfer.errors.same_location'))
102
+ errors.add(:source_location, :same_location, message: I18n.t('spree.stock_transfer.errors.same_location'))
101
103
  end
102
104
 
103
105
  # A transfer moves a merchant's own stock between their own warehouses. Two
@@ -108,7 +110,7 @@ module Spree
108
110
  return if source_location.store_id == destination_location.store_id
109
111
 
110
112
  errors.add(:destination_location, :must_belong_to_same_store,
111
- message: Spree.t('stock_transfer.errors.locations_in_different_stores'))
113
+ message: I18n.t('spree.stock_transfer.errors.locations_in_different_stores'))
112
114
  end
113
115
  end
114
116
  end
@@ -13,6 +13,7 @@ module Spree
13
13
  include Spree::Stores::Setup
14
14
  include Spree::Stores::Markets
15
15
  include Spree::Stores::Channels
16
+ include Spree::Stores::EmailBranding
16
17
  include Spree::StoreDataSources
17
18
  include Spree::Security::Stores if defined?(Spree::Security::Stores)
18
19
  include Spree::UserManagement
@@ -45,6 +46,9 @@ module Spree
45
46
  preference :timezone, :string, default: Time.zone.name
46
47
  preference :weight_unit, :string, default: 'lb'
47
48
  preference :unit_system, :string, default: 'imperial'
49
+ # Anonymous identifier of this installation, kept on the default store
50
+ # (see Spree.install_id). Written by Spree, never by the operator.
51
+ preference :install_id, :string, internal: true, nullable: true
48
52
  # email preferences
49
53
  preference :send_consumer_transactional_emails, :boolean, default: true
50
54
  # Sellers are a different audience from shoppers, with their own reasons to
@@ -69,7 +73,8 @@ module Spree
69
73
  # Who pays this store's sellers. Blank means core's record-only provider,
70
74
  # so a marketplace that has connected nothing still keeps a correct ledger
71
75
  # and settles by hand.
72
- preference :payout_provider, :string, default: nil
76
+ preference :payout_provider, :string, default: nil,
77
+ parse_on_set: ->(value) { Spree::ApiTyped.class_name_for(Spree.payout_providers, value) }
73
78
  # Refused at write time rather than silently falling back at read time: an
74
79
  # operator who mistypes a provider would otherwise get a bookkeeping-only
75
80
  # ledger and no indication anywhere that their choice was ignored.
@@ -151,7 +156,8 @@ module Spree
151
156
  preference :digital_asset_link_expire_time, :integer, default: 300
152
157
  # Class name of the Spree::OrderRouting::Strategy::Base subclass that
153
158
  # decides which StockLocation fulfills which items.
154
- preference :order_routing_strategy, :string, default: 'Spree::OrderRouting::Strategy::Rules'
159
+ preference :order_routing_strategy, :string, default: 'Spree::OrderRouting::Strategy::Rules',
160
+ parse_on_set: ->(value) { Spree::ApiTyped.class_name_for(Spree.order_routing.strategies, value) }
155
161
 
156
162
  # Document numbering (docs/plans/6.0-document-numbers.md). The format
157
163
  # applies to every numbered document; prefix, suffix and starting value
@@ -300,6 +306,8 @@ module Spree
300
306
  has_many :policies, class_name: 'Spree::Policy', dependent: :destroy, as: :owner
301
307
 
302
308
  has_many :webhook_endpoints, class_name: 'Spree::WebhookEndpoint', dependent: :destroy, inverse_of: :store
309
+ has_many :email_templates, class_name: 'Spree::EmailTemplate', dependent: :destroy, inverse_of: :store
310
+ has_many :email_template_drafts, class_name: 'Spree::EmailTemplateDraft', dependent: :destroy, inverse_of: :store
303
311
  has_many :webhook_deliveries, through: :webhook_endpoints, class_name: 'Spree::WebhookDelivery'
304
312
 
305
313
  has_many :channels, class_name: 'Spree::Channel', dependent: :destroy
@@ -384,6 +392,7 @@ module Spree
384
392
  before_validation :set_default_code, on: :create
385
393
  before_validation :normalize_preferred_storefront_url
386
394
  before_save :ensure_default_exists_and_is_unique
395
+ before_save :set_install_id
387
396
  after_create :create_default_policies
388
397
  after_create :create_default_delivery_profile
389
398
 
@@ -475,14 +484,12 @@ module Spree
475
484
  #
476
485
  # @return [Class]
477
486
  def payout_provider_class
478
- configured = preferred_payout_provider.presence
479
487
  # A name no longer in the registry — a typo, or a gem since removed —
480
488
  # would otherwise raise inside a subscriber on every fulfillment and stop
481
489
  # the ledger recording anything. The built-in provider keeps the books
482
490
  # until an operator fixes the setting.
483
- configured = nil unless Spree.payout_providers.any? { |provider| provider.to_s == configured }
484
-
485
- (configured || Spree.default_payout_provider.to_s).constantize
491
+ Spree::ApiTyped.registered_class(Spree.payout_providers, preferred_payout_provider) ||
492
+ Spree.default_payout_provider.to_s.constantize
486
493
  end
487
494
 
488
495
  def formatted_url
@@ -543,6 +550,21 @@ module Spree
543
550
  allowed_origins.any? { |allowed_origin| allowed_origin.matches?(url) }
544
551
  end
545
552
 
553
+ # The logo emails show: the dedicated mailer logo, else the store logo.
554
+ #
555
+ # @return [ActiveStorage::Attached::One, nil] nil when neither is an image
556
+ def email_logo
557
+ logo = mailer_logo.attached? ? mailer_logo : self.logo
558
+ logo if logo.attached? && logo.variable?
559
+ end
560
+
561
+ # The address customers are told to write to: the support address, else the sender address.
562
+ #
563
+ # @return [String, nil]
564
+ def support_email_address
565
+ customer_support_email.presence || mail_from_address
566
+ end
567
+
546
568
  # Returns the states available for checkout for the store
547
569
  # @param country [Spree::Country] the country to get the states for
548
570
  # @return [Array<Spree::State>]
@@ -613,7 +635,7 @@ module Spree
613
635
  @default_stock_location ||= begin
614
636
  stock_location_scope = stock_locations.first_party.where(default: true)
615
637
  stock_location_scope.first || ActiveRecord::Base.connected_to(role: :writing) do
616
- stock_location_scope.create(default: true, name: Spree.t(:default_stock_location_name),
638
+ stock_location_scope.create(default: true, name: I18n.t('spree.default_stock_location_name'),
617
639
  country_code: default_country&.iso)
618
640
  end
619
641
  end
@@ -699,6 +721,10 @@ module Spree
699
721
  I18n.t(key, locale: locale, default: I18n.t(key, locale: :en))
700
722
  end
701
723
 
724
+ def set_install_id
725
+ self.preferred_install_id = SecureRandom.uuid if preferred_install_id.blank?
726
+ end
727
+
702
728
  def ensure_default_exists_and_is_unique
703
729
  if default
704
730
  Spree::Store.where.not(id: id).update_all(default: false)
@@ -18,7 +18,6 @@ module Spree
18
18
  AUTHORIZE_ACTION = 'authorize'.freeze
19
19
  ALLOCATION_ACTION = 'allocation'.freeze
20
20
 
21
- belongs_to :store, class_name: 'Spree::Store'
22
21
  # A gift card applied to a guest cart issues store credit with no customer
23
22
  # to attach it to.
24
23
  belongs_to :customer, class_name: "::#{Spree.customer_class}", optional: true
@@ -146,16 +145,16 @@ module Spree
146
145
  )
147
146
  authorization_code
148
147
  else
149
- errors.add(:base, :insufficient_authorized_amount, message: Spree.t('store_credit_payment_method.insufficient_authorized_amount'))
148
+ errors.add(:base, :insufficient_authorized_amount, message: I18n.t('spree.store_credit_payment_method.insufficient_authorized_amount'))
150
149
  false
151
150
  end
152
151
  end
153
152
 
154
153
  def validate_authorization(amount, order_currency)
155
154
  if BigDecimal(amount_remaining, 3) < BigDecimal(amount, 3)
156
- errors.add(:base, :insufficient_funds, message: Spree.t('store_credit_payment_method.insufficient_funds'))
155
+ errors.add(:base, :insufficient_funds, message: I18n.t('spree.store_credit_payment_method.insufficient_funds'))
157
156
  elsif currency != order_currency
158
- errors.add(:base, :currency_mismatch, message: Spree.t('store_credit_payment_method.currency_mismatch'))
157
+ errors.add(:base, :currency_mismatch, message: I18n.t('spree.store_credit_payment_method.currency_mismatch'))
159
158
  end
160
159
  errors.blank?
161
160
  end
@@ -165,7 +164,7 @@ module Spree
165
164
 
166
165
  if amount <= amount_authorized
167
166
  if currency != order_currency
168
- errors.add(:base, :currency_mismatch, message: Spree.t('store_credit_payment_method.currency_mismatch'))
167
+ errors.add(:base, :currency_mismatch, message: I18n.t('spree.store_credit_payment_method.currency_mismatch'))
169
168
  false
170
169
  else
171
170
  update!(
@@ -179,7 +178,7 @@ module Spree
179
178
  authorization_code
180
179
  end
181
180
  else
182
- errors.add(:base, :insufficient_authorized_amount, message: Spree.t('store_credit_payment_method.insufficient_authorized_amount'))
181
+ errors.add(:base, :insufficient_authorized_amount, message: I18n.t('spree.store_credit_payment_method.insufficient_authorized_amount'))
183
182
  false
184
183
  end
185
184
  end
@@ -196,7 +195,7 @@ module Spree
196
195
  true
197
196
  else
198
197
  errors.add(:base, :unable_to_void, auth_code: authorization_code,
199
- message: Spree.t('store_credit_payment_method.unable_to_void', auth_code: authorization_code))
198
+ message: I18n.t('spree.store_credit_payment_method.unable_to_void', auth_code: authorization_code))
200
199
  false
201
200
  end
202
201
  end
@@ -206,7 +205,7 @@ module Spree
206
205
  capture_event = store_credit_events.find_by(action: CAPTURE_ACTION, authorization_code: authorization_code)
207
206
 
208
207
  if currency != order_currency # sanity check to make sure the order currency hasn't changed since the auth
209
- errors.add(:base, :currency_mismatch, message: Spree.t('store_credit_payment_method.currency_mismatch'))
208
+ errors.add(:base, :currency_mismatch, message: I18n.t('spree.store_credit_payment_method.currency_mismatch'))
210
209
  false
211
210
  elsif capture_event && amount <= capture_event.amount
212
211
  action_attributes = {
@@ -219,7 +218,7 @@ module Spree
219
218
  true
220
219
  else
221
220
  errors.add(:base, :unable_to_credit, auth_code: authorization_code,
222
- message: Spree.t('store_credit_payment_method.unable_to_credit', auth_code: authorization_code))
221
+ message: I18n.t('spree.store_credit_payment_method.unable_to_credit', auth_code: authorization_code))
223
222
  false
224
223
  end
225
224
  end