spree_core 5.2.6 → 5.3.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 (294) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/addresses_helper.rb +1 -23
  3. data/app/helpers/spree/base_helper.rb +25 -77
  4. data/app/helpers/spree/currency_helper.rb +2 -2
  5. data/app/helpers/spree/images_helper.rb +5 -0
  6. data/app/helpers/spree/mail_helper.rb +1 -1
  7. data/app/helpers/spree/products_helper.rb +1 -1
  8. data/app/jobs/spree/events/subscriber_job.rb +58 -0
  9. data/app/jobs/spree/products/refresh_metrics_job.rb +14 -0
  10. data/app/jobs/spree/products/touch_taxons_job.rb +0 -1
  11. data/app/jobs/spree/variants/touch_job.rb +9 -0
  12. data/app/models/concerns/spree/adjustment_source.rb +8 -0
  13. data/app/models/concerns/spree/parameterizable_name.rb +1 -1
  14. data/app/models/concerns/spree/product_scopes.rb +54 -8
  15. data/app/models/concerns/spree/publishable.rb +253 -0
  16. data/app/models/concerns/spree/unique_name.rb +1 -1
  17. data/app/models/concerns/spree/user_methods.rb +21 -1
  18. data/app/models/spree/ability.rb +3 -3
  19. data/app/models/spree/address.rb +0 -3
  20. data/app/models/spree/adjustable/promotion_accumulator.rb +1 -1
  21. data/app/models/spree/adjustment.rb +32 -6
  22. data/app/models/spree/asset.rb +6 -3
  23. data/app/models/spree/base.rb +3 -1
  24. data/app/models/spree/classification.rb +2 -2
  25. data/app/models/spree/credit_card.rb +0 -3
  26. data/app/models/spree/current.rb +24 -3
  27. data/app/models/spree/custom_domain.rb +1 -1
  28. data/app/models/spree/customer_group.rb +94 -0
  29. data/app/models/spree/customer_group_user.rb +16 -0
  30. data/app/models/spree/customer_return.rb +2 -3
  31. data/app/models/spree/digital.rb +2 -4
  32. data/app/models/spree/digital_link.rb +2 -3
  33. data/app/models/spree/event.rb +104 -0
  34. data/app/models/spree/export.rb +7 -1
  35. data/app/models/spree/gift_card.rb +13 -1
  36. data/app/models/spree/gift_card_batch.rb +3 -1
  37. data/app/models/spree/image.rb +20 -5
  38. data/app/models/spree/import.rb +17 -12
  39. data/app/models/spree/import_row.rb +13 -26
  40. data/app/models/spree/inventory_unit.rb +0 -3
  41. data/app/models/spree/invitation.rb +12 -6
  42. data/app/models/spree/line_item.rb +57 -4
  43. data/app/models/spree/newsletter_subscriber.rb +2 -0
  44. data/app/models/spree/option_type.rb +2 -5
  45. data/app/models/spree/option_value.rb +1 -4
  46. data/app/models/spree/order/checkout.rb +7 -2
  47. data/app/models/spree/order/emails.rb +3 -11
  48. data/app/models/spree/order/store_credit.rb +5 -1
  49. data/app/models/spree/order.rb +80 -31
  50. data/app/models/spree/order_updater.rb +28 -9
  51. data/app/models/spree/payment/custom_events.rb +37 -0
  52. data/app/models/spree/payment.rb +13 -4
  53. data/app/models/spree/payment_capture_event.rb +0 -4
  54. data/app/models/spree/payment_method.rb +1 -1
  55. data/app/models/spree/permission_sets/default_customer.rb +3 -3
  56. data/app/models/spree/policy.rb +1 -8
  57. data/app/models/spree/post.rb +2 -7
  58. data/app/models/spree/post_category.rb +2 -0
  59. data/app/models/spree/price.rb +8 -6
  60. data/app/models/spree/price_list.rb +263 -0
  61. data/app/models/spree/price_rule.rb +26 -0
  62. data/app/models/spree/price_rules/customer_group_rule.rb +21 -0
  63. data/app/models/spree/price_rules/user_rule.rb +19 -0
  64. data/app/models/spree/price_rules/volume_rule.rb +21 -0
  65. data/app/models/spree/price_rules/zone_rule.rb +19 -0
  66. data/app/models/spree/product.rb +117 -76
  67. data/app/models/spree/product_property.rb +2 -2
  68. data/app/models/spree/promotion/rules/customer_group.rb +22 -0
  69. data/app/models/spree/promotion/rules/user.rb +2 -2
  70. data/app/models/spree/promotion.rb +3 -4
  71. data/app/models/spree/promotion_handler/coupon.rb +3 -3
  72. data/app/models/spree/property.rb +3 -6
  73. data/app/models/spree/prototype.rb +0 -3
  74. data/app/models/spree/refund.rb +2 -3
  75. data/app/models/spree/reimbursement.rb +5 -3
  76. data/app/models/spree/report.rb +7 -1
  77. data/app/models/spree/reports/products_performance.rb +1 -1
  78. data/app/models/spree/reports/sales_total.rb +1 -1
  79. data/app/models/spree/return_authorization.rb +7 -3
  80. data/app/models/spree/return_item.rb +15 -4
  81. data/app/models/spree/shipment/custom_events.rb +47 -0
  82. data/app/models/spree/shipment.rb +12 -5
  83. data/app/models/spree/shipping_category.rb +0 -3
  84. data/app/models/spree/shipping_method.rb +0 -3
  85. data/app/models/spree/stock/quantifier.rb +1 -1
  86. data/app/models/spree/stock_item.rb +2 -0
  87. data/app/models/spree/stock_location.rb +0 -3
  88. data/app/models/spree/stock_movement/custom_events.rb +44 -0
  89. data/app/models/spree/stock_movement.rb +3 -0
  90. data/app/models/spree/stock_transfer.rb +2 -3
  91. data/app/models/spree/store.rb +64 -92
  92. data/app/models/spree/store_credit.rb +3 -4
  93. data/app/models/spree/store_product.rb +14 -0
  94. data/app/models/spree/subscriber.rb +186 -0
  95. data/app/models/spree/tax_category.rb +0 -4
  96. data/app/models/spree/tax_rate.rb +0 -3
  97. data/app/models/spree/taxon.rb +3 -36
  98. data/app/models/spree/taxonomy.rb +0 -3
  99. data/app/models/spree/variant.rb +114 -21
  100. data/app/models/spree/webhook_delivery.rb +60 -0
  101. data/app/models/spree/webhook_endpoint.rb +53 -0
  102. data/app/models/spree/wished_item.rb +2 -4
  103. data/app/models/spree/wishlist.rb +2 -3
  104. data/app/models/spree/zone.rb +0 -9
  105. data/app/paginators/spree/shared/paginate.rb +4 -0
  106. data/app/serializers/spree/events/asset_serializer.rb +22 -0
  107. data/app/serializers/spree/events/base_serializer.rb +61 -0
  108. data/app/serializers/spree/events/customer_return_serializer.rb +20 -0
  109. data/app/serializers/spree/events/digital_link_serializer.rb +20 -0
  110. data/app/serializers/spree/events/digital_serializer.rb +18 -0
  111. data/app/serializers/spree/events/export_serializer.rb +22 -0
  112. data/app/serializers/spree/events/gift_card_batch_serializer.rb +24 -0
  113. data/app/serializers/spree/events/gift_card_serializer.rb +29 -0
  114. data/app/serializers/spree/events/image_serializer.rb +9 -0
  115. data/app/serializers/spree/events/import_row_serializer.rb +23 -0
  116. data/app/serializers/spree/events/import_serializer.rb +24 -0
  117. data/app/serializers/spree/events/invitation_serializer.rb +28 -0
  118. data/app/serializers/spree/events/line_item_serializer.rb +31 -0
  119. data/app/serializers/spree/events/newsletter_subscriber_serializer.rb +21 -0
  120. data/app/serializers/spree/events/order_serializer.rb +39 -0
  121. data/app/serializers/spree/events/payment_serializer.rb +24 -0
  122. data/app/serializers/spree/events/post_category_serializer.rb +20 -0
  123. data/app/serializers/spree/events/post_serializer.rb +26 -0
  124. data/app/serializers/spree/events/price_serializer.rb +22 -0
  125. data/app/serializers/spree/events/product_serializer.rb +24 -0
  126. data/app/serializers/spree/events/promotion_serializer.rb +32 -0
  127. data/app/serializers/spree/events/refund_serializer.rb +23 -0
  128. data/app/serializers/spree/events/reimbursement_serializer.rb +22 -0
  129. data/app/serializers/spree/events/report_serializer.rb +23 -0
  130. data/app/serializers/spree/events/return_authorization_serializer.rb +22 -0
  131. data/app/serializers/spree/events/return_item_serializer.rb +27 -0
  132. data/app/serializers/spree/events/shipment_serializer.rb +24 -0
  133. data/app/serializers/spree/events/stock_item_serializer.rb +22 -0
  134. data/app/serializers/spree/events/stock_movement_serializer.rb +22 -0
  135. data/app/serializers/spree/events/stock_transfer_serializer.rb +22 -0
  136. data/app/serializers/spree/events/store_credit_serializer.rb +30 -0
  137. data/app/serializers/spree/events/user_serializer.rb +18 -0
  138. data/app/serializers/spree/events/variant_serializer.rb +34 -0
  139. data/app/serializers/spree/events/wished_item_serializer.rb +20 -0
  140. data/app/serializers/spree/events/wishlist_serializer.rb +22 -0
  141. data/app/services/spree/addresses/update.rb +1 -1
  142. data/app/services/spree/cart/add_item.rb +1 -1
  143. data/app/services/spree/coupon_codes/coupon_codes_handler.rb +2 -1
  144. data/app/services/spree/data_feeds/google/required_attributes.rb +4 -4
  145. data/app/services/spree/newsletter/verify.rb +5 -0
  146. data/app/services/spree/products/auto_match_taxons.rb +1 -1
  147. data/app/services/spree/seeds/all.rb +1 -1
  148. data/app/services/spree/taxons/add_products.rb +8 -4
  149. data/app/services/spree/taxons/regenerate_products.rb +8 -0
  150. data/app/services/spree/taxons/remove_products.rb +12 -7
  151. data/app/subscribers/spree/event_log_subscriber.rb +64 -0
  152. data/app/subscribers/spree/export_subscriber.rb +26 -0
  153. data/app/subscribers/spree/invitation_email_subscriber.rb +40 -0
  154. data/app/subscribers/spree/product_metrics_subscriber.rb +29 -0
  155. data/app/subscribers/spree/report_subscriber.rb +26 -0
  156. data/config/locales/en.yml +126 -0
  157. data/db/migrate/20251110120000_create_spree_price_lists.rb +22 -0
  158. data/db/migrate/20251110120001_create_spree_price_rules.rb +13 -0
  159. data/db/migrate/20251110120002_add_price_list_id_to_spree_prices.rb +6 -0
  160. data/db/migrate/20251110120003_add_price_list_id_to_spree_line_items.rb +5 -0
  161. data/db/migrate/20251201172118_fix_indexes_on_friendly_id_slugs.rb +8 -0
  162. data/db/migrate/20251214000001_create_spree_webhook_endpoints.rb +19 -0
  163. data/db/migrate/20251214000002_create_spree_webhook_deliveries.rb +23 -0
  164. data/db/migrate/20251222000000_add_performance_indexes_to_spree_adjustments.rb +25 -0
  165. data/db/migrate/20260112000000_fix_spree_prices_unique_indexes.rb +33 -0
  166. data/db/migrate/20260115120000_create_spree_customer_groups.rb +14 -0
  167. data/db/migrate/20260115120001_create_spree_customer_group_users.rb +14 -0
  168. data/db/migrate/20260117140831_remove_not_null_constraint_from_policy_name.rb +5 -0
  169. data/db/migrate/20260118120000_add_statistics_to_store_products.rb +11 -0
  170. data/db/migrate/20260119120000_add_counter_caches_to_spree_products.rb +9 -0
  171. data/db/migrate/20260119170000_add_counter_caches_to_spree_taxons.rb +9 -0
  172. data/db/migrate/20260120120000_add_image_count_to_spree_variants.rb +9 -0
  173. data/lib/generators/spree/dummy/dummy_generator.rb +14 -2
  174. data/lib/spree/core/configuration.rb +1 -0
  175. data/lib/spree/core/controller_helpers/auth.rb +0 -15
  176. data/lib/spree/core/controller_helpers/currency.rb +13 -9
  177. data/lib/spree/core/controller_helpers/search.rb +1 -1
  178. data/lib/spree/core/controller_helpers/store.rb +5 -1
  179. data/lib/spree/core/engine.rb +61 -78
  180. data/lib/spree/core/importer/order.rb +1 -1
  181. data/lib/spree/core/importer/product.rb +1 -1
  182. data/lib/spree/core/preferences/preferable.rb +14 -1
  183. data/lib/spree/core/pricing/context.rb +63 -0
  184. data/lib/spree/core/pricing/resolver.rb +129 -0
  185. data/lib/spree/core/search/base.rb +1 -1
  186. data/lib/spree/core/token_generator.rb +1 -1
  187. data/lib/spree/core/version.rb +1 -1
  188. data/lib/spree/core.rb +42 -47
  189. data/lib/spree/events/adapters/active_support_notifications.rb +112 -0
  190. data/lib/spree/events/adapters/base.rb +193 -0
  191. data/lib/spree/events/registry.rb +99 -0
  192. data/lib/spree/events.rb +240 -0
  193. data/lib/spree/permitted_attributes.rb +13 -2
  194. data/lib/spree/testing_support/common_rake.rb +68 -35
  195. data/lib/spree/testing_support/factories/customer_group_factory.rb +6 -0
  196. data/lib/spree/testing_support/factories/customer_group_user_factory.rb +6 -0
  197. data/lib/spree/testing_support/factories/price_factory.rb +4 -0
  198. data/lib/spree/testing_support/factories/price_list_factory.rb +34 -0
  199. data/lib/spree/testing_support/factories/price_rule_factory.rb +49 -0
  200. data/lib/spree/testing_support/factories/promotion_rule_factory.rb +12 -0
  201. data/lib/spree/testing_support/factories/stock_item_factory.rb +6 -4
  202. data/lib/spree/testing_support/factories/store_product_factory.rb +6 -0
  203. data/lib/spree/testing_support/factories/taxon_factory.rb +0 -1
  204. data/lib/spree/testing_support/factories/webhook_delivery_factory.rb +48 -0
  205. data/lib/spree/testing_support/factories/webhook_endpoint_factory.rb +22 -0
  206. data/lib/spree/testing_support/lifecycle_events.rb +38 -0
  207. data/lib/spree/testing_support/store.rb +4 -2
  208. data/lib/spree/webhooks.rb +22 -0
  209. data/lib/tasks/products.rake +40 -0
  210. data/lib/tasks/taxons.rake +19 -0
  211. data/lib/tasks/variants.rake +18 -0
  212. metadata +112 -114
  213. data/app/jobs/spree/themes/duplicate_components_job.rb +0 -59
  214. data/app/jobs/spree/themes/screenshot_job.rb +0 -81
  215. data/app/models/concerns/spree/has_page_links.rb +0 -53
  216. data/app/models/spree/page.rb +0 -188
  217. data/app/models/spree/page_block.rb +0 -73
  218. data/app/models/spree/page_blocks/buttons.rb +0 -29
  219. data/app/models/spree/page_blocks/heading.rb +0 -18
  220. data/app/models/spree/page_blocks/image.rb +0 -20
  221. data/app/models/spree/page_blocks/link.rb +0 -21
  222. data/app/models/spree/page_blocks/mega_nav.rb +0 -33
  223. data/app/models/spree/page_blocks/mega_nav_with_subcategories.rb +0 -32
  224. data/app/models/spree/page_blocks/metafields.rb +0 -18
  225. data/app/models/spree/page_blocks/nav.rb +0 -15
  226. data/app/models/spree/page_blocks/newsletter_form.rb +0 -18
  227. data/app/models/spree/page_blocks/products/brand.rb +0 -15
  228. data/app/models/spree/page_blocks/products/buy_buttons.rb +0 -24
  229. data/app/models/spree/page_blocks/products/description.rb +0 -18
  230. data/app/models/spree/page_blocks/products/price.rb +0 -18
  231. data/app/models/spree/page_blocks/products/quantity_selector.rb +0 -20
  232. data/app/models/spree/page_blocks/products/share.rb +0 -8
  233. data/app/models/spree/page_blocks/products/title.rb +0 -19
  234. data/app/models/spree/page_blocks/products/variant_picker.rb +0 -13
  235. data/app/models/spree/page_blocks/subheading.rb +0 -17
  236. data/app/models/spree/page_blocks/text.rb +0 -16
  237. data/app/models/spree/page_link.rb +0 -60
  238. data/app/models/spree/page_section.rb +0 -222
  239. data/app/models/spree/page_sections/announcement_bar.rb +0 -28
  240. data/app/models/spree/page_sections/breadcrumbs.rb +0 -12
  241. data/app/models/spree/page_sections/collection_banner.rb +0 -18
  242. data/app/models/spree/page_sections/custom_code.rb +0 -11
  243. data/app/models/spree/page_sections/featured_posts.rb +0 -45
  244. data/app/models/spree/page_sections/featured_product.rb +0 -50
  245. data/app/models/spree/page_sections/featured_taxon.rb +0 -90
  246. data/app/models/spree/page_sections/featured_taxons.rb +0 -45
  247. data/app/models/spree/page_sections/footer.rb +0 -101
  248. data/app/models/spree/page_sections/header.rb +0 -62
  249. data/app/models/spree/page_sections/image_banner.rb +0 -55
  250. data/app/models/spree/page_sections/image_with_text.rb +0 -65
  251. data/app/models/spree/page_sections/main_password_footer.rb +0 -18
  252. data/app/models/spree/page_sections/main_password_header.rb +0 -20
  253. data/app/models/spree/page_sections/newsletter.rb +0 -54
  254. data/app/models/spree/page_sections/page_title.rb +0 -19
  255. data/app/models/spree/page_sections/post_details.rb +0 -19
  256. data/app/models/spree/page_sections/post_grid.rb +0 -19
  257. data/app/models/spree/page_sections/product_details.rb +0 -53
  258. data/app/models/spree/page_sections/product_grid.rb +0 -13
  259. data/app/models/spree/page_sections/related_products.rb +0 -58
  260. data/app/models/spree/page_sections/rich_text.rb +0 -31
  261. data/app/models/spree/page_sections/taxon_banner.rb +0 -18
  262. data/app/models/spree/page_sections/taxon_grid.rb +0 -17
  263. data/app/models/spree/page_sections/video.rb +0 -107
  264. data/app/models/spree/pages/account.rb +0 -19
  265. data/app/models/spree/pages/cart.rb +0 -19
  266. data/app/models/spree/pages/checkout.rb +0 -15
  267. data/app/models/spree/pages/custom.rb +0 -38
  268. data/app/models/spree/pages/homepage.rb +0 -72
  269. data/app/models/spree/pages/login.rb +0 -19
  270. data/app/models/spree/pages/password.rb +0 -59
  271. data/app/models/spree/pages/post.rb +0 -27
  272. data/app/models/spree/pages/post_list.rb +0 -36
  273. data/app/models/spree/pages/product_details.rb +0 -30
  274. data/app/models/spree/pages/search_results.rb +0 -43
  275. data/app/models/spree/pages/shop_all.rb +0 -40
  276. data/app/models/spree/pages/taxon.rb +0 -29
  277. data/app/models/spree/pages/taxon_list.rb +0 -41
  278. data/app/models/spree/pages/wishlist.rb +0 -15
  279. data/app/models/spree/theme.rb +0 -233
  280. data/app/models/spree/themes/default.rb +0 -97
  281. data/app/services/spree/taxons/touch_featured_sections.rb +0 -21
  282. data/db/migrate/20250120094216_create_page_builder_models.rb +0 -78
  283. data/db/migrate/20250305121352_remove_page_builder_indices.rb +0 -11
  284. data/db/migrate/20250825175217_add_missing_page_builder_indexes.rb +0 -7
  285. data/db/migrate/20250913130044_add_page_links_counter_cache_to_spree_stores.rb +0 -10
  286. data/lib/generators/spree/dummy/templates/initializers/devise.rb +0 -3
  287. data/lib/generators/spree/install/install_generator.rb +0 -219
  288. data/lib/generators/spree/install/templates/config/initializers/spree.rb +0 -126
  289. data/lib/spree/core/webhooks.rb +0 -21
  290. data/lib/spree/testing_support/factories/page_block_factory.rb +0 -22
  291. data/lib/spree/testing_support/factories/page_factory.rb +0 -33
  292. data/lib/spree/testing_support/factories/page_link_factory.rb +0 -7
  293. data/lib/spree/testing_support/factories/page_section_factory.rb +0 -27
  294. data/lib/spree/testing_support/factories/theme_factory.rb +0 -14
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ class Shipment < Spree.base_class
5
+ # Publishes custom shipment events beyond basic lifecycle events.
6
+ #
7
+ # Events:
8
+ # - shipment.shipped: Shipment was shipped
9
+ # - order.shipped: All order shipments are shipped
10
+ # - shipment.canceled: Shipment was canceled
11
+ # - shipment.resumed: Shipment was resumed from canceled state
12
+ #
13
+ # NOTE: These methods are called from the state machine's after_transition callbacks
14
+ # defined in the Shipment model, not via ActiveRecord callbacks.
15
+ #
16
+ module CustomEvents
17
+ extend ActiveSupport::Concern
18
+
19
+ def publish_shipment_shipped_event
20
+ return unless Spree::Events.enabled?
21
+
22
+ publish_event('shipment.shipped')
23
+ # Force reload of shipments association to see the new state
24
+ order.shipments.reset
25
+ publish_order_shipped_event if order.fully_shipped?
26
+ end
27
+
28
+ def publish_shipment_canceled_event
29
+ return unless Spree::Events.enabled?
30
+
31
+ publish_event('shipment.canceled')
32
+ end
33
+
34
+ def publish_shipment_resumed_event
35
+ return unless Spree::Events.enabled?
36
+
37
+ publish_event('shipment.resumed')
38
+ end
39
+
40
+ private
41
+
42
+ def publish_order_shipped_event
43
+ order.publish_event('order.shipped')
44
+ end
45
+ end
46
+ end
47
+ end
@@ -15,6 +15,9 @@ module Spree
15
15
  end
16
16
  include Spree::Shipment::Emails
17
17
  include Spree::Shipment::Webhooks
18
+ include Spree::Shipment::CustomEvents
19
+
20
+ publishes_lifecycle_events
18
21
 
19
22
  with_options inverse_of: :shipments do
20
23
  belongs_to :address, class_name: 'Spree::Address'
@@ -82,12 +85,12 @@ module Spree
82
85
  event :ship do
83
86
  transition from: %i(ready canceled), to: :shipped
84
87
  end
85
- after_transition to: :shipped, do: [:after_ship, :send_shipment_shipped_webhook]
88
+ after_transition to: :shipped, do: [:after_ship, :send_shipment_shipped_webhook, :publish_shipment_shipped_event]
86
89
 
87
90
  event :cancel do
88
91
  transition to: :canceled, from: %i(pending ready)
89
92
  end
90
- after_transition to: :canceled, do: :after_cancel
93
+ after_transition to: :canceled, do: [:after_cancel, :publish_shipment_canceled_event]
91
94
 
92
95
  event :resume do
93
96
  transition from: :canceled, to: :ready, if: lambda { |shipment|
@@ -95,7 +98,7 @@ module Spree
95
98
  }
96
99
  transition from: :canceled, to: :pending
97
100
  end
98
- after_transition from: :canceled, to: %i(pending ready shipped), do: :after_resume
101
+ after_transition from: :canceled, to: %i(pending ready shipped), do: [:after_resume, :publish_shipment_resumed_event]
99
102
 
100
103
  after_transition do |shipment, transition|
101
104
  shipment.state_changes.create!(
@@ -112,7 +115,7 @@ module Spree
112
115
  money_methods :cost, :discounted_cost, :final_price, :item_cost, :additional_tax_total, :included_tax_total, :tax_total
113
116
  alias display_amount display_cost
114
117
 
115
- auto_strip_attributes :tracking
118
+ normalizes :tracking, with: ->(value) { value&.to_s&.squish&.presence }
116
119
 
117
120
  # Returns the shipment number and shipping method name
118
121
  #
@@ -209,7 +212,8 @@ module Spree
209
212
  #
210
213
  # @return [Boolean]
211
214
  def with_free_shipping_promotion?
212
- adjustments.promotion.includes(:source).any? { |p| p.source.respond_to?(:free_shipping?) && p.source.free_shipping? }
215
+ adjustments.promotion.joins("INNER JOIN #{Spree::PromotionAction.table_name} ON #{Spree::PromotionAction.table_name}.id = #{Spree::Adjustment.table_name}.source_id").
216
+ where("#{Spree::PromotionAction.table_name}.type = 'Spree::Promotion::Actions::FreeShipping'").exists?
213
217
  end
214
218
 
215
219
  def finalize!
@@ -454,6 +458,9 @@ module Spree
454
458
  ShipmentHandler.factory(self).perform
455
459
  end
456
460
 
461
+ # publish_shipment_shipped_event, publish_shipment_canceled_event, and
462
+ # publish_shipment_resumed_event are defined in Spree::Shipment::CustomEvents
463
+
457
464
  def can_get_rates?
458
465
  return true unless order.requires_ship_address?
459
466
 
@@ -3,9 +3,6 @@ module Spree
3
3
  DIGITAL_NAME = 'Digital'
4
4
 
5
5
  include Spree::UniqueName
6
- if defined?(Spree::Webhooks::HasWebhooks)
7
- include Spree::Webhooks::HasWebhooks
8
- end
9
6
 
10
7
  with_options inverse_of: :shipping_category do
11
8
  has_many :products
@@ -5,9 +5,6 @@ module Spree
5
5
  include Spree::Metafields
6
6
  include Spree::Metadata
7
7
  include Spree::DisplayOn
8
- if defined?(Spree::Webhooks::HasWebhooks)
9
- include Spree::Webhooks::HasWebhooks
10
- end
11
8
  if defined?(Spree::VendorConcern)
12
9
  include Spree::VendorConcern
13
10
  end
@@ -35,7 +35,7 @@ module Spree
35
35
  private
36
36
 
37
37
  def association_loaded?
38
- variant.association(:stock_items).loaded? && variant.association(:stock_locations).loaded?
38
+ variant.association(:stock_items).loaded?
39
39
  end
40
40
 
41
41
  def scope_to_location(collection)
@@ -6,6 +6,8 @@ module Spree
6
6
  include Spree::Metadata
7
7
  include Spree::StockItem::Webhooks
8
8
 
9
+ publishes_lifecycle_events
10
+
9
11
  with_options inverse_of: :stock_items do
10
12
  belongs_to :stock_location, class_name: 'Spree::StockLocation'
11
13
  belongs_to :variant, -> { with_deleted }, class_name: 'Spree::Variant'
@@ -1,9 +1,6 @@
1
1
  module Spree
2
2
  class StockLocation < Spree.base_class
3
3
  include Spree::UniqueName
4
- if defined?(Spree::Webhooks::HasWebhooks)
5
- include Spree::Webhooks::HasWebhooks
6
- end
7
4
  if defined?(Spree::Security::StockLocations)
8
5
  include Spree::Security::StockLocations
9
6
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ class StockMovement < Spree.base_class
5
+ # Publishes custom stock events when stock levels change.
6
+ #
7
+ # Events:
8
+ # - product.out_of_stock: Product has no stock left for any variant
9
+ # - product.back_in_stock: Product was out of stock and now has stock again
10
+ #
11
+ module CustomEvents
12
+ extend ActiveSupport::Concern
13
+
14
+ included do
15
+ around_save :track_stock_changes_for_events
16
+ end
17
+
18
+ private
19
+
20
+ def track_stock_changes_for_events
21
+ return yield unless Spree::Events.enabled?
22
+
23
+ the_product = product
24
+ product_was_in_stock = the_product.any_variant_in_stock_or_backorderable?
25
+
26
+ yield
27
+
28
+ # Reload to get fresh stock data
29
+ the_product.reload
30
+ product_now_in_stock = the_product.any_variant_in_stock_or_backorderable?
31
+
32
+ if product_was_in_stock && !product_now_in_stock
33
+ the_product.publish_event('product.out_of_stock')
34
+ elsif !product_was_in_stock && product_now_in_stock
35
+ the_product.publish_event('product.back_in_stock')
36
+ end
37
+ end
38
+
39
+ def product
40
+ stock_item.variant.product
41
+ end
42
+ end
43
+ end
44
+ end
@@ -6,6 +6,9 @@ module Spree
6
6
  }.freeze
7
7
 
8
8
  include Spree::StockMovement::Webhooks
9
+ include Spree::StockMovement::CustomEvents
10
+
11
+ publishes_lifecycle_events
9
12
 
10
13
  belongs_to :stock_item, class_name: 'Spree::StockItem', inverse_of: :stock_movements
11
14
  belongs_to :originator, polymorphic: true
@@ -5,9 +5,8 @@ module Spree
5
5
  include Spree::NumberAsParam
6
6
  include Spree::Metafields
7
7
  include Spree::Metadata
8
- if defined?(Spree::Webhooks::HasWebhooks)
9
- include Spree::Webhooks::HasWebhooks
10
- end
8
+
9
+ publishes_lifecycle_events
11
10
 
12
11
  has_many :stock_movements, as: :originator
13
12
  accepts_nested_attributes_for :stock_movements, reject_if: proc { |attributes|
@@ -12,10 +12,8 @@ module Spree
12
12
  include Spree::Metadata
13
13
  include Spree::Stores::Setup
14
14
  include Spree::Stores::Socials
15
- include Spree::Webhooks::HasWebhooks if defined?(Spree::Webhooks::HasWebhooks)
16
15
  include Spree::Security::Stores if defined?(Spree::Security::Stores)
17
16
  include Spree::UserManagement
18
- include Spree::HasPageLinks
19
17
 
20
18
  #
21
19
  # Magic methods
@@ -115,23 +113,10 @@ module Spree
115
113
 
116
114
  has_many :policies, class_name: 'Spree::Policy', dependent: :destroy, as: :owner
117
115
 
118
- #
119
- # Page Builder associations
120
- #
121
- has_many :themes, -> { without_previews }, class_name: 'Spree::Theme', dependent: :destroy, inverse_of: :store
122
- has_many :theme_previews,
123
- -> { only_previews },
124
- class_name: 'Spree::Theme',
125
- through: :themes,
126
- source: :previews,
127
- inverse_of: :store,
128
- dependent: :destroy
129
- has_one :default_theme, -> { without_previews.where(default: true) }, class_name: 'Spree::Theme', inverse_of: :store
130
- alias theme default_theme
131
- has_many :theme_pages, class_name: 'Spree::Page', through: :themes, source: :pages
132
- has_many :theme_page_previews, class_name: 'Spree::Page', through: :theme_pages, source: :previews
133
- has_many :pages, -> { without_previews.custom }, class_name: 'Spree::Pages::Custom', dependent: :destroy, as: :pageable
134
- has_many :page_previews, class_name: 'Spree::Pages::Custom', through: :pages, as: :pageable, source: :previews
116
+ has_many :webhook_endpoints, class_name: 'Spree::WebhookEndpoint', dependent: :destroy, inverse_of: :store
117
+ has_many :webhook_deliveries, through: :webhook_endpoints, class_name: 'Spree::WebhookDelivery'
118
+
119
+ has_many :customer_groups, class_name: 'Spree::CustomerGroup', dependent: :destroy, inverse_of: :store
135
120
 
136
121
  #
137
122
  # ActionText
@@ -189,7 +174,6 @@ module Spree
189
174
  after_create :ensure_default_post_categories_are_created
190
175
  after_create :import_products_from_store, if: -> { import_products_from_store_id.present? }
191
176
  after_create :import_payment_methods_from_store, if: -> { import_payment_methods_from_store_id.present? }
192
- after_create :create_default_theme
193
177
  after_create :create_default_policies
194
178
  before_destroy :validate_not_last, unless: :skip_validate_not_last
195
179
  before_destroy :pass_default_flag_to_other_store
@@ -347,12 +331,6 @@ module Spree
347
331
  end
348
332
  end
349
333
 
350
- def checkout_zone_or_default
351
- Spree::Deprecation.warn('Store#checkout_zone_or_default is deprecated and will be removed in Spree 5')
352
-
353
- @checkout_zone_or_default ||= checkout_zone || Spree::Zone.default_checkout_zone
354
- end
355
-
356
334
  def supported_shipping_zones
357
335
  @supported_shipping_zones ||= if checkout_zone.present?
358
336
  [checkout_zone]
@@ -373,7 +351,7 @@ module Spree
373
351
  end
374
352
 
375
353
  def admin_users
376
- Spree::Deprecation.warn('Store#admin_users is deprecated and will be removed in Spree 6.0. Please use Store#users instead.')
354
+ Spree::Deprecation.warn('Store#admin_users is deprecated and will be removed in Spree 5.5. Please use Store#users instead.')
377
355
 
378
356
  users
379
357
  end
@@ -420,19 +398,12 @@ module Spree
420
398
 
421
399
  %w[customer_terms_of_service customer_privacy_policy customer_returns_policy customer_shipping_policy].each do |policy_method|
422
400
  define_method policy_method do
423
- Spree::Deprecation.warn("Store##{policy_method} is deprecated and will be removed in Spree 6.0. Please use Store#policies instead.")
401
+ Spree::Deprecation.warn("Store##{policy_method} is deprecated and will be removed in Spree 5.5. Please use Store#policies instead.")
424
402
 
425
403
  ActionText::RichText.find_by(name: policy_method, record: self)
426
404
  end
427
405
  end
428
406
 
429
- # Returns all active webhooks subscribers for the store
430
- #
431
- # @return [Array<Spree::Webhooks::Subscriber>]
432
- def active_webhooks_subscribers
433
- @active_webhooks_subscribers ||= Spree::Webhooks::Subscriber.active
434
- end
435
-
436
407
  private
437
408
 
438
409
  def countries_available_for_checkout_cache_key
@@ -496,69 +467,77 @@ module Spree
496
467
  end
497
468
 
498
469
  def ensure_default_taxonomies_are_created
499
- [
500
- translate_with_store_locale_fallback('spree.taxonomy_categories_name'),
501
- translate_with_store_locale_fallback('spree.taxonomy_brands_name'),
502
- translate_with_store_locale_fallback('spree.taxonomy_collections_name')
503
- ].each do |taxonomy_name|
504
- # Manual exists?/create to work around Mobility bug with find_or_create_by
505
- next if taxonomies.with_matching_name(taxonomy_name).exists?
506
-
507
- taxonomies.create(name: taxonomy_name)
470
+ Spree::Events.disable do
471
+ [
472
+ translate_with_store_locale_fallback('spree.taxonomy_categories_name'),
473
+ translate_with_store_locale_fallback('spree.taxonomy_brands_name'),
474
+ translate_with_store_locale_fallback('spree.taxonomy_collections_name')
475
+ ].each do |taxonomy_name|
476
+ # Manual exists?/create to work around Mobility bug with find_or_create_by
477
+ next if taxonomies.with_matching_name(taxonomy_name).exists?
478
+
479
+ taxonomies.create(name: taxonomy_name)
480
+ end
508
481
  end
509
482
  end
510
483
 
511
484
  def ensure_default_automatic_taxons
512
- # Use Mobility-safe lookup for taxonomy
513
- collections_taxonomy = taxonomies.with_matching_name(translate_with_store_locale_fallback('spree.taxonomy_collections_name')).first
514
- return unless collections_taxonomy.present?
515
-
516
- automatic_taxons_config = [
517
- { name: translate_with_store_locale_fallback('spree.automatic_taxon_names.on_sale'), rule_type: 'Spree::TaxonRules::Sale', rule_value: 'true' },
518
- { name: translate_with_store_locale_fallback('spree.automatic_taxon_names.new_arrivals'), rule_type: 'Spree::TaxonRules::AvailableOn', rule_value: 30 }
519
- ]
520
-
521
- automatic_taxons_config.map do |config|
522
- # Manual exists?/create to work around Mobility bug with first_or_create
523
- taxon_scope = collections_taxonomy.taxons.automatic.with_matching_name(config[:name])
524
-
525
- if taxon_scope.exists?
526
- taxon_scope.first
527
- else
528
- collections_taxonomy.taxons.create!(
529
- name: config[:name],
530
- automatic: true,
531
- parent: collections_taxonomy.root,
532
- taxon_rules: [TaxonRule.new(type: config[:rule_type], value: config[:rule_value])]
533
- )
485
+ Spree::Events.disable do
486
+ # Use Mobility-safe lookup for taxonomy
487
+ collections_taxonomy = taxonomies.with_matching_name(translate_with_store_locale_fallback('spree.taxonomy_collections_name')).first
488
+ return unless collections_taxonomy.present?
489
+
490
+ automatic_taxons_config = [
491
+ { name: translate_with_store_locale_fallback('spree.automatic_taxon_names.on_sale'), rule_type: 'Spree::TaxonRules::Sale', rule_value: 'true' },
492
+ { name: translate_with_store_locale_fallback('spree.automatic_taxon_names.new_arrivals'), rule_type: 'Spree::TaxonRules::AvailableOn', rule_value: 30 }
493
+ ]
494
+
495
+ automatic_taxons_config.map do |config|
496
+ # Manual exists?/create to work around Mobility bug with first_or_create
497
+ taxon_scope = collections_taxonomy.taxons.automatic.with_matching_name(config[:name])
498
+
499
+ if taxon_scope.exists?
500
+ taxon_scope.first
501
+ else
502
+ collections_taxonomy.taxons.create!(
503
+ name: config[:name],
504
+ automatic: true,
505
+ parent: collections_taxonomy.root,
506
+ taxon_rules: [TaxonRule.new(type: config[:rule_type], value: config[:rule_value])]
507
+ )
508
+ end
534
509
  end
535
510
  end
536
511
  end
537
512
 
538
513
  def ensure_default_post_categories_are_created
539
- [
540
- translate_with_store_locale_fallback('spree.default_post_categories.resources'),
541
- translate_with_store_locale_fallback('spree.default_post_categories.articles'),
542
- translate_with_store_locale_fallback('spree.default_post_categories.news')
543
- ].each do |category_title|
544
- # Use exists?/create pattern for safety
545
- next if post_categories.where(title: category_title).exists?
546
-
547
- post_categories.create(title: category_title)
514
+ Spree::Events.disable do
515
+ [
516
+ translate_with_store_locale_fallback('spree.default_post_categories.resources'),
517
+ translate_with_store_locale_fallback('spree.default_post_categories.articles'),
518
+ translate_with_store_locale_fallback('spree.default_post_categories.news')
519
+ ].each do |category_title|
520
+ # Use exists?/create pattern for safety
521
+ next if post_categories.where(title: category_title).exists?
522
+
523
+ post_categories.create(title: category_title)
524
+ end
548
525
  end
549
526
  end
550
527
 
551
528
  def create_default_policies
552
- [
553
- translate_with_store_locale_fallback('spree.terms_of_service'),
554
- translate_with_store_locale_fallback('spree.privacy_policy'),
555
- translate_with_store_locale_fallback('spree.returns_policy'),
556
- translate_with_store_locale_fallback('spree.shipping_policy')
557
- ].each do |policy_name|
558
- # Manual exists?/create to work around Mobility bug with find_or_create_by
559
- next if policies.with_matching_name(policy_name).exists?
560
-
561
- policies.create(name: policy_name)
529
+ Spree::Events.disable do
530
+ [
531
+ translate_with_store_locale_fallback('spree.terms_of_service'),
532
+ translate_with_store_locale_fallback('spree.privacy_policy'),
533
+ translate_with_store_locale_fallback('spree.returns_policy'),
534
+ translate_with_store_locale_fallback('spree.shipping_policy')
535
+ ].each do |policy_name|
536
+ # Manual exists?/create to work around Mobility bug with find_or_create_by
537
+ next if policies.with_matching_name(policy_name).exists?
538
+
539
+ policies.create(name: policy_name)
540
+ end
562
541
  end
563
542
  end
564
543
 
@@ -605,12 +584,5 @@ module Spree
605
584
 
606
585
  self.url = [code, Spree.root_domain].join('.')
607
586
  end
608
-
609
- def create_default_theme
610
- themes.find_or_initialize_by(default: true) do |theme|
611
- theme.name = Spree.t(:default_theme_name)
612
- theme.save!
613
- end
614
- end
615
587
  end
616
588
  end
@@ -3,9 +3,8 @@ module Spree
3
3
  include Spree::SingleStoreResource
4
4
  include Spree::Metafields
5
5
  include Spree::Metadata
6
- if defined?(Spree::Webhooks::HasWebhooks)
7
- include Spree::Webhooks::HasWebhooks
8
- end
6
+
7
+ publishes_lifecycle_events
9
8
 
10
9
  acts_as_paranoid
11
10
 
@@ -186,7 +185,7 @@ module Spree
186
185
 
187
186
  class << self
188
187
  def default_created_by
189
- Spree::Deprecation.warn('StoreCredit#default_created_by is deprecated and will be removed in Spree 6.0. Please use store.users.first instead.')
188
+ Spree::Deprecation.warn('StoreCredit#default_created_by is deprecated and will be removed in Spree 5.5. Please use store.users.first instead.')
190
189
 
191
190
  Spree::Store.current.users.first
192
191
  end
@@ -7,5 +7,19 @@ module Spree
7
7
 
8
8
  validates :store, :product, presence: true
9
9
  validates :store_id, uniqueness: { scope: :product_id }
10
+
11
+ def refresh_metrics!
12
+ completed_order_ids = product.completed_orders.where(store_id: store_id).select(:id)
13
+ variant_ids = product.variants_including_master.ids
14
+
15
+ line_items = Spree::LineItem.joins(:order)
16
+ .where(spree_orders: { id: completed_order_ids })
17
+ .where(variant_id: variant_ids)
18
+
19
+ update!(
20
+ units_sold_count: line_items.sum(:quantity),
21
+ revenue: line_items.sum(:pre_tax_amount)
22
+ )
23
+ end
10
24
  end
11
25
  end