spree_core 6.0.0.beta3 → 6.0.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (495) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/app/helpers/spree/images_helper.rb +0 -57
  4. data/app/jobs/spree/base_job.rb +21 -0
  5. data/app/jobs/spree/carts/reap_expired_job.rb +2 -2
  6. data/app/jobs/spree/collections/regenerate_time_based_job.rb +6 -3
  7. data/app/jobs/spree/orders/finalize_stale_drafts_job.rb +8 -5
  8. data/app/jobs/spree/payments/cancel_unused_sessions_job.rb +61 -0
  9. data/app/jobs/spree/seller_payouts/sweep_seller_job.rb +2 -0
  10. data/app/mailers/spree/admin_user_mailer.rb +4 -19
  11. data/app/mailers/spree/base_mailer.rb +96 -13
  12. data/app/mailers/spree/email_template_mailer.rb +27 -0
  13. data/app/mailers/spree/export_mailer.rb +3 -4
  14. data/app/mailers/spree/import_mailer.rb +3 -4
  15. data/app/mailers/spree/invitation_mailer.rb +24 -7
  16. data/app/mailers/spree/seller_user_mailer.rb +4 -16
  17. data/app/mailers/spree/webhook_mailer.rb +2 -2
  18. data/app/models/concerns/spree/acted_by.rb +1 -1
  19. data/app/models/concerns/spree/admin_user_methods.rb +13 -1
  20. data/app/models/concerns/spree/api_typed.rb +91 -0
  21. data/app/models/concerns/spree/calculator/tiers.rb +76 -0
  22. data/app/models/concerns/spree/customer_methods.rb +15 -14
  23. data/app/models/concerns/spree/email_template_identity.rb +52 -0
  24. data/app/models/concerns/spree/parameterizable_name.rb +2 -0
  25. data/app/models/concerns/spree/payment_session_transitions.rb +8 -2
  26. data/app/models/concerns/spree/post_sale/taxation.rb +54 -0
  27. data/app/models/concerns/spree/post_sale/taxed_line.rb +51 -0
  28. data/app/models/concerns/spree/preference_schema.rb +4 -12
  29. data/app/models/concerns/spree/product_scopes.rb +11 -1
  30. data/app/models/concerns/spree/provider_listing.rb +41 -0
  31. data/app/models/concerns/spree/publishable.rb +82 -14
  32. data/app/models/concerns/spree/purchase/addresses.rb +23 -10
  33. data/app/models/concerns/spree/purchase/currency.rb +1 -1
  34. data/app/models/concerns/spree/purchase/deprecated_aliases.rb +87 -0
  35. data/app/models/concerns/spree/purchase/gift_cards.rb +10 -4
  36. data/app/models/concerns/spree/purchase/locale.rb +1 -1
  37. data/app/models/concerns/spree/purchase/payment_processing.rb +7 -3
  38. data/app/models/concerns/spree/purchase/purchase_order.rb +2 -2
  39. data/app/models/concerns/spree/purchase/totals.rb +49 -1
  40. data/app/models/concerns/spree/receivable.rb +1 -1
  41. data/app/models/concerns/spree/searchable.rb +0 -4
  42. data/app/models/concerns/spree/secret_preferences.rb +59 -0
  43. data/app/models/concerns/spree/single_store_resource.rb +1 -1
  44. data/app/models/concerns/spree/stores/email_branding.rb +31 -0
  45. data/app/models/concerns/spree/type_labels.rb +38 -0
  46. data/app/models/concerns/spree/typed_adjustment_line.rb +1 -1
  47. data/app/models/concerns/spree/typed_associations.rb +6 -0
  48. data/app/models/spree/address.rb +11 -1
  49. data/app/models/spree/adjusters/promotion.rb +1 -15
  50. data/app/models/spree/allowed_origin.rb +0 -1
  51. data/app/models/spree/api_key.rb +5 -6
  52. data/app/models/spree/authentication/strategies/oidc_strategy.rb +5 -5
  53. data/app/models/spree/base.rb +70 -22
  54. data/app/models/spree/calculator/flat_percent_item_total.rb +1 -3
  55. data/app/models/spree/calculator/flat_rate.rb +1 -3
  56. data/app/models/spree/calculator/flexi_rate.rb +1 -3
  57. data/app/models/spree/calculator/percent_on_line_item.rb +1 -1
  58. data/app/models/spree/calculator/price_sack.rb +1 -3
  59. data/app/models/spree/calculator/shipping/digital_delivery.rb +1 -3
  60. data/app/models/spree/calculator/shipping/flat_percent_item_total.rb +1 -3
  61. data/app/models/spree/calculator/shipping/flat_rate.rb +1 -3
  62. data/app/models/spree/calculator/shipping/flexi_rate.rb +1 -3
  63. data/app/models/spree/calculator/shipping/per_item.rb +1 -3
  64. data/app/models/spree/calculator/shipping/price_sack.rb +1 -3
  65. data/app/models/spree/calculator/tiered_flat_rate.rb +6 -23
  66. data/app/models/spree/calculator/tiered_percent.rb +7 -25
  67. data/app/models/spree/cart.rb +78 -18
  68. data/app/models/spree/channel/gating.rb +1 -1
  69. data/app/models/spree/channel.rb +4 -4
  70. data/app/models/spree/claim.rb +21 -1
  71. data/app/models/spree/claim_line_item.rb +41 -6
  72. data/app/models/spree/collection.rb +0 -1
  73. data/app/models/spree/collection_rule.rb +5 -21
  74. data/app/models/spree/commission_line.rb +1 -1
  75. data/app/models/spree/commission_rate.rb +2 -3
  76. data/app/models/spree/commission_rate_value.rb +1 -1
  77. data/app/models/spree/commission_rule.rb +4 -11
  78. data/app/models/spree/commission_rules/product_rule.rb +1 -1
  79. data/app/models/spree/company_invitation.rb +1 -0
  80. data/app/models/spree/consent_record.rb +0 -1
  81. data/app/models/spree/credit_card.rb +1 -1
  82. data/app/models/spree/current.rb +23 -0
  83. data/app/models/spree/custom_field.rb +2 -2
  84. data/app/models/spree/custom_field_definition.rb +9 -23
  85. data/app/models/spree/custom_fields/boolean.rb +1 -1
  86. data/app/models/spree/custom_fields/json.rb +1 -1
  87. data/app/models/spree/data_feed/google.rb +0 -2
  88. data/app/models/spree/data_request.rb +1 -1
  89. data/app/models/spree/delivery_method.rb +30 -13
  90. data/app/models/spree/delivery_method_rule.rb +4 -11
  91. data/app/models/spree/delivery_rate.rb +4 -5
  92. data/app/models/spree/delivery_rate_provider/base.rb +1 -0
  93. data/app/models/spree/delivery_zone.rb +0 -1
  94. data/app/models/spree/delivery_zone_member.rb +4 -4
  95. data/app/models/spree/digital_asset.rb +7 -0
  96. data/app/models/spree/digital_asset_provider/base.rb +2 -0
  97. data/app/models/spree/digital_link.rb +2 -5
  98. data/app/models/spree/discount.rb +1 -1
  99. data/app/models/spree/email_template.rb +25 -0
  100. data/app/models/spree/email_template_draft.rb +13 -0
  101. data/app/models/spree/email_template_revision.rb +17 -0
  102. data/app/models/spree/email_templates/entry.rb +118 -0
  103. data/app/models/spree/email_templates/sample_record.rb +16 -0
  104. data/app/models/spree/emails/branding.rb +80 -0
  105. data/app/models/spree/exchange.rb +15 -1
  106. data/app/models/spree/exchange_line_item.rb +121 -5
  107. data/app/models/spree/export.rb +0 -1
  108. data/app/models/spree/exports/products.rb +10 -1
  109. data/app/models/spree/exports/report.rb +1 -1
  110. data/app/models/spree/fee.rb +4 -2
  111. data/app/models/spree/fulfillment.rb +53 -27
  112. data/app/models/spree/fulfillment_changer.rb +2 -2
  113. data/app/models/spree/fulfillment_item.rb +0 -4
  114. data/app/models/spree/fulfillment_provider/base.rb +1 -0
  115. data/app/models/spree/gateway/bogus.rb +4 -0
  116. data/app/models/spree/gateway/custom_payment_source_method.rb +5 -0
  117. data/app/models/spree/gateway.rb +5 -1
  118. data/app/models/spree/gift_card.rb +12 -1
  119. data/app/models/spree/gift_card_batch.rb +0 -1
  120. data/app/models/spree/import.rb +2 -7
  121. data/app/models/spree/import_mapping.rb +1 -2
  122. data/app/models/spree/import_row.rb +2 -2
  123. data/app/models/spree/import_schemas/products.rb +1 -0
  124. data/app/models/spree/integration.rb +8 -18
  125. data/app/models/spree/invitation.rb +5 -5
  126. data/app/models/spree/line_item.rb +13 -2
  127. data/app/models/spree/locale.rb +11 -8
  128. data/app/models/spree/market.rb +1 -0
  129. data/app/models/spree/newsletter_subscriber.rb +3 -0
  130. data/app/models/spree/order.rb +56 -138
  131. data/app/models/spree/order_group.rb +11 -2
  132. data/app/models/spree/order_inventory.rb +10 -5
  133. data/app/models/spree/order_promotion.rb +1 -1
  134. data/app/models/spree/order_routing/has_strategy_preference.rb +1 -1
  135. data/app/models/spree/order_routing/strategy/base.rb +4 -2
  136. data/app/models/spree/order_routing/strategy/rules.rb +1 -1
  137. data/app/models/spree/order_routing_rule.rb +6 -22
  138. data/app/models/spree/package_type.rb +1 -2
  139. data/app/models/spree/payment/gateway_options.rb +14 -52
  140. data/app/models/spree/payment/processing.rb +2 -4
  141. data/app/models/spree/payment.rb +20 -5
  142. data/app/models/spree/payment_method/session_instrumentation.rb +1 -0
  143. data/app/models/spree/payment_method/store_credit.rb +4 -4
  144. data/app/models/spree/payment_method.rb +36 -4
  145. data/app/models/spree/payment_session.rb +5 -1
  146. data/app/models/spree/payment_setup_session.rb +1 -0
  147. data/app/models/spree/payout_provider/base.rb +3 -1
  148. data/app/models/spree/pickup_point_provider/base.rb +2 -0
  149. data/app/models/spree/price_list.rb +1 -2
  150. data/app/models/spree/price_rules/channel_rule.rb +1 -1
  151. data/app/models/spree/price_rules/customer_group_rule.rb +1 -1
  152. data/app/models/spree/product.rb +31 -7
  153. data/app/models/spree/product_submission.rb +0 -12
  154. data/app/models/spree/product_type.rb +0 -1
  155. data/app/models/spree/promotion/actions/create_line_items.rb +27 -17
  156. data/app/models/spree/promotion/rules/category.rb +0 -4
  157. data/app/models/spree/promotion/rules/channel.rb +0 -4
  158. data/app/models/spree/promotion/rules/country.rb +0 -4
  159. data/app/models/spree/promotion/rules/currency.rb +0 -4
  160. data/app/models/spree/promotion/rules/customer_group.rb +0 -4
  161. data/app/models/spree/promotion/rules/first_order.rb +0 -4
  162. data/app/models/spree/promotion/rules/item_total.rb +4 -8
  163. data/app/models/spree/promotion/rules/market.rb +0 -4
  164. data/app/models/spree/promotion/rules/one_use_per_user.rb +0 -4
  165. data/app/models/spree/promotion/rules/option_value.rb +0 -3
  166. data/app/models/spree/promotion/rules/product.rb +0 -4
  167. data/app/models/spree/promotion/rules/user.rb +0 -4
  168. data/app/models/spree/promotion/rules/user_logged_in.rb +0 -4
  169. data/app/models/spree/promotion.rb +35 -6
  170. data/app/models/spree/promotion_action.rb +7 -14
  171. data/app/models/spree/promotion_action_line_item.rb +1 -1
  172. data/app/models/spree/promotion_handler/coupon.rb +59 -27
  173. data/app/models/spree/promotion_rule.rb +7 -14
  174. data/app/models/spree/purchase_order.rb +1 -0
  175. data/app/models/spree/quantity_rule.rb +1 -1
  176. data/app/models/spree/refund.rb +28 -3
  177. data/app/models/spree/return.rb +30 -2
  178. data/app/models/spree/return_line_item.rb +65 -14
  179. data/app/models/spree/role.rb +7 -7
  180. data/app/models/spree/saved_report.rb +1 -1
  181. data/app/models/spree/seller.rb +2 -1
  182. data/app/models/spree/seller_payout.rb +7 -0
  183. data/app/models/spree/seller_requirement.rb +4 -12
  184. data/app/models/spree/seller_requirement_custom_field.rb +1 -2
  185. data/app/models/spree/seller_requirement_submission.rb +2 -2
  186. data/app/models/spree/seller_requirements/accept_terms.rb +2 -3
  187. data/app/models/spree/seller_transfer.rb +5 -0
  188. data/app/models/spree/shipping_label.rb +1 -0
  189. data/app/models/spree/stock/availability_validator.rb +2 -2
  190. data/app/models/spree/stock/inventory_unit_builder.rb +1 -1
  191. data/app/models/spree/stock/packer.rb +2 -2
  192. data/app/models/spree/stock/splitter/weight.rb +1 -1
  193. data/app/models/spree/stock_level/custom_events.rb +6 -0
  194. data/app/models/spree/stock_level.rb +1 -1
  195. data/app/models/spree/stock_movement.rb +1 -1
  196. data/app/models/spree/stock_receipt_item.rb +1 -1
  197. data/app/models/spree/stock_reservation.rb +2 -2
  198. data/app/models/spree/stock_transfer.rb +4 -2
  199. data/app/models/spree/store.rb +33 -7
  200. data/app/models/spree/store_credit.rb +8 -9
  201. data/app/models/spree/store_credit_event.rb +5 -5
  202. data/app/models/spree/tax_exemption_certificate.rb +2 -0
  203. data/app/models/spree/tax_identifier.rb +3 -1
  204. data/app/models/spree/tax_line.rb +53 -3
  205. data/app/models/spree/tax_provider/base.rb +71 -14
  206. data/app/models/spree/tax_provider/internal.rb +59 -33
  207. data/app/models/spree/tax_provider/recorded_share.rb +114 -0
  208. data/app/models/spree/tax_rate.rb +1 -2
  209. data/app/models/spree/taxonomy.rb +0 -1
  210. data/app/models/spree/variant.rb +4 -3
  211. data/app/models/spree/variant_media.rb +1 -1
  212. data/app/models/spree/webhook_endpoint.rb +10 -13
  213. data/app/models/spree/wishlist.rb +1 -6
  214. data/app/models/spree/wishlist_item/custom_events.rb +6 -0
  215. data/app/presenters/spree/csv/customer_presenter.rb +1 -1
  216. data/app/presenters/spree/csv/newsletter_subscriber_presenter.rb +1 -1
  217. data/app/presenters/spree/csv/order_line_item_presenter.rb +1 -1
  218. data/app/presenters/spree/csv/product_variant_presenter.rb +42 -12
  219. data/app/serializers/spree/emails/base_serializer.rb +14 -0
  220. data/app/serializers/spree/emails/export_serializer.rb +13 -0
  221. data/app/serializers/spree/emails/import_serializer.rb +19 -0
  222. data/app/serializers/spree/emails/invitation_serializer.rb +28 -0
  223. data/app/serializers/spree/emails/store_serializer.rb +31 -0
  224. data/app/serializers/spree/emails/user_serializer.rb +12 -0
  225. data/app/serializers/spree/emails/webhook_endpoint_serializer.rb +16 -0
  226. data/app/services/spree/carts/create.rb +8 -2
  227. data/app/services/spree/carts/destroy.rb +2 -2
  228. data/app/services/spree/carts/remove_out_of_stock_items.rb +2 -2
  229. data/app/services/spree/carts/split_by_seller.rb +1 -1
  230. data/app/services/spree/carts/update.rb +19 -5
  231. data/app/services/spree/catalogs/set_product_terms.rb +1 -1
  232. data/app/services/spree/categories/add_products.rb +13 -31
  233. data/app/services/spree/categories/remove_products.rb +12 -51
  234. data/app/services/spree/checkout/advance.rb +1 -4
  235. data/app/services/spree/collections/add_products.rb +10 -23
  236. data/app/services/spree/collections/remove_products.rb +7 -43
  237. data/app/services/spree/coupon_codes/bulk_generate.rb +8 -14
  238. data/app/services/spree/data_requests/create.rb +1 -1
  239. data/app/services/spree/deliveries/destroy.rb +2 -2
  240. data/app/services/spree/email_templates/check.rb +58 -0
  241. data/app/services/spree/email_templates/no_sample_record.rb +7 -0
  242. data/app/services/spree/email_templates/preview.rb +99 -0
  243. data/app/services/spree/emails/file_system.rb +23 -0
  244. data/app/services/spree/emails/filters.rb +84 -0
  245. data/app/services/spree/emails/liquid_context.rb +48 -0
  246. data/app/services/spree/emails/rendered_email.rb +14 -0
  247. data/app/services/spree/emails/renderer.rb +125 -0
  248. data/app/services/spree/emails/template.rb +51 -0
  249. data/app/services/spree/emails/template_data.rb +23 -0
  250. data/app/services/spree/emails/template_resolver.rb +109 -0
  251. data/app/services/spree/emails/text_converter.rb +72 -0
  252. data/app/services/spree/emails/translation_inliner.rb +134 -0
  253. data/app/services/spree/imports/row_processors/price_list_price.rb +10 -10
  254. data/app/services/spree/imports/row_processors/product_variant.rb +65 -34
  255. data/app/services/spree/imports/row_processors/purchase_order.rb +13 -13
  256. data/app/services/spree/locales.rb +4 -5
  257. data/app/services/spree/newsletter/subscribe.rb +8 -12
  258. data/app/services/spree/orders/build_fulfillments.rb +3 -2
  259. data/app/services/spree/orders/create.rb +2 -1
  260. data/app/services/spree/orders/discounts/create.rb +7 -4
  261. data/app/services/spree/orders/discounts/destroy.rb +1 -0
  262. data/app/services/spree/orders/discounts/update.rb +1 -0
  263. data/app/services/spree/orders/fees/create.rb +1 -0
  264. data/app/services/spree/orders/fees/destroy.rb +1 -0
  265. data/app/services/spree/orders/fees/update.rb +1 -0
  266. data/app/services/spree/orders/remove_line_item.rb +13 -3
  267. data/app/services/spree/orders/update.rb +10 -6
  268. data/app/services/spree/orders/update_item.rb +6 -3
  269. data/app/services/spree/orders/update_statuses.rb +3 -1
  270. data/app/services/spree/product_memberships.rb +61 -0
  271. data/app/services/spree/returns/eligibility_validator.rb +1 -1
  272. data/app/services/spree/search_provider/custom_field_schema.rb +2 -2
  273. data/app/services/spree/seeds/commission_rates.rb +1 -1
  274. data/app/services/spree/seeds/digital_delivery.rb +1 -1
  275. data/app/services/spree/seeds/payment_methods.rb +2 -2
  276. data/app/services/spree/seeds/saved_reports.rb +3 -3
  277. data/app/services/spree/sellers/requirements.rb +1 -2
  278. data/app/services/spree/shipping_labels/confirm_refund.rb +1 -1
  279. data/app/services/spree/shipping_labels/store_file.rb +3 -3
  280. data/app/services/spree/stock_levels/correct.rb +3 -3
  281. data/app/services/spree/stock_reservations/reserve.rb +2 -3
  282. data/app/services/spree/store_credits/apply.rb +15 -13
  283. data/app/services/spree/stores/provision_defaults.rb +15 -14
  284. data/app/subscribers/spree/seller_onboarding_subscriber.rb +1 -1
  285. data/app/views/layouts/spree/base_mailer.html.erb +2 -43
  286. data/app/views/layouts/spree/base_mailer.liquid +79 -0
  287. data/app/views/spree/admin_user_mailer/password_reset_email.liquid +14 -0
  288. data/app/views/spree/export_mailer/export_done.liquid +12 -0
  289. data/app/views/spree/import_mailer/import_done.liquid +15 -0
  290. data/app/views/spree/invitation_mailer/invitation_accepted.liquid +9 -0
  291. data/app/views/spree/invitation_mailer/invitation_email.liquid +13 -0
  292. data/app/views/spree/seller_user_mailer/password_reset_email.liquid +14 -0
  293. data/app/views/spree/shared/_mailer_button.html.erb +4 -5
  294. data/app/views/spree/shared/_mailer_hero.html.erb +6 -5
  295. data/app/views/spree/shared/_summary_row.liquid +8 -0
  296. data/app/views/spree/webhook_mailer/endpoint_disabled.liquid +21 -0
  297. data/app/workflows/spree/carts/add_item.rb +4 -4
  298. data/app/workflows/spree/carts/complete.rb +13 -3
  299. data/app/workflows/spree/carts/merge.rb +1 -1
  300. data/app/workflows/spree/carts/recalculate_totals.rb +5 -0
  301. data/app/workflows/spree/carts/upsert_items.rb +18 -9
  302. data/app/workflows/spree/claims/approve.rb +5 -22
  303. data/app/workflows/spree/claims/cancel.rb +5 -21
  304. data/app/workflows/spree/claims/create.rb +15 -0
  305. data/app/workflows/spree/claims/deny.rb +7 -0
  306. data/app/workflows/spree/claims/resolve.rb +34 -31
  307. data/app/workflows/spree/customers/anonymize.rb +3 -4
  308. data/app/workflows/spree/customers/create.rb +1 -1
  309. data/app/workflows/spree/data_requests/fulfill.rb +2 -2
  310. data/app/workflows/spree/deliveries/update_tracking.rb +1 -1
  311. data/app/workflows/spree/email_templates/discard_draft.rb +20 -0
  312. data/app/workflows/spree/email_templates/publish.rb +70 -0
  313. data/app/workflows/spree/email_templates/restore_revision.rb +33 -0
  314. data/app/workflows/spree/email_templates/revert.rb +29 -0
  315. data/app/workflows/spree/email_templates/save_draft.rb +85 -0
  316. data/app/workflows/spree/exchanges/approve.rb +5 -22
  317. data/app/workflows/spree/exchanges/cancel.rb +5 -21
  318. data/app/workflows/spree/exchanges/create.rb +6 -0
  319. data/app/workflows/spree/exchanges/fulfill.rb +75 -51
  320. data/app/workflows/spree/exchanges/receive.rb +6 -65
  321. data/app/workflows/spree/fulfillments/cancel.rb +1 -1
  322. data/app/workflows/spree/fulfillments/create.rb +51 -47
  323. data/app/workflows/spree/fulfillments/fulfill.rb +23 -27
  324. data/app/workflows/spree/fulfillments/mark_delivered.rb +1 -1
  325. data/app/workflows/spree/fulfillments/replacements.rb +59 -0
  326. data/app/workflows/spree/gift_cards/apply.rb +2 -2
  327. data/app/workflows/spree/orders/add_item.rb +5 -1
  328. data/app/workflows/spree/orders/cancel.rb +3 -3
  329. data/app/workflows/spree/orders/complete.rb +16 -8
  330. data/app/workflows/spree/payments/process.rb +2 -2
  331. data/app/workflows/spree/post_sale/approval.rb +35 -0
  332. data/app/workflows/spree/post_sale/cancellation.rb +44 -0
  333. data/app/workflows/spree/post_sale/receipt.rb +92 -0
  334. data/app/workflows/spree/products/nested_attributes.rb +1 -1
  335. data/app/workflows/spree/purchase_orders/cancel.rb +1 -1
  336. data/app/workflows/spree/purchase_orders/close.rb +3 -31
  337. data/app/workflows/spree/purchase_orders/mark_draft.rb +2 -2
  338. data/app/workflows/spree/purchase_orders/mark_ordered.rb +2 -2
  339. data/app/workflows/spree/purchase_orders/receive.rb +1 -1
  340. data/app/workflows/spree/purchase_orders/update.rb +5 -34
  341. data/app/workflows/spree/receivables/closing_short.rb +46 -0
  342. data/app/workflows/spree/receivables/draft_editing.rb +48 -0
  343. data/app/workflows/spree/refunds/create.rb +5 -2
  344. data/app/workflows/spree/refunds/order_payments.rb +14 -2
  345. data/app/workflows/spree/refunds/tax_credit.rb +67 -0
  346. data/app/workflows/spree/returns/approve.rb +5 -22
  347. data/app/workflows/spree/returns/cancel.rb +5 -22
  348. data/app/workflows/spree/returns/create.rb +8 -0
  349. data/app/workflows/spree/returns/receive.rb +5 -76
  350. data/app/workflows/spree/returns/refund.rb +31 -8
  351. data/app/workflows/spree/seller_requirement_submissions/create.rb +1 -2
  352. data/app/workflows/spree/seller_transfers/reverse.rb +28 -1
  353. data/app/workflows/spree/shipping_labels/purchase.rb +8 -8
  354. data/app/workflows/spree/shipping_labels/record.rb +5 -5
  355. data/app/workflows/spree/shipping_labels/refund.rb +2 -2
  356. data/app/workflows/spree/stock_receipts/recording.rb +4 -4
  357. data/app/workflows/spree/stock_transfers/cancel.rb +2 -2
  358. data/app/workflows/spree/stock_transfers/close.rb +3 -30
  359. data/app/workflows/spree/stock_transfers/mark_draft.rb +1 -1
  360. data/app/workflows/spree/stock_transfers/mark_in_transit.rb +3 -3
  361. data/app/workflows/spree/stock_transfers/mark_ready.rb +2 -2
  362. data/app/workflows/spree/stock_transfers/receive.rb +1 -1
  363. data/app/workflows/spree/stock_transfers/update.rb +5 -33
  364. data/config/i18n-tasks.yml +32 -18
  365. data/config/initializers/alba.rb +7 -0
  366. data/config/language_names.csv +264 -0
  367. data/config/locales/ar.yml +811 -0
  368. data/config/locales/be.yml +855 -0
  369. data/config/locales/bg.yml +723 -0
  370. data/config/locales/ca.yml +689 -0
  371. data/config/locales/cs.yml +855 -0
  372. data/config/locales/da.yml +771 -0
  373. data/config/locales/de-CH.yml +248 -0
  374. data/config/locales/de.yml +835 -0
  375. data/config/locales/el.yml +769 -0
  376. data/config/locales/en.yml +37 -503
  377. data/config/locales/es-CL.yml +301 -0
  378. data/config/locales/es-EC.yml +186 -0
  379. data/config/locales/es-MX.yml +334 -0
  380. data/config/locales/es.yml +831 -0
  381. data/config/locales/et.yml +644 -0
  382. data/config/locales/fa.yml +709 -0
  383. data/config/locales/fi.yml +771 -0
  384. data/config/locales/fr.yml +830 -0
  385. data/config/locales/hu.yml +751 -0
  386. data/config/locales/id.yml +756 -0
  387. data/config/locales/it.yml +833 -0
  388. data/config/locales/ja.yml +726 -0
  389. data/config/locales/km.yml +759 -0
  390. data/config/locales/ko.yml +575 -0
  391. data/config/locales/ku.yml +753 -0
  392. data/config/locales/lt.yml +821 -0
  393. data/config/locales/lv.yml +685 -0
  394. data/config/locales/nb.yml +824 -0
  395. data/config/locales/ne.yml +823 -0
  396. data/config/locales/nl.yml +826 -0
  397. data/config/locales/pl.yml +835 -0
  398. data/config/locales/pt-BR.yml +547 -0
  399. data/config/locales/pt.yml +636 -0
  400. data/config/locales/ro.yml +750 -0
  401. data/config/locales/ru.yml +834 -0
  402. data/config/locales/sk.yml +680 -0
  403. data/config/locales/sl-SI.yml +600 -0
  404. data/config/locales/sv.yml +827 -0
  405. data/config/locales/th.yml +745 -0
  406. data/config/locales/tr.yml +777 -0
  407. data/config/locales/uk.yml +836 -0
  408. data/config/locales/vi.yml +768 -0
  409. data/config/locales/zh-CN.yml +786 -0
  410. data/config/locales/zh-TW.yml +834 -0
  411. data/db/migrate/20260925000001_add_replacement_to_spree_fulfillment_items.rb +5 -0
  412. data/db/migrate/20261001000001_add_post_sale_tax.rb +30 -0
  413. data/db/migrate/20261001000001_convert_preferences_to_json.rb +166 -0
  414. data/db/migrate/20261001000001_create_spree_email_templates.rb +52 -0
  415. data/lib/generators/spree/dummy/dummy_generator.rb +0 -9
  416. data/lib/generators/spree/dummy/templates/rails/test.rb +7 -0
  417. data/lib/spree/checkout/default_requirements.rb +37 -13
  418. data/lib/spree/core/configuration.rb +1 -1
  419. data/lib/spree/core/controller_helpers/store.rb +1 -1
  420. data/lib/spree/core/dependencies.rb +5 -4
  421. data/lib/spree/core/emails/escaped_output.rb +60 -0
  422. data/lib/spree/core/engine.rb +48 -167
  423. data/lib/spree/core/permissions/default_catalog.rb +5 -0
  424. data/lib/spree/core/preferences/json_conversion.rb +182 -0
  425. data/lib/spree/core/preferences/masking.rb +4 -1
  426. data/lib/spree/core/preferences/preferable.rb +137 -83
  427. data/lib/spree/core/preferences/preferable_class_methods.rb +81 -146
  428. data/lib/spree/core/previews/import_preview.rb +10 -0
  429. data/lib/spree/core/tax/provider_error.rb +37 -0
  430. data/lib/spree/core/version.rb +1 -1
  431. data/lib/spree/core.rb +85 -370
  432. data/lib/spree/emails/editable_templates.rb +82 -0
  433. data/lib/spree/events/catalog.rb +189 -0
  434. data/lib/spree/events.rb +21 -1
  435. data/lib/spree/hooks.rb +0 -17
  436. data/lib/spree/i18n.rb +32 -15
  437. data/lib/spree/reporting/adapters/live.rb +6 -6
  438. data/lib/spree/reporting/default_vocabulary.rb +22 -10
  439. data/lib/spree/reporting/hydration.rb +1 -1
  440. data/lib/spree/reporting/query.rb +8 -0
  441. data/lib/spree/reporting/schema.rb +10 -10
  442. data/lib/spree/testing_support/editable_email_templates.rb +61 -0
  443. data/lib/spree/testing_support/emails.rb +22 -0
  444. data/lib/spree/testing_support/factories/cart_factory.rb +1 -1
  445. data/lib/spree/testing_support/factories/email_template_factory.rb +28 -0
  446. data/lib/spree/testing_support/factories/order_factory.rb +2 -2
  447. data/lib/spree/testing_support/factories/refund_factory.rb +1 -1
  448. data/lib/spree/testing_support/factories/stock_factory.rb +1 -1
  449. data/lib/spree/testing_support/factories/variant_factory.rb +1 -1
  450. data/lib/spree/testing_support/i18n.rb +3 -94
  451. data/lib/spree/testing_support/lifecycle_events.rb +1 -1
  452. data/lib/spree/testing_support/order_walkthrough.rb +1 -1
  453. data/lib/spree/testing_support/preferences.rb +0 -5
  454. data/lib/spree/testing_support/store.rb +9 -1
  455. data/lib/spree/upgrades/5_6_to_6_0/manifest.yml +45 -0
  456. data/lib/tasks/categories_and_collections.rake +3 -3
  457. data/lib/tasks/core.rake +6 -4
  458. data/lib/tasks/media.rake +3 -3
  459. data/lib/tasks/package_types.rake +1 -1
  460. data/lib/tasks/preferences_json.rake +101 -0
  461. data/spec/fixtures/mailer_views/custom_views_probe_mailer/welcome_email.html.erb +4 -0
  462. metadata +188 -44
  463. data/app/models/acts_as_taggable_on/tag_decorator.rb +0 -10
  464. data/app/models/concerns/spree/has_image_alt_text.rb +0 -18
  465. data/app/models/spree/base_analytics_event_handler.rb +0 -54
  466. data/app/models/spree/invitations/store.rb +0 -6
  467. data/app/models/spree/preference.rb +0 -14
  468. data/app/models/spree/promotion_handler/page.rb +0 -25
  469. data/app/models/spree/promotion_handler/promotion_duplicator.rb +0 -60
  470. data/app/models/spree/stock/differentiator.rb +0 -45
  471. data/app/presenters/spree/filters/options_presenter.rb +0 -47
  472. data/app/services/spree/carts/empty.rb +0 -42
  473. data/app/services/spree/country_to_timezone.rb +0 -273
  474. data/app/services/spree/payments/create.rb +0 -92
  475. data/app/validators/spree/url_validator.rb +0 -23
  476. data/app/views/active_storage/blobs/_blob.html.erb +0 -14
  477. data/app/views/spree/admin_user_mailer/password_reset_email.html.erb +0 -8
  478. data/app/views/spree/export_mailer/export_done.html.erb +0 -7
  479. data/app/views/spree/import_mailer/import_done.html.erb +0 -12
  480. data/app/views/spree/invitation_mailer/invitation_accepted.html.erb +0 -4
  481. data/app/views/spree/invitation_mailer/invitation_email.html.erb +0 -12
  482. data/app/views/spree/seller_user_mailer/password_reset_email.html.erb +0 -8
  483. data/app/views/spree/shared/_base_mailer_footer.html.erb +0 -19
  484. data/app/views/spree/shared/_base_mailer_header.html.erb +0 -8
  485. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +0 -399
  486. data/app/views/spree/shared/_mailer_logo.html.erb +0 -41
  487. data/app/views/spree/shared/_purchased_items_styles.html.erb +0 -119
  488. data/app/views/spree/webhook_mailer/endpoint_disabled.html.erb +0 -51
  489. data/app/views/spree/webhook_mailer/endpoint_disabled.text.erb +0 -10
  490. data/app/workflows/spree/fulfillments/purchase_label.rb +0 -40
  491. data/app/workflows/spree/returns/purchase_label.rb +0 -32
  492. data/lib/spree/analytics.rb +0 -11
  493. data/lib/spree/core/preferences/scoped_store.rb +0 -34
  494. data/lib/spree/core/preferences/store.rb +0 -105
  495. data/lib/spree/translation_migrations.rb +0 -52
@@ -10,40 +10,19 @@ module Spree
10
10
 
11
11
  attr_reader :payment, :order
12
12
  delegate :currency, to: :payment
13
- delegate :email, to: :order
14
-
15
- def statement_descriptor_suffix
16
- order.number
17
- end
18
-
19
- def customer
20
- order.email
21
- end
22
-
23
- def customer_id
24
- order.customer_id
25
- end
26
-
27
- def ip
28
- order.last_ip_address
29
- end
30
-
31
- # The payment number already names its order (`R1001-P1`), so this is
32
- # the payment number alone rather than the two concatenated.
33
- def order_id
34
- payment.number
35
- end
36
-
37
- def payment_id
38
- payment.number
39
- end
40
-
13
+ delegate :email, :customer_id, to: :order
41
14
  # Stable handle for a gateway to find its own payment back. The derived
42
15
  # number cannot be queried (its column is NULL on 6.0 rows) and shifts
43
16
  # if an earlier sibling is destroyed — nothing durable may key on it.
44
- def payment_prefixed_id
45
- payment.prefixed_id
46
- end
17
+ delegate :prefixed_id, to: :payment, prefix: true
18
+
19
+ def statement_descriptor_suffix = order.number
20
+ def customer = order.email
21
+ def ip = order.last_ip_address
22
+ # The payment number already names its order (`R1001-P1`), so this is
23
+ # the payment number alone rather than the two concatenated.
24
+ def order_id = payment.number
25
+ def payment_id = payment.number
47
26
 
48
27
  # Built on the prefixed ID, not the number: a derived number shifts if
49
28
  # an earlier sibling payment is destroyed, and a shifted idempotency
@@ -53,7 +32,7 @@ module Spree
53
32
  end
54
33
 
55
34
  def shipping
56
- order.ship_total * exchange_multiplier
35
+ order.delivery_total * exchange_multiplier
57
36
  end
58
37
 
59
38
  def tax
@@ -77,29 +56,12 @@ module Spree
77
56
  end
78
57
 
79
58
  def hash_methods
80
- [
81
- :email,
82
- :customer,
83
- :customer_id,
84
- :ip,
85
- :order_id,
86
- :payment_id,
87
- :payment_prefixed_id,
88
- :idempotency_key,
89
- :shipping,
90
- :tax,
91
- :subtotal,
92
- :discount,
93
- :currency,
94
- :billing_address,
95
- :shipping_address
96
- ]
59
+ %i[email customer customer_id ip order_id payment_id payment_prefixed_id idempotency_key shipping tax
60
+ subtotal discount currency billing_address shipping_address]
97
61
  end
98
62
 
99
63
  def to_hash
100
- Hash[hash_methods.map do |method|
101
- [method, send(method)]
102
- end]
64
+ hash_methods.index_with { |method| send(method) }
103
65
  end
104
66
 
105
67
  private
@@ -1,5 +1,3 @@
1
- require_dependency 'spree/payment/gateway_options'
2
-
3
1
  module Spree
4
2
  class Payment < Spree.base_class
5
3
  # Gateway mechanics shared by the payments workflows and the payment
@@ -144,11 +142,11 @@ module Spree
144
142
  text = if error.is_a? Spree::PaymentResponse
145
143
  error.params['message'] || error.params['response_reason_text'] || error.message
146
144
  elsif error.is_a? Spree::PaymentConnectionError
147
- Spree.t(:unable_to_connect_to_gateway)
145
+ I18n.t('spree.unable_to_connect_to_gateway')
148
146
  else
149
147
  error.to_s
150
148
  end
151
- Rails.logger.error(Spree.t(:gateway_error))
149
+ Rails.logger.error(I18n.t('spree.gateway_error'))
152
150
  Rails.logger.error(" #{error.to_yaml}")
153
151
  raise Core::GatewayError, text
154
152
  end
@@ -1,5 +1,3 @@
1
- require_dependency 'spree/payment/processing'
2
-
3
1
  module Spree
4
2
  class Payment < Spree.base_class
5
3
  has_prefix_id :py # Stripe: py_
@@ -18,6 +16,7 @@ module Spree
18
16
  include Spree::Payment::CustomEvents
19
17
 
20
18
  publishes_lifecycle_events
19
+ publishes_events :captured, :completed, :paid, :refunded, :voided
21
20
 
22
21
  NON_RISKY_AVS_CODES = ['B', 'D', 'H', 'J', 'M', 'Q', 'T', 'V', 'X', 'Y'].freeze
23
22
  RISKY_AVS_CODES = ['A', 'C', 'E', 'F', 'G', 'I', 'K', 'L', 'N', 'O', 'P', 'R', 'S', 'U', 'W', 'Z'].freeze
@@ -244,6 +243,9 @@ module Spree
244
243
  return amount if owner.nil?
245
244
 
246
245
  amount_from_order = owner.total - owner.payment_total
246
+ # A refund for returned goods settled what was owed for them; without
247
+ # this the order would accept a fresh charge for goods it took back.
248
+ amount_from_order -= owner.returned_items_refund_total if owner.respond_to?(:returned_items_refund_total)
247
249
 
248
250
  if payment_method&.store_credit?
249
251
  store_credits = owner.available_store_credits
@@ -263,6 +265,19 @@ module Spree
263
265
  end || amount
264
266
  end
265
267
 
268
+ # Resizes a store credit payment that has not been taken yet. Completion
269
+ # takes it against the eligibility event recorded for its amount, so the
270
+ # new amount needs one of its own.
271
+ #
272
+ # @param new_amount [BigDecimal]
273
+ # @return [void]
274
+ def update_store_credit_amount!(new_amount)
275
+ return if amount == new_amount
276
+
277
+ update_column(:amount, new_amount)
278
+ create_eligible_credit_event
279
+ end
280
+
266
281
  def offsets_total
267
282
  offsets.sum(:amount)
268
283
  end
@@ -502,7 +517,7 @@ module Spree
502
517
  end
503
518
 
504
519
  def exactly_one_owner
505
- errors.add(:base, :exactly_one_of_cart_or_order, message: Spree.t('errors.messages.exactly_one_of_cart_or_order')) unless [order, cart, order_group].compact.one?
520
+ errors.add(:base, :exactly_one_of_cart_or_order, message: I18n.t('spree.errors.messages.exactly_one_of_cart_or_order')) unless [order, cart, order_group].compact.one?
506
521
  end
507
522
 
508
523
  def set_amount
@@ -532,8 +547,8 @@ module Spree
532
547
  end
533
548
 
534
549
  def add_source_error(field, message)
535
- field_name = I18n.t("activerecord.attributes.#{source.class.to_s.underscore}.#{field}")
536
- errors.add(Spree.t(source.class.to_s.demodulize.underscore), "#{field_name} #{message}")
550
+ field_name = source.class.human_attribute_name(field)
551
+ errors.add(source.class.model_name.human, "#{field_name} #{message}")
537
552
  end
538
553
 
539
554
 
@@ -16,6 +16,7 @@ module Spree
16
16
  create_payment_session
17
17
  update_payment_session
18
18
  complete_payment_session
19
+ cancel_payment_session
19
20
  create_payment_setup_session
20
21
  complete_payment_setup_session
21
22
  ].freeze
@@ -22,7 +22,7 @@ module Spree
22
22
 
23
23
  def authorize(amount_in_cents, store_credit, gateway_options = {})
24
24
  if store_credit.nil?
25
- Spree::PaymentResponse.new(false, Spree.t('store_credit_payment_method.unable_to_find'), {}, {})
25
+ Spree::PaymentResponse.new(false, I18n.t('spree.store_credit_payment_method.unable_to_find'), {}, {})
26
26
  else
27
27
  action = lambda do |store_credit|
28
28
  store_credit.authorize(
@@ -58,7 +58,7 @@ module Spree
58
58
  end
59
59
 
60
60
  if event.blank?
61
- Spree::PaymentResponse.new(false, Spree.t('store_credit_payment_method.unable_to_find'), {}, {})
61
+ Spree::PaymentResponse.new(false, I18n.t('spree.store_credit_payment_method.unable_to_find'), {}, {})
62
62
  else
63
63
  capture(amount_in_cents, event.authorization_code, gateway_options)
64
64
  end
@@ -115,7 +115,7 @@ module Spree
115
115
  # note that we only need to return the auth code on an 'auth', but it's innocuous to always return
116
116
  Spree::PaymentResponse.new(
117
117
  true,
118
- Spree.t('store_credit_payment_method.successful_action', action: action_name),
118
+ I18n.t('spree.store_credit_payment_method.successful_action', action: action_name),
119
119
  {},
120
120
  authorization: auth_code || response
121
121
  )
@@ -132,7 +132,7 @@ module Spree
132
132
  if store_credit.nil?
133
133
  Spree::PaymentResponse.new(
134
134
  false,
135
- Spree.t('store_credit_payment_method.unable_to_find_for_action', auth_code: auth_code, action: action_name),
135
+ I18n.t('spree.store_credit_payment_method.unable_to_find_for_action', auth_code: auth_code, action: action_name),
136
136
  {},
137
137
  {}
138
138
  )
@@ -13,7 +13,9 @@ module Spree
13
13
  include Spree::StorePreferences
14
14
  include Spree::HasCustomFields
15
15
  include Spree::Metadata
16
+ include Spree::SecretPreferences
16
17
  include Spree::CaptureMethod
18
+ include Spree::ProviderListing
17
19
  if defined?(Spree::Security::PaymentMethods)
18
20
  include Spree::Security::PaymentMethods
19
21
  end
@@ -57,7 +59,6 @@ module Spree
57
59
  validates :storefront_visible, inclusion: { in: [true, false] }
58
60
  normalizes :name, with: ->(value) { value&.to_s&.squish&.presence }
59
61
 
60
- belongs_to :store, class_name: 'Spree::Store'
61
62
 
62
63
  has_many :payments, class_name: 'Spree::Payment', inverse_of: :payment_method, dependent: :nullify
63
64
  has_many :credit_cards, class_name: 'Spree::CreditCard', dependent: :destroy # CCs are soft deleted
@@ -74,6 +75,20 @@ module Spree
74
75
  # `providers`; declaring it keeps subclass resolution to a single rule.
75
76
  registers_subclasses_via { providers }
76
77
 
78
+ # Whether this method is backed by an external payment provider (Stripe,
79
+ # Adyen…) rather than handled by the store itself (check, store credit).
80
+ # Only third-party methods are listed under Settings → Integrations.
81
+ #
82
+ # @return [Boolean]
83
+ def self.third_party?
84
+ false
85
+ end
86
+
87
+ # @return [Hash] the listing attributes, plus whether the method is third-party
88
+ def self.provider_listing
89
+ super.merge(third_party: third_party?)
90
+ end
91
+
77
92
  def provider_class
78
93
  raise ::NotImplementedError, 'You must implement provider_class method for this gateway.'
79
94
  end
@@ -136,6 +151,25 @@ module Spree
136
151
  raise ::NotImplementedError, 'You must implement complete_payment_session method for this gateway.'
137
152
  end
138
153
 
154
+ # Cancels a session nobody is going to complete, so the provider can no
155
+ # longer take money against it. Called for the sessions left pending when
156
+ # an order is placed. The default leaves the session untouched: a
157
+ # gateway that cannot cancel at the provider must not report as canceled
158
+ # a session that can still be paid.
159
+ #
160
+ # Raise Spree::Core::GatewayError only for a refusal, such as a session
161
+ # that was already paid. A timeout, a rate limit, an outage, or rejected
162
+ # credentials is raised as itself. Callers that treat those as a refusal
163
+ # leave a session the buyer can still pay.
164
+ #
165
+ # @param payment_session [Spree::PaymentSession]
166
+ # @return [Boolean] whether the session was canceled
167
+ # @raise [Spree::Core::GatewayError] when the provider refuses, e.g. the
168
+ # session has already been paid
169
+ def cancel_payment_session(payment_session:)
170
+ false
171
+ end
172
+
139
173
  # Parses an incoming webhook payload from the payment provider.
140
174
  # Override in gateway subclasses to implement provider-specific webhook parsing.
141
175
  #
@@ -284,9 +318,7 @@ module Spree
284
318
  end
285
319
 
286
320
  def public_preferences
287
- public_preference_keys.each_with_object({}) do |key, hash|
288
- hash[key] = preferences[key]
289
- end
321
+ public_preference_keys.index_with { |key| get_preference(key) }
290
322
  end
291
323
 
292
324
  # @deprecated Use {#storefront_visible?}; removed in 6.1.
@@ -9,6 +9,7 @@ module Spree
9
9
  self.event_prefix = 'payment_session'
10
10
 
11
11
  publishes_lifecycle_events
12
+ publishes_events :canceled, :completed, :expired, :failed, :processing
12
13
 
13
14
  belongs_to :order, class_name: 'Spree::Order', optional: true
14
15
  belongs_to :cart, class_name: 'Spree::Cart', optional: true, inverse_of: :payment_sessions
@@ -28,6 +29,9 @@ module Spree
28
29
 
29
30
  scope :not_expired, -> { where('expires_at IS NULL OR expires_at > ?', Time.current) }
30
31
  scope :active, -> { not_expired.where(status: %w[pending processing]) }
32
+ # A declined confirm marks the session failed while its intent can still
33
+ # be paid, so placement has to cancel those too.
34
+ scope :unused, -> { where(status: %w[pending failed]).where.missing(:payment) }
31
35
 
32
36
  before_validation :set_defaults_from_order, on: :create
33
37
 
@@ -195,7 +199,7 @@ module Spree
195
199
  end
196
200
 
197
201
  def exactly_one_owner
198
- errors.add(:base, :exactly_one_of_cart_or_order, message: Spree.t('errors.messages.exactly_one_of_cart_or_order')) unless [order, cart].compact.one?
202
+ errors.add(:base, :exactly_one_of_cart_or_order, message: I18n.t('spree.errors.messages.exactly_one_of_cart_or_order')) unless [order, cart].compact.one?
199
203
  end
200
204
 
201
205
  def publish_processing_event
@@ -9,6 +9,7 @@ module Spree
9
9
  self.event_prefix = 'payment_setup_session'
10
10
 
11
11
  publishes_lifecycle_events
12
+ publishes_events :canceled, :completed, :expired, :failed, :processing
12
13
 
13
14
  belongs_to :customer, class_name: Spree.customer_class.to_s, optional: true
14
15
  belongs_to :payment_method, class_name: 'Spree::PaymentMethod'
@@ -22,6 +22,8 @@ module Spree
22
22
  # delivery-rate providers are; anything request-specific arrives as an
23
23
  # argument.
24
24
  class Base
25
+ extend Spree::ApiTyped
26
+
25
27
  # The name a merchant sees when choosing how sellers are paid.
26
28
  #
27
29
  # @return [String]
@@ -57,7 +59,7 @@ module Spree
57
59
  # @return [Hash]
58
60
  def self.to_api_hash(store)
59
61
  {
60
- id: name,
62
+ id: api_type,
61
63
  name: display_name,
62
64
  available: available_for_store?(store),
63
65
  requires_payout_account: requires_payout_account?,
@@ -10,6 +10,8 @@ module Spree
10
10
  # integrations) and +find_nearby+ gains +zipcode:+/+query:+ params.
11
11
  # Build third-party providers against 6.1, not this shape.
12
12
  class Base
13
+ extend Spree::ApiTyped
14
+
13
15
  # @return [Array<Spree::PickupPointOption>]
14
16
  def find_nearby(latitude:, longitude:, limit: 20)
15
17
  raise NotImplementedError, "Please implement 'find_nearby' in your pickup point provider: #{self.class.name}"
@@ -10,7 +10,6 @@ module Spree
10
10
 
11
11
  MATCH_POLICIES = %w[all any].freeze
12
12
 
13
- belongs_to :store, class_name: 'Spree::Store'
14
13
  # The owning agreement, or nil for a standalone list. An owned list is
15
14
  # reached only through its catalog — generic rule matching skips it in
16
15
  # SQL (see .for_context) — so a deactivated catalog's list goes dormant
@@ -155,7 +154,7 @@ module Spree
155
154
  end
156
155
 
157
156
  def self.match_policies
158
- MATCH_POLICIES.map { |key| [Spree.t(key), key] }
157
+ MATCH_POLICIES.map { |key| [I18n.t("spree.#{key}"), key] }
159
158
  end
160
159
 
161
160
  # Returns true if the price list is applicable to the context
@@ -32,7 +32,7 @@ module Spree
32
32
  end
33
33
 
34
34
  def self.description
35
- Spree.t('price_rules.channel_rule.description')
35
+ I18n.t('spree.price_rules.channel_rule.description')
36
36
  end
37
37
  end
38
38
  end
@@ -33,7 +33,7 @@ module Spree
33
33
  end
34
34
 
35
35
  def self.description
36
- Spree.t('price_rules.customer_group_rule.description')
36
+ I18n.t('spree.price_rules.customer_group_rule.description')
37
37
  end
38
38
 
39
39
  def self.superseded?
@@ -38,6 +38,7 @@ module Spree
38
38
  include Spree::StorePreferences
39
39
 
40
40
  publishes_lifecycle_events
41
+ publishes_events :activated, :approved, :archived, :back_in_stock, :drafted, :out_of_stock, :proposed, :rejected
41
42
 
42
43
  MEMOIZED_METHODS = %w[total_on_hand category_and_ancestors
43
44
  default_variant_id tax_category default_variant variant_for_images
@@ -302,9 +303,6 @@ module Spree
302
303
  where(id: (product_ids + custom_field_ids).uniq.compact)
303
304
  }
304
305
 
305
- # Backward compatibility alias — remove in Spree 6.0
306
- scope :multi_search, ->(*args) { search(*args) }
307
-
308
306
  scope :archivable, -> { where(status: %w[active draft]) }
309
307
  scope :by_source, ->(source) { send(source) }
310
308
  scope :published, -> { where(status: 'active') }
@@ -375,7 +373,7 @@ module Spree
375
373
  self.storefront_ransackable_associations = %w[tags categories collections]
376
374
  self.whitelisted_ransackable_scopes = %w[not_discontinued search_by_name in_taxon in_category in_categories in_collection price_between
377
375
  price_lte price_gte
378
- search multi_search in_stock out_of_stock with_option_value_ids
376
+ search in_stock out_of_stock with_option_value_ids
379
377
 
380
378
  ascend_by_price descend_by_price]
381
379
 
@@ -693,7 +691,7 @@ module Spree
693
691
  # variant), reading preloaded associations to avoid an N+1.
694
692
  # @return [ActiveSupport::TimeWithZone, nil]
695
693
  def preorder_ships_at
696
- candidates = has_variants? ? variants : [master]
694
+ candidates = has_variants? ? variants : [default_variant]
697
695
  candidates.select(&:preorder?).filter_map(&:preorder_ships_at).max
698
696
  end
699
697
 
@@ -817,7 +815,13 @@ module Spree
817
815
  Spree::Products::AutoMatchCollectionsJob.set(wait: 30.seconds).perform_later(id)
818
816
  end
819
817
 
820
- def to_csv(store = nil)
818
+ # @param seller [Spree::Seller, nil] the seller exporting their own
819
+ # products, whose rows cover only their own locations; nil for the
820
+ # operator, whose rows cover the marketplace's and the product's seller's
821
+ # @param default_stock_location [Spree::StockLocation, nil] the location
822
+ # the full rows carry; further locations get stock-only rows. The
823
+ # store's default when nil
824
+ def to_csv(store = nil, seller: nil, default_stock_location: nil)
821
825
  store ||= self.store
822
826
  properties_for_csv = if respond_to?(:product_properties) && Spree::Config.respond_to?(:product_properties_enabled) && Spree::Config[:product_properties_enabled]
823
827
  Spree::Property.order(:position).flat_map do |property|
@@ -840,11 +844,12 @@ module Spree
840
844
  all_variants = variants.to_a
841
845
  default_currency = store.default_currency
842
846
  additional_currencies = store.supported_currencies_list.map(&:iso_code) - [default_currency]
847
+ default_stock_location ||= store.default_stock_location
843
848
 
844
849
  # Primary rows in the store's default currency
845
850
  all_variants.each_with_index do |variant, index|
846
851
  csv_lines << Spree::CSV::ProductVariantPresenter.new(self, variant, index, properties_for_csv, categories_for_csv, store,
847
- custom_field_values).call
852
+ custom_field_values, default_stock_location: default_stock_location).call
848
853
  end
849
854
 
850
855
  # Price-only rows for each additional currency
@@ -857,6 +862,25 @@ module Spree
857
862
  end
858
863
  end
859
864
 
865
+ # Stock-only rows for every other location the importer can name back. A
866
+ # row the import cannot route to its variant is left out: with no SKU it
867
+ # would land on the default variant.
868
+ location_owner_ids = seller ? [seller.id] : [nil, seller_id].uniq
869
+ all_variants.each do |variant|
870
+ next unless variant.should_track_inventory?
871
+ next if variant.sku.blank? && variant != default_variant
872
+
873
+ variant.stock_levels.each do |stock_level|
874
+ location = stock_level.stock_location
875
+ next if location.nil? || location == default_stock_location
876
+ next unless location.store_id == store.id && location_owner_ids.include?(location.seller_id)
877
+ next if stock_level.count_on_hand.zero?
878
+
879
+ csv_lines << Spree::CSV::ProductVariantPresenter.new(self, variant, 0, [], [], store,
880
+ stock_level: stock_level).call
881
+ end
882
+ end
883
+
860
884
  csv_lines
861
885
  end
862
886
 
@@ -56,17 +56,5 @@ module Spree
56
56
  def store
57
57
  product&.store
58
58
  end
59
-
60
- # Named explicitly rather than left to the `Spree::Api::V3::` convention:
61
- # a serializer at that level is picked up by the Store API's generated
62
- # types and OpenAPI document, and a review decision is not something the
63
- # storefront has any business describing. The seller's own view is the
64
- # right shape for an event — it already withholds who decided, which a
65
- # webhook payload leaving the marketplace should not carry.
66
- #
67
- # @return [Class]
68
- def event_serializer_class
69
- Spree::Api::V3::Seller::ProductSubmissionSerializer
70
- end
71
59
  end
72
60
  end
@@ -10,7 +10,6 @@ module Spree
10
10
  TRANSLATABLE_FIELDS = %i[name].freeze
11
11
  translates(*TRANSLATABLE_FIELDS, column_fallback: Spree.mobility_column_fallback)
12
12
 
13
- belongs_to :store, class_name: 'Spree::Store'
14
13
  # Creation-time template only: stamped onto the product when it is
15
14
  # created with this type, never managed through it afterwards — the same
16
15
  # doctrine as every other type-driven attribute. Nil means the store's
@@ -104,6 +104,31 @@ module Spree
104
104
  quantifier.can_supply? quantity
105
105
  end
106
106
 
107
+ # Whether the promotion gives its gift on this order, judged with other
108
+ # promotions' gifts still counting toward its own rules — a single
109
+ # level, so two gift promotions measuring each other cannot loop.
110
+ #
111
+ # @param order [Spree::Order, Spree::Cart]
112
+ # @return [Boolean]
113
+ def gives_away?(order)
114
+ promotion.eligible?(order, own_gift_only: true) && qualifies_beyond_the_gift?(order, own_gift_only: true)
115
+ end
116
+
117
+ # How many units of this line the promotion pays for — never more than
118
+ # the line holds, so a shopper buying three of a variant gifted once
119
+ # still pays for two. `quantity` is nullable and written by `upsert_all`,
120
+ # which skips validation, so a missing or negative one gifts nothing
121
+ # rather than raising or turning the discount into a surcharge.
122
+ #
123
+ # @param line_item [Spree::LineItem]
124
+ # @return [Integer]
125
+ def gifted_quantity_of(line_item)
126
+ gifted = promotion_action_line_items.detect { |item| item.variant_id == line_item.variant_id }
127
+ return 0 if gifted.nil?
128
+
129
+ [[line_item.quantity, gifted.quantity.to_i].min, 0].max
130
+ end
131
+
107
132
  private
108
133
 
109
134
  # Whether this line holds a variant the promotion gives away.
@@ -117,30 +142,15 @@ module Spree
117
142
  # to hold a line the rules count with units this action is not already
118
143
  # covering. A promotion with no rules qualifies on anything, so there is
119
144
  # nothing for the gift to stand in for.
120
- def qualifies_beyond_the_gift?(order)
145
+ def qualifies_beyond_the_gift?(order, options = {})
121
146
  return true if promotion.promotion_rules.empty?
122
147
 
123
148
  order.line_items.any? do |line_item|
124
- promotion.line_item_actionable?(order, line_item) &&
149
+ promotion.line_item_actionable?(order, line_item, options) &&
125
150
  gifted_quantity_of(line_item) < line_item.quantity
126
151
  end
127
152
  end
128
153
 
129
- # How many units of this line the promotion pays for — never more than
130
- # the line holds, so a shopper buying three of a variant gifted once
131
- # still pays for two. `quantity` is nullable and written by `upsert_all`,
132
- # which skips validation, so a missing or negative one gifts nothing
133
- # rather than raising or turning the discount into a surcharge.
134
- #
135
- # @param line_item [Spree::LineItem]
136
- # @return [Integer]
137
- def gifted_quantity_of(line_item)
138
- gifted = promotion_action_line_items.detect { |item| item.variant_id == line_item.variant_id }
139
- return 0 if gifted.nil?
140
-
141
- [[line_item.quantity, gifted.quantity.to_i].min, 0].max
142
- end
143
-
144
154
  def add_missing_line_items(order)
145
155
  added_results = promotion_action_line_items.map do |item|
146
156
  missing = item.quantity.to_i - order.quantity_of(item.variant)
@@ -47,10 +47,6 @@ module Spree
47
47
  #
48
48
  after_save :add_categories
49
49
 
50
- def applicable?(promotable)
51
- promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
52
- end
53
-
54
50
  def eligible_category_ids
55
51
  @eligible_category_ids ||= promotion_rule_categories.pluck(:category_id)
56
52
  end
@@ -14,10 +14,6 @@ module Spree
14
14
  scope: ->(rule) { rule.promotion.store.channels }
15
15
  )
16
16
 
17
- def applicable?(promotable)
18
- promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
19
- end
20
-
21
17
  def channels
22
18
  return Spree::Channel.none if preferred_channel_ids.blank?
23
19
 
@@ -15,10 +15,6 @@ module Spree
15
15
  preference :country_id, :integer # legacy single-country shortcut
16
16
  preference :country_code, :string # legacy ISO-based shortcut
17
17
 
18
- def applicable?(promotable)
19
- promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
20
- end
21
-
22
18
  def countries
23
19
  isos = configured_country_codes.presence || [configured_country_code].compact_blank
24
20
  isos = [legacy_country_code(preferred_country_id)].compact_blank if isos.blank? && preferred_country_id.present?
@@ -5,10 +5,6 @@ module Spree
5
5
  class Currency < Spree::PromotionRule
6
6
  preference :currency, :string
7
7
 
8
- def applicable?(promotable)
9
- promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
10
- end
11
-
12
8
  def eligible?(order, options = {})
13
9
  return true if order.currency == preferred_currency
14
10
 
@@ -15,10 +15,6 @@ module Spree
15
15
  scope: ->(rule) { rule.promotion.store.customer_groups }
16
16
  )
17
17
 
18
- def applicable?(promotable)
19
- promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
20
- end
21
-
22
18
  def customer_groups
23
19
  return Spree::CustomerGroup.none if preferred_customer_group_ids.blank?
24
20
 
@@ -4,10 +4,6 @@ module Spree
4
4
  class FirstOrder < Spree::PromotionRule
5
5
  attr_reader :user, :email, :store
6
6
 
7
- def applicable?(promotable)
8
- promotable.is_a?(Spree::Order) || promotable.is_a?(Spree::Cart)
9
- end
10
-
11
7
  def eligible?(order, options = {})
12
8
  @user = order.try(:customer) || options[:user]
13
9
  @email = if options[:email].present?