spree_core 6.0.0.beta4 → 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
@@ -1,5 +1,3 @@
1
- require 'ostruct'
2
-
3
1
  module Spree
4
2
  class Fulfillment < Spree.base_class
5
3
  has_prefix_id :ful
@@ -15,6 +13,9 @@ module Spree
15
13
  include Spree::Fulfillment::CustomEvents
16
14
 
17
15
  publishes_lifecycle_events
16
+ publishes_events :canceled, :delivered, :fulfilled
17
+ publishes_event 'shipment.shipped', deprecated_alias_of: 'fulfillment.fulfilled'
18
+ publishes_event 'shipment.canceled', deprecated_alias_of: 'fulfillment.canceled'
18
19
 
19
20
  with_options inverse_of: :fulfillments do
20
21
  belongs_to :address, class_name: 'Spree::Address', optional: true
@@ -92,6 +93,18 @@ module Spree
92
93
 
93
94
  delegate :store, :currency, to: :owner
94
95
 
96
+ # What a rebuild hands to {#carry_over_selection}, read before the old
97
+ # proposals and their rates are deleted. Grouped rather than keyed by
98
+ # location: one location can ship several proposals (a digital item beside
99
+ # a physical one, stock on hand beside a backorder), each with its own choice.
100
+ #
101
+ # @return [Hash{Integer => Array<Spree::DeliveryRate>}] the selected rates
102
+ # of each stock location's proposals
103
+ def self.selected_rates_by_stock_location
104
+ includes(:selected_delivery_rate).group_by(&:stock_location_id).
105
+ transform_values { |proposals| proposals.filter_map(&:selected_delivery_rate) }
106
+ end
107
+
95
108
  # The exactly-one owner of this fulfillment — the cart during checkout,
96
109
  # the order after completion. New code must read +owner+, never assume
97
110
  # +order+.
@@ -221,7 +234,7 @@ module Spree
221
234
  #
222
235
  # @return [String, Spree::Money]
223
236
  def display_cost(**options)
224
- return Spree.t('delivery_rates.quoted_after_review') if unpriced?
237
+ return I18n.t('spree.delivery_rates.quoted_after_review') if unpriced?
225
238
 
226
239
  Spree::Money.new(cost, { currency: currency }.merge(options))
227
240
  end
@@ -338,7 +351,7 @@ module Spree
338
351
  #
339
352
  # @return [String, Spree::Money]
340
353
  def display_final_price(**options)
341
- return Spree.t('delivery_rates.quoted_after_review') if unpriced?
354
+ return I18n.t('spree.delivery_rates.quoted_after_review') if unpriced?
342
355
 
343
356
  Spree::Money.new(final_price, { currency: currency }.merge(options))
344
357
  end
@@ -543,34 +556,13 @@ module Spree
543
556
 
544
557
  # StockEstimator.new assignment below will replace the current delivery_method
545
558
  original_shipping_method_id = delivery_method.try(:id)
546
- # A carrier method quotes several rates (one per service), so the
547
- # method id alone no longer identifies the customer's choice — capture
548
- # the selected rate's carrier service too.
549
559
  original_selection = selected_delivery_rate
550
- original_carrier = original_selection&.carrier
551
- original_service_level = original_selection&.service_level
552
560
 
553
561
  self.delivery_rates = Stock::Estimator.new(owner).
554
562
  delivery_rates(to_package, audience)
555
563
 
556
564
  if delivery_method
557
- # Keep the previously chosen carrier service when it is still quoted,
558
- # then the previously chosen method, then the estimator's own pick
559
- # (the cheapest rate, already flagged selected) rather than leaving
560
- # the fulfillment unselected.
561
- selected_rate =
562
- if original_shipping_method_id && original_carrier
563
- delivery_rates.detect do |rate|
564
- rate.delivery_method_id == original_shipping_method_id &&
565
- rate.carrier == original_carrier &&
566
- rate.service_level == original_service_level
567
- end
568
- end
569
- selected_rate ||=
570
- if original_shipping_method_id
571
- delivery_rates.detect { |rate| rate.delivery_method_id == original_shipping_method_id }
572
- end
573
- selected_rate ||= delivery_rates.detect(&:selected)
565
+ selected_rate = successor_rate(original_shipping_method_id, original_selection)
574
566
 
575
567
  save!
576
568
  self.selected_shipping_rate_id = selected_rate.id if selected_rate
@@ -755,6 +747,25 @@ module Spree
755
747
  end
756
748
  end
757
749
 
750
+ # Carries the customer's choice onto a freshly built, unsaved proposal:
751
+ # takes the first of +candidates+ whose method this proposal is quoted for,
752
+ # selects the rate standing for it and prices the proposal at it, so a
753
+ # rebuild neither resets the choice nor drops its cost. The rate taken is
754
+ # removed from +candidates+, leaving the rest to this proposal's siblings.
755
+ #
756
+ # @param candidates [Array<Spree::DeliveryRate>] the rates selected on the
757
+ # proposals this stock location had before the rebuild
758
+ # @return [void]
759
+ def carry_over_selection(candidates)
760
+ quoted_method_ids = delivery_rates.map(&:delivery_method_id)
761
+ previous_rate = candidates.find { |rate| quoted_method_ids.include?(rate.delivery_method_id) }
762
+ candidates.delete(previous_rate)
763
+
764
+ choice = successor_rate(previous_rate&.delivery_method_id, previous_rate)
765
+ delivery_rates.each { |rate| rate.selected = rate == choice }
766
+ self.cost = choice&.cost || 0
767
+ end
768
+
758
769
  # @deprecated No-op since 6.0; removed in 6.1.
759
770
  #
760
771
  # This re-derived the status from the order's payment state on every order
@@ -792,6 +803,21 @@ module Spree
792
803
 
793
804
  private
794
805
 
806
+ # The previously chosen carrier service when it is still quoted, then the
807
+ # previously chosen method, then the estimator's own pick - a carrier
808
+ # method quotes one rate per service, so the method alone does not
809
+ # identify the choice.
810
+ def successor_rate(delivery_method_id, previous_rate)
811
+ same_method = delivery_method_id ? delivery_rates.select { |rate| rate.delivery_method_id == delivery_method_id } : []
812
+ same_service = if previous_rate&.carrier
813
+ same_method.detect do |rate|
814
+ rate.carrier == previous_rate.carrier && rate.service_level == previous_rate.service_level
815
+ end
816
+ end
817
+
818
+ same_service || same_method.first || delivery_rates.detect(&:selected)
819
+ end
820
+
795
821
  # @return [Boolean] whether this parcel's order shares its payment with
796
822
  # others placed in the same checkout
797
823
  # Draws one parcel's worth from a payment and reports what it took.
@@ -957,7 +983,7 @@ module Spree
957
983
  end
958
984
 
959
985
  def exactly_one_owner
960
- 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?
986
+ 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?
961
987
  end
962
988
 
963
989
  def update_adjustments
@@ -106,8 +106,8 @@ module Spree
106
106
  end
107
107
 
108
108
  def get_desired_shipment_inventory_unit(state)
109
- desired_shipment.fulfillment_items.find_or_create_by(status: state, variant: variant) do |unit|
110
- current_shipment_unit = current_shipment_units.first
109
+ current_shipment_unit = current_shipment_units.first
110
+ desired_shipment.fulfillment_items.find_or_create_by(status: state, variant: variant, replacement: current_shipment_unit&.replacement || false) do |unit|
111
111
  unit.variant_id = current_shipment_unit.variant_id
112
112
  unit.order_id = current_shipment_unit.order_id
113
113
  unit.line_item_id = current_shipment_unit.line_item_id
@@ -15,11 +15,7 @@ module Spree
15
15
  belongs_to :line_item, class_name: 'Spree::LineItem'
16
16
  end
17
17
 
18
- scope :backordered, -> { where status: 'backordered' }
19
- scope :on_hand, -> { where status: 'on_hand' }
20
18
  scope :on_hand_or_backordered, -> { where status: ['backordered', 'on_hand'] }
21
- scope :shipped, -> { where status: 'shipped' }
22
- scope :returned, -> { where status: 'returned' }
23
19
  scope :backordered_per_variant, ->(stock_level) do
24
20
  includes(:fulfillment, :order).
25
21
  where.not(Spree::Fulfillment.table_name => { status: 'canceled' }).
@@ -6,6 +6,7 @@ module Spree
6
6
  # ShipmentHandler name-constantize mechanism.
7
7
  class Base
8
8
  include Spree::IntegrationBackedProvider
9
+ extend Spree::ApiTyped
9
10
 
10
11
  class << self
11
12
  # Behavior predicates — the class hierarchy IS the vocabulary.
@@ -117,6 +117,10 @@ module Spree
117
117
  payment_session.complete
118
118
  end
119
119
 
120
+ def cancel_payment_session(payment_session:)
121
+ payment_session.cancel
122
+ end
123
+
120
124
  def setup_session_supported?
121
125
  true
122
126
  end
@@ -1,5 +1,10 @@
1
1
  module Spree
2
2
  class Gateway::CustomPaymentSourceMethod < Gateway
3
+ # Sources recorded by the merchant, not charged through a provider.
4
+ def self.third_party?
5
+ false
6
+ end
7
+
3
8
  def provider_class
4
9
  self.class
5
10
  end
@@ -31,6 +31,10 @@ module Spree
31
31
  outer ? outer.underscore : leaf
32
32
  end
33
33
 
34
+ def self.third_party?
35
+ true
36
+ end
37
+
34
38
  def payment_source_class
35
39
  CreditCard
36
40
  end
@@ -49,7 +53,7 @@ module Spree
49
53
  end
50
54
 
51
55
  def options
52
- preferences.each_with_object({}) { |(key, value), memo| memo[key.to_sym] = value; }
56
+ preference_values.symbolize_keys
53
57
  end
54
58
 
55
59
  def method_missing(method, *args)
@@ -8,6 +8,7 @@ module Spree
8
8
  include Spree::Security::GiftCards if defined?(Spree::Security::GiftCards)
9
9
 
10
10
  publishes_lifecycle_events
11
+ publishes_events :canceled, :partially_redeemed, :redeemed
11
12
 
12
13
  #
13
14
  # Status
@@ -35,7 +36,6 @@ module Spree
35
36
  #
36
37
  # Associations
37
38
  #
38
- belongs_to :store, class_name: 'Spree::Store'
39
39
  belongs_to :customer, class_name: Spree.customer_class.to_s, optional: true
40
40
  include Spree::DeprecatedCustomerAlias
41
41
  belongs_to :created_by, class_name: Spree.admin_user_class.to_s, optional: true
@@ -94,6 +94,17 @@ module Spree
94
94
  amount - amount_used - amount_authorized
95
95
  end
96
96
 
97
+ # What the card can still give towards an order: nothing once it has
98
+ # expired, been redeemed or been cancelled, and never less than zero,
99
+ # since an admin can lower the amount below what is already in use.
100
+ #
101
+ # @return [BigDecimal]
102
+ def spendable_amount
103
+ return BigDecimal('0') if expired? || redeemed? || canceled?
104
+
105
+ [amount_remaining, 0].max
106
+ end
107
+
97
108
  delegate :email, to: :customer, prefix: true, allow_nil: true
98
109
 
99
110
  def self.json_api_columns
@@ -10,7 +10,6 @@ module Spree
10
10
  #
11
11
  # Associations
12
12
  #
13
- belongs_to :store, class_name: 'Spree::Store'
14
13
  belongs_to :created_by, class_name: Spree.admin_user_class.to_s, optional: true
15
14
  has_many :gift_cards, class_name: 'Spree::GiftCard', inverse_of: :batch, dependent: :destroy
16
15
 
@@ -17,6 +17,7 @@ module Spree
17
17
  'https://raw.githubusercontent.com/spree/spree/refs/tags/v%<version>s/spree/core/db/sample_data'.freeze
18
18
 
19
19
  publishes_lifecycle_events
20
+ publishes_events :completed, :progress
20
21
 
21
22
  # Set event prefix for all Import subclasses
22
23
  # This ensures Spree::Imports::Products publishes 'import.create' not 'products.create'
@@ -191,12 +192,6 @@ module Spree
191
192
  Spree.import_retry_failed_rows_workflow.call(import: self).success?
192
193
  end
193
194
 
194
- # Returns true if the import is in mapping state
195
- # @return [Boolean]
196
- def mapping?
197
- status == 'mapping'
198
- end
199
-
200
195
  # Returns true if the import is processing or completed mapping
201
196
  # @return [Boolean]
202
197
  def processing?
@@ -331,7 +326,7 @@ module Spree
331
326
  # Returns the display name for the import
332
327
  # @return [String]
333
328
  def display_name
334
- "#{Spree.t(type.demodulize.pluralize.downcase)} #{number}"
329
+ "#{self.class.model_name.human(count: 2)} #{number}"
335
330
  end
336
331
 
337
332
  # Returns the headers of the csv file
@@ -5,7 +5,7 @@ module Spree
5
5
  #
6
6
  # Associations
7
7
  #
8
- belongs_to :import, class_name: 'Spree::Import', inverse_of: :mappings, required: true
8
+ belongs_to :import, class_name: 'Spree::Import', inverse_of: :mappings
9
9
 
10
10
  #
11
11
  # Validations
@@ -17,7 +17,6 @@ module Spree
17
17
  #
18
18
  # Scopes
19
19
  #
20
- scope :required, -> { where(schema_field: import.required_fields) }
21
20
  scope :mapped, -> { where.not(file_column: [nil, '']) }
22
21
 
23
22
  # Returns true if the mapping is required
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class ImportRow < Spree.base_class
3
+ publishes_events :completed, :failed
4
+
3
5
  has_prefix_id :imrow
4
6
 
5
7
  # Set event prefix for ImportRow
@@ -41,8 +43,6 @@ module Spree
41
43
  # Scopes
42
44
  #
43
45
  scope :pending_and_failed, -> { where(status: %i[pending failed]) }
44
- scope :completed, -> { where(status: :completed) }
45
- scope :failed, -> { where(status: :failed) }
46
46
  scope :processed, -> { where(status: %i[completed failed]) }
47
47
  # Rows still legitimately blocking import completion: `pending` (not started) or
48
48
  # `processing` with a recent updated_at (worker still alive). Orphaned `processing`
@@ -25,6 +25,7 @@ module Spree
25
25
  { name: 'track_inventory', label: 'Track Inventory' },
26
26
  { name: 'inventory_count', label: 'Inventory Count' },
27
27
  { name: 'inventory_backorderable', label: 'Inventory Backorderable' },
28
+ { name: 'stock_location', label: 'Stock Location' },
28
29
  { name: 'tax_category', label: 'Tax Category' },
29
30
  { name: 'product_type', label: 'Product Type' },
30
31
  { name: 'hs_code', label: 'HS Code' },
@@ -10,6 +10,9 @@ module Spree
10
10
 
11
11
  include Spree::SingleStoreResource
12
12
  include Spree::PreferenceSchema
13
+ include Spree::SecretPreferences
14
+ include Spree::ProviderListing
15
+
13
16
 
14
17
  # Spree::Current.integrations snapshots the active set for the request;
15
18
  # connecting or deactivating one mid-request (the activate-and-verify
@@ -58,25 +61,12 @@ module Spree
58
61
  attr_accessor :connection_error_message
59
62
 
60
63
  # Associates the integration to a group.
61
- # The name here will be used as Spree.t key to display the group name.
64
+ # The name is read as a translation key under +spree.+ to display the group name.
62
65
  # Leave blank to leave the integration ungrouped.
63
66
  def self.integration_group
64
67
  nil
65
68
  end
66
69
 
67
- # Logo shown on the gallery card: an absolute URL to publicly hosted
68
- # brand assets, or a `data:` URI for gems that want to be self-contained
69
- # (works air-gapped, no CSP domain to allowlist). Anything an `<img src>`
70
- # accepts. Deliberately not an asset-pipeline path — integration gems
71
- # must not force an asset pipeline onto headless API hosts. Hosted logos
72
- # are a courtesy, not a guarantee: the dashboard falls back to a letter
73
- # avatar when unset or unreachable.
74
- #
75
- # @return [String, nil]
76
- def self.logo_url
77
- nil
78
- end
79
-
80
70
  # One-line marketing description shown on the integration's gallery card.
81
71
  # Localize by shipping a Rails translation instead (or additionally):
82
72
  # `spree.integrations.<api_type>.description` wins over this fallback.
@@ -92,7 +82,7 @@ module Spree
92
82
  #
93
83
  # @return [String, nil]
94
84
  def self.human_description
95
- Spree.t("integrations.#{api_type}.description", default: description)
85
+ I18n.t("spree.integrations.#{api_type}.description", default: description)
96
86
  end
97
87
 
98
88
  # Wire entries for the admin types-discovery endpoint — every registered
@@ -107,7 +97,7 @@ module Spree
107
97
  name: klass.integration_name,
108
98
  group: klass.integration_group,
109
99
  description: klass.human_description,
110
- logo_url: klass.logo_url,
100
+ **klass.provider_listing,
111
101
  preference_schema: klass.serialized_preference_schema
112
102
  }
113
103
  end.sort_by { |entry| entry[:name] }
@@ -172,7 +162,7 @@ module Spree
172
162
  return if Spree.integrations.empty?
173
163
  return if Spree.integrations.map(&:to_s).include?(type)
174
164
 
175
- errors.add(:type, :integration_type_not_registered, message: Spree.t('errors.messages.integration_type_not_registered'))
165
+ errors.add(:type, :integration_type_not_registered, message: I18n.t('spree.errors.messages.integration_type_not_registered'))
176
166
  end
177
167
 
178
168
  # On :base, not :active — the seller's message ("This api key is no
@@ -182,7 +172,7 @@ module Spree
182
172
  def must_connect_when_activating
183
173
  return if can_connect?
184
174
 
185
- errors.add(:base, connection_error_message.presence || Spree.t('errors.messages.integration_connection_failed'))
175
+ errors.add(:base, connection_error_message.presence || I18n.t('spree.errors.messages.integration_connection_failed'))
186
176
  end
187
177
  end
188
178
  end
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class Invitation < Spree.base_class
3
+ publishes_events :accepted, :created, :resent
4
+
3
5
  has_prefix_id :inv
4
6
 
5
7
  has_secure_token
@@ -32,8 +34,6 @@ module Spree
32
34
  #
33
35
  # Scopes
34
36
  #
35
- scope :pending, -> { where(status: 'pending') }
36
- scope :accepted, -> { where(status: 'accepted') }
37
37
  scope :not_expired, -> { where('expires_at > ?', Time.current) }
38
38
 
39
39
  #
@@ -144,7 +144,7 @@ module Spree
144
144
 
145
145
  def invitee_is_not_inviter
146
146
  if invitee == inviter
147
- errors.add(:invitee, :same_as_inviter, message: Spree.t('errors.messages.same_as_inviter'))
147
+ errors.add(:invitee, :same_as_inviter, message: I18n.t('spree.errors.messages.same_as_inviter'))
148
148
  end
149
149
  end
150
150
 
@@ -154,7 +154,7 @@ module Spree
154
154
  return if role.blank? || resource.blank?
155
155
  return if role.resource == resource
156
156
 
157
- errors.add(:role, :invitation_role_resource_mismatch, message: Spree.t(:invitation_role_resource_mismatch))
157
+ errors.add(:role, :invitation_role_resource_mismatch, message: I18n.t('spree.invitation_role_resource_mismatch'))
158
158
  end
159
159
 
160
160
  def invitee_already_exists
@@ -165,7 +165,7 @@ module Spree
165
165
  return if user_to_check.blank?
166
166
 
167
167
  if resource.role_users.exists?(user: user_to_check)
168
- errors.add(:email, :already_a_member, message: Spree.t('errors.messages.already_a_member'))
168
+ errors.add(:email, :already_a_member, message: I18n.t('spree.errors.messages.already_a_member'))
169
169
  end
170
170
  end
171
171
 
@@ -63,7 +63,7 @@ module Spree
63
63
  # https://github.com/spree/spree/issues/2695#issuecomment-143314161
64
64
  validates :quantity, numericality: {
65
65
  in: 0..DB_INTEGER_MAX,
66
- only_integer: true, message: Spree.t('validation.must_be_int')
66
+ only_integer: true, message: I18n.t('spree.validation.must_be_int')
67
67
  }
68
68
 
69
69
  validates :price, numericality: true
@@ -260,6 +260,17 @@ module Spree
260
260
  alias discounted_money display_discounted_amount
261
261
  alias discounted_amount taxable_amount
262
262
 
263
+ # What +units+ of this line cost the customer after discounts. Multiplied
264
+ # before dividing, so all of a line's units together come to the whole line.
265
+ #
266
+ # @param units [Integer]
267
+ # @return [BigDecimal] rounded to the currency, never negative
268
+ def discounted_amount_for(units)
269
+ return BigDecimal(0) if quantity.to_i.zero?
270
+
271
+ Spree::Money::Rounding.to_currency([discounted_amount, 0].max * units.to_i / quantity, currency)
272
+ end
273
+
263
274
  # Returns the amount this line item is taxed on. Whole-order promotions
264
275
  # are distributed to line-item Discount rows at application time, so the
265
276
  # discounted amount already carries the line's share. Never negative.
@@ -485,7 +496,7 @@ module Spree
485
496
  end
486
497
 
487
498
  def exactly_one_owner
488
- 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?
499
+ 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?
489
500
  end
490
501
  end
491
502
  end
@@ -1,3 +1,5 @@
1
+ require 'csv'
2
+
1
3
  module Spree
2
4
  # Virtual model for a supported locale. Wraps a locale +code+ and exposes its
3
5
  # display name, layout direction, and whether it is a store's default — the
@@ -21,16 +23,17 @@ module Spree
21
23
  code = self.code.to_s
22
24
 
23
25
  if I18n.exists?('spree.i18n.this_file_language', locale: code, fallback: false)
24
- return normalize_name(Spree.t('i18n.this_file_language', locale: code))
25
- end
26
-
27
- if defined?(SpreeI18n::Locale) && (name = SpreeI18n::Locale.local_language_name(code))
28
- return normalize_name(name)
26
+ return normalize_name(I18n.t('spree.i18n.this_file_language', locale: code))
29
27
  end
30
28
 
31
- return 'English' if code == 'en'
29
+ name = self.class.language_names[code.downcase] || self.class.language_names[language_code]
30
+ name ? normalize_name(name) : code
31
+ end
32
32
 
33
- code
33
+ # Each language's name in that language ("Deutsch", "日本語"), keyed by code.
34
+ # @return [Hash{String => String}]
35
+ def self.language_names
36
+ @language_names ||= ::CSV.read(File.expand_path('../../../config/language_names.csv', __dir__)).to_h { |row| [row[0], row[2]] }
34
37
  end
35
38
 
36
39
  # Select label, e.g. "EN — English".
@@ -88,7 +91,7 @@ module Spree
88
91
 
89
92
  private
90
93
 
91
- # Strip a trailing " (CODE)" suffix from Spree I18n locale labels. Uses
94
+ # Strip a trailing " (CODE)" suffix from a locale file's own name. Uses
92
95
  # plain string ops rather than a regex to avoid polynomial backtracking on
93
96
  # adversarial input (ReDoS).
94
97
  def normalize_name(name)
@@ -69,6 +69,7 @@ module Spree
69
69
  # An unregistered class name would only fail at checkout, when a customer is
70
70
  # waiting on a total. The list is read at validation time so a provider gem
71
71
  # loaded after boot still counts.
72
+ normalizes :tax_provider, with: ->(value) { Spree::ApiTyped.class_name_for(Spree.tax_providers, value) }
72
73
  validates :tax_provider,
73
74
  inclusion: { in: ->(_market) { Spree.tax_providers.map(&:to_s) } },
74
75
  allow_blank: true
@@ -6,6 +6,9 @@ module Spree
6
6
  include Spree::SingleStoreResource
7
7
 
8
8
  publishes_lifecycle_events
9
+ publishes_event :verified
10
+ publishes_events :subscription_requested, :unsubscribe_requested,
11
+ serializer: 'Spree::Api::V3::NewsletterSubscriberRequestEventSerializer'
9
12
 
10
13
  has_secure_token :verification_token
11
14