spree_core 6.0.0.beta4 → 6.0.0.rc2

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 (575) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +5 -5
  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/search_provider/index_job.rb +4 -0
  10. data/app/jobs/spree/seller_payouts/sweep_seller_job.rb +2 -0
  11. data/app/jobs/spree/uploads/purge_unattached_job.rb +49 -0
  12. data/app/mailers/spree/admin_user_mailer.rb +4 -19
  13. data/app/mailers/spree/base_mailer.rb +97 -13
  14. data/app/mailers/spree/email_template_mailer.rb +27 -0
  15. data/app/mailers/spree/export_mailer.rb +3 -4
  16. data/app/mailers/spree/import_mailer.rb +3 -4
  17. data/app/mailers/spree/invitation_mailer.rb +24 -7
  18. data/app/mailers/spree/seller_user_mailer.rb +4 -16
  19. data/app/mailers/spree/webhook_mailer.rb +2 -2
  20. data/app/models/concerns/spree/acted_by.rb +1 -1
  21. data/app/models/concerns/spree/admin_user_methods.rb +15 -1
  22. data/app/models/concerns/spree/api_typed.rb +91 -0
  23. data/app/models/concerns/spree/calculated_adjustments.rb +41 -13
  24. data/app/models/concerns/spree/calculator/currency_amounts.rb +1 -1
  25. data/app/models/concerns/spree/calculator/tiers.rb +63 -0
  26. data/app/models/concerns/spree/canonical_decimals.rb +61 -0
  27. data/app/models/concerns/spree/customer_methods.rb +23 -18
  28. data/app/models/concerns/spree/derived_number.rb +11 -13
  29. data/app/models/concerns/spree/email_template_identity.rb +52 -0
  30. data/app/models/concerns/spree/parameterizable_name.rb +2 -0
  31. data/app/models/concerns/spree/payment_session_transitions.rb +8 -2
  32. data/app/models/concerns/spree/post_sale/taxation.rb +54 -0
  33. data/app/models/concerns/spree/post_sale/taxed_line.rb +51 -0
  34. data/app/models/concerns/spree/preference_schema/json_schema.rb +139 -0
  35. data/app/models/concerns/spree/preference_schema.rb +62 -32
  36. data/app/models/concerns/spree/product_scopes.rb +16 -2
  37. data/app/models/concerns/spree/provider_listing.rb +41 -0
  38. data/app/models/concerns/spree/publishable.rb +82 -14
  39. data/app/models/concerns/spree/purchase/addresses.rb +23 -10
  40. data/app/models/concerns/spree/purchase/currency.rb +1 -1
  41. data/app/models/concerns/spree/purchase/deprecated_aliases.rb +87 -0
  42. data/app/models/concerns/spree/purchase/gift_cards.rb +10 -4
  43. data/app/models/concerns/spree/purchase/locale.rb +1 -1
  44. data/app/models/concerns/spree/purchase/payment_processing.rb +8 -4
  45. data/app/models/concerns/spree/purchase/purchase_order.rb +9 -4
  46. data/app/models/concerns/spree/purchase/store_credits.rb +36 -1
  47. data/app/models/concerns/spree/purchase/totals.rb +49 -8
  48. data/app/models/concerns/spree/ransackable_attributes.rb +46 -0
  49. data/app/models/concerns/spree/receivable.rb +1 -1
  50. data/app/models/concerns/spree/searchable.rb +0 -4
  51. data/app/models/concerns/spree/secret_preferences.rb +59 -0
  52. data/app/models/concerns/spree/signed_id_attachments.rb +54 -0
  53. data/app/models/concerns/spree/single_store_resource.rb +1 -1
  54. data/app/models/concerns/spree/stores/email_branding.rb +32 -0
  55. data/app/models/concerns/spree/stores/markets.rb +5 -2
  56. data/app/models/concerns/spree/type_labels.rb +38 -0
  57. data/app/models/concerns/spree/typed_adjustment_line.rb +5 -6
  58. data/app/models/concerns/spree/typed_associations.rb +12 -18
  59. data/app/models/concerns/spree/vat_price_calculation.rb +5 -6
  60. data/app/models/spree/address.rb +12 -2
  61. data/app/models/spree/adjusters/largest_remainder.rb +14 -1
  62. data/app/models/spree/adjusters/promotion.rb +4 -19
  63. data/app/models/spree/allowed_origin.rb +3 -1
  64. data/app/models/spree/api_key.rb +5 -6
  65. data/app/models/spree/authentication/strategies/oidc_strategy.rb +5 -5
  66. data/app/models/spree/base.rb +99 -22
  67. data/app/models/spree/calculator/flat_percent_item_total.rb +2 -4
  68. data/app/models/spree/calculator/flat_rate.rb +3 -5
  69. data/app/models/spree/calculator/flexi_rate.rb +4 -6
  70. data/app/models/spree/calculator/percent_on_line_item.rb +2 -2
  71. data/app/models/spree/calculator/price_sack.rb +5 -7
  72. data/app/models/spree/calculator/shipping/digital_delivery.rb +3 -5
  73. data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +7 -7
  74. data/app/models/spree/calculator/shipping/flat_rate.rb +5 -7
  75. data/app/models/spree/calculator/shipping/flexi_rate.rb +4 -6
  76. data/app/models/spree/calculator/shipping/per_item.rb +3 -5
  77. data/app/models/spree/calculator/shipping/price_sack.rb +5 -7
  78. data/app/models/spree/calculator/tiered_flat_rate.rb +7 -24
  79. data/app/models/spree/calculator/tiered_percent.rb +7 -25
  80. data/app/models/spree/cart.rb +78 -18
  81. data/app/models/spree/catalog.rb +1 -0
  82. data/app/models/spree/catalog_price.rb +0 -7
  83. data/app/models/spree/catalog_price_tier.rb +0 -8
  84. data/app/models/spree/category.rb +3 -1
  85. data/app/models/spree/channel/gating.rb +3 -2
  86. data/app/models/spree/channel.rb +6 -4
  87. data/app/models/spree/claim.rb +22 -1
  88. data/app/models/spree/claim_line_item.rb +41 -6
  89. data/app/models/spree/collection.rb +2 -1
  90. data/app/models/spree/collection_rule.rb +5 -21
  91. data/app/models/spree/commission_line.rb +1 -1
  92. data/app/models/spree/commission_rate.rb +7 -4
  93. data/app/models/spree/commission_rate_value.rb +1 -1
  94. data/app/models/spree/commission_rule.rb +4 -11
  95. data/app/models/spree/commission_rules/category_rule.rb +2 -5
  96. data/app/models/spree/commission_rules/item_total_rule.rb +2 -2
  97. data/app/models/spree/commission_rules/product_rule.rb +1 -1
  98. data/app/models/spree/commission_rules/seller_rule.rb +2 -5
  99. data/app/models/spree/company.rb +1 -0
  100. data/app/models/spree/company_invitation.rb +1 -0
  101. data/app/models/spree/consent_record.rb +0 -1
  102. data/app/models/spree/credit_card.rb +1 -1
  103. data/app/models/spree/current.rb +23 -0
  104. data/app/models/spree/custom_field.rb +2 -2
  105. data/app/models/spree/custom_field_definition.rb +20 -35
  106. data/app/models/spree/custom_fields/boolean.rb +1 -1
  107. data/app/models/spree/custom_fields/json.rb +1 -1
  108. data/app/models/spree/customer_group.rb +1 -0
  109. data/app/models/spree/data_feed/google.rb +0 -2
  110. data/app/models/spree/data_request.rb +2 -1
  111. data/app/models/spree/delivery_method.rb +36 -14
  112. data/app/models/spree/delivery_method_rule.rb +4 -11
  113. data/app/models/spree/delivery_method_rules/channel_rule.rb +2 -5
  114. data/app/models/spree/delivery_method_rules/item_total_rule.rb +2 -2
  115. data/app/models/spree/delivery_profile.rb +1 -0
  116. data/app/models/spree/delivery_rate.rb +7 -8
  117. data/app/models/spree/delivery_rate_provider/base.rb +1 -0
  118. data/app/models/spree/delivery_zone.rb +1 -2
  119. data/app/models/spree/delivery_zone_member.rb +4 -4
  120. data/app/models/spree/digital_asset.rb +7 -0
  121. data/app/models/spree/digital_asset_provider/base.rb +2 -0
  122. data/app/models/spree/digital_link.rb +2 -5
  123. data/app/models/spree/discount.rb +1 -1
  124. data/app/models/spree/email_template.rb +25 -0
  125. data/app/models/spree/email_template_draft.rb +13 -0
  126. data/app/models/spree/email_template_revision.rb +17 -0
  127. data/app/models/spree/email_templates/entry.rb +118 -0
  128. data/app/models/spree/email_templates/sample_record.rb +16 -0
  129. data/app/models/spree/emails/branding.rb +80 -0
  130. data/app/models/spree/exchange.rb +16 -1
  131. data/app/models/spree/exchange_line_item.rb +121 -5
  132. data/app/models/spree/export.rb +0 -1
  133. data/app/models/spree/exports/products.rb +10 -1
  134. data/app/models/spree/exports/report.rb +3 -3
  135. data/app/models/spree/fee.rb +4 -2
  136. data/app/models/spree/file_upload.rb +155 -0
  137. data/app/models/spree/fulfillment.rb +66 -30
  138. data/app/models/spree/fulfillment_changer.rb +2 -2
  139. data/app/models/spree/fulfillment_group.rb +6 -1
  140. data/app/models/spree/fulfillment_item.rb +0 -4
  141. data/app/models/spree/fulfillment_provider/base.rb +1 -0
  142. data/app/models/spree/gateway/bogus.rb +6 -0
  143. data/app/models/spree/gateway/custom_payment_source_method.rb +5 -0
  144. data/app/models/spree/gateway.rb +6 -2
  145. data/app/models/spree/gift_card.rb +18 -8
  146. data/app/models/spree/gift_card_batch.rb +3 -5
  147. data/app/models/spree/import.rb +5 -7
  148. data/app/models/spree/import_mapping.rb +1 -2
  149. data/app/models/spree/import_row.rb +2 -2
  150. data/app/models/spree/import_schemas/products.rb +1 -0
  151. data/app/models/spree/integration.rb +9 -19
  152. data/app/models/spree/invitation.rb +5 -5
  153. data/app/models/spree/line_item.rb +15 -4
  154. data/app/models/spree/locale.rb +11 -8
  155. data/app/models/spree/market.rb +8 -2
  156. data/app/models/spree/media.rb +1 -9
  157. data/app/models/spree/newsletter_subscriber.rb +4 -0
  158. data/app/models/spree/option_type.rb +2 -1
  159. data/app/models/spree/option_value.rb +2 -1
  160. data/app/models/spree/order.rb +60 -142
  161. data/app/models/spree/order_group.rb +18 -4
  162. data/app/models/spree/order_inventory.rb +10 -5
  163. data/app/models/spree/order_promotion.rb +1 -1
  164. data/app/models/spree/order_routing/has_strategy_preference.rb +1 -1
  165. data/app/models/spree/order_routing/strategy/base.rb +4 -2
  166. data/app/models/spree/order_routing/strategy/rules.rb +1 -1
  167. data/app/models/spree/order_routing_rule.rb +6 -22
  168. data/app/models/spree/package_type.rb +2 -2
  169. data/app/models/spree/payment/gateway_options.rb +15 -53
  170. data/app/models/spree/payment/processing.rb +2 -4
  171. data/app/models/spree/payment.rb +21 -13
  172. data/app/models/spree/payment_method/check.rb +2 -0
  173. data/app/models/spree/payment_method/session_instrumentation.rb +1 -0
  174. data/app/models/spree/payment_method/store_credit.rb +15 -13
  175. data/app/models/spree/payment_method.rb +78 -6
  176. data/app/models/spree/payment_session.rb +5 -1
  177. data/app/models/spree/payment_setup_session.rb +1 -0
  178. data/app/models/spree/payout_provider/base.rb +3 -1
  179. data/app/models/spree/pickup_point_provider/base.rb +2 -0
  180. data/app/models/spree/policy.rb +1 -0
  181. data/app/models/spree/price.rb +17 -14
  182. data/app/models/spree/price_adjustment_tier.rb +4 -0
  183. data/app/models/spree/price_list.rb +14 -9
  184. data/app/models/spree/price_rules/channel_rule.rb +3 -6
  185. data/app/models/spree/price_rules/customer_group_rule.rb +3 -6
  186. data/app/models/spree/price_rules/market_rule.rb +2 -5
  187. data/app/models/spree/price_rules/user_rule.rb +1 -3
  188. data/app/models/spree/product/channels.rb +9 -2
  189. data/app/models/spree/product.rb +54 -30
  190. data/app/models/spree/product_submission.rb +0 -12
  191. data/app/models/spree/product_type.rb +1 -1
  192. data/app/models/spree/promotion/actions/create_line_items.rb +28 -18
  193. data/app/models/spree/promotion/rules/category.rb +1 -5
  194. data/app/models/spree/promotion/rules/channel.rb +2 -9
  195. data/app/models/spree/promotion/rules/country.rb +3 -8
  196. data/app/models/spree/promotion/rules/currency.rb +1 -5
  197. data/app/models/spree/promotion/rules/customer_group.rb +2 -9
  198. data/app/models/spree/promotion/rules/first_order.rb +0 -4
  199. data/app/models/spree/promotion/rules/item_total.rb +6 -10
  200. data/app/models/spree/promotion/rules/market.rb +2 -9
  201. data/app/models/spree/promotion/rules/one_use_per_user.rb +0 -4
  202. data/app/models/spree/promotion/rules/option_value.rb +2 -6
  203. data/app/models/spree/promotion/rules/product.rb +1 -5
  204. data/app/models/spree/promotion/rules/user.rb +0 -4
  205. data/app/models/spree/promotion/rules/user_logged_in.rb +0 -4
  206. data/app/models/spree/promotion.rb +36 -6
  207. data/app/models/spree/promotion_action.rb +7 -14
  208. data/app/models/spree/promotion_action_line_item.rb +1 -1
  209. data/app/models/spree/promotion_handler/coupon.rb +59 -27
  210. data/app/models/spree/promotion_rule.rb +7 -14
  211. data/app/models/spree/purchase_order.rb +3 -0
  212. data/app/models/spree/quantity_rule.rb +1 -1
  213. data/app/models/spree/refund.rb +41 -18
  214. data/app/models/spree/return.rb +31 -2
  215. data/app/models/spree/return_line_item.rb +65 -14
  216. data/app/models/spree/role.rb +7 -7
  217. data/app/models/spree/saved_report.rb +2 -1
  218. data/app/models/spree/search_provider/base.rb +4 -0
  219. data/app/models/spree/seller.rb +10 -2
  220. data/app/models/spree/seller_payout.rb +8 -0
  221. data/app/models/spree/seller_requirement.rb +5 -12
  222. data/app/models/spree/seller_requirement_custom_field.rb +1 -2
  223. data/app/models/spree/seller_requirement_submission.rb +3 -2
  224. data/app/models/spree/seller_requirements/accept_terms.rb +2 -3
  225. data/app/models/spree/seller_transfer.rb +6 -0
  226. data/app/models/spree/shipping_label.rb +2 -0
  227. data/app/models/spree/stock/availability_validator.rb +2 -2
  228. data/app/models/spree/stock/estimator.rb +1 -1
  229. data/app/models/spree/stock/inventory_unit_builder.rb +1 -1
  230. data/app/models/spree/stock/packer.rb +2 -2
  231. data/app/models/spree/stock/splitter/weight.rb +1 -1
  232. data/app/models/spree/stock_level/custom_events.rb +6 -0
  233. data/app/models/spree/stock_level.rb +3 -1
  234. data/app/models/spree/stock_location.rb +1 -0
  235. data/app/models/spree/stock_movement.rb +1 -1
  236. data/app/models/spree/stock_receipt_item.rb +1 -1
  237. data/app/models/spree/stock_reservation.rb +2 -2
  238. data/app/models/spree/stock_transfer.rb +6 -2
  239. data/app/models/spree/store.rb +70 -19
  240. data/app/models/spree/store_credit.rb +13 -13
  241. data/app/models/spree/store_credit_event.rb +5 -5
  242. data/app/models/spree/supplier.rb +1 -0
  243. data/app/models/spree/tax_category.rb +1 -0
  244. data/app/models/spree/tax_exemption_certificate.rb +3 -0
  245. data/app/models/spree/tax_identifier.rb +3 -1
  246. data/app/models/spree/tax_line.rb +53 -3
  247. data/app/models/spree/tax_provider/base.rb +71 -14
  248. data/app/models/spree/tax_provider/internal.rb +70 -41
  249. data/app/models/spree/tax_provider/recorded_share.rb +114 -0
  250. data/app/models/spree/tax_rate.rb +30 -8
  251. data/app/models/spree/taxon.rb +1 -1
  252. data/app/models/spree/taxonomy.rb +0 -1
  253. data/app/models/spree/update_status.rb +25 -0
  254. data/app/models/spree/variant.rb +10 -12
  255. data/app/models/spree/variant_media.rb +1 -1
  256. data/app/models/spree/webhook_delivery.rb +1 -0
  257. data/app/models/spree/webhook_endpoint.rb +46 -13
  258. data/app/models/spree/wishlist.rb +1 -6
  259. data/app/models/spree/wishlist_item/custom_events.rb +6 -0
  260. data/app/presenters/spree/csv/customer_presenter.rb +1 -1
  261. data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +1 -1
  262. data/app/presenters/spree/csv/order_line_item_presenter.rb +10 -6
  263. data/app/presenters/spree/csv/price_list_price_presenter.rb +3 -8
  264. data/app/presenters/spree/csv/product_variant_presenter.rb +59 -20
  265. data/app/presenters/spree/csv/purchase_order_item_presenter.rb +1 -3
  266. data/app/serializers/spree/emails/base_serializer.rb +14 -0
  267. data/app/serializers/spree/emails/export_serializer.rb +13 -0
  268. data/app/serializers/spree/emails/import_serializer.rb +19 -0
  269. data/app/serializers/spree/emails/invitation_serializer.rb +28 -0
  270. data/app/serializers/spree/emails/store_serializer.rb +31 -0
  271. data/app/serializers/spree/emails/user_serializer.rb +12 -0
  272. data/app/serializers/spree/emails/webhook_endpoint_serializer.rb +16 -0
  273. data/app/services/spree/carts/create.rb +8 -2
  274. data/app/services/spree/carts/destroy.rb +2 -2
  275. data/app/services/spree/carts/remove_out_of_stock_items.rb +2 -2
  276. data/app/services/spree/carts/split_by_seller.rb +1 -1
  277. data/app/services/spree/carts/update.rb +22 -8
  278. data/app/services/spree/catalogs/set_product_terms.rb +1 -1
  279. data/app/services/spree/categories/add_products.rb +13 -31
  280. data/app/services/spree/categories/remove_products.rb +12 -51
  281. data/app/services/spree/checkout/advance.rb +1 -4
  282. data/app/services/spree/collections/add_products.rb +10 -23
  283. data/app/services/spree/collections/remove_products.rb +7 -43
  284. data/app/services/spree/coupon_codes/bulk_generate.rb +8 -14
  285. data/app/services/spree/data_requests/create.rb +1 -1
  286. data/app/services/spree/deliveries/destroy.rb +2 -2
  287. data/app/services/spree/email_templates/check.rb +58 -0
  288. data/app/services/spree/email_templates/no_sample_record.rb +7 -0
  289. data/app/services/spree/email_templates/preview.rb +99 -0
  290. data/app/services/spree/emails/file_system.rb +23 -0
  291. data/app/services/spree/emails/filters.rb +84 -0
  292. data/app/services/spree/emails/liquid_context.rb +48 -0
  293. data/app/services/spree/emails/rendered_email.rb +14 -0
  294. data/app/services/spree/emails/renderer.rb +125 -0
  295. data/app/services/spree/emails/template.rb +51 -0
  296. data/app/services/spree/emails/template_data.rb +23 -0
  297. data/app/services/spree/emails/template_resolver.rb +109 -0
  298. data/app/services/spree/emails/text_converter.rb +72 -0
  299. data/app/services/spree/emails/translation_inliner.rb +134 -0
  300. data/app/services/spree/imports/row_processors/price_list_price.rb +10 -10
  301. data/app/services/spree/imports/row_processors/product_variant.rb +77 -34
  302. data/app/services/spree/imports/row_processors/purchase_order.rb +13 -13
  303. data/app/services/spree/locales.rb +43 -10
  304. data/app/services/spree/newsletter/subscribe.rb +8 -12
  305. data/app/services/spree/orders/build_fulfillments.rb +3 -2
  306. data/app/services/spree/orders/create.rb +5 -4
  307. data/app/services/spree/orders/discounts/create.rb +14 -11
  308. data/app/services/spree/orders/discounts/destroy.rb +1 -0
  309. data/app/services/spree/orders/discounts/update.rb +1 -0
  310. data/app/services/spree/orders/fees/create.rb +1 -0
  311. data/app/services/spree/orders/fees/destroy.rb +1 -0
  312. data/app/services/spree/orders/fees/update.rb +1 -0
  313. data/app/services/spree/orders/remove_line_item.rb +13 -3
  314. data/app/services/spree/orders/update.rb +10 -6
  315. data/app/services/spree/orders/update_item.rb +6 -3
  316. data/app/services/spree/orders/update_statuses.rb +3 -1
  317. data/app/services/spree/prices/bulk_upsert.rb +32 -25
  318. data/app/services/spree/product_memberships.rb +61 -0
  319. data/app/services/spree/returns/eligibility_validator.rb +1 -1
  320. data/app/services/spree/search_provider/custom_field_schema.rb +2 -2
  321. data/app/services/spree/seeds/commission_rates.rb +1 -1
  322. data/app/services/spree/seeds/digital_delivery.rb +1 -1
  323. data/app/services/spree/seeds/payment_methods.rb +2 -2
  324. data/app/services/spree/seeds/saved_reports.rb +3 -3
  325. data/app/services/spree/seeds/stores.rb +1 -1
  326. data/app/services/spree/sellers/requirements.rb +1 -2
  327. data/app/services/spree/shipping_labels/confirm_refund.rb +1 -1
  328. data/app/services/spree/shipping_labels/store_file.rb +3 -3
  329. data/app/services/spree/stock_levels/correct.rb +3 -3
  330. data/app/services/spree/stock_reservations/reserve.rb +2 -3
  331. data/app/services/spree/store_credits/apply.rb +15 -13
  332. data/app/services/spree/stores/provision_defaults.rb +23 -25
  333. data/app/services/spree/update_check.rb +160 -0
  334. data/app/services/spree/uploads/assign_stores.rb +92 -0
  335. data/app/subscribers/spree/seller_onboarding_subscriber.rb +1 -1
  336. data/app/validators/spree/attachment_content_type_validator.rb +19 -2
  337. data/app/validators/spree/locale_codes_validator.rb +22 -0
  338. data/app/views/layouts/spree/base_mailer.html.erb +2 -43
  339. data/app/views/layouts/spree/base_mailer.liquid +79 -0
  340. data/app/views/spree/admin_user_mailer/password_reset_email.liquid +14 -0
  341. data/app/views/spree/export_mailer/export_done.liquid +12 -0
  342. data/app/views/spree/import_mailer/import_done.liquid +15 -0
  343. data/app/views/spree/invitation_mailer/invitation_accepted.liquid +9 -0
  344. data/app/views/spree/invitation_mailer/invitation_email.liquid +13 -0
  345. data/app/views/spree/seller_user_mailer/password_reset_email.liquid +14 -0
  346. data/app/views/spree/shared/_mailer_button.html.erb +4 -5
  347. data/app/views/spree/shared/_mailer_hero.html.erb +6 -5
  348. data/app/views/spree/shared/_summary_row.liquid +8 -0
  349. data/app/views/spree/webhook_mailer/endpoint_disabled.liquid +21 -0
  350. data/app/workflows/spree/carts/add_item.rb +4 -4
  351. data/app/workflows/spree/carts/complete.rb +13 -3
  352. data/app/workflows/spree/carts/merge.rb +1 -1
  353. data/app/workflows/spree/carts/recalculate_totals.rb +5 -0
  354. data/app/workflows/spree/carts/upsert_items.rb +18 -9
  355. data/app/workflows/spree/claims/approve.rb +5 -22
  356. data/app/workflows/spree/claims/cancel.rb +5 -21
  357. data/app/workflows/spree/claims/create.rb +15 -0
  358. data/app/workflows/spree/claims/deny.rb +7 -0
  359. data/app/workflows/spree/claims/resolve.rb +34 -31
  360. data/app/workflows/spree/customers/anonymize.rb +3 -4
  361. data/app/workflows/spree/customers/create.rb +1 -1
  362. data/app/workflows/spree/data_requests/fulfill.rb +2 -2
  363. data/app/workflows/spree/deliveries/update_tracking.rb +1 -1
  364. data/app/workflows/spree/email_templates/discard_draft.rb +20 -0
  365. data/app/workflows/spree/email_templates/publish.rb +70 -0
  366. data/app/workflows/spree/email_templates/restore_revision.rb +33 -0
  367. data/app/workflows/spree/email_templates/revert.rb +29 -0
  368. data/app/workflows/spree/email_templates/save_draft.rb +85 -0
  369. data/app/workflows/spree/exchanges/approve.rb +5 -22
  370. data/app/workflows/spree/exchanges/cancel.rb +5 -21
  371. data/app/workflows/spree/exchanges/create.rb +6 -0
  372. data/app/workflows/spree/exchanges/fulfill.rb +75 -51
  373. data/app/workflows/spree/exchanges/receive.rb +6 -65
  374. data/app/workflows/spree/fulfillments/cancel.rb +1 -1
  375. data/app/workflows/spree/fulfillments/create.rb +53 -49
  376. data/app/workflows/spree/fulfillments/fulfill.rb +23 -27
  377. data/app/workflows/spree/fulfillments/mark_delivered.rb +1 -1
  378. data/app/workflows/spree/fulfillments/replacements.rb +59 -0
  379. data/app/workflows/spree/gift_cards/apply.rb +2 -2
  380. data/app/workflows/spree/orders/add_item.rb +5 -1
  381. data/app/workflows/spree/orders/cancel.rb +3 -3
  382. data/app/workflows/spree/orders/complete.rb +16 -8
  383. data/app/workflows/spree/payments/capture.rb +20 -5
  384. data/app/workflows/spree/payments/process.rb +5 -3
  385. data/app/workflows/spree/post_sale/approval.rb +35 -0
  386. data/app/workflows/spree/post_sale/cancellation.rb +44 -0
  387. data/app/workflows/spree/post_sale/receipt.rb +92 -0
  388. data/app/workflows/spree/price_lists/update.rb +4 -4
  389. data/app/workflows/spree/products/nested_attributes.rb +1 -1
  390. data/app/workflows/spree/purchase_orders/cancel.rb +1 -1
  391. data/app/workflows/spree/purchase_orders/close.rb +3 -31
  392. data/app/workflows/spree/purchase_orders/create.rb +3 -1
  393. data/app/workflows/spree/purchase_orders/mark_draft.rb +2 -2
  394. data/app/workflows/spree/purchase_orders/mark_ordered.rb +2 -2
  395. data/app/workflows/spree/purchase_orders/receive.rb +1 -1
  396. data/app/workflows/spree/purchase_orders/update.rb +5 -34
  397. data/app/workflows/spree/receivables/closing_short.rb +46 -0
  398. data/app/workflows/spree/receivables/draft_editing.rb +48 -0
  399. data/app/workflows/spree/refunds/create.rb +5 -2
  400. data/app/workflows/spree/refunds/order_payments.rb +14 -2
  401. data/app/workflows/spree/refunds/tax_credit.rb +67 -0
  402. data/app/workflows/spree/returns/approve.rb +5 -22
  403. data/app/workflows/spree/returns/cancel.rb +5 -22
  404. data/app/workflows/spree/returns/create.rb +8 -0
  405. data/app/workflows/spree/returns/receive.rb +5 -76
  406. data/app/workflows/spree/returns/refund.rb +31 -8
  407. data/app/workflows/spree/seller_requirement_submissions/create.rb +1 -2
  408. data/app/workflows/spree/seller_transfers/reverse.rb +28 -1
  409. data/app/workflows/spree/shipping_labels/purchase.rb +8 -8
  410. data/app/workflows/spree/shipping_labels/record.rb +7 -7
  411. data/app/workflows/spree/shipping_labels/refund.rb +2 -2
  412. data/app/workflows/spree/stock_receipts/recording.rb +4 -4
  413. data/app/workflows/spree/stock_transfers/cancel.rb +2 -2
  414. data/app/workflows/spree/stock_transfers/close.rb +3 -30
  415. data/app/workflows/spree/stock_transfers/create.rb +3 -1
  416. data/app/workflows/spree/stock_transfers/mark_draft.rb +1 -1
  417. data/app/workflows/spree/stock_transfers/mark_in_transit.rb +3 -3
  418. data/app/workflows/spree/stock_transfers/mark_ready.rb +2 -2
  419. data/app/workflows/spree/stock_transfers/receive.rb +1 -1
  420. data/app/workflows/spree/stock_transfers/update.rb +5 -33
  421. data/config/currency_exponents.json +180 -0
  422. data/config/i18n-tasks.yml +32 -18
  423. data/config/initializers/alba.rb +7 -0
  424. data/config/language_names.csv +264 -0
  425. data/config/locales/ar.yml +811 -0
  426. data/config/locales/be.yml +855 -0
  427. data/config/locales/bg.yml +723 -0
  428. data/config/locales/ca.yml +689 -0
  429. data/config/locales/content_locales.rb +9 -0
  430. data/config/locales/cs.yml +855 -0
  431. data/config/locales/da.yml +771 -0
  432. data/config/locales/de-CH.yml +248 -0
  433. data/config/locales/de.yml +835 -0
  434. data/config/locales/el.yml +769 -0
  435. data/config/locales/en.yml +47 -505
  436. data/config/locales/es-CL.yml +301 -0
  437. data/config/locales/es-EC.yml +186 -0
  438. data/config/locales/es-MX.yml +334 -0
  439. data/config/locales/es.yml +831 -0
  440. data/config/locales/et.yml +644 -0
  441. data/config/locales/fa.yml +709 -0
  442. data/config/locales/fi.yml +771 -0
  443. data/config/locales/fr.yml +830 -0
  444. data/config/locales/hu.yml +751 -0
  445. data/config/locales/id.yml +756 -0
  446. data/config/locales/it.yml +833 -0
  447. data/config/locales/ja.yml +726 -0
  448. data/config/locales/km.yml +759 -0
  449. data/config/locales/ko.yml +575 -0
  450. data/config/locales/ku.yml +753 -0
  451. data/config/locales/lt.yml +821 -0
  452. data/config/locales/lv.yml +685 -0
  453. data/config/locales/nb.yml +824 -0
  454. data/config/locales/ne.yml +823 -0
  455. data/config/locales/nl.yml +826 -0
  456. data/config/locales/pl.yml +835 -0
  457. data/config/locales/pt-BR.yml +547 -0
  458. data/config/locales/pt.yml +636 -0
  459. data/config/locales/ro.yml +750 -0
  460. data/config/locales/ru.yml +834 -0
  461. data/config/locales/sk.yml +680 -0
  462. data/config/locales/sl-SI.yml +600 -0
  463. data/config/locales/sv.yml +827 -0
  464. data/config/locales/th.yml +745 -0
  465. data/config/locales/tr.yml +777 -0
  466. data/config/locales/uk.yml +836 -0
  467. data/config/locales/vi.yml +768 -0
  468. data/config/locales/zh-CN.yml +786 -0
  469. data/config/locales/zh-TW.yml +834 -0
  470. data/db/migrate/20260925000001_add_replacement_to_spree_fulfillment_items.rb +5 -0
  471. data/db/migrate/20261001000001_add_post_sale_tax.rb +30 -0
  472. data/db/migrate/20261001000001_convert_preferences_to_json.rb +166 -0
  473. data/db/migrate/20261001000001_create_spree_email_templates.rb +52 -0
  474. data/db/migrate/20261009000001_change_money_columns_to_decimal_19_4.rb +116 -0
  475. data/db/migrate/20261010000001_add_store_id_to_active_storage_blobs.rb +12 -0
  476. data/lib/generators/spree/dummy/dummy_generator.rb +0 -9
  477. data/lib/generators/spree/dummy/templates/rails/test.rb +26 -0
  478. data/lib/spree/checkout/default_requirements.rb +37 -13
  479. data/lib/spree/core/configuration.rb +19 -1
  480. data/lib/spree/core/controller_helpers/store.rb +1 -1
  481. data/lib/spree/core/dependencies.rb +5 -4
  482. data/lib/spree/core/emails/escaped_output.rb +60 -0
  483. data/lib/spree/core/engine.rb +72 -165
  484. data/lib/spree/core/permissions/default_catalog.rb +5 -0
  485. data/lib/spree/core/preferences/invalid_preferences.rb +18 -0
  486. data/lib/spree/core/preferences/json_conversion.rb +186 -0
  487. data/lib/spree/core/preferences/masking.rb +55 -2
  488. data/lib/spree/core/preferences/preferable.rb +343 -103
  489. data/lib/spree/core/preferences/preferable_class_methods.rb +188 -136
  490. data/lib/spree/core/previews/import_preview.rb +10 -0
  491. data/lib/spree/core/ransack_configuration.rb +13 -2
  492. data/lib/spree/core/tax/provider_error.rb +37 -0
  493. data/lib/spree/core/version.rb +1 -1
  494. data/lib/spree/core.rb +91 -373
  495. data/lib/spree/emails/editable_templates.rb +82 -0
  496. data/lib/spree/events/catalog.rb +189 -0
  497. data/lib/spree/events.rb +21 -1
  498. data/lib/spree/hooks.rb +0 -17
  499. data/lib/spree/i18n.rb +32 -15
  500. data/lib/spree/iso_data.rb +7 -0
  501. data/lib/spree/localized_number.rb +6 -0
  502. data/lib/spree/migrations.rb +4 -1
  503. data/lib/spree/money.rb +196 -15
  504. data/lib/spree/reporting/adapters/base.rb +7 -6
  505. data/lib/spree/reporting/adapters/live.rb +6 -6
  506. data/lib/spree/reporting/default_vocabulary.rb +22 -10
  507. data/lib/spree/reporting/hydration.rb +1 -1
  508. data/lib/spree/reporting/query.rb +8 -0
  509. data/lib/spree/reporting/registry.rb +13 -0
  510. data/lib/spree/reporting/schema.rb +10 -10
  511. data/lib/spree/testing_support/editable_email_templates.rb +61 -0
  512. data/lib/spree/testing_support/emails.rb +22 -0
  513. data/lib/spree/testing_support/factories/cart_factory.rb +1 -1
  514. data/lib/spree/testing_support/factories/email_template_factory.rb +28 -0
  515. data/lib/spree/testing_support/factories/order_factory.rb +2 -2
  516. data/lib/spree/testing_support/factories/product_factory.rb +9 -0
  517. data/lib/spree/testing_support/factories/refund_factory.rb +1 -1
  518. data/lib/spree/testing_support/factories/stock_factory.rb +1 -1
  519. data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
  520. data/lib/spree/testing_support/i18n.rb +3 -94
  521. data/lib/spree/testing_support/lifecycle_events.rb +1 -1
  522. data/lib/spree/testing_support/order_walkthrough.rb +1 -1
  523. data/lib/spree/testing_support/preferences.rb +0 -5
  524. data/lib/spree/testing_support/store.rb +9 -1
  525. data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +67 -1
  526. data/lib/spree/uploads.rb +101 -0
  527. data/lib/tasks/categories_and_collections.rake +3 -3
  528. data/lib/tasks/core.rake +6 -4
  529. data/lib/tasks/markets.rake +2 -1
  530. data/lib/tasks/media.rake +3 -3
  531. data/lib/tasks/migrate_users_to_customers.rake +23 -18
  532. data/lib/tasks/money.rake +46 -0
  533. data/lib/tasks/package_types.rake +1 -1
  534. data/lib/tasks/preferences_json.rake +101 -0
  535. data/lib/tasks/seller_sample_data.rake +2 -2
  536. data/lib/tasks/store_settings.rake +13 -6
  537. data/lib/tasks/typed_adjustments_migration.rake +1 -1
  538. data/lib/tasks/uploads.rake +19 -0
  539. data/spec/fixtures/mailer_views/custom_views_probe_mailer/welcome_email.html.erb +4 -0
  540. metadata +219 -46
  541. data/app/jobs/spree/products/touch_categories_job.rb +0 -15
  542. data/app/jobs/spree/products/touch_taxons_job.rb +0 -13
  543. data/app/models/acts_as_taggable_on/tag_decorator.rb +0 -10
  544. data/app/models/concerns/spree/has_image_alt_text.rb +0 -18
  545. data/app/models/spree/base_analytics_event_handler.rb +0 -54
  546. data/app/models/spree/invitations/store.rb +0 -6
  547. data/app/models/spree/preference.rb +0 -14
  548. data/app/models/spree/promotion_handler/page.rb +0 -25
  549. data/app/models/spree/promotion_handler/promotion_duplicator.rb +0 -60
  550. data/app/models/spree/stock/differentiator.rb +0 -45
  551. data/app/presenters/spree/filters/options_presenter.rb +0 -47
  552. data/app/services/spree/carts/empty.rb +0 -42
  553. data/app/services/spree/country_to_timezone.rb +0 -273
  554. data/app/services/spree/payments/create.rb +0 -92
  555. data/app/validators/spree/url_validator.rb +0 -23
  556. data/app/views/active_storage/blobs/_blob.html.erb +0 -14
  557. data/app/views/spree/admin_user_mailer/password_reset_email.html.erb +0 -8
  558. data/app/views/spree/export_mailer/export_done.html.erb +0 -7
  559. data/app/views/spree/import_mailer/import_done.html.erb +0 -12
  560. data/app/views/spree/invitation_mailer/invitation_accepted.html.erb +0 -4
  561. data/app/views/spree/invitation_mailer/invitation_email.html.erb +0 -12
  562. data/app/views/spree/seller_user_mailer/password_reset_email.html.erb +0 -8
  563. data/app/views/spree/shared/_base_mailer_footer.html.erb +0 -19
  564. data/app/views/spree/shared/_base_mailer_header.html.erb +0 -8
  565. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +0 -399
  566. data/app/views/spree/shared/_mailer_logo.html.erb +0 -41
  567. data/app/views/spree/shared/_purchased_items_styles.html.erb +0 -119
  568. data/app/views/spree/webhook_mailer/endpoint_disabled.html.erb +0 -51
  569. data/app/views/spree/webhook_mailer/endpoint_disabled.text.erb +0 -10
  570. data/app/workflows/spree/fulfillments/purchase_label.rb +0 -40
  571. data/app/workflows/spree/returns/purchase_label.rb +0 -32
  572. data/lib/spree/analytics.rb +0 -11
  573. data/lib/spree/core/preferences/scoped_store.rb +0 -34
  574. data/lib/spree/core/preferences/store.rb +0 -105
  575. 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
@@ -0,0 +1,116 @@
1
+ # Widens every money column to decimal(19,4): room for a dinar's third decimal,
2
+ # sub-cent unit prices and very large amounts. Every stored value fits exactly,
3
+ # so no value changes. A column already at (19,4) is skipped, so an install
4
+ # that widened its largest tables online ahead of the upgrade runs this as a
5
+ # no-op for those (see the 5.6 to 6.0 upgrade guide).
6
+ class ChangeMoneyColumnsToDecimal194 < ActiveRecord::Migration[8.1]
7
+ COLUMNS = {
8
+ spree_adjustments: %i[amount],
9
+ spree_carts: %i[additional_tax_total adjustment_total delivery_total discount_total fee_total included_tax_total
10
+ item_total non_taxable_adjustment_total payment_total taxable_adjustment_total total],
11
+ spree_catalog_order_minimums: %i[amount],
12
+ spree_claim_line_items: %i[additional_tax_total included_tax_total refund_amount],
13
+ spree_commission_lines: %i[amount tax_amount total],
14
+ spree_commission_rate_values: %i[amount max_amount min_amount],
15
+ spree_delivery_method_services: %i[markup_flat],
16
+ spree_delivery_methods: %i[markup_flat],
17
+ spree_delivery_rates: %i[cost],
18
+ spree_discounts: %i[amount value],
19
+ spree_exchange_line_items: %i[new_additional_tax_total new_included_tax_total original_additional_tax_total
20
+ original_included_tax_total],
21
+ spree_fees: %i[amount],
22
+ spree_fulfillments: %i[additional_tax_total adjustment_total cost discount_total included_tax_total
23
+ non_taxable_adjustment_total pre_tax_amount taxable_adjustment_total],
24
+ spree_gift_card_batches: %i[amount],
25
+ spree_gift_cards: %i[amount amount_authorized amount_used],
26
+ spree_line_items: %i[additional_tax_total adjustment_total cost_price discount_total included_tax_total
27
+ non_taxable_adjustment_total pre_tax_amount price taxable_adjustment_total],
28
+ spree_orders: %i[additional_tax_total adjustment_total commission_amount_total commission_tax_total commission_total
29
+ delivery_total discount_total fee_total included_tax_total item_total non_taxable_adjustment_total
30
+ payment_total taxable_adjustment_total total],
31
+ spree_payment_capture_events: %i[amount],
32
+ spree_payment_sessions: %i[amount],
33
+ spree_payment_splits: %i[authorized_amount captured_amount claimed_amount refunded_amount],
34
+ spree_payments: %i[amount],
35
+ spree_price_histories: %i[amount compare_at_amount],
36
+ spree_prices: %i[amount compare_at_amount],
37
+ spree_products: %i[revenue],
38
+ spree_products_stores: %i[revenue],
39
+ spree_purchase_order_items: %i[unit_cost],
40
+ spree_refunds: %i[amount tax_amount],
41
+ spree_reimbursement_credits: %i[amount],
42
+ spree_reimbursements: %i[total],
43
+ spree_return_items: %i[additional_tax_total included_tax_total pre_tax_amount],
44
+ spree_return_line_items: %i[additional_tax_total included_tax_total pre_tax_amount],
45
+ spree_seller_payouts: %i[amount],
46
+ spree_seller_transfers: %i[amount settled_amount],
47
+ spree_sellers: %i[minimum_payout_amount],
48
+ spree_shipping_labels: %i[cost],
49
+ spree_stock_movements: %i[unit_cost],
50
+ spree_store_credit_events: %i[amount user_total_amount],
51
+ spree_store_credits: %i[amount amount_authorized amount_used],
52
+ spree_tax_lines: %i[amount],
53
+ spree_variants: %i[cost_price]
54
+ }.freeze
55
+
56
+ # What `down` restores: decimal(10,2) unless listed.
57
+ ORIGINAL = {
58
+ 'spree_delivery_rates.cost' => [8, 2],
59
+ 'spree_store_credit_events.amount' => [8, 2],
60
+ 'spree_store_credit_events.user_total_amount' => [8, 2],
61
+ 'spree_store_credits.amount' => [8, 2],
62
+ 'spree_store_credits.amount_authorized' => [8, 2],
63
+ 'spree_store_credits.amount_used' => [8, 2],
64
+ 'spree_fulfillments.pre_tax_amount' => [12, 4],
65
+ 'spree_line_items.pre_tax_amount' => [12, 4],
66
+ 'spree_return_items.additional_tax_total' => [12, 4],
67
+ 'spree_return_items.included_tax_total' => [12, 4],
68
+ 'spree_return_items.pre_tax_amount' => [12, 4],
69
+ 'spree_products.revenue' => [16, 4]
70
+ }.freeze
71
+
72
+ def up
73
+ deliberately_rewriting { COLUMNS.each_key { |table| retype(table) { [19, 4] } } }
74
+ end
75
+
76
+ def down
77
+ deliberately_rewriting do
78
+ COLUMNS.each_key { |table| retype(table) { |column| ORIGINAL.fetch("#{table}.#{column}", [10, 2]) } }
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ # The rewrite is the point of this migration and the upgrade guide documents
85
+ # it, so an app running strong_migrations is not stopped by it.
86
+ def deliberately_rewriting(&block)
87
+ respond_to?(:safety_assured) ? safety_assured(&block) : yield
88
+ end
89
+
90
+ # One ALTER TABLE per table, so each is rewritten once rather than once per
91
+ # column. PostgreSQL keeps NOT NULL and the default through a type change,
92
+ # and Rails' MySQL adapter carries them over itself; passing them would add a
93
+ # SET NOT NULL that scans every row. SQLite rebuilds the table from the new
94
+ # definition, so it is told both.
95
+ def retype(table)
96
+ return unless table_exists?(table)
97
+
98
+ existing = connection.columns(table).index_by(&:name)
99
+ changes = COLUMNS[table].filter_map do |name|
100
+ column = existing[name.to_s]
101
+ next if column.nil?
102
+
103
+ precision, scale = yield(name)
104
+ [column, precision, scale] unless column.precision == precision && column.scale == scale
105
+ end
106
+ return if changes.empty?
107
+
108
+ change_table(table, bulk: true) do |definition|
109
+ changes.each do |column, precision, scale|
110
+ options = { precision: precision, scale: scale }
111
+ options.merge!(null: column.null, default: column.default) if connection.adapter_name.match?(/sqlite/i)
112
+ definition.change column.name, :decimal, **options
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,12 @@
1
+ # Records which store owns every uploaded file, so a file from one store cannot
2
+ # be attached in another and storage can be measured and cleaned up per store.
3
+ # Active Storage ignores columns it does not define. Existing rows are filled
4
+ # in by `spree:uploads:assign_stores` in the 6.0 upgrade.
5
+ class AddStoreIdToActiveStorageBlobs < ActiveRecord::Migration[8.1]
6
+ def change
7
+ return unless table_exists?(:active_storage_blobs)
8
+ return if column_exists?(:active_storage_blobs, :store_id)
9
+
10
+ add_reference :active_storage_blobs, :store, null: true, index: true
11
+ end
12
+ 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,29 @@ 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 and specs must not call these deprecated APIs. A spec
46
+ # covering one on purpose stubs Spree::Deprecation.warn or silences it.
47
+ config.active_support.disallowed_deprecation = :raise
48
+ config.active_support.disallowed_deprecation_warnings = [
49
+ /Spree\.t is deprecated/,
50
+ /Spree::Product#(master|variants_including_master) is deprecated/,
51
+ /Spree::(Order|Cart)#(shipment_total|promo_total|item_count|shipping_discount) is deprecated/,
52
+ /Spree::Order#finalize! is deprecated/,
53
+ /Spree::Payment#state=? is deprecated/,
54
+ /Spree::Payment#(process|authorize|purchase|capture|void_transaction)! is deprecated/,
55
+ /Spree::(Category|Product)#classifications is deprecated/,
56
+ /Spree::Product#taxons=? is deprecated/,
57
+ /Spree::Promotion::Rules::Category#taxon/,
58
+ /Spree::Fulfillment#(add_)?shipping_method is deprecated/,
59
+ /Spree::Stock::Package#shipping_methods is deprecated/,
60
+ /Calling Spree::(Carts::AddItem|StockReservations::(Reserve|Extend)) with order: is deprecated/,
61
+ /Spree::Config\[:admin_url\] is deprecated/,
62
+ /private_metadata=? is deprecated/,
63
+ /Spree::Pricing::Resolver is deprecated/,
64
+ /Spree::Invitation#accept! is deprecated/,
65
+ /`preference :\w+, in:` is deprecated/
66
+ ]
67
+
42
68
  config.active_job.queue_adapter = :test
43
69
 
44
70
  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
@@ -119,6 +119,19 @@ module Spree
119
119
  # requirement, in bytes. Small on purpose: these are certificates and
120
120
  # registrations, and an unbounded upload is a way to fill a disk.
121
121
  preference :max_seller_document_upload_size, :integer, default: 20_971_520, env: 'SPREE_MAX_SELLER_DOCUMENT_UPLOAD_SIZE' # 20 MB
122
+
123
+ # Largest file the Admin and Seller APIs accept for upload straight to
124
+ # storage, in bytes. Digital products (courses, archives) can be large.
125
+ preference :max_upload_size, :integer, default: 5_368_709_120, env: 'SPREE_MAX_UPLOAD_SIZE' # 5 GB
126
+
127
+ # Largest file accepted as bytes in the upload request itself, in bytes.
128
+ # Lower, because those bytes pass through the application.
129
+ preference :max_multipart_upload_size, :integer, default: 10_485_760, env: 'SPREE_MAX_MULTIPART_UPLOAD_SIZE' # 10 MB
130
+
131
+ # Days an uploaded file that was never attached to anything is kept
132
+ # before the daily purge deletes it. Longer than an upload reference
133
+ # stays valid (one day), so nothing a client can still attach is lost.
134
+ preference :unattached_upload_retention_days, :integer, default: 2, env: 'SPREE_UNATTACHED_UPLOAD_RETENTION_DAYS'
122
135
  preference :non_expiring_credit_types, :array, default: [], deprecated: 'Nothing reads this in Spree 6 — store credits no longer carry a category, and expiry lives on Spree::GiftCard'
123
136
  preference :products_per_page, :integer, default: 12, deprecated: 'Nothing reads this in Spree 6 — pass per_page to the API instead'
124
137
  preference :restock_inventory, :boolean, default: true, deprecated: 'Restocking is decided per line item by Spree::ReturnLineItem#resellable'
@@ -137,10 +150,15 @@ module Spree
137
150
  preference :empty_cart_expiry_hours, :integer, default: 48, env: 'SPREE_EMPTY_CART_EXPIRY_HOURS'
138
151
  preference :show_variant_full_price, :boolean, default: false, deprecated: 'Nothing reads this in Spree 6 — a storefront decides how it renders prices'
139
152
  preference :show_products_without_price, :boolean, default: false, deprecated: 'Set it on the store instead'
153
+ # The daily update check also reports usage (see Spree::UpdateCheck).
154
+ # SPREE_TELEMETRY_DISABLED or DO_NOT_TRACK turns it off as well.
155
+ preference :telemetry_enabled, :boolean, default: true
140
156
  preference :tax_using_ship_address, :boolean, default: true, deprecated: 'Set it on the store instead'
141
157
  preference :track_inventory_levels, :boolean, default: true, deprecated: 'Set it on the store instead' # Determines whether to track on_hand values for variants / products.
142
158
  preference :track_price_history, :boolean, default: true, deprecated: 'Set it on the store instead' # Records price changes for Omnibus Directive compliance. Disable for non-EU stores.
143
159
  preference :price_history_retention_days, :integer, default: 30, deprecated: 'Set it on the store instead' # Days to retain price history records. Used by spree:price_history:prune rake task.
160
+ # SPREE_UPDATE_CHECK_DISABLED turns it off as well.
161
+ preference :update_check_enabled, :boolean, default: true
144
162
  preference :use_user_locale, :boolean, default: true, deprecated: true
145
163
 
146
164
  # Sets the path used for products, taxons and pages.
@@ -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',