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
@@ -7,6 +7,8 @@ module Spree
7
7
  # decide at resolution time rather than at claim creation.
8
8
  class Resolve < Spree::Workflow
9
9
  include Spree::Refunds::OrderPayments
10
+ include Spree::Refunds::TaxCredit
11
+ include Spree::Fulfillments::Replacements
10
12
 
11
13
  hooks :validate, :before_settle, :after_resolve
12
14
 
@@ -32,6 +34,8 @@ module Spree
32
34
  step :resolve_amount if refunding?
33
35
  run_hooks :validate
34
36
 
37
+ step :settle_tax
38
+
35
39
  ApplicationRecord.transaction do
36
40
  run_hooks :before_settle
37
41
  step :build_replacement_fulfillments if replacing?
@@ -40,6 +44,7 @@ module Spree
40
44
  end
41
45
 
42
46
  external_step :refund_at_gateway if refunding? && !internal_refund?
47
+ external_step :refund_tax if refunding?
43
48
 
44
49
  step :recalculate_order
45
50
  run_hooks :after_resolve
@@ -68,14 +73,35 @@ module Spree
68
73
  # attribute, and ActiveModel raises when an error names one that
69
74
  # does not exist on the record.
70
75
  claim.errors.add(:base, :invalid_refund_method,
71
- message: Spree.t('errors.messages.invalid_refund_method'))
76
+ message: I18n.t('spree.errors.messages.invalid_refund_method'))
72
77
  failure(claim)
73
78
  end
74
79
  failure(claim, :not_approved) unless claim.approved?
75
80
  end
76
81
 
82
+ # Rewrites the credit rows for the money actually going back — each line's
83
+ # share of it follows the refund agreed for that line — or clears them
84
+ # when the claim is put right with a replacement alone.
85
+ def settle_tax
86
+ lines = claim.claim_line_items.to_a
87
+ amounts = if refunding?
88
+ weights = lines.index_with(&:refund_amount)
89
+ weights = lines.index_with(&:paid_amount) if weights.values.sum.zero?
90
+ allocate_refund(@amount_to_refund, weights, claim.currency)
91
+ else
92
+ lines.index_with { 0.to_d }
93
+ end
94
+
95
+ with_tax_provider(claim) { claim.settle_tax!(amounts: amounts) }
96
+ @tax_amount = claim.credited_tax_total
97
+ end
98
+
99
+ def refund_tax
100
+ file_tax_credit(claim, claim.claim_line_items.to_a, @amount_to_refund)
101
+ end
102
+
77
103
  # A claim can never refund more than the customer paid for the affected
78
- # items.
104
+ # items, tax included.
79
105
  def resolve_amount
80
106
  @amount_to_refund = (amount || claim.refund_total).to_d
81
107
  ceiling = claim.claim_line_items.sum(&:paid_amount).to_d
@@ -96,37 +122,13 @@ module Spree
96
122
  end
97
123
 
98
124
  def build_replacement_fulfillments
99
- units = claim.replacement_line_items.map do |line|
100
- claim.order.fulfillment_items.new(
101
- variant: line.variant_to_send,
102
- quantity: line.quantity,
103
- line_item: line.line_item,
104
- order: claim.order,
105
- status: 'on_hand'
106
- )
107
- end
108
-
109
- failure(claim, :nothing_to_replace) if units.empty?
110
-
111
- @fulfillments = Spree::Stock::Coordinator.new(claim.order, units).fulfillments
112
- if @fulfillments.flat_map(&:fulfillment_items).sum(&:quantity) != units.sum(&:quantity)
113
- failure(claim, :replacement_out_of_stock)
125
+ items = claim.replacement_line_items.map do |line|
126
+ { variant: line.variant_to_send, quantity: line.quantity, line_item: line.line_item }
114
127
  end
115
128
 
116
- claim.order.fulfillments += @fulfillments
117
- claim.order.save!
118
- @fulfillments.each { |fulfillment| allocate_replacement_stock(fulfillment) }
119
- end
120
-
121
- # See Spree::Exchanges::Fulfill — a replacement that holds no promise
122
- # ships without writing a movement, so the shelf never follows the goods.
123
- def allocate_replacement_stock(fulfillment)
124
- fulfillment.manifest.each do |item|
125
- next unless item.variant.track_inventory?
126
- next unless item.quantity.positive?
129
+ failure(claim, :nothing_to_replace) if items.empty?
127
130
 
128
- fulfillment.stock_location.allocate(item.variant, item.quantity, fulfillment)
129
- end
131
+ @fulfillments = build_replacements(claim, items)
130
132
  end
131
133
 
132
134
  def issue_store_credit
@@ -144,7 +146,8 @@ module Spree
144
146
  order: claim.order,
145
147
  amount: @amount_to_refund,
146
148
  record: claim,
147
- refunder: resolver
149
+ refunder: resolver,
150
+ tax_amount: @tax_amount
148
151
  )
149
152
 
150
153
  failure(claim, :no_refundable_payments) if @refunds.empty?
@@ -91,7 +91,7 @@ module Spree
91
91
  # release gateway data, and hand back success, for a row that is still
92
92
  # fully identified.
93
93
  unless committed
94
- return failure(customer, Spree.t('customer_errors.anonymize_failed'))
94
+ return failure(customer, I18n.t('spree.customer_errors.anonymize_failed'))
95
95
  end
96
96
 
97
97
  purge_collected_attachments
@@ -103,9 +103,8 @@ module Spree
103
103
  customer.reload
104
104
 
105
105
  customer.publish_event(
106
- 'customer.anonymized',
107
- store_id: store&.prefixed_id,
108
- requested_by_id: requested_by&.prefixed_id
106
+ 'customer.anonymized', nil,
107
+ store_id: store&.prefixed_id, requested_by_id: requested_by&.prefixed_id
109
108
  )
110
109
 
111
110
  run_hooks :after_anonymize
@@ -195,7 +195,7 @@ module Spree
195
195
  # vocabulary: a store may not have one called "Terms of Service", and
196
196
  # then there is nothing to snapshot rather than something to guess at.
197
197
  def consent_policies
198
- store.policies.with_matching_name(Spree.t(:terms_of_service)).to_a
198
+ store.policies.with_matching_name(I18n.t('spree.terms_of_service')).to_a
199
199
  end
200
200
 
201
201
  def link_order
@@ -53,7 +53,7 @@ module Spree
53
53
  def ensure_pending
54
54
  return if data_request.pending?
55
55
 
56
- failure(data_request, Spree.t('data_request_errors.not_pending'))
56
+ failure(data_request, I18n.t('spree.data_request_errors.not_pending'))
57
57
  end
58
58
 
59
59
  def mark_processing
@@ -92,7 +92,7 @@ module Spree
92
92
 
93
93
  return unless refused
94
94
 
95
- failure(data_request, Spree.t('data_request_errors.already_anonymized'))
95
+ failure(data_request, I18n.t('spree.data_request_errors.already_anonymized'))
96
96
  end
97
97
 
98
98
  def attach_export
@@ -49,7 +49,7 @@ module Spree
49
49
  return if tracking_status.blank?
50
50
  return if Spree::Delivery::STATUSES.include?(tracking_status.to_s)
51
51
 
52
- failure(delivery, Spree.t('fulfillments.errors.unknown_tracking_status', status: tracking_status))
52
+ failure(delivery, I18n.t('spree.fulfillments.errors.unknown_tracking_status', status: tracking_status))
53
53
  end
54
54
 
55
55
  # Overwrite rather than accumulate: this axis answers "where is it now",
@@ -0,0 +1,20 @@
1
+ module Spree
2
+ module EmailTemplates
3
+ # Throws away the draft of one template and language. Given the
4
+ # `lock_version` the draft was seen at, a draft saved or published since
5
+ # is reported as a conflict.
6
+ class DiscardDraft < Spree::Workflow
7
+ def perform(store:, key:, locale: Spree::EmailTemplate::ANY_LOCALE, lock_version: nil)
8
+ super
9
+ draft = store.email_template_drafts.find_by(key: key, locale: locale)
10
+ return failure(nil, :stale) if !lock_version.nil? && lock_version.to_i != draft&.lock_version
11
+ return success(nil) unless draft
12
+
13
+ draft.destroy!
14
+ success(nil)
15
+ rescue ActiveRecord::StaleObjectError
16
+ failure(nil, :stale)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,70 @@
1
+ module Spree
2
+ module EmailTemplates
3
+ # Makes a draft live. The draft is first rendered the way customers would
4
+ # receive it (see Spree::EmailTemplates::Check); a draft that does not
5
+ # render is refused with what is wrong and where, so an email already
6
+ # being sent can never break. Publishing records a revision and clears
7
+ # the draft. Given the `lock_version` the draft was reviewed at, a draft
8
+ # saved since is refused rather than published unseen.
9
+ class Publish < Spree::Workflow
10
+ def perform(store:, key:, locale: Spree::EmailTemplate::ANY_LOCALE, actor: nil, lock_version: nil)
11
+ super
12
+ step :ensure_draft
13
+ step :ensure_current
14
+ step :check
15
+
16
+ begin
17
+ ApplicationRecord.transaction do
18
+ step :publish
19
+ step :record_revision
20
+ step :clear_draft
21
+ end
22
+ rescue ActiveRecord::StaleObjectError, ActiveRecord::RecordNotUnique
23
+ failure(nil, :stale)
24
+ end
25
+
26
+ template.publish_event('email_template.published')
27
+ success(template)
28
+ end
29
+
30
+ private
31
+
32
+ def draft
33
+ @draft ||= store.email_template_drafts.find_by(key: key, locale: locale)
34
+ end
35
+
36
+ def template
37
+ @template ||= store.email_templates.find_or_initialize_by(key: draft.key, locale: draft.locale)
38
+ end
39
+
40
+ def ensure_draft
41
+ failure(nil, :no_draft) unless draft
42
+ end
43
+
44
+ def ensure_current
45
+ failure(nil, :stale) if !lock_version.nil? && lock_version.to_i != draft.lock_version
46
+ end
47
+
48
+ def check
49
+ problems = Spree::EmailTemplates::Check.new(store: store, draft: draft).call
50
+ failure(problems, :invalid_template) if problems.any?
51
+ end
52
+
53
+ def publish
54
+ template.assign_attributes(
55
+ subject: draft.subject, body: draft.body, base_subject: draft.base_subject, base_body: draft.base_body,
56
+ status: :published, published_at: Time.current, published_by: actor, reverted_at: nil, reverted_by: nil
57
+ )
58
+ template.save!
59
+ end
60
+
61
+ def record_revision
62
+ template.revisions.create!(subject: template.subject, body: template.body, published_by: actor)
63
+ end
64
+
65
+ def clear_draft
66
+ draft.destroy!
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,33 @@
1
+ module Spree
2
+ module EmailTemplates
3
+ # Copies a published revision into the draft, to be checked and
4
+ # published like any other change. A revision of the version for every
5
+ # language can be restored into one language's draft (`locale`).
6
+ class RestoreRevision < Spree::Workflow
7
+ # @param draft_version [Hash] `{ lock_version: }` to check the draft as saving does; empty checks nothing
8
+ def perform(revision:, actor: nil, locale: nil, draft_version: {})
9
+ super
10
+ template = revision.email_template
11
+ target_locale = locale.presence || template.locale
12
+
13
+ Spree.email_template_save_draft_workflow.call(
14
+ store: template.store, key: template.key, locale: target_locale, actor: actor,
15
+ attributes: {
16
+ subject: readable(revision.subject, target_locale, escape: false),
17
+ body: readable(revision.body, target_locale),
18
+ **draft_version.slice(:lock_version)
19
+ }
20
+ )
21
+ end
22
+
23
+ private
24
+
25
+ # A shared revision restored into one language reads in that language's words.
26
+ def readable(text, target_locale, escape: true)
27
+ return text if target_locale == Spree::EmailTemplate::ANY_LOCALE
28
+
29
+ Spree::Emails::TranslationInliner.call(text, locale: target_locale, escape: escape)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,29 @@
1
+ module Spree
2
+ module EmailTemplates
3
+ # Goes back to Spree's default for one template and language: the
4
+ # published version is marked reverted, so the file answers again, and
5
+ # any draft is discarded. The row and its revisions stay, so the history
6
+ # can still be restored. Given the `lock_version` the draft was seen at,
7
+ # a draft saved or published since is not thrown away.
8
+ class Revert < Spree::Workflow
9
+ def perform(store:, key:, locale: Spree::EmailTemplate::ANY_LOCALE, actor: nil, lock_version: nil)
10
+ super
11
+ template = store.email_templates.find_by(key: key, locale: locale)
12
+ draft = store.email_template_drafts.find_by(key: key, locale: locale)
13
+ return failure(nil, :stale) if !lock_version.nil? && lock_version.to_i != draft&.lock_version
14
+
15
+ begin
16
+ ApplicationRecord.transaction do
17
+ draft&.destroy!
18
+ template&.update!(status: :reverted, reverted_at: Time.current, reverted_by: actor) if template&.published?
19
+ end
20
+ rescue ActiveRecord::StaleObjectError
21
+ return failure(nil, :stale)
22
+ end
23
+
24
+ template&.publish_event('email_template.reverted') if template&.reverted?
25
+ success(template)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,85 @@
1
+ module Spree
2
+ module EmailTemplates
3
+ # Saves a draft of an editable email template, in any state: it is work
4
+ # in progress and customers keep receiving the published version. The
5
+ # first save starts from what is published, else Spree's default, and
6
+ # remembers that default so an upgrade changing it can be spotted.
7
+ #
8
+ # A save carrying an older `lock_version` than the draft has is refused,
9
+ # so two admins editing at once cannot overwrite each other. `rebase`
10
+ # marks the draft as based on Spree's current default, for a merchant who
11
+ # reviewed an upgraded default and keeps their own version.
12
+ class SaveDraft < Spree::Workflow
13
+ def perform(store:, key:, attributes:, locale: Spree::EmailTemplate::ANY_LOCALE, actor: nil)
14
+ super
15
+ step :ensure_editable
16
+ step :ensure_current
17
+ step :save
18
+
19
+ success(draft)
20
+ end
21
+
22
+ private
23
+
24
+ def draft
25
+ @draft ||= store.email_template_drafts.find_or_initialize_by(key: key, locale: locale)
26
+ end
27
+
28
+ def ensure_editable
29
+ failure(nil, :not_editable) unless Spree.editable_email_templates.include?(key)
30
+ end
31
+
32
+ # Without `lock_version` nothing is checked. `nil` says the caller saw no
33
+ # draft, so one saved since is refused rather than overwritten.
34
+ def ensure_current
35
+ return unless attributes.key?(:lock_version)
36
+
37
+ expected = attributes[:lock_version]
38
+ if expected.nil?
39
+ failure(nil, :stale) if draft.persisted?
40
+ elsif draft.new_record? || expected.to_i != draft.lock_version
41
+ # A draft that is gone was published or discarded since it was loaded.
42
+ failure(nil, :stale)
43
+ end
44
+ end
45
+
46
+ def save
47
+ start_from_current if draft.new_record?
48
+ rebase if ActiveModel::Type::Boolean.new.cast(attributes[:rebase])
49
+ draft.subject = attributes[:subject] if attributes.key?(:subject)
50
+ draft.body = attributes[:body] if attributes.key?(:body)
51
+ draft.updated_by = actor
52
+ failure(draft) unless draft.save
53
+ rescue ActiveRecord::StaleObjectError, ActiveRecord::RecordNotUnique
54
+ failure(nil, :stale)
55
+ end
56
+
57
+ # Starts from what customers in this language receive, with Spree's
58
+ # translation keys written out as text in it.
59
+ def start_from_current
60
+ published = store.email_templates.published.where(key: key, locale: [draft.locale, Spree::EmailTemplate::ANY_LOCALE]).
61
+ min_by { |template| template.locale == draft.locale ? 0 : 1 }
62
+
63
+ draft.subject = readable(published&.subject || default&.subject, escape: false)
64
+ draft.body = readable(published&.body || default&.body)
65
+ draft.base_subject = published ? published.base_subject : default&.subject
66
+ draft.base_body = published ? published.base_body : default&.body
67
+ end
68
+
69
+ def rebase
70
+ draft.base_subject = default&.subject
71
+ draft.base_body = default&.body
72
+ end
73
+
74
+ def readable(text, escape: true)
75
+ return text if draft.locale == Spree::EmailTemplate::ANY_LOCALE
76
+
77
+ Spree::Emails::TranslationInliner.call(text, locale: draft.locale, escape: escape)
78
+ end
79
+
80
+ def default
81
+ @default ||= Spree::Emails::TemplateResolver.for_mailers.find_default(key)
82
+ end
83
+ end
84
+ end
85
+ end
@@ -1,38 +1,21 @@
1
1
  module Spree
2
2
  module Exchanges
3
3
  class Approve < Spree::Workflow
4
+ include Spree::PostSale::Approval
5
+
4
6
  hooks :validate, :after_approve
5
7
 
6
8
  # @param exchange [Spree::Exchange]
7
9
  # @param approver [Object, nil]
8
10
  def perform(exchange:, approver: nil)
9
11
  super
10
-
11
- step :ensure_approvable
12
- run_hooks :validate
13
-
14
- ApplicationRecord.transaction do
15
- step :mark_approved
16
- end
17
-
18
- run_hooks :after_approve
19
- exchange.publish_event('exchange.approved')
20
- success(exchange.reload)
12
+ approve
21
13
  end
22
14
 
23
15
  private
24
16
 
25
- def ensure_approvable
26
- failure(exchange, :not_requested) unless exchange.requested?
27
- end
28
-
29
- def mark_approved
30
- exchange.update!(
31
- status: 'approved',
32
- approved_at: Time.current,
33
- created_by: exchange.created_by || approver
34
- )
35
- end
17
+ def post_sale_record = exchange
18
+ def approvable_status = 'requested'
36
19
  end
37
20
  end
38
21
  end
@@ -4,37 +4,21 @@ module Spree
4
4
  # merchant holds the customer's items and must either fulfill the
5
5
  # replacement or refund.
6
6
  class Cancel < Spree::Workflow
7
+ include Spree::PostSale::Cancellation
8
+
7
9
  hooks :validate, :after_cancel
8
10
 
9
11
  # @param exchange [Spree::Exchange]
10
12
  # @param reason [String, nil]
11
13
  def perform(exchange:, reason: nil)
12
14
  super
13
-
14
- step :ensure_cancellable
15
- run_hooks :validate
16
-
17
- ApplicationRecord.transaction do
18
- step :mark_canceled
19
- end
20
-
21
- run_hooks :after_cancel
22
- exchange.publish_event('exchange.canceled')
23
- success(exchange.reload)
15
+ cancel
24
16
  end
25
17
 
26
18
  private
27
19
 
28
- def ensure_cancellable
29
- return if exchange.requested? || exchange.approved?
30
-
31
- failure(exchange, :not_cancellable)
32
- end
33
-
34
- def mark_canceled
35
- memo = [exchange.memo, reason].compact_blank.join("\n")
36
- exchange.update!(status: 'canceled', canceled_at: Time.current, memo: memo.presence)
37
- end
20
+ def post_sale_record = exchange
21
+ def cancellable_statuses = %w[requested approved]
38
22
  end
39
23
  end
40
24
  end
@@ -4,6 +4,7 @@ module Spree
4
4
  # in their place.
5
5
  class Create < Spree::Workflow
6
6
  include Spree::Returns::ReturnableQuantity
7
+ include Spree::Refunds::TaxCredit
7
8
 
8
9
  hooks :validate, :after_create
9
10
 
@@ -24,6 +25,7 @@ module Spree
24
25
 
25
26
  ApplicationRecord.transaction do
26
27
  step :build_exchange
28
+ step :calculate_tax
27
29
  end
28
30
 
29
31
  run_hooks :after_create
@@ -92,6 +94,10 @@ module Spree
92
94
  failure(@exchange) unless @exchange.save
93
95
  end
94
96
 
97
+ def calculate_tax
98
+ with_tax_provider(exchange) { exchange.calculate_tax! }
99
+ end
100
+
95
101
  def default_stock_location
96
102
  @normalized_items.first[:fulfillment_item].fulfillment&.stock_location ||
97
103
  order.store.stock_locations.first