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
@@ -1,169 +1,221 @@
1
1
  module Spree::Preferences
2
2
  module PreferableClassMethods
3
- # Declaration order, so an admin form can present preferences the way
4
- # their author grouped them — credentials before the optional settings
5
- # that depend on them. `defined_preferences` reads Ruby's own `methods`,
6
- # whose order is an implementation detail, so it cannot answer this.
7
- def declared_preference_order
8
- @declared_preference_order ||= begin
9
- inherited = superclass.respond_to?(:declared_preference_order) ? superclass.declared_preference_order : []
10
- inherited.dup
3
+ # Bumped by every declaration anywhere, so a class's cached view of its
4
+ # preferences is rebuilt when an ancestor gains one after the class was
5
+ # loaded — a decorator adding a secret to Spree::PaymentMethod, say.
6
+ @declarations = 0
7
+
8
+ class << self
9
+ attr_accessor :declarations
10
+ end
11
+
12
+ # Every preference the class declares, inherited ones included, in
13
+ # declaration order — so an admin form can present them the way their
14
+ # author grouped them.
15
+ #
16
+ # @return [Hash{Symbol => Hash}] name => `{ type:, default:, deprecated:, internal:, choices:, nullable:, parse_on_set: }`
17
+ def preference_definitions
18
+ preference_declarations_cache[:definitions] ||= begin
19
+ inherited = superclass.respond_to?(:preference_definitions) ? superclass.preference_definitions : {}
20
+ inherited.merge(own_preference_definitions).freeze
11
21
  end
12
22
  end
13
23
 
24
+ # Each declared preference's type, known from the declarations alone —
25
+ # without building a record.
26
+ #
27
+ # @return [Hash{Symbol => Symbol}]
28
+ def declared_preference_types
29
+ preference_declarations_cache[:types] ||= preference_definitions.transform_values { |definition| definition[:type] }.freeze
30
+ end
31
+
32
+ # Names of the `:password` preferences, which a model including
33
+ # {Spree::SecretPreferences} stores encrypted in `secret_preferences`.
34
+ #
35
+ # @return [Array<Symbol>]
36
+ def secret_preference_names
37
+ preference_declarations_cache[:secrets] ||= declared_preference_types.filter_map { |name, type| name if type == :password }.freeze
38
+ end
39
+
40
+ # The value types a list item, a hash value or an object property may
41
+ # declare — what {Spree::PreferenceSchema::JsonSchema} compiles to JSON
42
+ # Schema, along with its `FORMATS`.
43
+ ITEM_TYPES = %i[string integer decimal money boolean id object].freeze
44
+
45
+ # Declares a typed preference with a `preferred_<name>` reader and writer
46
+ # and a `prefers_<name>?` query. On a model, the value lives in the
47
+ # `preferences` column (`secret_preferences` for a `:password` one) as a
48
+ # Rails store accessor, which brings the change methods with it:
49
+ # `preferred_<name>_changed?`, `_change`, `_was`,
50
+ # `saved_change_to_preferred_<name>?` and `preferred_<name>_before_last_save`.
51
+ #
52
+ # The declaration is the preference's contract on the wire, so it states
53
+ # the full type:
54
+ #
55
+ # preference :amount_min, :money
56
+ # preference :channel_ids, :array, of: :id, model: 'Spree::Channel', scope: ->(rule) { rule.store.channels }
57
+ # preference :country_codes, :array, of: :string, format: :iso_country
58
+ # preference :match_policy, :string, choices: %w[any all none]
59
+ # preference :amounts, :hash, keys: :currency, values: :money
60
+ # preference :tiers, :array, of: :object, properties: { threshold: :money, value: :decimal }
61
+ #
62
+ # @param name [Symbol]
63
+ # @param type [Symbol] `:string`, `:text`, `:boolean`, `:integer`, `:decimal`, `:money`,
64
+ # `:array`, `:hash`, `:password`, `:date` or `:datetime`
65
+ # @option options [Symbol] :of the type of each item of an `:array`
66
+ # @option options [Symbol] :keys the type of a `:hash`'s keys (`:string` or `:currency`)
67
+ # @option options [Symbol] :values the type of a `:hash`'s values
68
+ # @option options [Hash{Symbol => Symbol}] :properties the fields of each `of: :object` item
69
+ # @option options [Symbol] :format a string's domain format (see {Spree::PreferenceSchema::JsonSchema::FORMATS})
70
+ # @option options [String, Proc] :model the record class an `of: :id` list points at
71
+ # @option options [Proc] :scope `->(owner) { relation }` the ids must be found in
72
+ # @option options [Array, Proc] :choices the fixed set a value must come from
73
+ # @option options [Symbol] :currency where a `:money` preference's currency comes
74
+ # from: another preference or a method of the record. Defaults to the
75
+ # record's `currency` preference; with neither, the amount is in any currency.
14
76
  def preference(name, type, *args)
15
- declared_preference_order << name.to_sym unless declared_preference_order.include?(name.to_sym)
16
- options = args.extract_options!
17
- options.assert_valid_keys(:default, :deprecated, :in, :internal, :nullable, :parse_on_set)
18
- default = options[:default]
19
- default = -> { options[:default] } unless default.is_a?(Proc)
20
- deprecated = options[:deprecated]
21
- internal = options[:internal]
22
- # The fixed set a value must come from. Turns a text box into a picker
23
- # in every admin form, and is what the inclusion validation would have
24
- # told the operator only after a failed save.
25
- choices = options[:in]
26
- nullable = options[:nullable]
27
- parse_on_set = options[:parse_on_set]
28
-
29
- # cache_key will be nil for new objects, then if we check if there
30
- # is a pending preference before going to default
31
- define_method preference_getter_method(name) do
32
- # Rows persisted before the model gained preferences can load with a
33
- # NULL column — treat that as "no stored preferences".
34
- (preferences || {}).fetch(name) do
35
- default.call
36
- end
37
- end
38
-
39
- define_method preference_setter_method(name) do |value|
40
- if parse_on_set.is_a?(Proc)
41
- # Procs that accept more than one arg opt into receiving the
42
- # owning record so they can scope (e.g. `normalize_id_preference`
43
- # rejecting cross-store IDs). `arity.abs > 1` covers both the
44
- # `(value, owner)` and `(value, owner = nil)` shapes.
45
- value = parse_on_set.arity.abs > 1 ? parse_on_set.call(value, self) : parse_on_set.call(value)
46
- end
47
- value = convert_preference_value(value, type, nullable: nullable)
48
- self.preferences = {} if preferences.nil?
49
- preferences[name] = value
50
-
51
- Spree::Deprecation.warn("`#{name}` is deprecated. #{deprecated}") if deprecated
52
-
53
- # If this is an activerecord object, we need to inform
54
- # ActiveRecord::Dirty that this value has changed, since this is an
55
- # in-place update to the preferences hash.
56
- preferences_will_change! if respond_to?(:preferences_will_change!)
57
- end
58
-
59
- define_method preference_default_getter_method(name), &default
60
-
61
- define_method preference_type_getter_method(name) do
62
- type
77
+ name = name.to_sym
78
+ secret = type == :password
79
+ if secret && !include?(Spree::SecretPreferences)
80
+ raise ArgumentError, "#{self.name} declares the secret preference `#{name}` but cannot encrypt it. " \
81
+ 'Include Spree::SecretPreferences and add a `secret_preferences` text column to its table.'
63
82
  end
64
83
 
65
- define_method preference_deprecated_getter_method(name) do
66
- deprecated
84
+ options = args.extract_options!
85
+ options.assert_valid_keys(:default, :deprecated, :in, :choices, :internal, :nullable, :parse_on_set,
86
+ :of, :keys, :values, :properties, :format, :model, :scope, :currency)
87
+ if options.key?(:in)
88
+ Spree::Deprecation.warn("`preference :#{name}, in:` is deprecated. Use `choices:` instead.")
89
+ options[:choices] ||= options.delete(:in)
67
90
  end
91
+ check_preference_declaration(name, type, options)
68
92
 
69
- # Whether this preference is the system's to write rather than the
70
- # operator's to supply — a value a provider hands back to us, not one
71
- # anybody could know in advance.
72
- define_method preference_internal_getter_method(name) do
73
- internal
93
+ default = options[:default]
94
+ dynamic_default = default.is_a?(Proc)
95
+ default = -> { options[:default] } unless dynamic_default
96
+ own_preference_definitions[name] = {
97
+ type: type,
98
+ default: default,
99
+ # Computed when read (the default store's currency, say), so it
100
+ # differs between stores and is not part of the preference's schema.
101
+ dynamic_default: dynamic_default,
102
+ deprecated: options[:deprecated],
103
+ # Whether the system writes the value rather than the operator — a
104
+ # value a provider hands back, kept out of every admin form.
105
+ internal: options[:internal],
106
+ # The fixed set a value must come from; turns a text box into a picker.
107
+ choices: options[:choices],
108
+ nullable: options[:nullable],
109
+ parse_on_set: options[:parse_on_set],
110
+ of: options[:of],
111
+ keys: options[:keys],
112
+ values: options[:values],
113
+ properties: options[:properties],
114
+ format: options[:format],
115
+ model: options[:model],
116
+ scope: options[:scope],
117
+ currency: options[:currency]
118
+ }
119
+ PreferableClassMethods.declarations += 1
120
+
121
+ store_accessor(secret ? :secret_preferences : :preferences, name, prefix: :preferred)
122
+
123
+ # Overrides the store accessor's reader and writer, which treat a missing
124
+ # key as nil; here a missing key means the declared default.
125
+ define_method(:"preferred_#{name}") do
126
+ Spree::Preferences::Preferable.restore_value(stored_preference(name) { stored_default(name) }, type)
74
127
  end
75
128
 
76
- define_method preference_choices_getter_method(name) do
77
- choices.respond_to?(:call) ? choices.call : choices
129
+ define_method(:"preferred_#{name}=") do |value|
130
+ write_preference(name, cast_preference(name, value))
78
131
  end
79
132
 
80
- define_method prefers_query_method(name) do
81
- preferences.fetch(name).to_b
133
+ define_method(:"prefers_#{name}?") do
134
+ stored_preference(name) { raise KeyError, "key not found: #{name.inspect}" }.to_b
82
135
  end
136
+ end
83
137
 
84
- define_method preference_change_method(name) do
85
- preference_change(name, changes) if respond_to?(:changes)
138
+ # Gives each named preference a plain reader, writer and predicate, so the
139
+ # API reads and writes `guest_checkout` rather than the DSL's
140
+ # `preferred_guest_checkout` — the methods Rails' `has_delegated_json`
141
+ # defines, so callers would not change if the storage ever moved to it.
142
+ # Grants no write access: each API controller still lists what it permits.
143
+ #
144
+ # exposes_preferences :guest_checkout, :timezone
145
+ #
146
+ # @param names [Array<Symbol>]
147
+ # @return [void]
148
+ def exposes_preferences(*names)
149
+ names.each do |name|
150
+ name = name.to_sym
151
+ raise ArgumentError, "#{self.name} has no preference `#{name}` to expose" unless preference_definitions.key?(name)
152
+ raise ArgumentError, "#{self.name} cannot expose the preference `#{name}`: a method of that name exists" if method_defined?(name)
153
+
154
+ define_method(name) { public_send(:"preferred_#{name}") }
155
+ define_method(:"#{name}=") { |value| public_send(:"preferred_#{name}=", value) }
156
+ # As `has_json` defines one; a predicate the model already has (one
157
+ # that falls back to another setting, say) is kept.
158
+ define_method(:"#{name}?") { public_send(:"preferred_#{name}").present? } unless method_defined?(:"#{name}?")
86
159
  end
160
+ self.exposed_preference_names = (exposed_preference_names | names.map(&:to_sym)).freeze
161
+ end
87
162
 
88
- define_method preference_was_method(name) do
89
- return unless respond_to?(:changes)
163
+ # The plain name an exposed preference goes by, for a key still using the
164
+ # DSL's `preferred_` prefix — an error key or a 5.x request parameter.
165
+ #
166
+ # @param key [Symbol, String] e.g. `preferred_guest_checkout`
167
+ # @return [Symbol, nil] e.g. `:guest_checkout`, or nil when the key names no exposed preference
168
+ def exposed_preference_name(key)
169
+ name = key.to_s.delete_prefix('preferred_')
170
+ name.to_sym if name != key.to_s && exposed_preference_names.include?(name.to_sym)
171
+ end
90
172
 
91
- preference_change(name, changes)&.first || get_preference(name)
92
- end
173
+ private
93
174
 
94
- define_method preference_changed_method(name) do
95
- respond_to?(:changes) && preference_change(name, changes).present?
175
+ # A declaration is the preference's contract on the wire. One Spree ships
176
+ # is always complete (a spec enforces it); an incomplete one from an
177
+ # extension still works in 6.0, with a schema that accepts any value.
178
+ def check_preference_declaration(name, type, options)
179
+ if options[:of] && type != :array
180
+ raise ArgumentError, "`of:` applies to an :array preference, not `#{name}` (#{type})"
96
181
  end
97
-
98
- define_method preference_previous_change_method(name) do
99
- preference_change(name, previous_changes) if respond_to?(:previous_changes)
182
+ if (options[:keys] || options[:values]) && type != :hash
183
+ raise ArgumentError, "`keys:` and `values:` apply to a :hash preference, not `#{name}` (#{type})"
100
184
  end
101
-
102
- define_method preference_previous_was_method(name) do
103
- return unless respond_to?(:previous_changes)
104
-
105
- preference_change(name, previous_changes)&.first
185
+ [options[:of], options[:values], *options[:properties]&.values].compact.each do |item_type|
186
+ raise ArgumentError, "Unknown item type `#{item_type}` on preference `#{name}`" unless ITEM_TYPES.include?(item_type)
106
187
  end
107
-
108
- define_method preference_previous_changed_method(name) do
109
- respond_to?(:previous_changes) && preference_change(name, previous_changes).present?
188
+ if options[:format] && !Spree::PreferenceSchema::JsonSchema::FORMATS.key?(options[:format])
189
+ raise ArgumentError, "Unknown format `#{options[:format]}` on preference `#{name}`"
110
190
  end
111
- end
112
-
113
- def preference_getter_method(name)
114
- "preferred_#{name}".to_sym
115
- end
191
+ raise ArgumentError, "`of: :id` needs `model:` on preference `#{name}`" if options[:of] == :id && options[:model].blank?
192
+ raise ArgumentError, "`currency:` applies to a :money preference, not `#{name}` (#{type})" if options[:currency] && type != :money
193
+ raise ArgumentError, "`of: :object` needs `properties:` on preference `#{name}`" if options[:of] == :object && options[:properties].blank?
116
194
 
117
- def preference_setter_method(name)
118
- "preferred_#{name}=".to_sym
119
- end
120
-
121
- def preference_default_getter_method(name)
122
- "preferred_#{name}_default".to_sym
123
- end
124
-
125
- def preference_deprecated_getter_method(name)
126
- "preferred_#{name}_deprecated".to_sym
127
- end
128
-
129
- def preference_internal_getter_method(name)
130
- "preferred_#{name}_internal".to_sym
131
- end
132
-
133
- def preference_choices_getter_method(name)
134
- "preferred_#{name}_choices".to_sym
135
- end
136
-
137
- def preference_type_getter_method(name)
138
- "preferred_#{name}_type".to_sym
139
- end
140
-
141
- def prefers_query_method(name)
142
- "prefers_#{name}?".to_sym
143
- end
144
-
145
- def preference_change_method(name)
146
- "preferred_#{name}_change".to_sym
147
- end
195
+ missing = case type
196
+ when :any then 'a type other than :any'
197
+ when :array then '`of:`' unless options[:of]
198
+ when :hash then '`keys:` and `values:`' unless options[:keys] && options[:values]
199
+ end
200
+ return unless missing
148
201
 
149
- def preference_was_method(name)
150
- "preferred_#{name}_was".to_sym
202
+ Spree::Deprecation.warn(
203
+ "#{self.name} preference `#{name}` (#{type}) needs #{missing}; until it has one, its schema accepts any value. " \
204
+ 'Spree 6.1 will raise for an incomplete declaration.'
205
+ )
151
206
  end
152
207
 
153
- def preference_changed_method(name)
154
- "preferred_#{name}_changed?".to_sym
208
+ def own_preference_definitions
209
+ @own_preference_definitions ||= {}
155
210
  end
156
211
 
157
- def preference_previous_change_method(name)
158
- "preferred_#{name}_previous_change".to_sym
159
- end
160
-
161
- def preference_previous_was_method(name)
162
- "preferred_#{name}_previously_was".to_sym
163
- end
164
-
165
- def preference_previous_changed_method(name)
166
- "preferred_#{name}_previously_changed?".to_sym
212
+ def preference_declarations_cache
213
+ generation = PreferableClassMethods.declarations
214
+ unless @preference_declarations_generation == generation
215
+ @preference_declarations_generation = generation
216
+ @preference_declarations_cache = {}
217
+ end
218
+ @preference_declarations_cache
167
219
  end
168
220
  end
169
221
  end
@@ -0,0 +1,10 @@
1
+ require_relative 'preview_data'
2
+
3
+ # Preview Spree import emails at /rails/mailers/spree/import
4
+ class Spree::ImportPreview < ActionMailer::Preview
5
+ include Spree::PreviewData::LocaleParam
6
+
7
+ def import_done
8
+ Spree::ImportMailer.import_done(Spree::Import.where.not(user_id: nil).last)
9
+ end
10
+ end
@@ -6,7 +6,7 @@ module Spree
6
6
  #
7
7
  # @example Adding custom searchable fields
8
8
  # Spree.ransack.add_attribute(Spree::Product, :seller_id)
9
- # Spree.ransack.add_scope(Spree::Product, :by_seller)
9
+ # Spree.ransack.add_scope(Spree::Product, :by_seller, type: 'id')
10
10
  # Spree.ransack.add_association(Spree::Product, :seller)
11
11
  #
12
12
  class RansackConfiguration
@@ -14,6 +14,7 @@ module Spree
14
14
  @custom_attributes = Hash.new { |h, k| h[k] = [] }
15
15
  @custom_associations = Hash.new { |h, k| h[k] = [] }
16
16
  @custom_scopes = Hash.new { |h, k| h[k] = [] }
17
+ @custom_scope_types = Hash.new { |h, k| h[k] = {} }
17
18
  end
18
19
 
19
20
  # Add a custom ransackable attribute to a model.
@@ -38,8 +39,11 @@ module Spree
38
39
  #
39
40
  # @param model [Class] the model class to configure (e.g., Spree::Product)
40
41
  # @param scope [String, Symbol] the scope to add
42
+ # @param type [String, Array<String>, Hash, nil] the arguments it takes, as
43
+ # in `ransackable_scope_types` — `'boolean'` for a scope taking none
41
44
  # @return [Array<String>] the updated list of custom scopes
42
- def add_scope(model, scope)
45
+ def add_scope(model, scope, type: nil)
46
+ @custom_scope_types[model.name.to_sym][scope.to_s] = type unless type.nil?
43
47
  @custom_scopes[model.name.to_sym] |= [scope.to_s]
44
48
  end
45
49
 
@@ -67,6 +71,12 @@ module Spree
67
71
  @custom_scopes[model.name.to_sym]
68
72
  end
69
73
 
74
+ # @param model [Class] the model class to query
75
+ # @return [Hash{String => Object}] argument types declared with {#add_scope}
76
+ def custom_scope_types_for(model)
77
+ @custom_scope_types[model.name.to_sym]
78
+ end
79
+
70
80
  # Reset all custom configurations. Useful for testing.
71
81
  #
72
82
  # @return [void]
@@ -74,6 +84,7 @@ module Spree
74
84
  @custom_attributes.clear
75
85
  @custom_associations.clear
76
86
  @custom_scopes.clear
87
+ @custom_scope_types.clear
77
88
  end
78
89
  end
79
90
  end
@@ -0,0 +1,37 @@
1
+ module Spree
2
+ module Tax
3
+ # A tax engine did not produce tax for a sale. Providers raise one of the
4
+ # two subclasses below; core rescues this and refuses the sale rather than
5
+ # completing one whose tax nobody computed — charging nothing because nobody
6
+ # could be asked is the error a merchant discovers in a filing.
7
+ #
8
+ # Split because a customer can only act on one of them. Retrying an outage
9
+ # may work; retrying a postcode the engine rejects never will, and telling
10
+ # someone to try again is worse than telling them nothing.
11
+ #
12
+ # The parallel is {Spree::Pricing::PriceResolution::ProviderUnavailable},
13
+ # which refuses a sale whose prices could not be confirmed. Neither is for a
14
+ # provider a merchant switched off: that is configuration, not failure, and
15
+ # an unconfigured engine simply has no opinion and writes no rows.
16
+ class ProviderError < StandardError
17
+ # @return [String, nil] which engine failed, for the report
18
+ attr_reader :provider_key
19
+
20
+ def initialize(message = nil, provider_key: nil)
21
+ @provider_key = provider_key
22
+ super(message)
23
+ end
24
+ end
25
+
26
+ # The engine could not be asked — unreachable, timed out, or answering with
27
+ # a fault of its own. Nothing is known about the tax, and trying again
28
+ # later may well succeed, so the customer is told to retry.
29
+ class ProviderUnavailable < ProviderError; end
30
+
31
+ # The engine was asked and said no: an address it will not accept, a
32
+ # misconfigured company code, a request it rejects. The service is working,
33
+ # so retrying changes nothing — what the engine said is the useful part and
34
+ # core passes its message on rather than inventing one.
35
+ class CalculationRefused < ProviderError; end
36
+ end
37
+ end
@@ -1,5 +1,5 @@
1
1
  module Spree
2
- VERSION = '6.0.0.beta4'.freeze
2
+ VERSION = '6.0.0.rc2'.freeze
3
3
 
4
4
  def self.version
5
5
  VERSION