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
@@ -0,0 +1,5 @@
1
+ class AddReplacementToSpreeFulfillmentItems < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :spree_fulfillment_items, :replacement, :boolean, default: false, null: false
4
+ end
5
+ end
@@ -0,0 +1,30 @@
1
+ class AddPostSaleTax < ActiveRecord::Migration[8.1]
2
+ def change
3
+ change_table :spree_tax_lines, bulk: true do |t|
4
+ t.references :return_line_item, index: true
5
+ t.references :claim_line_item, index: true
6
+ t.references :exchange_line_item, index: true
7
+ t.references :original_tax_line, index: true
8
+ t.boolean :credit, default: false, null: false
9
+ end
10
+
11
+ change_table :spree_return_line_items, bulk: true do |t|
12
+ t.decimal :included_tax_total, precision: 10, scale: 2, default: 0, null: false
13
+ t.decimal :additional_tax_total, precision: 10, scale: 2, default: 0, null: false
14
+ end
15
+
16
+ change_table :spree_claim_line_items, bulk: true do |t|
17
+ t.decimal :included_tax_total, precision: 10, scale: 2, default: 0, null: false
18
+ t.decimal :additional_tax_total, precision: 10, scale: 2, default: 0, null: false
19
+ end
20
+
21
+ change_table :spree_exchange_line_items, bulk: true do |t|
22
+ t.decimal :original_included_tax_total, precision: 10, scale: 2, default: 0, null: false
23
+ t.decimal :original_additional_tax_total, precision: 10, scale: 2, default: 0, null: false
24
+ t.decimal :new_included_tax_total, precision: 10, scale: 2, default: 0, null: false
25
+ t.decimal :new_additional_tax_total, precision: 10, scale: 2, default: 0, null: false
26
+ end
27
+
28
+ add_column :spree_refunds, :tax_amount, :decimal, precision: 10, scale: 2, default: 0, null: false
29
+ end
30
+ end
@@ -0,0 +1,166 @@
1
+ class ConvertPreferencesToJson < ActiveRecord::Migration[8.1]
2
+ # The tables whose classes declare `:password` preferences. Their secrets move
3
+ # to an encrypted `secret_preferences` column (see Spree::SecretPreferences).
4
+ SECRET_TABLES = %i[spree_payment_methods spree_integrations].freeze
5
+
6
+ # Records which tables had their `text` column replaced, so `down` restores
7
+ # `text` only there — a table whose column was already JSON (delivery method
8
+ # rules, commission rules) keeps it. Dropped on the way back down.
9
+ CONVERTED_TABLES_REGISTRY = :spree_converted_preference_tables
10
+
11
+ # The conversion lives here rather than in the upgrade rake task because the
12
+ # manifest runs AFTER db:migrate — a task scheduled there would find the YAML
13
+ # column already replaced. Secrets are moved in plain text, so this needs no
14
+ # encryption keys; they stay readable and `spree:upgrade:encrypt_secret_preferences`
15
+ # encrypts them.
16
+ def up
17
+ SECRET_TABLES.each do |table|
18
+ next unless table_exists?(table)
19
+
20
+ add_column table, :secret_preferences, :text unless column_exists?(table, :secret_preferences)
21
+ end
22
+
23
+ create_table CONVERTED_TABLES_REGISTRY, if_not_exists: true do |t|
24
+ t.string :table_name, null: false
25
+ end
26
+
27
+ conversion.preference_tables.each do |table|
28
+ if conversion.text_column?(table)
29
+ conversion.convert_text_table(table)
30
+ execute(Arel::InsertManager.new.tap do |insert|
31
+ registry = Arel::Table.new(CONVERTED_TABLES_REGISTRY)
32
+ insert.into(registry).insert([[registry[:table_name], table]])
33
+ end.to_sql)
34
+ else
35
+ conversion.convert_table(table)
36
+ end
37
+ end
38
+
39
+ drop_table :spree_preferences, if_exists: true
40
+ end
41
+
42
+ # Restores YAML: a `text` column where `up` replaced one, and a YAML document
43
+ # inside a JSON string where the column was already JSON, as before the
44
+ # upgrade. Secrets go back into `preferences` — decrypted, so rolling back an
45
+ # encrypted secret needs the encryption keys — tiers become a hash keyed by
46
+ # threshold again, and decimals return as BigDecimal for every row whose class
47
+ # can be resolved; anything else comes back as the string JSON held.
48
+ def down
49
+ restore = Restore.new(connection, conversion)
50
+ create_table :spree_preferences, if_not_exists: true do |t|
51
+ t.text :value
52
+ t.string :key
53
+ t.timestamps
54
+ t.index :key, unique: true
55
+ end
56
+
57
+ converted = converted_tables
58
+ conversion.preference_tables.each do |table|
59
+ if converted.include?(table)
60
+ add_column table, :preferences_yaml, :text
61
+ restore.table(table, target: 'preferences_yaml')
62
+ remove_column table, :preferences
63
+ rename_column table, :preferences_yaml, :preferences
64
+ else
65
+ restore.table(table, target: 'preferences', inside_json: true)
66
+ end
67
+ end
68
+
69
+ drop_table CONVERTED_TABLES_REGISTRY, if_exists: true
70
+
71
+ SECRET_TABLES.each do |table|
72
+ remove_column table, :secret_preferences if table_exists?(table) && column_exists?(table, :secret_preferences)
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def converted_tables
79
+ return [] unless table_exists?(CONVERTED_TABLES_REGISTRY)
80
+
81
+ registry = Arel::Table.new(CONVERTED_TABLES_REGISTRY)
82
+ connection.select_values(registry.project(registry[:table_name]))
83
+ end
84
+
85
+ def conversion
86
+ @conversion ||= Spree::Preferences::JsonConversion.new(connection, log: ->(message) { say(message, true) })
87
+ end
88
+
89
+ class Restore
90
+ def initialize(connection, conversion)
91
+ @connection = connection
92
+ @conversion = conversion
93
+ end
94
+
95
+ def table(table, target:, inside_json: false)
96
+ typed = connection.column_exists?(table, :type)
97
+ secrets = connection.column_exists?(table, :secret_preferences)
98
+ columns = ['id', 'preferences', ('type' if typed), ('secret_preferences' if secrets)].compact
99
+
100
+ arel_table = Arel::Table.new(table)
101
+ connection.select_all(arel_table.project(*columns.map { |column| arel_table[column] })).each do |row|
102
+ next if row['preferences'].nil? && row['secret_preferences'].nil?
103
+
104
+ preferences = decode(row['preferences']) || {}
105
+ preferences.merge!(decode(decrypt(row['secret_preferences'])) || {}) if secrets
106
+ model = typed ? conversion.model_for(row['type']) : nil
107
+ restore_tiers(preferences, model)
108
+ restore_typed_values(preferences, model)
109
+
110
+ yaml = YAML.dump(preferences.transform_keys(&:to_sym))
111
+ conversion.write(table, row['id'], target => inside_json ? JSON.generate(yaml) : yaml)
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ attr_reader :connection, :conversion
118
+
119
+ def decode(raw)
120
+ raw.is_a?(String) ? JSON.parse(raw) : raw
121
+ end
122
+
123
+ # A value written before encryption was enabled is plain JSON and comes
124
+ # back as it is. An encrypted one that cannot be decrypted stops the
125
+ # rollback: writing its ciphertext into `preferences` would replace a
126
+ # gateway's keys with garbage.
127
+ def decrypt(raw)
128
+ return raw if raw.nil? || !encrypted?(raw)
129
+
130
+ unless ActiveRecord::Encryption.config.has_primary_key?
131
+ raise ActiveRecord::IrreversibleMigration, 'Encrypted secret_preferences cannot be rolled back without the Active Record encryption keys. Configure them and run the rollback again.'
132
+ end
133
+
134
+ ActiveRecord::Encryption.encryptor.decrypt(raw)
135
+ rescue ActiveRecord::Encryption::Errors::Decryption
136
+ raise ActiveRecord::IrreversibleMigration, 'An encrypted secret_preferences value could not be decrypted with the configured keys. Restore the keys it was encrypted with and run the rollback again.'
137
+ end
138
+
139
+ def encrypted?(raw)
140
+ parsed = JSON.parse(raw)
141
+ parsed.is_a?(Hash) && parsed.key?('p') && parsed.key?('h')
142
+ rescue JSON::ParserError
143
+ false
144
+ end
145
+
146
+ def restore_tiers(preferences, model)
147
+ tiers = preferences['tiers']
148
+ return unless tiers.is_a?(Array) && Spree::Preferences::JsonConversion.tiered_calculator?(model)
149
+
150
+ # A value that is not a number is kept as written, so two such tiers
151
+ # cannot collapse into one nil key.
152
+ preferences['tiers'] = tiers.to_h do |tier|
153
+ [Spree::Calculator::Tiers.decimal(tier['threshold']) || tier['threshold'],
154
+ Spree::Calculator::Tiers.decimal(tier['value']) || tier['value']]
155
+ end
156
+ end
157
+
158
+ def restore_typed_values(preferences, model)
159
+ return unless model.respond_to?(:declared_preference_types)
160
+
161
+ model.declared_preference_types.each do |name, type|
162
+ preferences[name.to_s] = Spree::Preferences::Preferable.restore_value(preferences[name.to_s], type) if preferences.key?(name.to_s)
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,52 @@
1
+ class CreateSpreeEmailTemplates < ActiveRecord::Migration[8.1]
2
+ def change
3
+ create_table :spree_email_templates do |t|
4
+ t.references :store, null: false, index: false
5
+ t.string :key, null: false
6
+ t.string :locale, null: false
7
+ t.string :status, null: false
8
+ t.text :subject
9
+ t.text :body, null: false
10
+ t.text :base_subject
11
+ t.text :base_body, null: false
12
+ t.datetime :published_at, null: false
13
+ t.references :published_by, polymorphic: true, index: true
14
+ t.datetime :reverted_at
15
+ t.references :reverted_by, polymorphic: true, index: true
16
+
17
+ t.timestamps
18
+ end
19
+
20
+ add_index :spree_email_templates, [:store_id, :key, :locale], unique: true,
21
+ name: 'index_spree_email_templates_on_store_key_locale'
22
+ add_index :spree_email_templates, :status
23
+
24
+ create_table :spree_email_template_drafts do |t|
25
+ t.references :store, null: false, index: false
26
+ t.string :key, null: false
27
+ t.string :locale, null: false
28
+ t.text :subject
29
+ t.text :body, null: false
30
+ t.text :base_subject
31
+ t.text :base_body, null: false
32
+ t.integer :lock_version, null: false, default: 0
33
+ t.references :updated_by, polymorphic: true, index: true
34
+
35
+ t.timestamps
36
+ end
37
+
38
+ add_index :spree_email_template_drafts, [:store_id, :key, :locale], unique: true,
39
+ name: 'index_spree_email_template_drafts_on_store_key_locale'
40
+
41
+ create_table :spree_email_template_revisions do |t|
42
+ t.references :email_template, null: false, index: false
43
+ t.text :subject
44
+ t.text :body, null: false
45
+ t.references :published_by, polymorphic: true, index: true
46
+ t.datetime :created_at, null: false
47
+ end
48
+
49
+ add_index :spree_email_template_revisions, [:email_template_id, :created_at],
50
+ name: 'index_spree_email_template_revisions_on_template_and_created'
51
+ end
52
+ end
@@ -63,7 +63,6 @@ module Spree
63
63
 
64
64
  puts "Rails app generator args: #{args.inspect}"
65
65
  Rails::Generators.invoke('app', args)
66
- inject_yaml_permitted_classes
67
66
  end
68
67
 
69
68
  def test_dummy_config
@@ -96,14 +95,6 @@ module Spree
96
95
 
97
96
  protected
98
97
 
99
- def inject_yaml_permitted_classes
100
- inside dummy_path do
101
- inject_into_file 'config/application.rb', %Q[
102
- config.active_record.yaml_column_permitted_classes = [Symbol, BigDecimal, ActiveSupport::HashWithIndifferentAccess, ActiveSupport::TimeWithZone, ActiveSupport::TimeZone, Time]
103
- ], after: /config\.load_defaults.*$/, verbose: true
104
- end
105
- end
106
-
107
98
  def dummy_path
108
99
  ENV['DUMMY_PATH'] || 'spec/dummy'
109
100
  end
@@ -28,6 +28,9 @@ Dummy::Application.configure do
28
28
  # the suite sees it, as every real Spree app does.
29
29
  config.action_controller.raise_on_missing_callback_actions = true
30
30
 
31
+ # Fail a spec that looks up a translation key that does not exist.
32
+ config.i18n.raise_on_missing_translations = true
33
+
31
34
  # Tell Action Mailer not to deliver emails to the real world.
32
35
  # The :test delivery method accumulates sent emails in the
33
36
  # ActionMailer::Base.deliveries array.
@@ -39,6 +42,10 @@ Dummy::Application.configure do
39
42
  # Print deprecation notices to the stderr
40
43
  config.active_support.deprecation = :stderr
41
44
 
45
+ # Spree's own code must not call the deprecated Spree.t.
46
+ config.active_support.disallowed_deprecation = :raise
47
+ config.active_support.disallowed_deprecation_warnings = [/Spree\.t is deprecated/]
48
+
42
49
  config.active_job.queue_adapter = :test
43
50
 
44
51
  config.cache_store = :null_store
@@ -29,19 +29,19 @@ module Spree
29
29
 
30
30
  def advisory_requirements
31
31
  [].tap do |r|
32
- r << req('cart', 'line_items', Spree.t('checkout_requirements.line_items_required')) unless @cart.line_items.any?
33
- r << req('address', 'email', Spree.t('checkout_requirements.email_required')) unless @cart.email.present?
34
- r << req('address', 'ship_address', Spree.t('checkout_requirements.ship_address_required')) if @cart.shipping_address_required? && @cart.ship_address.blank?
35
- r << req('delivery', 'delivery_method', Spree.t('checkout_requirements.delivery_method_required')) if delivery_step_required? && !delivery_method_selected?
36
- r << req('payment', 'payment', Spree.t('checkout_requirements.payment_required')) if payment_required? && !payment_satisfied?
37
- r << req('address', 'po_number', Spree.t('checkout_requirements.po_number_required')) if po_number_missing?
32
+ r << req('cart', 'line_items', I18n.t('spree.checkout_requirements.line_items_required')) unless @cart.line_items.any?
33
+ r << req('address', 'email', I18n.t('spree.checkout_requirements.email_required')) unless @cart.email.present?
34
+ r << req('address', 'ship_address', I18n.t('spree.checkout_requirements.ship_address_required')) if @cart.shipping_address_required? && @cart.ship_address.blank?
35
+ r << req('delivery', 'delivery_method', I18n.t('spree.checkout_requirements.delivery_method_required')) if delivery_step_required? && !delivery_method_selected?
36
+ r << req('payment', 'payment', I18n.t('spree.checkout_requirements.payment_required')) if payment_required? && !payment_satisfied?
37
+ r << req('address', 'po_number', I18n.t('spree.checkout_requirements.po_number_required')) if po_number_missing?
38
38
  r << order_minimum_requirement if below_order_minimum?
39
39
  end
40
40
  end
41
41
 
42
42
  def completion_requirements
43
- errors = stock_errors + quantity_rule_errors
44
- errors << req('address', 'email', Spree.t(:guest_checkout_not_allowed), code: 'guest_checkout_not_allowed') if @cart.guest_checkout_disallowed?
43
+ errors = stock_errors + quantity_rule_errors + assortment_errors
44
+ errors << req('address', 'email', I18n.t('spree.guest_checkout_not_allowed'), code: 'guest_checkout_not_allowed') if @cart.guest_checkout_disallowed?
45
45
  errors
46
46
  end
47
47
 
@@ -55,6 +55,22 @@ module Spree
55
55
  end
56
56
  end
57
57
 
58
+ # Re-checked at completion because a line can outlive the add that
59
+ # admitted it: a guest cart claimed after sign-in, a cart moved to
60
+ # another company, an assortment edited since. An order arriving here is
61
+ # staff's draft, which may sell outside the buyer's catalogs. A
62
+ # discontinued line already has its own requirement.
63
+ def assortment_errors
64
+ return [] unless @cart.is_a?(Spree::Cart)
65
+
66
+ line_items = completion_line_items.reject { |line_item| discontinued?(line_item) }
67
+ orderable_ids = @cart.orderable_variants.where(id: line_items.map(&:variant_id)).pluck(:id).to_set
68
+
69
+ line_items.reject { |line_item| orderable_ids.include?(line_item.variant_id) }.map do |line_item|
70
+ req('cart', 'line_items', I18n.t('spree.cart_line_item.not_orderable', li_name: line_item.name), code: 'not_orderable')
71
+ end
72
+ end
73
+
58
74
  # Advisory rather than completion-only: a buyer has to be able to build
59
75
  # the order below the threshold and read how far off they are while
60
76
  # they can still add to it. Completion refuses it because the advisory
@@ -68,7 +84,7 @@ module Spree
68
84
  shortfall = Spree::Money.new(@cart.order_minimum_shortfall, currency: minimum.currency)
69
85
 
70
86
  req('cart', 'order_minimum',
71
- Spree.t('checkout_requirements.order_minimum_not_met',
87
+ I18n.t('spree.checkout_requirements.order_minimum_not_met',
72
88
  minimum: minimum.display_amount.to_s, shortfall: shortfall.to_s),
73
89
  code: 'order_minimum_not_met')
74
90
  end
@@ -77,15 +93,23 @@ module Spree
77
93
  # while an individual variant hit its discontinue_on date after it
78
94
  # entered the cart.
79
95
  def stock_errors
80
- @cart.line_items.includes(variant: :product).filter_map do |line_item|
81
- if line_item.variant.nil? || line_item.variant.discontinued? || line_item.variant.product.discontinued?
82
- req('cart', 'line_items', Spree.t('cart_line_item.discontinued', li_name: line_item.name), code: 'discontinued')
96
+ completion_line_items.filter_map do |line_item|
97
+ if discontinued?(line_item)
98
+ req('cart', 'line_items', I18n.t('spree.cart_line_item.discontinued', li_name: line_item.name), code: 'discontinued')
83
99
  elsif !line_item.sufficient_stock?
84
- req('cart', 'line_items', Spree.t('cart_line_item.out_of_stock', li_name: line_item.name), code: 'out_of_stock')
100
+ req('cart', 'line_items', I18n.t('spree.cart_line_item.out_of_stock', li_name: line_item.name), code: 'out_of_stock')
85
101
  end
86
102
  end
87
103
  end
88
104
 
105
+ def discontinued?(line_item)
106
+ line_item.variant.nil? || line_item.variant.discontinued? || line_item.variant.product.discontinued?
107
+ end
108
+
109
+ def completion_line_items
110
+ @completion_line_items ||= @cart.line_items.includes(variant: :product).to_a
111
+ end
112
+
89
113
  def delivery_step_required?
90
114
  @cart.delivery_step_required?
91
115
  end
@@ -60,7 +60,7 @@ module Spree
60
60
 
61
61
  # Preprocessed product image variant sizes at 2x retina resolution.
62
62
  # These variants are generated on upload to reduce runtime processing.
63
- # When using spree_image_tag, pass variant option instead of width and height.
63
+ # Pass the variant to spree_image_url instead of a width and height.
64
64
  #
65
65
  # Default sizes:
66
66
  # mini (128x128) - admin thumbnails, checkout line items
@@ -26,7 +26,7 @@ module Spree
26
26
 
27
27
  if object.has_attribute?(:store_id)
28
28
  if object.store.present? && object.store != current_store
29
- raise Spree.t('errors.messages.store_is_already_set')
29
+ raise I18n.t('spree.errors.messages.store_is_already_set')
30
30
  else
31
31
  object.store = current_store
32
32
  end
@@ -37,7 +37,6 @@ module Spree
37
37
  order_update_item_service: 'Spree::Orders::UpdateItem',
38
38
  order_remove_line_item_service: 'Spree::Orders::RemoveLineItem',
39
39
  order_remove_item_service: 'Spree::Orders::RemoveItem',
40
- cart_empty_service: 'Spree::Carts::Empty',
41
40
  cart_destroy_service: 'Spree::Carts::Destroy',
42
41
  cart_associate_service: 'Spree::Carts::Associate',
43
42
  cart_remove_out_of_stock_items_service: 'Spree::Carts::RemoveOutOfStockItems',
@@ -83,7 +82,6 @@ module Spree
83
82
  fulfillment_fulfill_workflow: 'Spree::Fulfillments::Fulfill',
84
83
  fulfillment_cancel_workflow: 'Spree::Fulfillments::Cancel',
85
84
  fulfillment_mark_delivered_workflow: 'Spree::Fulfillments::MarkDelivered',
86
- fulfillment_purchase_label_workflow: 'Spree::Fulfillments::PurchaseLabel',
87
85
 
88
86
  # shipping labels + deliveries
89
87
  shipping_label_purchase_workflow: 'Spree::ShippingLabels::Purchase',
@@ -97,7 +95,6 @@ module Spree
97
95
  fulfillment_stand_down_service: 'Spree::Fulfillments::StandDownProvider',
98
96
  fulfillment_recalculate_delivery_service: 'Spree::Fulfillments::RecalculateDelivery',
99
97
  delivery_destroy_service: 'Spree::Deliveries::Destroy',
100
- return_purchase_label_workflow: 'Spree::Returns::PurchaseLabel',
101
98
 
102
99
  # inventory operations
103
100
  stock_level_correct_service: 'Spree::StockLevels::Correct',
@@ -242,7 +239,6 @@ module Spree
242
239
  # payment is arranged (deposits, net terms).
243
240
  purchase_amount_due_at_checkout_service: 'Spree::Purchases::AmountDueAtCheckout',
244
241
 
245
- payment_create_service: 'Spree::Payments::Create',
246
242
  payment_process_workflow: 'Spree::Payments::Process',
247
243
  payment_capture_workflow: 'Spree::Payments::Capture',
248
244
  payment_void_workflow: 'Spree::Payments::Void',
@@ -253,6 +249,11 @@ module Spree
253
249
  seller_payout_complete_workflow: 'Spree::SellerPayouts::Complete',
254
250
  payments_handle_webhook_workflow: 'Spree::Payments::HandleWebhook',
255
251
  payment_session_complete_workflow: 'Spree::PaymentSessions::Complete',
252
+ email_template_save_draft_workflow: 'Spree::EmailTemplates::SaveDraft',
253
+ email_template_publish_workflow: 'Spree::EmailTemplates::Publish',
254
+ email_template_revert_workflow: 'Spree::EmailTemplates::Revert',
255
+ email_template_discard_draft_workflow: 'Spree::EmailTemplates::DiscardDraft',
256
+ email_template_restore_revision_workflow: 'Spree::EmailTemplates::RestoreRevision',
256
257
 
257
258
  # finders
258
259
  current_store_finder: 'Spree::Stores::FindDefault',
@@ -0,0 +1,60 @@
1
+ module Spree
2
+ module Core
3
+ module Emails
4
+ # HTML-escapes what a Liquid email template prints, unless the value was
5
+ # marked `| raw`. Applied where output is written rather than through
6
+ # Liquid's global filter, which `{% assign %}` also runs through — that
7
+ # would escape a value once when assigned and again when printed, and
8
+ # turn assigned numbers and lists into strings.
9
+ #
10
+ # Switched on by the `escape_output` register, which Liquid hands down to
11
+ # every partial, so any other Liquid the host app runs is untouched.
12
+ module EscapedOutput
13
+ def self.active?(context)
14
+ context.registers[:escape_output]
15
+ end
16
+
17
+ # `{{ value }}` and `{% echo value %}`. Variables are not tags, so this
18
+ # is the one place Liquid itself has to be extended.
19
+ module Variable
20
+ def render_to_output_buffer(context, output)
21
+ return super unless EscapedOutput.active?(context)
22
+
23
+ write_escaped(render(context), output)
24
+ output
25
+ end
26
+
27
+ private
28
+
29
+ def write_escaped(value, output)
30
+ case value
31
+ when nil then nil
32
+ when Array then value.each { |item| write_escaped(item, output) }
33
+ else output << ERB::Util.html_escape(Liquid::Utils.to_s(value))
34
+ end
35
+ end
36
+ end
37
+
38
+ # A captured block was escaped as it rendered, so it is kept as safe
39
+ # HTML rather than escaped a second time when printed.
40
+ class Capture < Liquid::Capture
41
+ def render(context)
42
+ output = super
43
+ EscapedOutput.active?(context) ? output.html_safe : output
44
+ end
45
+ end
46
+
47
+ # `{% cycle %}` writes its values straight to the output.
48
+ class Cycle < Liquid::Cycle
49
+ def render_to_output_buffer(context, output)
50
+ return super unless EscapedOutput.active?(context)
51
+
52
+ output << ERB::Util.html_escape(super(context, +''))
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ Liquid::Variable.prepend(Spree::Core::Emails::EscapedOutput::Variable)