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,60 @@
1
+ module Spree
2
+ module Core
3
+ module Emails
4
+ # HTML-escapes what a Liquid email template prints, unless the value was
5
+ # marked `| raw`. Applied where output is written rather than through
6
+ # Liquid's global filter, which `{% assign %}` also runs through — that
7
+ # would escape a value once when assigned and again when printed, and
8
+ # turn assigned numbers and lists into strings.
9
+ #
10
+ # Switched on by the `escape_output` register, which Liquid hands down to
11
+ # every partial, so any other Liquid the host app runs is untouched.
12
+ module EscapedOutput
13
+ def self.active?(context)
14
+ context.registers[:escape_output]
15
+ end
16
+
17
+ # `{{ value }}` and `{% echo value %}`. Variables are not tags, so this
18
+ # is the one place Liquid itself has to be extended.
19
+ module Variable
20
+ def render_to_output_buffer(context, output)
21
+ return super unless EscapedOutput.active?(context)
22
+
23
+ write_escaped(render(context), output)
24
+ output
25
+ end
26
+
27
+ private
28
+
29
+ def write_escaped(value, output)
30
+ case value
31
+ when nil then nil
32
+ when Array then value.each { |item| write_escaped(item, output) }
33
+ else output << ERB::Util.html_escape(Liquid::Utils.to_s(value))
34
+ end
35
+ end
36
+ end
37
+
38
+ # A captured block was escaped as it rendered, so it is kept as safe
39
+ # HTML rather than escaped a second time when printed.
40
+ class Capture < Liquid::Capture
41
+ def render(context)
42
+ output = super
43
+ EscapedOutput.active?(context) ? output.html_safe : output
44
+ end
45
+ end
46
+
47
+ # `{% cycle %}` writes its values straight to the output.
48
+ class Cycle < Liquid::Cycle
49
+ def render_to_output_buffer(context, output)
50
+ return super unless EscapedOutput.active?(context)
51
+
52
+ output << ERB::Util.html_escape(super(context, +''))
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ Liquid::Variable.prepend(Spree::Core::Emails::EscapedOutput::Variable)
@@ -19,6 +19,7 @@ module Spree
19
19
  :payout_providers,
20
20
  :password_validator,
21
21
  :fulfillment_providers,
22
+ :pickup_point_providers,
22
23
  :tracking_carriers,
23
24
  :stock_splitters,
24
25
  :commission_rules,
@@ -37,19 +38,11 @@ module Spree
37
38
  :taxon_rules,
38
39
  :collection_rules,
39
40
  :time_based_collection_rules,
40
- :themes,
41
- :theme_layout_sections,
42
- :pages,
43
- :page_sections,
44
- :page_blocks,
45
- :reports,
46
41
  :translatable_resources,
47
42
  :taggable_types,
48
43
  :actor_classes,
49
44
  :custom_fields,
50
45
  :reporting,
51
- :analytics_events,
52
- :analytics_event_handlers,
53
46
  :integrations,
54
47
  :number_generators,
55
48
  :subscribers,
@@ -60,16 +53,10 @@ module Spree
60
53
  PromoEnvironment = Struct.new(:rules, :actions)
61
54
  PricingEnvironment = Struct.new(:rules)
62
55
  OrderRoutingEnvironment = Struct.new(:strategies, :rules)
63
- # Spree::Validators, not a Struct: the sets carry register/unregister so
64
- # a host can drop a core rule (see Spree.validators.addresses).
65
- SpreeValidators = Spree::Validators
66
56
  CustomFieldsEnvironment = Struct.new(:types, :enabled_resources)
67
57
  isolate_namespace Spree
68
58
  engine_name 'spree'
69
59
 
70
- # Add app/subscribers to autoload paths
71
- config.paths.add 'app/subscribers', eager_load: true
72
-
73
60
  # Register bundled ActionMailer previews so they show up at /rails/mailers
74
61
  # without the host app having to copy any files.
75
62
  initializer 'spree.mailer_previews' do |app|
@@ -79,14 +66,28 @@ module Spree
79
66
  end
80
67
 
81
68
  initializer 'spree.environment', before: :load_config_initializers do |app|
82
- app.config.spree = Environment.new(SpreeCalculators.new, SpreeValidators.new, Spree::Core::Configuration.new, Spree::Core::Dependencies.new)
69
+ # Spree::Validators, not a Struct: the sets carry register/unregister so
70
+ # a host can drop a core rule (see Spree.validators.addresses).
71
+ app.config.spree = Environment.new(SpreeCalculators.new, Spree::Validators.new, Spree::Core::Configuration.new, Spree::Core::Dependencies.new)
72
+
73
+ # Registries are seeded before application initializers so extensions
74
+ # and host apps can append their own entries from initializer files.
75
+ # Core's defaults are concatenated in after_initialize below.
76
+ %i[subscribers actor_classes commission_rules delivery_method_rules seller_requirements
77
+ delivery_rate_providers tax_providers pricing_providers inventory_providers
78
+ payout_providers digital_asset_providers delivery_profile_types pickup_point_providers].each do |registry|
79
+ app.config.spree[registry] = []
80
+ end
81
+ app.config.spree.line_item_comparison_hooks = Set.new
82
+ app.config.spree.order_routing = OrderRoutingEnvironment.new([], [])
83
+ app.config.spree.custom_fields = CustomFieldsEnvironment.new([], [])
84
+ app.config.spree.promotions = PromoEnvironment.new([])
85
+ app.config.spree.pricing = PricingEnvironment.new([])
83
86
 
84
- app.config.active_record.yaml_column_permitted_classes ||= []
85
- app.config.active_record.yaml_column_permitted_classes.concat([Symbol, BigDecimal, ActiveSupport::HashWithIndifferentAccess, ActiveSupport::TimeWithZone, ActiveSupport::TimeZone, Time])
86
87
  Spree::Config = app.config.spree.preferences
87
- Spree::RuntimeConfig = app.config.spree.preferences # for compatibility
88
88
  Spree::Dependencies = app.config.spree.dependencies
89
89
  Spree::Deprecation = ActiveSupport::Deprecation.new('6.0', 'Spree')
90
+ app.deprecators[:spree] = Spree::Deprecation
90
91
  end
91
92
 
92
93
  # Runs after initializers so an explicitly assigned preference — which
@@ -107,19 +108,32 @@ module Spree
107
108
  app.middleware.use ::I18n::Middleware
108
109
  end
109
110
 
110
- initializer 'spree.register.subscribers', before: :load_config_initializers do |app|
111
- # Initialize subscribers array early so engines can add subscribers via initializers
112
- app.config.spree.subscribers = []
111
+ # Spree's translations cover only part of the English keys in most
112
+ # languages, so the rest fall back to English, the one complete language.
113
+ # `true` (Rails' generated production.rb) falls back to the app's default
114
+ # locale only, so English is added after it. An app that configured its
115
+ # own fallback chain, or turned fallbacks off, keeps its choice.
116
+ initializer 'spree.i18n.fallbacks', before: :load_config_initializers do |app|
117
+ fallbacks = app.config.i18n.fallbacks
118
+ if fallbacks == true
119
+ app.config.i18n.fallbacks = [app.config.i18n.default_locale || :en, :en].uniq
120
+ elsif fallbacks.is_a?(ActiveSupport::OrderedOptions) && fallbacks.empty?
121
+ app.config.i18n.fallbacks = [:en]
122
+ end
113
123
  end
114
124
 
115
- # Seeded before application initializers so an extension registering an
116
- # actor class has something to append to. The defaults are unioned in
117
- # after initialization, where Spree.admin_user_class is finally known.
118
- initializer 'spree.register.actor_classes', before: :load_config_initializers do |app|
119
- app.config.spree.actor_classes = []
120
- end
125
+ # Spree's gems ship translations for dozens of languages. An app that
126
+ # limits `config.i18n.available_locales` loads only those (and English),
127
+ # instead of parsing every file at boot and discarding the rest.
128
+ initializer 'spree.i18n.locale_files', before: :load_config_initializers do |app|
129
+ allowed = Array(app.config.i18n.available_locales).map(&:to_s)
130
+ next if allowed.empty?
121
131
 
122
- initializer 'spree.register.calculators', before: :after_initialize do |app|
132
+ # A regional file (de-CH) holds only its differences from its base (de).
133
+ locales = allowed | allowed.map { |locale| locale.split('-').first } | ['en']
134
+ %w[Spree::Core::Engine Spree::Api::Engine Spree::Emails::Engine].filter_map(&:safe_constantize).each do |engine|
135
+ engine.config.paths['config/locales'].glob = "{#{locales.join(',')}}.yml"
136
+ end
123
137
  end
124
138
 
125
139
  # Seeded before application initializers so a host's
@@ -128,13 +142,6 @@ module Spree
128
142
  app.config.spree.number_generators = Spree::NumberGenerators::Registry.new
129
143
  end
130
144
 
131
- initializer 'spree.register.stock_splitters', before: :load_config_initializers do |app|
132
- end
133
-
134
- initializer 'spree.register.line_item_comparison_hooks', before: :load_config_initializers do |app|
135
- app.config.spree.line_item_comparison_hooks = Set.new
136
- end
137
-
138
145
  # The one eligibility rule core ships: a per-market return window,
139
146
  # bypassed by staff. Registered before application initializers so a
140
147
  # store can unregister it in its own initializer.
@@ -143,78 +150,6 @@ module Spree
143
150
  Spree.hooks.register('exchanges.create.validate', 'Spree::Returns::EligibilityValidator')
144
151
  end
145
152
 
146
- initializer 'spree.register.payment_methods', after: 'acts_as_list.insert_into_active_record' do |app|
147
- end
148
-
149
- initializer 'spree.register.adjustable_adjusters' do |app|
150
- end
151
-
152
- # Seed the order routing registries early so engines and apps can append
153
- # their own strategies / rule kinds from initializer files. Core's defaults
154
- # are concatenated in after_initialize below.
155
- initializer 'spree.register.order_routing', before: :load_config_initializers do |app|
156
- app.config.spree.order_routing = OrderRoutingEnvironment.new
157
- app.config.spree.order_routing.strategies = []
158
- app.config.spree.order_routing.rules = []
159
- end
160
-
161
- # Seeded early for the same reason as order routing: initializer files
162
- # append custom rule kinds. Core defaults concatenate in after_initialize.
163
- initializer 'spree.register.commission_rules', before: :load_config_initializers do |app|
164
- app.config.spree.commission_rules = []
165
- end
166
-
167
- initializer 'spree.register.delivery_method_rules', before: :load_config_initializers do |app|
168
- app.config.spree.delivery_method_rules = []
169
- end
170
-
171
- # Seeded early like the other kind registries: an initializer file adds
172
- # a marketplace's own requirement kinds, core's concatenate after.
173
- initializer 'spree.register.seller_requirements', before: :load_config_initializers do |app|
174
- app.config.spree.seller_requirements = []
175
- end
176
-
177
- initializer 'spree.register.delivery_rate_providers', before: :load_config_initializers do |app|
178
- app.config.spree.delivery_rate_providers = []
179
- end
180
-
181
- # Same reason again: a tax provider gem, or a host app, registers its
182
- # engine from an initializer file. Core's Internal concatenates below.
183
- initializer 'spree.register.tax_providers', before: :load_config_initializers do |app|
184
- app.config.spree.tax_providers = []
185
- end
186
-
187
- # Same reason as the tax providers above: a connector gem registers its
188
- # engine from an initializer file, and core's Internal concatenates below.
189
- initializer 'spree.register.pricing_providers', before: :load_config_initializers do |app|
190
- app.config.spree.pricing_providers = []
191
- end
192
-
193
- initializer 'spree.register.inventory_providers', before: :load_config_initializers do |app|
194
- app.config.spree.inventory_providers = []
195
- end
196
-
197
- # How sellers get paid. A provider gem — the Stripe Connect one, or a
198
- # marketplace's own — registers from an initializer file; core's
199
- # record-only System concatenates below.
200
- initializer 'spree.register.payout_providers', before: :load_config_initializers do |app|
201
- app.config.spree.payout_providers = []
202
- end
203
-
204
- initializer 'spree.register.digital_asset_providers', before: :load_config_initializers do |app|
205
- app.config.spree.digital_asset_providers = []
206
- end
207
-
208
- initializer 'spree.register.delivery_profile_types', before: :load_config_initializers do |app|
209
- app.config.spree.delivery_profile_types = []
210
- end
211
-
212
- initializer 'spree.register.custom_fields' do |app|
213
- app.config.spree.custom_fields = CustomFieldsEnvironment.new
214
- app.config.spree.custom_fields.types = []
215
- app.config.spree.custom_fields.enabled_resources = []
216
- end
217
-
218
153
  # Seed the reporting vocabulary before app initializers so applications
219
154
  # and extensions can register their own metrics/dimensions in
220
155
  # config/initializers (see docs/plans/6.0-analytics-semantic-layer.md).
@@ -223,29 +158,13 @@ module Spree
223
158
  Spree::Reporting::DefaultVocabulary.install(app.config.spree.reporting)
224
159
  end
225
160
 
226
- # We need to define promotions rules here so extensions and existing apps
227
- # can add their custom classes on their initializer files
228
- initializer 'spree.promo.environment' do |app|
229
- app.config.spree.promotions = PromoEnvironment.new
230
- app.config.spree.promotions.rules = []
231
- end
232
-
233
- initializer 'spree.promo.register.promotion.calculators' do |app|
234
- end
235
-
236
- # Pricing configuration for price lists and price rules
237
- initializer 'spree.pricing.environment', after: 'spree.environment' do |app|
238
- app.config.spree.pricing = PricingEnvironment.new
239
- app.config.spree.pricing.rules = []
240
- end
241
-
242
161
  # Country and subdivision names are translated by the countries gem, which
243
162
  # only loads the locales it is told about. This runs after initialization
244
163
  # because Spree.available_locales reads i18n configuration that is not
245
164
  # populated while initializers are still running.
246
165
  config.after_initialize do
247
166
  ISO3166.configure do |iso_config|
248
- iso_config.locales = (Spree.available_locales.map { |locale| locale.to_s.downcase } << 'en').uniq
167
+ iso_config.locales = (Spree.configured_locales.map { |locale| locale.to_s.downcase } << 'en').uniq
249
168
  end
250
169
 
251
170
  Spree::IsoData.reset!
@@ -293,7 +212,6 @@ module Spree
293
212
  Spree::Collection
294
213
  ]
295
214
 
296
-
297
215
  # The fallback engine when a market names none (see docs/plans/6.0-tax-provider.md).
298
216
  # Assigned only if an initializer file has not already named one — an app
299
217
  # that picks its own default must keep it.
@@ -355,7 +273,6 @@ module Spree
355
273
  'deutsche_post' => { name: 'Deutsche Post DHL', url: 'https://www.dhl.de/de/privatkunden/pakete-empfangen/verfolgen.html?piececode=:tracking' }
356
274
  }
357
275
 
358
-
359
276
  # Quoting strategies selectable on a delivery method. Internal prices
360
277
  # through the method's calculator; carrier gems append theirs.
361
278
  Rails.application.config.spree.delivery_rate_providers.concat [
@@ -603,33 +520,6 @@ module Spree
603
520
  Spree.customer_class
604
521
  ]
605
522
 
606
- Rails.application.config.spree.analytics_events = {
607
- product_viewed: 'Product Viewed',
608
- product_list_viewed: 'Product List Viewed',
609
- product_searched: 'Product Searched',
610
- product_added: 'Product Added',
611
- product_removed: 'Product Removed',
612
-
613
- product_added_to_wishlist: 'Product Added to Wishlist',
614
- product_removed_from_wishlist: 'Product Removed from Wishlist',
615
-
616
- subscribed_to_newsletter: 'Subscribed to Newsletter',
617
- unsubscribed_from_newsletter: 'Unsubscribed from Newsletter',
618
-
619
- payment_info_entered: 'Payment Info Entered',
620
- coupon_entered: 'Coupon Entered',
621
- coupon_removed: 'Coupon Removed',
622
- coupon_applied: 'Coupon Applied',
623
- coupon_denied: 'Coupon Denied',
624
-
625
- checkout_started: 'Checkout Started',
626
- checkout_email_entered: 'Checkout Email Entered',
627
- checkout_step_viewed: 'Checkout Step Viewed',
628
- checkout_step_completed: 'Checkout Step Completed',
629
- order_completed: 'Order Completed',
630
- }
631
- Rails.application.config.spree.analytics_event_handlers = []
632
-
633
523
  Rails.application.config.spree.integrations = []
634
524
 
635
525
  Rails.application.config.spree.validators.addresses = [
@@ -648,6 +538,7 @@ module Spree
648
538
  Spree::SellerTransferSubscriber,
649
539
  Spree::SellerTransferReversalSubscriber,
650
540
  Spree::ExportSubscriber,
541
+ Spree::ImportEmailSubscriber,
651
542
  Spree::InvitationEmailSubscriber,
652
543
  Spree::SellerOnboardingSubscriber,
653
544
  Spree::AdminUserEmailSubscriber,
@@ -668,7 +559,23 @@ module Spree
668
559
  )
669
560
  end
670
561
 
671
- initializer 'spree.promo.register.promotions.actions' do |app|
562
+ # Deprecated constant aliases warn when their file loads. Leaving them
563
+ # out of eager loading keeps every boot quiet; they still load, and
564
+ # warn, the first time something references the old name.
565
+ DEPRECATED_ALIAS_FILES = %w[
566
+ app/models/concerns/spree/user_methods.rb
567
+ app/models/spree/asset.rb
568
+ app/models/spree/classification.rb
569
+ app/models/spree/promotion/rules/taxon.rb
570
+ app/models/spree/promotion_rule_taxon.rb
571
+ app/models/spree/return_authorization_reason.rb
572
+ app/models/spree/taxon.rb
573
+ app/services/spree/checkout/add_store_credit.rb
574
+ app/services/spree/checkout/remove_store_credit.rb
575
+ ].freeze
576
+
577
+ initializer 'spree.deprecated_aliases.skip_eager_load' do
578
+ Rails.autoloaders.main.do_not_eager_load(*DEPRECATED_ALIAS_FILES.map { |path| root.join(path) })
672
579
  end
673
580
 
674
581
  # filter sensitive information during logging
@@ -684,6 +591,13 @@ module Spree
684
591
  ]
685
592
  end
686
593
 
594
+ # Records which store owns every uploaded file, and reports attaching a
595
+ # file to another store's record (docs/plans/6.0-uploads-and-file-ownership.md).
596
+ initializer 'spree.active_storage.store_ownership' do
597
+ ActiveSupport.on_load(:active_storage_blob) { include Spree::Uploads::BlobStoreOwnership }
598
+ ActiveSupport.on_load(:active_storage_attachment) { include Spree::Uploads::AttachmentStoreCheck }
599
+ end
600
+
687
601
  initializer 'spree.core.checking_migrations' do |app|
688
602
  app.config.after_initialize do
689
603
  Migrations.new(config, engine_name).check unless Rails.env.test? || Spree::Config.disable_migration_check
@@ -711,17 +625,10 @@ module Spree
711
625
  end
712
626
 
713
627
  config.to_prepare do
714
- # Ensure spree locale paths are present before decorators
715
- I18n.load_path.unshift(*(Dir.glob(
716
- File.join(
717
- File.dirname(__FILE__), '../../../config/locales', '*.{rb,yml}'
718
- )
719
- ) - I18n.load_path))
720
-
721
- # Load application's model / class decorators
722
- Dir.glob(File.join(File.dirname(__FILE__), '../../../app/**/*_decorator*.rb')) do |c|
723
- Rails.configuration.cache_classes ? require(c) : load(c)
724
- end
628
+ I18n.load_path.unshift(*(Spree::Core::Engine.config.paths['config/locales'].existent - I18n.load_path))
629
+
630
+ ActsAsTaggableOn::Tag.include(Spree::RansackableAttributes)
631
+ ActsAsTaggableOn::Tag.whitelisted_ransackable_attributes = %w[id name]
725
632
 
726
633
  # Reset and re-activate event subscribers on code reload
727
634
  # activate! will register all subscribers from Spree.subscribers
@@ -194,6 +194,11 @@ module Spree
194
194
  catalog.register_scope(:webhooks, group: :settings, resources: -> {
195
195
  [Spree::WebhookEndpoint, Spree::WebhookDelivery]
196
196
  })
197
+ # Separate from settings: a template writes what every customer receives,
198
+ # links included, so managing store settings does not grant it.
199
+ catalog.register_scope(:email_templates, group: :settings, resources: -> {
200
+ [Spree::EmailTemplate, Spree::EmailTemplateDraft, Spree::EmailTemplateRevision]
201
+ })
197
202
  catalog.register_scope(:integrations, group: :settings, resources: -> { [Spree::Integration] })
198
203
 
199
204
  catalog.register_scope(:api_keys, group: :access, resources: -> { [Spree::ApiKey] })
@@ -0,0 +1,18 @@
1
+ module Spree
2
+ module Preferences
3
+ # Raised when a `preferences` payload does not match the schema its type
4
+ # declares. Each failure names the value by a JSON pointer into the
5
+ # payload, so a client can show it next to the field it belongs to.
6
+ class InvalidPreferences < StandardError
7
+ # @return [Array<Hash{Symbol => String}>] `{ pointer:, message: }` per failure
8
+ attr_reader :failures
9
+
10
+ # @param failures [Array<Hash{Symbol => String}>] pointers relative to the preferences object
11
+ # @param prefix [String] where the preferences object sits in the request
12
+ def initialize(failures, prefix: '/preferences')
13
+ @failures = failures.map { |failure| failure.merge(pointer: "#{prefix}#{failure[:pointer]}") }
14
+ super(@failures.map { |failure| "#{failure[:pointer]}: #{failure[:message]}" }.join('; '))
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,186 @@
1
+
2
+ module Spree
3
+ module Preferences
4
+ # Converts preferences stored as YAML to JSON, for the 6.0 migration and the
5
+ # `spree:upgrade:preferences_json` safety net.
6
+ #
7
+ # Rows are read and written through the connection, never through models:
8
+ # a model would load the column through whichever coder the current code
9
+ # declares, which is the thing being converted. The only thing asked of a
10
+ # model class is which of its preferences are secrets, from its
11
+ # declarations alone.
12
+ class JsonConversion
13
+ PERMITTED_CLASSES = [
14
+ Symbol, BigDecimal, Date, Time,
15
+ ActiveSupport::TimeWithZone, ActiveSupport::TimeZone, ActiveSupport::HashWithIndifferentAccess
16
+ ].freeze
17
+
18
+ class UnreadableRowError < StandardError; end
19
+
20
+ # Whether a row's class keeps a tier ladder, which moves from a hash keyed
21
+ # by threshold to a list, because a JSON object key is always a string.
22
+ #
23
+ # @param model [Class, nil] the class the row's `type` names
24
+ # @return [Boolean]
25
+ def self.tiered_calculator?(model)
26
+ model.is_a?(Class) && model.include?(Spree::Calculator::Tiers)
27
+ end
28
+
29
+ # @param connection [ActiveRecord::ConnectionAdapters::AbstractAdapter]
30
+ # @param log [#call, nil] receives a line for each row left as it was
31
+ def initialize(connection, log: nil)
32
+ @connection = connection
33
+ @log = log || ->(_message) {}
34
+ @models = Hash.new { |models, type| models[type] = type.presence&.safe_constantize }
35
+ end
36
+
37
+ # Resolves a row's `type`, once per type for the life of the conversion.
38
+ #
39
+ # @param type [String, nil]
40
+ # @return [Class, nil]
41
+ def model_for(type)
42
+ @models[type]
43
+ end
44
+
45
+ # @param table [String]
46
+ # @param id [Object] the row's primary key
47
+ # @param assignments [Hash{String => String}] column names and the values to write
48
+ def write(table, id, assignments)
49
+ arel_table = Arel::Table.new(table)
50
+ update = Arel::UpdateManager.new(arel_table).
51
+ set(assignments.map { |column, value| [arel_table[column], value] }).
52
+ where(arel_table[:id].eq(id))
53
+ connection.update(update)
54
+ end
55
+
56
+ # Tables with a `preferences` column that a model storing Spree
57
+ # preferences reads — Spree's, an extension's, or the application's own.
58
+ # A table another library owns is left alone.
59
+ #
60
+ # @return [Array<String>]
61
+ def preference_tables
62
+ Rails.application.eager_load! if defined?(Rails.application) && Rails.application
63
+ # An anonymous class (a spec's throwaway model) has no name to derive a
64
+ # table from, and asking raises, so it is skipped.
65
+ owned = ActiveRecord::Base.descendants.filter_map do |model|
66
+ next if model.name.nil? || model.abstract_class? || !model.include?(Spree::Preferences::Preferable)
67
+
68
+ model.table_name
69
+ end
70
+
71
+ connection.tables.select { |table| owned.include?(table) && connection.column_exists?(table, :preferences) }.sort
72
+ end
73
+
74
+ # Whether the table's `preferences` column is still `text` and needs a
75
+ # JSON column in its place.
76
+ #
77
+ # @param table [String]
78
+ # @return [Boolean]
79
+ def text_column?(table)
80
+ connection.columns(table).find { |column| column.name == 'preferences' }&.type == :text
81
+ end
82
+
83
+ # Replaces a `text` preferences column with a JSON one (`jsonb` where the
84
+ # adapter has it), converting every row on the way.
85
+ #
86
+ # @param table [String]
87
+ # @return [Integer] the number of rows written
88
+ def convert_text_table(table)
89
+ type = connection.native_database_types.key?(:jsonb) ? :jsonb : :json
90
+ # Left behind by an earlier run that stopped on an unreadable row, on a
91
+ # database that cannot roll back schema changes.
92
+ connection.add_column(table, :preferences_json, type) unless connection.column_exists?(table, :preferences_json)
93
+ converted = convert_table(table, target: 'preferences_json')
94
+ connection.remove_column(table, :preferences)
95
+ connection.rename_column(table, :preferences_json, :preferences)
96
+ converted
97
+ end
98
+
99
+ # Converts every row of `table` whose `source` column holds YAML (as text,
100
+ # or wrapped in a JSON string) and writes the JSON to `target`. Secrets
101
+ # move to `secret_preferences` when the table has that column, and tier
102
+ # ladders change shape.
103
+ #
104
+ # @param table [String]
105
+ # @param source [String] the column to read
106
+ # @param target [String] the column to write, the same one for an in-place conversion
107
+ # @return [Integer] the number of rows written
108
+ def convert_table(table, source: 'preferences', target: 'preferences')
109
+ typed = connection.column_exists?(table, :type)
110
+ secrets_column = connection.column_exists?(table, :secret_preferences)
111
+ rows = rows_with(table, source, ['id', source, ('type' if typed)].compact)
112
+
113
+ rows.count do |row|
114
+ preferences, already_json = read(row[source], table, row['id'])
115
+ type = row['type'] if typed
116
+ # A tiered calculator whose class was not loaded when the migration
117
+ # ran keeps its hash, so a JSON row is still reshaped when it has one.
118
+ reshaped = reshape_tiers(preferences, model_for(type))
119
+ next false if already_json && source == target && !reshaped
120
+
121
+ secrets = secrets_column ? extract_secrets(preferences, type, table, row['id']) : {}
122
+
123
+ assignments = { target => JSON.generate(preferences) }
124
+ assignments['secret_preferences'] = JSON.generate(secrets) if secrets.any?
125
+ write(table, row['id'], assignments)
126
+ true
127
+ end
128
+ end
129
+
130
+ private
131
+
132
+ attr_reader :connection, :log
133
+
134
+ # @return [Array(Hash, Boolean)] the preferences, and whether they were already JSON
135
+ def read(raw, table, id)
136
+ decoded = raw.is_a?(String) ? decode_json(raw) : raw
137
+ return [decoded.to_h, true] if decoded.is_a?(Hash)
138
+
139
+ yaml = decoded.is_a?(String) ? decoded : raw
140
+ value = parse_yaml(yaml, table, id) || {}
141
+ raise UnreadableRowError, "#{table} row #{id}: preferences are not a hash" unless value.is_a?(Hash)
142
+
143
+ [value.as_json, false]
144
+ end
145
+
146
+ def decode_json(raw)
147
+ JSON.parse(raw)
148
+ rescue JSON::ParserError
149
+ nil
150
+ end
151
+
152
+ def parse_yaml(text, table, id)
153
+ YAML.safe_load(text.to_s, permitted_classes: PERMITTED_CLASSES, aliases: true)
154
+ rescue Psych::Exception => e
155
+ raise UnreadableRowError, "#{table} row #{id}: #{e.message}. Fix or clear this row, then run the migration again."
156
+ end
157
+
158
+ def reshape_tiers(preferences, model)
159
+ tiers = preferences['tiers']
160
+ return unless tiers.is_a?(Hash) && self.class.tiered_calculator?(model)
161
+
162
+ preferences['tiers'] = tiers.map { |threshold, value| { 'threshold' => threshold.to_s, 'value' => value.to_s } }.
163
+ sort_by { |tier| Spree::Calculator::Tiers.decimal(tier['threshold']) || 0 }
164
+ end
165
+
166
+ def extract_secrets(preferences, type, table, id)
167
+ model = model_for(type)
168
+ unless model.respond_to?(:secret_preference_names)
169
+ log.call("#{table} row #{id}: class #{type.inspect} is not loaded, so its secrets stay in preferences until it is")
170
+ return {}
171
+ end
172
+
173
+ names = model.secret_preference_names.map(&:to_s)
174
+ secrets = preferences.slice(*names)
175
+ names.each { |name| preferences.delete(name) }
176
+ secrets
177
+ end
178
+
179
+ # The given columns of every row whose `present` column is not NULL.
180
+ def rows_with(table, present, columns)
181
+ arel_table = Arel::Table.new(table)
182
+ connection.select_all(arel_table.project(*columns.map { |column| arel_table[column] }).where(arel_table[present].not_eq(nil)))
183
+ end
184
+ end
185
+ end
186
+ end