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
@@ -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,35 +56,18 @@ 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
106
68
 
107
69
  def exchange_multiplier
108
- payment.payment_method.try(:exchange_multiplier) || 1.0
70
+ payment.payment_method.try(:exchange_multiplier) || 1
109
71
  end
110
72
  end
111
73
  end
@@ -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_
@@ -8,6 +6,9 @@ module Spree
8
6
  derives_number infix: 'P'
9
7
  include Spree::HasCustomFields
10
8
  include Spree::Metadata
9
+ include Spree::CanonicalDecimals
10
+
11
+ canonical_decimals :amount
11
12
  if defined?(Spree::Security::Payments)
12
13
  include Spree::Security::Payments
13
14
  end
@@ -18,6 +19,7 @@ module Spree
18
19
  include Spree::Payment::CustomEvents
19
20
 
20
21
  publishes_lifecycle_events
22
+ publishes_events :captured, :completed, :paid, :refunded, :voided
21
23
 
22
24
  NON_RISKY_AVS_CODES = ['B', 'D', 'H', 'J', 'M', 'Q', 'T', 'V', 'X', 'Y'].freeze
23
25
  RISKY_AVS_CODES = ['A', 'C', 'E', 'F', 'G', 'I', 'K', 'L', 'N', 'O', 'P', 'R', 'S', 'U', 'W', 'Z'].freeze
@@ -244,6 +246,9 @@ module Spree
244
246
  return amount if owner.nil?
245
247
 
246
248
  amount_from_order = owner.total - owner.payment_total
249
+ # A refund for returned goods settled what was owed for them; without
250
+ # this the order would accept a fresh charge for goods it took back.
251
+ amount_from_order -= owner.returned_items_refund_total if owner.respond_to?(:returned_items_refund_total)
247
252
 
248
253
  if payment_method&.store_credit?
249
254
  store_credits = owner.available_store_credits
@@ -253,14 +258,17 @@ module Spree
253
258
  end
254
259
  end
255
260
 
256
- def amount=(amount)
257
- self[:amount] =
258
- case amount
259
- when String
260
- separator = I18n.t('number.currency.format.separator')
261
- number = amount.delete("^0-9-#{separator}\.").tr(separator, '.')
262
- number.to_d if number.present?
263
- end || amount
261
+ # Resizes a store credit payment that has not been taken yet. Completion
262
+ # takes it against the eligibility event recorded for its amount, so the
263
+ # new amount needs one of its own.
264
+ #
265
+ # @param new_amount [BigDecimal]
266
+ # @return [void]
267
+ def update_store_credit_amount!(new_amount)
268
+ return if amount == new_amount
269
+
270
+ update_column(:amount, new_amount)
271
+ create_eligible_credit_event
264
272
  end
265
273
 
266
274
  def offsets_total
@@ -502,7 +510,7 @@ module Spree
502
510
  end
503
511
 
504
512
  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?
513
+ 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
514
  end
507
515
 
508
516
  def set_amount
@@ -532,8 +540,8 @@ module Spree
532
540
  end
533
541
 
534
542
  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}")
543
+ field_name = source.class.human_attribute_name(field)
544
+ errors.add(source.class.model_name.human, "#{field_name} #{message}")
537
545
  end
538
546
 
539
547
 
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class PaymentMethod::Check < ::Spree::PaymentMethod
3
+ self.accepts_money_amounts = true
4
+
3
5
  def actions
4
6
  %w{capture void}
5
7
  end
@@ -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
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class PaymentMethod::StoreCredit < ::Spree::PaymentMethod
3
+ self.accepts_money_amounts = true
4
+
3
5
  def payment_source_class
4
6
  ::Spree::StoreCredit
5
7
  end
@@ -20,13 +22,13 @@ module Spree
20
22
  payment.pending?
21
23
  end
22
24
 
23
- def authorize(amount_in_cents, store_credit, gateway_options = {})
25
+ def authorize(money, store_credit, gateway_options = {})
24
26
  if store_credit.nil?
25
- Spree::PaymentResponse.new(false, Spree.t('store_credit_payment_method.unable_to_find'), {}, {})
27
+ Spree::PaymentResponse.new(false, I18n.t('spree.store_credit_payment_method.unable_to_find'), {}, {})
26
28
  else
27
29
  action = lambda do |store_credit|
28
30
  store_credit.authorize(
29
- amount_in_cents / 100.0.to_d,
31
+ money.to_d,
30
32
  gateway_options[:currency],
31
33
  action_originator: gateway_options[:originator]
32
34
  )
@@ -35,10 +37,10 @@ module Spree
35
37
  end
36
38
  end
37
39
 
38
- def capture(amount_in_cents, auth_code, gateway_options = {})
40
+ def capture(money, auth_code, gateway_options = {})
39
41
  action = lambda do |store_credit|
40
42
  store_credit.capture(
41
- amount_in_cents / 100.0.to_d,
43
+ money.to_d,
42
44
  auth_code,
43
45
  gateway_options[:currency],
44
46
  action_originator: gateway_options[:originator]
@@ -47,9 +49,9 @@ module Spree
47
49
  handle_action(action, :capture, auth_code)
48
50
  end
49
51
 
50
- def purchase(amount_in_cents, store_credit, gateway_options = {})
52
+ def purchase(money, store_credit, gateway_options = {})
51
53
  eligible_events = store_credit.store_credit_events.where(
52
- amount: amount_in_cents / 100.0.to_d,
54
+ amount: money.to_d,
53
55
  action: Spree::StoreCredit::ELIGIBLE_ACTION
54
56
  )
55
57
  event = eligible_events.detect do |eligible_event|
@@ -58,9 +60,9 @@ module Spree
58
60
  end
59
61
 
60
62
  if event.blank?
61
- Spree::PaymentResponse.new(false, Spree.t('store_credit_payment_method.unable_to_find'), {}, {})
63
+ Spree::PaymentResponse.new(false, I18n.t('spree.store_credit_payment_method.unable_to_find'), {}, {})
62
64
  else
63
- capture(amount_in_cents, event.authorization_code, gateway_options)
65
+ capture(money, event.authorization_code, gateway_options)
64
66
  end
65
67
  end
66
68
 
@@ -71,12 +73,12 @@ module Spree
71
73
  handle_action(action, :void, auth_code)
72
74
  end
73
75
 
74
- def credit(amount_in_cents, auth_code, gateway_options)
76
+ def credit(money, auth_code, gateway_options)
75
77
  action = lambda do |store_credit|
76
78
  currency = gateway_options[:currency] || store_credit.currency
77
79
  originator = gateway_options[:originator]
78
80
 
79
- store_credit.credit(amount_in_cents / 100.0.to_d, auth_code, currency, action_originator: originator)
81
+ store_credit.credit(money.to_d, auth_code, currency, action_originator: originator)
80
82
  end
81
83
 
82
84
  handle_action(action, :credit, auth_code)
@@ -115,7 +117,7 @@ module Spree
115
117
  # note that we only need to return the auth code on an 'auth', but it's innocuous to always return
116
118
  Spree::PaymentResponse.new(
117
119
  true,
118
- Spree.t('store_credit_payment_method.successful_action', action: action_name),
120
+ I18n.t('spree.store_credit_payment_method.successful_action', action: action_name),
119
121
  {},
120
122
  authorization: auth_code || response
121
123
  )
@@ -132,7 +134,7 @@ module Spree
132
134
  if store_credit.nil?
133
135
  Spree::PaymentResponse.new(
134
136
  false,
135
- Spree.t('store_credit_payment_method.unable_to_find_for_action', auth_code: auth_code, action: action_name),
137
+ I18n.t('spree.store_credit_payment_method.unable_to_find_for_action', auth_code: auth_code, action: action_name),
136
138
  {},
137
139
  {}
138
140
  )
@@ -13,11 +13,20 @@ 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
20
22
 
23
+ # Whether authorize, purchase, capture and credit take the amount as a
24
+ # Spree::Money (amount and currency) and convert it to the provider's own
25
+ # unit. Methods that have not opted in receive an integer in hundredths of
26
+ # the currency, which is wrong for yen and dinar; that bridge is removed in
27
+ # Spree 6.1.
28
+ class_attribute :accepts_money_amounts, default: false, instance_writer: false
29
+
21
30
  # Blank falls back to the store, so a method only overrides when a merchant
22
31
  # deliberately picks one — the meaning the auto_capture boolean already had.
23
32
  normalizes :capture_method, with: ->(value) { value.presence }
@@ -46,8 +55,9 @@ module Spree
46
55
  scope :storefront_visible, -> { where(storefront_visible: true) }
47
56
  scope :admin_only, -> { where(storefront_visible: false) }
48
57
 
49
- # Real column, so admin clients filter it directly — no ransacker needed.
50
- self.whitelisted_ransackable_attributes = %w[storefront_visible]
58
+ # Real columns, so admin clients filter them directly — no ransacker needed.
59
+ self.whitelisted_ransackable_attributes = %w[storefront_visible active type]
60
+ search_by :name
51
61
 
52
62
  after_initialize :set_name, if: :new_record?
53
63
 
@@ -57,7 +67,6 @@ module Spree
57
67
  validates :storefront_visible, inclusion: { in: [true, false] }
58
68
  normalizes :name, with: ->(value) { value&.to_s&.squish&.presence }
59
69
 
60
- belongs_to :store, class_name: 'Spree::Store'
61
70
 
62
71
  has_many :payments, class_name: 'Spree::Payment', inverse_of: :payment_method, dependent: :nullify
63
72
  has_many :credit_cards, class_name: 'Spree::CreditCard', dependent: :destroy # CCs are soft deleted
@@ -74,6 +83,20 @@ module Spree
74
83
  # `providers`; declaring it keeps subclass resolution to a single rule.
75
84
  registers_subclasses_via { providers }
76
85
 
86
+ # Whether this method is backed by an external payment provider (Stripe,
87
+ # Adyen…) rather than handled by the store itself (check, store credit).
88
+ # Only third-party methods are listed under Settings → Integrations.
89
+ #
90
+ # @return [Boolean]
91
+ def self.third_party?
92
+ false
93
+ end
94
+
95
+ # @return [Hash] the listing attributes, plus whether the method is third-party
96
+ def self.provider_listing
97
+ super.merge(third_party: third_party?)
98
+ end
99
+
77
100
  def provider_class
78
101
  raise ::NotImplementedError, 'You must implement provider_class method for this gateway.'
79
102
  end
@@ -136,6 +159,25 @@ module Spree
136
159
  raise ::NotImplementedError, 'You must implement complete_payment_session method for this gateway.'
137
160
  end
138
161
 
162
+ # Cancels a session nobody is going to complete, so the provider can no
163
+ # longer take money against it. Called for the sessions left pending when
164
+ # an order is placed. The default leaves the session untouched: a
165
+ # gateway that cannot cancel at the provider must not report as canceled
166
+ # a session that can still be paid.
167
+ #
168
+ # Raise Spree::Core::GatewayError only for a refusal, such as a session
169
+ # that was already paid. A timeout, a rate limit, an outage, or rejected
170
+ # credentials is raised as itself. Callers that treat those as a refusal
171
+ # leave a session the buyer can still pay.
172
+ #
173
+ # @param payment_session [Spree::PaymentSession]
174
+ # @return [Boolean] whether the session was canceled
175
+ # @raise [Spree::Core::GatewayError] when the provider refuses, e.g. the
176
+ # session has already been paid
177
+ def cancel_payment_session(payment_session:)
178
+ false
179
+ end
180
+
139
181
  # Parses an incoming webhook payload from the payment provider.
140
182
  # Override in gateway subclasses to implement provider-specific webhook parsing.
141
183
  #
@@ -284,9 +326,7 @@ module Spree
284
326
  end
285
327
 
286
328
  def public_preferences
287
- public_preference_keys.each_with_object({}) do |key, hash|
288
- hash[key] = preferences[key]
289
- end
329
+ public_preference_keys.index_with { |key| get_preference(key) }
290
330
  end
291
331
 
292
332
  # @deprecated Use {#storefront_visible?}; removed in 6.1.
@@ -307,6 +347,27 @@ module Spree
307
347
  self.storefront_visible = value.to_s != 'back_end'
308
348
  end
309
349
 
350
+
351
+ # The amount in the form this payment method's gateway calls take.
352
+ #
353
+ # @param amount [BigDecimal] in the currency's own units ("19.99")
354
+ # @param currency [String]
355
+ # @return [Spree::Money, Integer] a Spree::Money for methods that accept
356
+ # money amounts, otherwise hundredths of the currency (deprecated)
357
+ def gateway_amount(amount, currency)
358
+ return Spree::Money.new(amount, currency: currency) if accepts_money_amounts
359
+
360
+ warn_about_hundredths
361
+ hundredths = Spree::Money::Rounding.to_hundredths(amount)
362
+ # A hundredth is the smallest amount these gateways can be sent; zero
363
+ # reads as "the whole amount" to many of them.
364
+ if hundredths.zero? && !amount.zero?
365
+ raise Spree::Core::GatewayError, "#{self.class.name} cannot take an amount below a hundredth of #{currency}"
366
+ end
367
+
368
+ hundredths
369
+ end
370
+
310
371
  protected
311
372
 
312
373
  def public_preference_keys
@@ -316,5 +377,16 @@ module Spree
316
377
  def set_name
317
378
  self.name ||= default_name
318
379
  end
380
+
381
+ def warn_about_hundredths
382
+ return if self.class.instance_variable_get(:@warned_about_hundredths)
383
+
384
+ self.class.instance_variable_set(:@warned_about_hundredths, true)
385
+ Spree::Deprecation.warn(
386
+ "#{self.class.name} receives amounts as an integer in hundredths, which is wrong for currencies " \
387
+ 'without two decimals. Set `self.accepts_money_amounts = true` and take a Spree::Money ' \
388
+ '(amount and currency); hundredths are removed in Spree 6.1.'
389
+ )
390
+ end
319
391
  end
320
392
  end
@@ -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}"
@@ -62,6 +62,7 @@ module Spree
62
62
  # Ransack
63
63
  #
64
64
  self.whitelisted_ransackable_attributes = %w[name owner_type owner_id]
65
+ search_by :name
65
66
 
66
67
  before_destroy :really_destroy_slugs!
67
68
 
@@ -11,12 +11,16 @@ module Spree
11
11
 
12
12
  include Spree::VatPriceCalculation
13
13
  include Spree::StorePreferences
14
+ include Spree::CanonicalDecimals
15
+
16
+ canonical_decimals :amount, :compare_at_amount, places: Spree::Money::Rounding::UNIT_PRICE_DECIMALS
14
17
 
15
18
  publishes_lifecycle_events
16
19
 
17
20
  acts_as_paranoid
18
21
 
19
- MAXIMUM_AMOUNT = BigDecimal('99_999_999.99')
22
+ # The largest amount decimal(19,4) holds.
23
+ MAXIMUM_AMOUNT = BigDecimal('999_999_999_999_999.9999')
20
24
 
21
25
  # How many breaks one variant may carry on one list in one currency. A UI
22
26
  # sanity bound rather than a technical one — but enforced here so no
@@ -24,7 +28,7 @@ module Spree
24
28
  # every priced line (docs/plans/6.0-volume-pricing.md).
25
29
  MAXIMUM_BREAKS_PER_VARIANT = 10
26
30
 
27
- belongs_to :variant, -> { with_deleted }, class_name: 'Spree::Variant', inverse_of: :prices, touch: true
31
+ belongs_to :variant, -> { with_deleted }, class_name: 'Spree::Variant', inverse_of: :prices
28
32
  belongs_to :price_list, class_name: 'Spree::PriceList', optional: true
29
33
 
30
34
  has_many :price_histories, class_name: 'Spree::PriceHistory', dependent: :delete_all
@@ -32,6 +36,8 @@ module Spree
32
36
  before_validation :ensure_currency
33
37
  before_save :remove_compare_at_amount_if_equals_amount
34
38
  after_save :record_price_history, if: :should_record_price_history?
39
+ after_save :touch_variant, if: -> { saved_changes? && (amount.present? || amount_before_last_save.present?) }
40
+ after_destroy :touch_variant, if: -> { amount.present? }
35
41
 
36
42
  # legacy behavior
37
43
  validates :amount, allow_nil: true, numericality: {
@@ -100,10 +106,6 @@ module Spree
100
106
  Spree::Money.new(amount || 0, currency: currency.upcase)
101
107
  end
102
108
 
103
- def amount=(amount)
104
- self[:amount] = amount.blank? ? nil : Spree::LocalizedNumber.parse(amount)
105
- end
106
-
107
109
  # Returns the amount in cents
108
110
  # @return [Integer]
109
111
  def amount_in_cents
@@ -114,12 +116,6 @@ module Spree
114
116
  Spree::Money.new(compare_at_amount || 0, currency: currency)
115
117
  end
116
118
 
117
- def compare_at_amount=(value)
118
- calculated_value = Spree::LocalizedNumber.parse(value) if value.present?
119
-
120
- self[:compare_at_amount] = calculated_value
121
- end
122
-
123
119
  # Returns the compare at amount for display
124
120
  # @return [Spree::Money, nil]
125
121
  def display_compare_at_amount
@@ -137,9 +133,7 @@ module Spree
137
133
  end
138
134
 
139
135
  alias_attribute :price, :amount
140
- alias_method :price=, :amount=
141
136
  alias_attribute :compare_at_price, :compare_at_amount
142
- alias_method :compare_at_price=, :compare_at_amount=
143
137
 
144
138
  # Whether this figure is the merchant's answer for the buyer's geography
145
139
  # already. A price list narrowed by market or country states its prices for
@@ -342,6 +336,15 @@ module Spree
342
336
  rungs.sort_by(&:first)
343
337
  end
344
338
 
339
+ # Stands in for `touch: true` on the variant, skipping placeholder rows: a
340
+ # price list holds one without an amount for every variant and currency
341
+ # nobody has priced yet, and it charges nothing, so writing one changes no
342
+ # price a shopper sees — yet the touch would expire the product's cached
343
+ # responses and reindex it for search.
344
+ def touch_variant
345
+ variant&.touch_later
346
+ end
347
+
345
348
  def should_record_price_history?
346
349
  price_list_id.nil? &&
347
350
  amount.present? &&
@@ -10,6 +10,10 @@ module Spree
10
10
  # and not twenty percent off an already-discounted number
11
11
  # (docs/plans/6.0-volume-pricing.md).
12
12
  class PriceAdjustmentTier < Spree.base_class
13
+ include Spree::CanonicalDecimals
14
+
15
+ canonical_decimals :percentage, places: 3
16
+
13
17
  has_prefix_id :pat
14
18
 
15
19
  # How many bands one list may carry, matching the break cap so a merchant