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,24 +1,22 @@
1
- require_dependency 'spree/shipping_calculator'
2
-
3
1
  module Spree
4
2
  module Calculator::Shipping
5
3
  class FlatRate < ShippingCalculator
6
4
  include Spree::Calculator::CurrencyAmounts
7
5
 
8
- preference :amount, :decimal, default: 0
6
+ preference :amount, :money, default: 0
9
7
  # @deprecated Together with the single `amount`, superseded by the
10
8
  # per-currency `amounts` hash; kept as the fallback for its own
11
9
  # currency so upgraded stores quote unchanged.
12
- preference :currency, :string, default: -> { Spree::Store.default.default_currency }
10
+ preference :currency, :string, format: :currency, default: -> { Spree::Store.default.default_currency }
13
11
 
14
- preference :minimum_item_total, :decimal, default: nil, nullable: true, deprecated: 'Use a Spree::DeliveryMethodRules rule on the delivery method instead; removed in Spree 6.1.'
15
- preference :maximum_item_total, :decimal, default: nil, nullable: true, deprecated: 'Use a Spree::DeliveryMethodRules rule on the delivery method instead; removed in Spree 6.1.'
12
+ preference :minimum_item_total, :money, default: nil, nullable: true, deprecated: 'Use a Spree::DeliveryMethodRules rule on the delivery method instead; removed in Spree 6.1.'
13
+ preference :maximum_item_total, :money, default: nil, nullable: true, deprecated: 'Use a Spree::DeliveryMethodRules rule on the delivery method instead; removed in Spree 6.1.'
16
14
 
17
15
  preference :minimum_weight, :decimal, default: nil, nullable: true, deprecated: 'Use a Spree::DeliveryMethodRules rule on the delivery method instead; removed in Spree 6.1.'
18
16
  preference :maximum_weight, :decimal, default: nil, nullable: true, deprecated: 'Use a Spree::DeliveryMethodRules rule on the delivery method instead; removed in Spree 6.1.'
19
17
 
20
18
  def self.description
21
- Spree.t(:shipping_flat_rate_per_order)
19
+ I18n.t('spree.shipping_flat_rate_per_order')
22
20
  end
23
21
 
24
22
  def compute_package(package)
@@ -1,12 +1,10 @@
1
- require_dependency 'spree/shipping_calculator'
2
-
3
1
  module Spree
4
2
  module Calculator::Shipping
5
3
  class FlexiRate < ShippingCalculator
6
- preference :first_item, :decimal, default: 0.0
7
- preference :additional_item, :decimal, default: 0.0
4
+ preference :first_item, :money, default: 0.0
5
+ preference :additional_item, :money, default: 0.0
8
6
  preference :max_items, :integer, default: 0
9
- preference :currency, :string, default: -> { Spree::Store.default.default_currency }
7
+ preference :currency, :string, format: :currency, default: -> { Spree::Store.default.default_currency }
10
8
 
11
9
  # Single-currency amounts: quoting another currency would mislabel
12
10
  # the configured numbers, so the method stays hidden there.
@@ -15,7 +13,7 @@ module Spree
15
13
  end
16
14
 
17
15
  def self.description
18
- Spree.t(:shipping_flexible_rate)
16
+ I18n.t('spree.shipping_flexible_rate')
19
17
  end
20
18
 
21
19
  def compute_package(package)
@@ -1,15 +1,13 @@
1
- require_dependency 'spree/shipping_calculator'
2
-
3
1
  module Spree
4
2
  module Calculator::Shipping
5
3
  class PerItem < ShippingCalculator
6
4
  include Spree::Calculator::CurrencyAmounts
7
5
 
8
- preference :amount, :decimal, default: 0
9
- preference :currency, :string, default: -> { Spree::Store.default.default_currency }
6
+ preference :amount, :money, default: 0
7
+ preference :currency, :string, format: :currency, default: -> { Spree::Store.default.default_currency }
10
8
 
11
9
  def self.description
12
- Spree.t(:shipping_flat_rate_per_item)
10
+ I18n.t('spree.shipping_flat_rate_per_item')
13
11
  end
14
12
 
15
13
  def compute_package(package)
@@ -1,12 +1,10 @@
1
- require_dependency 'spree/shipping_calculator'
2
-
3
1
  module Spree
4
2
  module Calculator::Shipping
5
3
  class PriceSack < ShippingCalculator
6
- preference :minimal_amount, :decimal, default: 0
7
- preference :normal_amount, :decimal, default: 0
8
- preference :discount_amount, :decimal, default: 0
9
- preference :currency, :string, default: -> { Spree::Store.default.default_currency }
4
+ preference :minimal_amount, :money, default: 0
5
+ preference :normal_amount, :money, default: 0
6
+ preference :discount_amount, :money, default: 0
7
+ preference :currency, :string, format: :currency, default: -> { Spree::Store.default.default_currency }
10
8
 
11
9
  # Single-currency amounts: quoting another currency would mislabel
12
10
  # the configured numbers, so the method stays hidden there.
@@ -15,7 +13,7 @@ module Spree
15
13
  end
16
14
 
17
15
  def self.description
18
- Spree.t(:shipping_price_sack)
16
+ I18n.t('spree.shipping_price_sack')
19
17
  end
20
18
 
21
19
  def compute_package(package)
@@ -1,42 +1,25 @@
1
- require_dependency 'spree/calculator'
2
-
3
1
  module Spree
4
2
  class Calculator::TieredFlatRate < Calculator
5
- preference :base_amount, :decimal, default: 0
6
- preference :currency, :string, default: -> { Spree::Store.default.default_currency }
7
- preference :tiers, :hash, default: {}
8
-
9
- before_validation do
10
- # Convert tier values to decimals. Strings don't do us much good.
11
- if preferred_tiers.is_a?(Hash)
12
- self.preferred_tiers = Hash[*preferred_tiers.flatten.map(&:to_f)]
13
- end
14
- end
3
+ include Spree::Calculator::Tiers
15
4
 
16
- validate :preferred_tiers_content
5
+ preference :base_amount, :money, default: 0
6
+ preference :currency, :string, format: :currency, default: -> { Spree::Store.default.default_currency }
17
7
 
18
8
  def self.description
19
- Spree.t(:tiered_flat_rate)
9
+ I18n.t('spree.tiered_flat_rate')
20
10
  end
21
11
 
22
12
  def compute(object = nil)
23
13
  return 0 unless object&.currency.present?
24
14
  return 0 unless preferred_currency.casecmp(object.currency.upcase).zero?
25
15
 
26
- base, amount = preferred_tiers.sort.reverse.detect { |b, _| object.amount >= b }
27
- amount || preferred_base_amount
16
+ tier_value_for(object.amount) || preferred_base_amount
28
17
  end
29
18
 
30
19
  private
31
20
 
32
- def preferred_tiers_content
33
- if preferred_tiers.is_a? Hash
34
- unless preferred_tiers.keys.all? { |k| k.is_a?(Numeric) && k > 0 }
35
- errors.add(:base, :keys_should_be_positive_number)
36
- end
37
- else
38
- errors.add(:preferred_tiers, :should_be_hash)
39
- end
21
+ def validate_tier_values(values)
22
+ errors.add(:preferred_tiers, :values_should_be_number) unless values.all? { |value| value && value >= 0 }
40
23
  end
41
24
  end
42
25
  end
@@ -1,45 +1,27 @@
1
- require_dependency 'spree/calculator'
2
-
3
1
  module Spree
4
2
  class Calculator::TieredPercent < Calculator
5
- preference :base_percent, :decimal, default: 0
6
- preference :tiers, :hash, default: {}
3
+ include Spree::Calculator::Tiers
7
4
 
8
- before_validation do
9
- # Convert tier values to decimals. Strings don't do us much good.
10
- if preferred_tiers.is_a?(Hash)
11
- self.preferred_tiers = Hash[*preferred_tiers.flatten.map(&:to_f)]
12
- end
13
- end
5
+ preference :base_percent, :decimal, default: 0
14
6
 
15
7
  validates :preferred_base_percent, numericality: {
16
8
  greater_than_or_equal_to: 0,
17
9
  less_than_or_equal_to: 100
18
10
  }
19
- validate :preferred_tiers_content
20
11
 
21
12
  def self.description
22
- Spree.t(:tiered_percent)
13
+ I18n.t('spree.tiered_percent')
23
14
  end
24
15
 
25
16
  def compute(object)
26
- base, percent = preferred_tiers.sort.reverse.detect { |b, _| object.amount >= b }
27
- (object.amount * (percent || preferred_base_percent) / 100).round(2)
17
+ percent = tier_value_for(object.amount) || preferred_base_percent
18
+ Spree::Money::Rounding.to_currency(object.amount * percent / 100, object.try(:currency))
28
19
  end
29
20
 
30
21
  private
31
22
 
32
- def preferred_tiers_content
33
- if preferred_tiers.is_a? Hash
34
- unless preferred_tiers.keys.all? { |k| k.is_a?(Numeric) && k > 0 }
35
- errors.add(:base, :keys_should_be_positive_number)
36
- end
37
- unless preferred_tiers.values.all? { |k| k.is_a?(Numeric) && k >= 0 && k <= 100 }
38
- errors.add(:base, :values_should_be_percent)
39
- end
40
- else
41
- errors.add(:preferred_tiers, :should_be_hash)
42
- end
23
+ def validate_tier_values(values)
24
+ errors.add(:preferred_tiers, :values_should_be_percent) unless values.all? { |value| value&.between?(0, 100) }
43
25
  end
44
26
  end
45
27
  end
@@ -36,6 +36,7 @@ module Spree
36
36
  include Spree::Purchase::Validations
37
37
  include Spree::Purchase::Totals
38
38
  include Spree::Purchase::Lifecycle
39
+ include Spree::Purchase::DeprecatedAliases
39
40
 
40
41
  # Concurrency is manual (the API's OrderLock semantics — compare
41
42
  # client-sent version, 409 on mismatch); Rails auto-locking must not
@@ -54,16 +55,7 @@ module Spree
54
55
  :discount_total, :fee_total, :delivery_total, :total, :payment_total, :outstanding_balance,
55
56
  :tax_total, :pre_tax_item_amount, :pre_tax_total, :amount_due
56
57
 
57
- alias display_promo_total display_discount_total
58
- alias display_ship_total display_delivery_total
59
- alias_attribute :ship_total, :delivery_total
60
-
61
58
  belongs_to :customer, class_name: "::#{Spree.customer_class}", optional: true
62
- # Order-parity aliases — shared Cart/Order code (services, Purchase
63
- # concerns) reads #user; the cart column is customer_id.
64
- alias_method :user, :customer
65
- alias_method :user=, :customer=
66
- alias_attribute :user_id, :customer_id
67
59
  # Codes are stored stripped + lowercased so lookups stay case-insensitive;
68
60
  # normalizes also applies to finder values.
69
61
  normalizes :coupon_code, with: ->(code) { code.to_s.strip.downcase.presence }
@@ -87,6 +79,8 @@ module Spree
87
79
  end
88
80
  end
89
81
  has_many :fulfillment_items, through: :fulfillments, class_name: 'Spree::FulfillmentItem'
82
+ # Legacy name (5.x carts were orders) — removed in 6.1
83
+ has_many :shipments, -> { order(:created_at, :id) }, class_name: 'Spree::Fulfillment', inverse_of: :cart, deprecated: true
90
84
  has_many :order_promotions, class_name: 'Spree::OrderPromotion', inverse_of: :cart, dependent: :destroy
91
85
  has_many :promotions, through: :order_promotions, class_name: 'Spree::Promotion'
92
86
  has_many :payments, class_name: 'Spree::Payment', inverse_of: :cart, dependent: :destroy
@@ -167,10 +161,18 @@ module Spree
167
161
 
168
162
  # Idempotent delivery-proposal rebuild — replaces the destructive
169
163
  # order-side create_proposed_fulfillments. Open fulfillments are rebuilt from
170
- # the current items/address; nothing here touches a completed cart.
171
- def rebuild_fulfillments!
164
+ # the current items/address; nothing here touches a completed cart. Until
165
+ # deliveries can be proposed the cart gets none: preselecting the pickup
166
+ # rate that is all a destination-less quote finds would choose pickup for
167
+ # a customer who had not been offered anything.
168
+ #
169
+ # @param keep_selection [Boolean] false when the destination changed: a
170
+ # choice made for one address is not a choice for another
171
+ def rebuild_fulfillments!(keep_selection: true)
172
172
  return if completed?
173
173
 
174
+ previous_selections = keep_selection ? fulfillments.selected_rates_by_stock_location : {}
175
+
174
176
  discounts.for_fulfillments.delete_all
175
177
  tax_lines.for_fulfillments.delete_all
176
178
  fees.for_fulfillments.delete_all
@@ -179,6 +181,7 @@ module Spree
179
181
  DeliveryRate.where(fulfillment_id: fulfillment_ids).delete_all
180
182
  fulfillments.delete_all
181
183
  fulfillment_items.reset
184
+ return fulfillments.reload unless can_propose_deliveries?
182
185
 
183
186
  # Appended rather than assigned: setting `cart` on each proposal already
184
187
  # files it under this cart's `fulfillments` (the association is
@@ -188,6 +191,7 @@ module Spree
188
191
  Spree::Stock::Coordinator.new(self).fulfillments.each do |fulfillment|
189
192
  fulfillment.address_id = ship_address_id
190
193
  fulfillment.order = nil
194
+ fulfillment.carry_over_selection(previous_selections.fetch(fulfillment.stock_location_id, []))
191
195
  fulfillments << fulfillment
192
196
  end
193
197
  prune_undeliverable_fulfillments!
@@ -206,7 +210,7 @@ module Spree
206
210
  self.warnings |= affected_line_items.map do |line_item|
207
211
  {
208
212
  code: 'delivery_unavailable',
209
- message: Spree.t('cart_line_item.delivery_unavailable', li_name: line_item.name),
213
+ message: I18n.t('spree.cart_line_item.delivery_unavailable', li_name: line_item.name),
210
214
  line_item_id: line_item.prefixed_id
211
215
  }
212
216
  end
@@ -214,7 +218,7 @@ module Spree
214
218
 
215
219
  def ensure_available_delivery_rates
216
220
  if fulfillments.empty? || fulfillments.any? { |fulfillment| fulfillment.delivery_rates.blank? }
217
- errors.add(:base, :items_cannot_be_shipped, message: Spree.t(:items_cannot_be_shipped))
221
+ errors.add(:base, :items_cannot_be_shipped, message: I18n.t('spree.items_cannot_be_shipped'))
218
222
  return false
219
223
  end
220
224
  true
@@ -229,7 +233,6 @@ module Spree
229
233
  updated_at: Time.current
230
234
  )
231
235
  end
232
- alias set_shipments_cost set_fulfillments_cost
233
236
 
234
237
  def ensure_updated_fulfillments
235
238
  rebuild_fulfillments! unless completed?
@@ -238,7 +241,11 @@ module Spree
238
241
  # Re-prices, re-taxes and rebuilds delivery proposals — the
239
242
  # recalculation-on-write replacement for transition-triggered rebuilds.
240
243
  # Called by Carts::Update after address/market changes.
241
- def recalculate_for_address_change!
244
+ #
245
+ # @param keep_selection [Boolean] true when the parcel still goes where it
246
+ # did (a market change, a corrected phone number), so the chosen
247
+ # delivery is kept and quoted again
248
+ def recalculate_for_address_change!(keep_selection: false)
242
249
  # One provider round for the whole cart, and persisted: a per-item loop
243
250
  # would ask an external pricing system once per line, and nothing here
244
251
  # saves the line items — rebuild_fulfillments! and recalculate_totals!
@@ -251,8 +258,7 @@ module Spree
251
258
  raise Spree::Pricing::PriceResolution::ProviderUnavailable, result.error.to_s if result.failure?
252
259
 
253
260
  Spree::Carts::PriceItems.apply(result.value)
254
- rebuild_fulfillments!
255
- set_fulfillments_cost
261
+ rebuild_fulfillments!(keep_selection: keep_selection)
256
262
  recalculate_totals!
257
263
  end
258
264
 
@@ -269,11 +275,27 @@ module Spree
269
275
  reload
270
276
  end
271
277
 
278
+ # The variants this cart's buyer may order: the channel's live
279
+ # publications, narrowed to the catalogs the cart's company, customer
280
+ # group or channel resolve to. Status, stock and currency are left to the
281
+ # add and checkout checks, which explain a refusal rather than reading as
282
+ # not found.
283
+ #
284
+ # @return [ActiveRecord::Relation<Spree::Variant>]
285
+ def orderable_variants
286
+ products = Spree.products_for_context_service.call(
287
+ store: store, channel: channel, customer: customer, company: resolved_company,
288
+ base: store.products.not_discontinued_on(channel)
289
+ ).value
290
+
291
+ Spree::Variant.for_products(products)
292
+ end
293
+
272
294
  # Removes out-of-stock/discontinued items and populates warnings
273
295
  # (mirrors Order#remove_out_of_stock_items!).
274
296
  def remove_out_of_stock_items!
275
297
  existing_warnings = warnings
276
- result = Spree::Carts::RemoveOutOfStockItems.call(cart: self)
298
+ result = Spree.cart_remove_out_of_stock_items_service.call(cart: self)
277
299
  return self unless result.success?
278
300
 
279
301
  cart, _messages, new_warnings = result.value
@@ -281,5 +303,43 @@ module Spree
281
303
  cart
282
304
  end
283
305
 
306
+ # Whether this cart shows a batch coupon code it does not hold — used,
307
+ # taken by another cart or order, or given up by one — and so can never
308
+ # discount it.
309
+ #
310
+ # @return [Boolean]
311
+ def coupon_code_unavailable?
312
+ code = read_attribute(:coupon_code)
313
+ return false if code.blank?
314
+
315
+ record = Spree::CouponCode.where(promotion_id: store.promotions.select(:id)).find_by(code: code)
316
+ record.present? && record.cart_id != id
317
+ end
318
+
319
+ # Drops an entered batch coupon code this cart no longer holds, and warns
320
+ # the shopper, who otherwise sees the discount vanish.
321
+ #
322
+ # @return [Spree::Cart]
323
+ def remove_unavailable_coupon_code!
324
+ return self unless coupon_code_unavailable?
325
+
326
+ # The lock refuses unsaved attributes and reloads the cart, so this
327
+ # request's warnings are set aside and put back.
328
+ existing_warnings = warnings
329
+ clear_attribute_changes([:warnings])
330
+ removed = with_lock do
331
+ # Checked under the lock, as the completion claim itself is: a
332
+ # completing cart's code sits on its draft order while the payment
333
+ # runs, and a failed payment hands it back.
334
+ next false if completion_claimed? || !coupon_code_unavailable?
335
+
336
+ Spree.coupon_handler.new(self, enable_gift_cards: false).remove(read_attribute(:coupon_code))
337
+ true
338
+ end
339
+ self.warnings = existing_warnings
340
+ self.warnings |= [{ code: 'coupon_code_unavailable', message: I18n.t('spree.coupon_code_unavailable') }] if removed
341
+ self
342
+ end
343
+
284
344
  end
285
345
  end
@@ -86,6 +86,7 @@ module Spree
86
86
  scope :by_position, -> { ordered }
87
87
 
88
88
  self.whitelisted_ransackable_attributes = %w[name active]
89
+ search_by :name
89
90
 
90
91
  # The catalogs that apply to a purchase node, nearest node first: the
91
92
  # node's own assignments before its parent's, catalog position breaking
@@ -80,13 +80,6 @@ module Spree
80
80
  break_count.to_i.positive?
81
81
  end
82
82
 
83
- # The amount as the wire carries it — see CatalogPriceTier#display_value.
84
- #
85
- # @return [String]
86
- def display_value
87
- amount.to_s
88
- end
89
-
90
83
  # @return [Spree::Money]
91
84
  def money
92
85
  Spree::Money.new(amount, currency: currency)
@@ -15,14 +15,6 @@ module Spree
15
15
 
16
16
  validates :min_quantity, numericality: { greater_than: 1 }
17
17
 
18
- # The amount as the wire carries it — a string, like every other
19
- # money-shaped value, since a float would round the merchant's figure.
20
- #
21
- # @return [String]
22
- def display_value
23
- amount.to_s
24
- end
25
-
26
18
  # @return [Spree::Money]
27
19
  def money
28
20
  Spree::Money.new(amount, currency: currency)
@@ -61,6 +61,7 @@ module Spree
61
61
  #
62
62
  has_one_attached :image, service: Spree.public_storage_service_name
63
63
  has_one_attached :square_image, service: Spree.public_storage_service_name
64
+ signed_id_attachments :image, :square_image
64
65
  # The slots double as media-library placements, so an upload here is
65
66
  # visible and reusable there (Spree::HasLibraryMedia).
66
67
  has_library_media :image, :square_image
@@ -133,8 +134,9 @@ module Spree
133
134
  # Ransack
134
135
  #
135
136
  self.whitelisted_ransackable_associations = %w[parent]
136
- self.whitelisted_ransackable_attributes = %w[name permalink automatic depth is_root children_count
137
+ self.whitelisted_ransackable_attributes = %w[name permalink automatic depth children_count
137
138
  products_count pretty_name parent_id]
139
+ search_by :name, :pretty_name
138
140
 
139
141
  #
140
142
  # Translations
@@ -17,8 +17,9 @@ module Spree
17
17
  # Empty -> falls back to the Store-level preference. Both are `nullable`
18
18
  # so a blank write stays nil (inherit) rather than coercing to "" or
19
19
  # false, while an explicit value remains a channel override.
20
- preference :storefront_access, :string, default: nil, nullable: true
20
+ preference :storefront_access, :string, default: nil, nullable: true, choices: STOREFRONT_ACCESS
21
21
  preference :guest_checkout, :boolean, default: nil, nullable: true
22
+ exposes_preferences :storefront_access, :guest_checkout
22
23
 
23
24
  validate :storefront_access_must_be_valid
24
25
  end
@@ -58,7 +59,7 @@ module Spree
58
59
  return if STOREFRONT_ACCESS.include?(value.to_s)
59
60
 
60
61
  errors.add(
61
- :preferred_storefront_access, :invalid_storefront_access, message: Spree.t(:invalid_storefront_access, scope: [:errors, :messages], default: 'is not a valid storefront access level')
62
+ :preferred_storefront_access, :invalid_storefront_access, message: I18n.t('spree.errors.messages.invalid_storefront_access')
62
63
  )
63
64
  end
64
65
  end
@@ -14,9 +14,10 @@ module Spree
14
14
  include Spree::Channel::Gating
15
15
 
16
16
  # Empty -> falls back to the Store-level preference.
17
- preference :order_routing_strategy, :string, default: nil
17
+ preference :order_routing_strategy, :string, default: nil,
18
+ parse_on_set: ->(value) { Spree::ApiTyped.class_name_for(Spree.order_routing.strategies, value) }
19
+ exposes_preferences :order_routing_strategy
18
20
 
19
- belongs_to :store, class_name: 'Spree::Store'
20
21
 
21
22
  has_many :orders, class_name: 'Spree::Order', inverse_of: :channel, dependent: :nullify
22
23
  has_many :order_routing_rules, class_name: 'Spree::OrderRoutingRule', dependent: :destroy
@@ -59,6 +60,7 @@ module Spree
59
60
  scope :default, -> { where(default: true) }
60
61
 
61
62
  self.whitelisted_ransackable_attributes = %w[name code active default store_id]
63
+ search_by :name
62
64
 
63
65
  # Publishes the given products on this channel by creating/upserting ProductPublications.
64
66
  # Optionally sets the publication window; if not given, the products will be published immediately
@@ -163,7 +165,7 @@ module Spree
163
165
  return unless default?
164
166
  return if destroyed_by_association.present?
165
167
 
166
- errors.add(:base, :cannot_delete_default_channel, message: Spree.t('errors.messages.cannot_delete_default_channel'))
168
+ errors.add(:base, :cannot_delete_default_channel, message: I18n.t('spree.errors.messages.cannot_delete_default_channel'))
167
169
  throw :abort
168
170
  end
169
171
 
@@ -176,7 +178,7 @@ module Spree
176
178
  return unless api_keys.active.exists?
177
179
  return if destroyed_by_association.present?
178
180
 
179
- errors.add(:base, :cannot_delete_channel_with_active_api_keys, message: Spree.t('errors.messages.cannot_delete_channel_with_active_api_keys'))
181
+ errors.add(:base, :cannot_delete_channel_with_active_api_keys, message: I18n.t('spree.errors.messages.cannot_delete_channel_with_active_api_keys'))
180
182
  throw :abort
181
183
  end
182
184
 
@@ -16,8 +16,10 @@ module Spree
16
16
  include Spree::HasStatus
17
17
  include Spree::HasCustomFields
18
18
  include Spree::Metadata
19
+ include Spree::PostSale::Taxation
19
20
 
20
21
  publishes_lifecycle_events
22
+ publishes_events :approved, :canceled, :denied, :opened, :resolved
21
23
 
22
24
  has_status :open, :approved, :resolved, :denied, :canceled, default: :open
23
25
 
@@ -25,7 +27,6 @@ module Spree
25
27
  # stays closed — an unrecognised value would silently do nothing.
26
28
  RESOLUTIONS = %w[refund replacement refund_and_replacement].freeze
27
29
 
28
- belongs_to :store, class_name: 'Spree::Store'
29
30
  belongs_to :order, class_name: 'Spree::Order', inverse_of: :claims
30
31
  belongs_to :reason, class_name: 'Spree::ClaimReason', optional: true, inverse_of: :claims
31
32
  acted_by :created_by
@@ -43,6 +44,7 @@ module Spree
43
44
  delegate :currency, to: :order
44
45
 
45
46
  self.whitelisted_ransackable_attributes = %w[number status created_at]
47
+ search_by :number
46
48
  self.whitelisted_ransackable_associations = %w[order reason]
47
49
 
48
50
  def refund_total
@@ -59,6 +61,21 @@ module Spree
59
61
  end
60
62
  end
61
63
 
64
+ # The tax inside {#refunded_line_amounts}, for the lines whose tax is
65
+ # recorded: each line's refund carries its tax in proportion to what the
66
+ # customer paid for it.
67
+ #
68
+ # @return [Hash{Integer => BigDecimal}] line item id => tax
69
+ def refunded_line_taxes
70
+ claim_line_items.each_with_object(Hash.new(0)) do |line, taxes|
71
+ next unless line.settles_tax? && line.paid_amount.positive?
72
+
73
+ taxes[line.line_item_id] += Spree::Money::Rounding.to_currency(
74
+ line.tax_total * line.refund_amount.to_d / line.paid_amount, currency
75
+ )
76
+ end
77
+ end
78
+
62
79
  def display_refund_total
63
80
  Spree::Money.new(refund_total, currency: currency)
64
81
  end
@@ -66,5 +83,9 @@ module Spree
66
83
  def replacement_line_items
67
84
  claim_line_items.select(&:send_replacement)
68
85
  end
86
+
87
+ def taxed_lines
88
+ claim_line_items
89
+ end
69
90
  end
70
91
  end
@@ -2,6 +2,8 @@ module Spree
2
2
  # One problem item on a {Spree::Claim}, and how it is being made right:
3
3
  # money back, a replacement, or both.
4
4
  class ClaimLineItem < Spree.base_class
5
+ include Spree::PostSale::TaxedLine
6
+
5
7
  has_prefix_id :cli
6
8
 
7
9
  belongs_to :claim, class_name: 'Spree::Claim', inverse_of: :claim_line_items
@@ -20,16 +22,45 @@ module Spree
20
22
 
21
23
  delegate :order, :currency, to: :claim
22
24
 
23
- # What the customer paid for the affected units, after discounts — the
24
- # ceiling for a refund on this line.
25
+ extend Spree::DisplayMoney
26
+ money_methods :refund_amount, :paid_amount, :pre_tax_amount, :included_tax_total,
27
+ :additional_tax_total, :tax_total
28
+
29
+ # What the customer paid for the affected units, after discounts and with
30
+ # the tax charged on top of the price — the ceiling for a refund on this
31
+ # line, which is entered tax included. A claim opened before claims
32
+ # carried tax has none, so its ceiling is unchanged.
33
+ #
34
+ # @return [BigDecimal]
25
35
  def paid_amount
26
- return 0 if line_item.nil? || line_item.quantity.to_i.zero?
36
+ discounted_amount + additional_tax_total
37
+ end
27
38
 
28
- (line_item.amount / line_item.quantity) * quantity.to_i
39
+ # @return [BigDecimal] the affected units before tax
40
+ def pre_tax_amount
41
+ discounted_amount - included_tax_total
29
42
  end
30
43
 
31
- def display_refund_amount
32
- Spree::Money.new(refund_amount, currency: currency)
44
+ def tax_total
45
+ included_tax_total + additional_tax_total
46
+ end
47
+
48
+ # Nothing is given back on a claim that was denied or withdrawn.
49
+ #
50
+ # @return [Integer]
51
+ def credited_quantity
52
+ claim.denied? || claim.canceled? ? 0 : quantity.to_i
53
+ end
54
+
55
+ # @return [BigDecimal]
56
+ def credited_worth
57
+ paid_amount
58
+ end
59
+
60
+ # @return [void]
61
+ def fold_tax_lines!
62
+ included, additional = tax_totals_from(tax_lines.credits.reload)
63
+ update_columns(included_tax_total: included, additional_tax_total: additional)
33
64
  end
34
65
 
35
66
  # What actually ships when the claim is resolved with a replacement.
@@ -39,6 +70,10 @@ module Spree
39
70
 
40
71
  private
41
72
 
73
+ def discounted_amount
74
+ line_item&.discounted_amount_for(quantity) || 0
75
+ end
76
+
42
77
  def set_variant_from_line_item
43
78
  self.variant ||= line_item&.variant
44
79
  end
@@ -58,7 +58,6 @@ module Spree
58
58
  #
59
59
  # Associations
60
60
  #
61
- belongs_to :store, class_name: 'Spree::Store'
62
61
 
63
62
  has_many :product_collections, class_name: 'Spree::ProductCollection', dependent: :destroy_async, inverse_of: :collection
64
63
  has_many :products, through: :product_collections
@@ -73,6 +72,7 @@ module Spree
73
72
  #
74
73
  has_one_attached :image, service: Spree.public_storage_service_name
75
74
  has_one_attached :square_image, service: Spree.public_storage_service_name
75
+ signed_id_attachments :image, :square_image
76
76
  # The slots double as media-library placements, so an upload here is
77
77
  # visible and reusable there (Spree::HasLibraryMedia).
78
78
  has_library_media :image, :square_image
@@ -102,6 +102,7 @@ module Spree
102
102
  # Ransack
103
103
  #
104
104
  self.whitelisted_ransackable_attributes = %w[permalink automatic sort_order products_count]
105
+ search_by :name
105
106
 
106
107
  #
107
108
  # Automatic (rule-based) membership