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,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ # Handles order completion events to update product metrics.
5
+ #
6
+ # When an order is completed, this subscriber enqueues background jobs
7
+ # to refresh the metrics (units_sold_count, revenue) for each product
8
+ # in the order.
9
+ class ProductMetricsSubscriber < Spree::Subscriber
10
+ subscribes_to 'order.completed'
11
+
12
+ on 'order.completed', :refresh_product_metrics
13
+
14
+ def refresh_product_metrics(event)
15
+ order_id = event.payload['id']
16
+ store_id = event.payload['store_id']
17
+ return unless order_id && store_id
18
+
19
+ order = Spree::Order.find_by(id: order_id)
20
+ return unless order
21
+
22
+ product_ids = order.line_items.includes(:variant).map { |li| li.variant.product_id }.uniq
23
+ return if product_ids.empty?
24
+
25
+ jobs = product_ids.map { |product_id| Spree::Products::RefreshMetricsJob.new(product_id, store_id) }
26
+ ActiveJob.perform_all_later(jobs)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ # Handles Report lifecycle events.
5
+ #
6
+ # This subscriber replaces the following callbacks in Spree::Report:
7
+ # - after_commit :generate_async, on: :create
8
+ #
9
+ # When a report is created, this subscriber triggers the async generation job.
10
+ #
11
+ # We use async: false because this subscriber just queues a background job,
12
+ # so there's no benefit to running the subscriber itself asynchronously.
13
+ #
14
+ class ReportSubscriber < Spree::Subscriber
15
+ subscribes_to 'report.created', async: false
16
+
17
+ on 'report.created', :generate_report_async
18
+
19
+ def generate_report_async(event)
20
+ report_id = event.payload['id']
21
+ return unless report_id
22
+
23
+ Spree::Reports::GenerateJob.perform_later(report_id)
24
+ end
25
+ end
26
+ end
@@ -50,6 +50,9 @@ en:
50
50
  number: Number
51
51
  verification_value: Verification Value
52
52
  year: Year
53
+ spree/customer_group:
54
+ description: Description
55
+ name: Name
53
56
  spree/gift_card:
54
57
  amount_used: Amount used
55
58
  expires_at: Expires at
@@ -80,6 +83,7 @@ en:
80
83
  coupon_code: Coupon Code
81
84
  created_at: Order Date
82
85
  email: Email
86
+ internal_note: Internal Note
83
87
  ip_address: IP Address
84
88
  item_count: Items
85
89
  item_total: Item Total
@@ -115,6 +119,17 @@ en:
115
119
  spree/policy:
116
120
  body: Body
117
121
  owner: Policy for
122
+ spree/price_list:
123
+ description: Description
124
+ ends_at: Ends At
125
+ match_policy: Match Policy
126
+ name: Name
127
+ priority: Priority
128
+ starts_at: Starts At
129
+ status: Status
130
+ spree/price_rule:
131
+ priority: Priority
132
+ type: Type
118
133
  spree/product:
119
134
  available_on: Available On
120
135
  cost_currency: Cost Currency
@@ -281,6 +296,10 @@ en:
281
296
  attributes:
282
297
  amount:
283
298
  greater_than_max_amount: is greater than the allowed maximum amount of %{max_amount}
299
+ spree/price_list:
300
+ attributes:
301
+ ends_at:
302
+ must_be_after_starts_at: must be after starts at
284
303
  spree/product:
285
304
  attributes:
286
305
  base:
@@ -365,6 +384,12 @@ en:
365
384
  spree/credit_card:
366
385
  one: Credit Card
367
386
  other: Credit Cards
387
+ spree/customer_group:
388
+ one: Customer Group
389
+ other: Customer Groups
390
+ spree/customer_group_user:
391
+ one: Customer Group User
392
+ other: Customer Group Users
368
393
  spree/customer_return:
369
394
  one: Customer Return
370
395
  other: Customer Returns
@@ -413,6 +438,12 @@ en:
413
438
  spree/price:
414
439
  one: Price
415
440
  other: Prices
441
+ spree/price_list:
442
+ one: Price List
443
+ other: Price Lists
444
+ spree/price_rule:
445
+ one: Price Rule
446
+ other: Price Rules
416
447
  spree/product:
417
448
  one: Product
418
449
  other: Products
@@ -562,6 +593,7 @@ en:
562
593
  number:
563
594
  currency:
564
595
  format:
596
+ delimiter: ","
565
597
  separator: "."
566
598
  percentage:
567
599
  format:
@@ -585,6 +617,7 @@ en:
585
617
  actions:
586
618
  approve: Approve
587
619
  cancel: Cancel
620
+ close: Close
588
621
  confirm: Confirm
589
622
  connect: Connect
590
623
  continue: Continue
@@ -596,6 +629,7 @@ en:
596
629
  list: List
597
630
  listing: Listing
598
631
  new: New
632
+ open: Open
599
633
  publish: Publish
600
634
  receive: Mark as received
601
635
  refund: Refund
@@ -614,6 +648,7 @@ en:
614
648
  add_address: Add address
615
649
  add_country: Add Country
616
650
  add_coupon_code: Add Coupon Code
651
+ add_customers: Add Customers
617
652
  add_gift_card: Add Gift Card
618
653
  add_link: Add link
619
654
  add_new_address: Add new address
@@ -630,6 +665,7 @@ en:
630
665
  add_products: Add Products
631
666
  add_rule_of_type: Add rule of type
632
667
  add_section: Add section
668
+ add_selected: Add Selected
633
669
  add_selected_products: Add selected products
634
670
  add_selected_variant: Add selected variant
635
671
  add_state: Add State
@@ -640,6 +676,7 @@ en:
640
676
  add_to_waitlist: Add to waitlist
641
677
  add_to_waitlist_description: Add to waitlist description
642
678
  add_variant: Add Variant
679
+ added_at: Added At
643
680
  added_to_cart: Added to cart successfully!
644
681
  additional_item: Additional Item
645
682
  address: Address
@@ -673,10 +710,13 @@ en:
673
710
  adjustable: Adjustable
674
711
  adjustment: Adjustment
675
712
  adjustment_amount: Amount
713
+ adjustment_amount_help: Positive values are charges, negative values are credits.
714
+ adjustment_closed_description: The adjustment is closed, and will not be recalculated.
676
715
  adjustment_labels:
677
716
  tax_rates:
678
717
  excluding_tax: "%{name}%{amount}"
679
718
  including_tax: "%{name}%{amount} (Included in Price)"
719
+ adjustment_open_description: The adjustment is open, and will be recalculated.
680
720
  adjustment_successfully_closed: Adjustment has been successfully closed!
681
721
  adjustment_successfully_opened: Adjustment has been successfully opened!
682
722
  adjustment_total: Adjustment Total
@@ -702,10 +742,14 @@ en:
702
742
  amount: Amount
703
743
  analytics: Analytics
704
744
  and: and
745
+ and_n_more:
746
+ one: "...and %{count} more"
747
+ other: "...and %{count} more"
705
748
  api_keys: API keys
706
749
  apis:
707
750
  platform: Platform API
708
751
  storefront: Storefront API
752
+ applied_to: Applied to
709
753
  apply: Apply
710
754
  apply_only_on_full_priced_items: Apply only on full priced items
711
755
  apply_store_credit: Apply Store Credit
@@ -829,6 +873,7 @@ en:
829
873
  close: Close
830
874
  close_all_adjustments: Close All Adjustments
831
875
  close_sidebar: Close sidebar
876
+ closed: Closed
832
877
  code: Code
833
878
  colors: Colors
834
879
  company: Company
@@ -909,13 +954,25 @@ en:
909
954
  custom_domains: Custom domains
910
955
  custom_font_code: Custom font code
911
956
  customer: Customer
957
+ customer_could_not_be_removed_from_group: Customer could not be removed from group
912
958
  customer_details: Customer Details
913
959
  customer_details_updated: Customer Details Updated
960
+ customer_group: Customer Group
961
+ customer_group_rule:
962
+ choose_customer_groups: 'Select the customer group(s) to which this promotion should apply:'
963
+ customer_groups: Customer Groups
964
+ customer_removed_from_group: Customer removed from group
914
965
  customer_return: Customer Return
915
966
  customer_returns: Customer Returns
916
967
  customer_search: Customer Search
917
968
  customer_support_email: Customer Support Email
918
969
  customers: Customers
970
+ customers_added_to_group:
971
+ one: "%{count} customer added to group"
972
+ other: "%{count} customers added to group"
973
+ customers_removed_from_group:
974
+ one: "%{count} customer removed from group"
975
+ other: "%{count} customers removed from group"
919
976
  cut: Cut
920
977
  cvv: CVV
921
978
  cvv_response: CVV Response
@@ -995,6 +1052,7 @@ en:
995
1052
  duplicating: Duplicating
996
1053
  edit: Edit
997
1054
  edit_address: Edit address
1055
+ edit_prices: Edit Prices
998
1056
  edit_refund: Edit refund
999
1057
  editing_resource: Editing %{resource}
1000
1058
  editing_rma_reason: Editing RMA Reason
@@ -1173,6 +1231,7 @@ en:
1173
1231
  image: Image
1174
1232
  image_alt_text: Add Alt text to your image
1175
1233
  images: Images
1234
+ immediately: Immediately
1176
1235
  implement_eligible_for_return: 'Must implement #eligible_for_return? for your EligibilityValidator.'
1177
1236
  implement_requires_manual_intervention: 'Must implement #requires_manual_intervention? for your EligibilityValidator.'
1178
1237
  import: Import
@@ -1198,6 +1257,7 @@ en:
1198
1257
  intercept_email_address: Intercept Email Address
1199
1258
  intercept_email_instructions: Override email recipient and replace with this address.
1200
1259
  internal_name: Internal Name
1260
+ internal_note: Internal Note
1201
1261
  internal_server_error: Internal Server Error
1202
1262
  internal_server_error_message: The server encountered an internal error and was unable to complete your request.
1203
1263
  internationalization: Internationalization
@@ -1246,6 +1306,7 @@ en:
1246
1306
  join_slack: Join Slack
1247
1307
  key: Key
1248
1308
  kind: Kind
1309
+ label: Label
1249
1310
  language: Language
1250
1311
  last_name: Last Name
1251
1312
  last_name_begins_with: Last Name Begins With
@@ -1284,6 +1345,9 @@ en:
1284
1345
  make_sure_the_above_reimbursement_amount_is_correct: Make sure the above reimbursement amount is correct
1285
1346
  manage_promotion_categories: Manage Promotion Categories
1286
1347
  manage_variants: Manage Variants
1348
+ manual: Manual
1349
+ manual_adjustment: Manual Adjustment
1350
+ manual_adjustments: Manual Adjustments
1287
1351
  manual_intervention_required: Manual intervention required
1288
1352
  mark_as_default: Mark as default
1289
1353
  master: Master
@@ -1292,8 +1356,12 @@ en:
1292
1356
  match_choices:
1293
1357
  all: All
1294
1358
  none: None
1359
+ match_policies:
1360
+ all: Match ALL rules
1361
+ any: Match ANY rule
1295
1362
  max: Max
1296
1363
  max_items: Max Items
1364
+ max_quantity: Max Quantity
1297
1365
  media: Media
1298
1366
  memo: Memo
1299
1367
  meta_description: Meta Description
@@ -1303,6 +1371,7 @@ en:
1303
1371
  metafield_definitions: Metafield Definitions
1304
1372
  metafields: Metafields
1305
1373
  min: Min
1374
+ min_quantity: Min Quantity
1306
1375
  minimal_amount: Minimal Amount
1307
1376
  minimum_password_length:
1308
1377
  one: "(%{count} character minimum)"
@@ -1325,6 +1394,7 @@ en:
1325
1394
  name_on_card: Name on card
1326
1395
  name_or_sku: Name or SKU (enter at least first 3 characters of product name)
1327
1396
  navigation: Navigation
1397
+ never: Never
1328
1398
  new: New
1329
1399
  new_address: New address
1330
1400
  new_adjustment: New Adjustment
@@ -1333,6 +1403,7 @@ en:
1333
1403
  new_country: New Country
1334
1404
  new_custom_domain: New Custom Domain
1335
1405
  new_customer: New Customer
1406
+ new_customer_group: New Customer Group
1336
1407
  new_customer_return: New Customer Return
1337
1408
  new_digital_asset: New digital asset
1338
1409
  new_domain: New domain
@@ -1352,6 +1423,7 @@ en:
1352
1423
  new_policy: New Policy
1353
1424
  new_post: New Post
1354
1425
  new_post_category: New Post Category
1426
+ new_price_list: New Price List
1355
1427
  new_product: New Product
1356
1428
  new_promotion: New Promotion
1357
1429
  new_promotion_category: New Promotion Category
@@ -1381,6 +1453,7 @@ en:
1381
1453
  new_tracker: New Tracker
1382
1454
  new_user: New User
1383
1455
  new_variant: New Variant
1456
+ new_webhook_endpoint: New Webhook Endpoint
1384
1457
  new_zone: New Zone
1385
1458
  newsletter_mailer:
1386
1459
  email_confirmation:
@@ -1397,11 +1470,15 @@ en:
1397
1470
  next: Next
1398
1471
  no_actions_added: No actions added
1399
1472
  no_address_given: No address given
1473
+ no_adjustments: No adjustments
1400
1474
  no_available_date_set: No available date set
1401
1475
  no_billing_address_available: No billing address available
1402
1476
  no_cc_type: N/A
1403
1477
  no_country: No country set
1478
+ no_customers_found: No customers found
1479
+ no_customers_in_group: No customers in this group yet
1404
1480
  no_email_provided: No email provided
1481
+ no_internal_note: No internal note
1405
1482
  no_limits_zone: No Limits
1406
1483
  no_payment_found: No payment found
1407
1484
  no_payment_provider_settings_message: This payment provider has no custom settings available
@@ -1411,6 +1488,8 @@ en:
1411
1488
  oops: Oops!
1412
1489
  no_products_added: No products added
1413
1490
  no_products_found: No products found
1491
+ no_products_in_price_list: No products in this price list
1492
+ no_promotions_applied: No promotions applied
1414
1493
  no_report_data: No report data
1415
1494
  no_resource_found: No %{resource} found
1416
1495
  no_results: No results
@@ -1420,7 +1499,9 @@ en:
1420
1499
  no_shipping_address_available: No shipping address available
1421
1500
  no_shipping_method_selected: No shipping method selected.
1422
1501
  no_state_changes: No state changes yet.
1502
+ no_taxes: No taxes
1423
1503
  no_tracking_present: No tracking details provided.
1504
+ no_users_selected: No users selected
1424
1505
  none: None
1425
1506
  none_selected: None Selected
1426
1507
  normal_amount: Normal Amount
@@ -1657,7 +1738,34 @@ en:
1657
1738
  previous: Previous
1658
1739
  previously_used_card: Previously Used Cards
1659
1740
  price: Price
1741
+ price_list_form:
1742
+ match_policies:
1743
+ all: Match all of these rules
1744
+ any: Match any of these rules
1745
+ price_list_statuses:
1746
+ active: Active
1747
+ draft: Draft
1748
+ inactive: Inactive
1749
+ scheduled: Scheduled
1750
+ scheduled_active: Scheduled (Active)
1751
+ price_lists: Price Lists
1660
1752
  price_range: Price Range
1753
+ price_rule_types:
1754
+ customer_group_rule:
1755
+ description: Apply pricing to specific customer groups
1756
+ name: Customer Group
1757
+ user_rule:
1758
+ description: Apply pricing to specific customers
1759
+ name: User
1760
+ volume_rule:
1761
+ description: Apply pricing based on quantity
1762
+ name: Volume
1763
+ zone_rule:
1764
+ description: Apply pricing based on geographic zone
1765
+ name: Zone
1766
+ price_rules:
1767
+ customer_group_rule:
1768
+ description: Apply pricing to specific customer groups
1661
1769
  price_sack: Price Sack
1662
1770
  prices: Prices
1663
1771
  pricing: Pricing
@@ -1681,7 +1789,9 @@ en:
1681
1789
  manual: Manually choose
1682
1790
  product_sold_out: This item is sold out
1683
1791
  products: Products
1792
+ products_added: Products added
1684
1793
  products_cannot_be_shipped: We cannot ship %{product_names} to your current location at the moment. Please change your shipping address or remove %{product_names} from your cart.
1794
+ products_removed: Products removed
1685
1795
  products_sort_options:
1686
1796
  best_selling: Best Selling
1687
1797
  manual: Manual
@@ -1725,6 +1835,9 @@ en:
1725
1835
  currency:
1726
1836
  description: Limit to orders in a specific currency
1727
1837
  name: Currency
1838
+ customer_group:
1839
+ description: Available only to customers in specified customer group(s)
1840
+ name: Customer Group(s)
1728
1841
  first_order:
1729
1842
  description: Must be the customer's first order
1730
1843
  name: First order
@@ -1886,8 +1999,11 @@ en:
1886
1999
  search: Search
1887
2000
  search_all: Search all
1888
2001
  search_by: 'Search by:'
2002
+ search_customers: Search customers
2003
+ search_customers_hint: Search by email address
1889
2004
  search_engine_listing: Search engine listing
1890
2005
  search_order_number: Order Number
2006
+ search_products: Search products
1891
2007
  search_results: Search results for '%{keywords}'
1892
2008
  search_results_for: Search results for %{query}
1893
2009
  searching: Searching
@@ -2025,6 +2141,7 @@ en:
2025
2141
  start_typing_to_search_for_products: Start typing to search for products
2026
2142
  start_typing_to_search_for_variants: Start typing to search for variants
2027
2143
  starting_from: Starts at
2144
+ starts_at: Starts at
2028
2145
  state: State
2029
2146
  state_based: State Based
2030
2147
  state_changes: State Changes
@@ -2252,6 +2369,8 @@ en:
2252
2369
  twitter: Twitter
2253
2370
  type: Type
2254
2371
  type_to_search: Type to search
2372
+ type_to_search_customers: Type to search for customers
2373
+ type_to_search_products: Type to search for products
2255
2374
  unable_to_connect_to_gateway: Unable to connect to gateway.
2256
2375
  unable_to_create_reimbursements: Unable to create reimbursements because there are items pending manual intervention.
2257
2376
  under_price: Under %{price}
@@ -2262,6 +2381,7 @@ en:
2262
2381
  imperial_system: Imperial system
2263
2382
  metric: metric
2264
2383
  metric_system: Metric system
2384
+ unlimited: Unlimited
2265
2385
  unlock: Unlock
2266
2386
  unprocessable_entity: Unprocessable Entity
2267
2387
  unprocessable_entity_message: Your request could not be processed. Please check the form for errors and try again.
@@ -2286,6 +2406,7 @@ en:
2286
2406
  use_shipping_address: Use Shipping Address
2287
2407
  used: Used
2288
2408
  user: User
2409
+ user_ids: Users
2289
2410
  user_not_found: User not found
2290
2411
  user_rule:
2291
2412
  choose_users: 'Input the full email address of a registered customer(s) in the search field to whom this discount should apply:'
@@ -2317,9 +2438,13 @@ en:
2317
2438
  visibility: Visibility
2318
2439
  visits: Visits
2319
2440
  void: Void
2441
+ volume_rule:
2442
+ cart_total: Cart Total Quantity
2443
+ line_item: Line Item Quantity
2320
2444
  we_are_busy_updating: We're updating the %{store_name} homepage.
2321
2445
  we_are_busy_updating_page: We're busy updating this page.
2322
2446
  we_will_be_back: We will be back.
2447
+ webhook_endpoints: Webhook Endpoints
2323
2448
  webhooks: Webhooks
2324
2449
  weight: Weight
2325
2450
  weight_unit: Weight unit
@@ -2342,6 +2467,7 @@ en:
2342
2467
  zipcode: Zip Code
2343
2468
  zipcode_required: Zip Code Required
2344
2469
  zone: Zone
2470
+ zone_ids: Zones
2345
2471
  zones: Zones
2346
2472
  views:
2347
2473
  pagination:
@@ -0,0 +1,22 @@
1
+ class CreateSpreePriceLists < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :spree_price_lists do |t|
4
+ t.belongs_to :store, null: false, foreign_key: false, index: true
5
+ t.string :name, null: false
6
+ t.text :description
7
+ t.integer :position, null: false, default: 0
8
+ t.string :status, null: false, default: 'draft'
9
+ t.datetime :starts_at
10
+ t.datetime :ends_at
11
+ t.string :match_policy, null: false, default: 'all'
12
+ t.timestamps
13
+ t.datetime :deleted_at
14
+ end
15
+
16
+ add_index :spree_price_lists, :status
17
+ add_index :spree_price_lists, :position
18
+ add_index :spree_price_lists, [:starts_at, :ends_at]
19
+ add_index :spree_price_lists, :deleted_at
20
+ add_index :spree_price_lists, [:store_id, :status, :position]
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ class CreateSpreePriceRules < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :spree_price_rules do |t|
4
+ t.belongs_to :price_list, null: false, foreign_key: false, index: true
5
+ t.string :type, null: false
6
+ t.text :preferences
7
+ t.timestamps
8
+ end
9
+
10
+ add_index :spree_price_rules, [:price_list_id, :type]
11
+ add_index :spree_price_rules, :type
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ class AddPriceListIdToSpreePrices < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_reference :spree_prices, :price_list, null: true
4
+ add_index :spree_prices, [:variant_id, :currency, :price_list_id], name: 'index_spree_prices_on_variant_currency_price_list', unique: true
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ class AddPriceListIdToSpreeLineItems < ActiveRecord::Migration[7.0]
2
+ def change
3
+ add_reference :spree_line_items, :price_list, null: true
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ class FixIndexesOnFriendlyIdSlugs < ActiveRecord::Migration[7.2]
2
+ def change
3
+ remove_index :friendly_id_slugs, [:slug, :sluggable_type, :locale]
4
+ add_index :friendly_id_slugs, [:slug, :sluggable_type, :locale], length: { slug: 140, sluggable_type: 50, locale: 5 }
5
+ remove_index :friendly_id_slugs, [:slug, :sluggable_type, :scope, :locale]
6
+ add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope, :locale], length: { slug: 70, sluggable_type: 50, scope: 70, locale: 5 }, unique: true, name: :index_friendly_id_slugs_unique
7
+ end
8
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateSpreeWebhookEndpoints < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :spree_webhook_endpoints do |t|
6
+ t.belongs_to :store, null: false, foreign_key: false, index: true
7
+ t.string :url, null: false
8
+ t.boolean :active, null: false, default: true, index: true
9
+ if t.respond_to? :jsonb
10
+ t.jsonb :subscriptions, null: false
11
+ else
12
+ t.json :subscriptions, null: false
13
+ end
14
+ t.string :secret_key, null: false
15
+ t.datetime :deleted_at, index: true
16
+ t.timestamps
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateSpreeWebhookDeliveries < ActiveRecord::Migration[7.0]
4
+ def change
5
+ create_table :spree_webhook_deliveries do |t|
6
+ t.belongs_to :webhook_endpoint, null: false, foreign_key: false, index: true
7
+ t.string :event_name, null: false, index: true
8
+ if t.respond_to?(:jsonb)
9
+ t.jsonb :payload, null: false
10
+ else
11
+ t.json :payload, null: false
12
+ end
13
+ t.integer :response_code, index: true
14
+ t.text :response_body
15
+ t.string :error_type
16
+ t.integer :execution_time
17
+ t.text :request_errors
18
+ t.boolean :success, index: true
19
+ t.datetime :delivered_at, index: true
20
+ t.timestamps
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,25 @@
1
+ class AddPerformanceIndexesToSpreeAdjustments < ActiveRecord::Migration[7.2]
2
+ def change
3
+ # Composite index for filtering adjustments by order, type and eligibility
4
+ # Used in OrderUpdater#update_adjustment_total when summing eligible adjustments
5
+ # and filtering by source_type (e.g., promotion adjustments)
6
+ add_index :spree_adjustments,
7
+ [:order_id, :eligible, :source_type],
8
+ name: 'index_spree_adjustments_on_order_eligible_source_type',
9
+ if_not_exists: true
10
+
11
+ # Composite index for AdjustmentsUpdater queries that filter by adjustable and source type
12
+ # Used when recalculating tax and promotion adjustments for a specific adjustable
13
+ add_index :spree_adjustments,
14
+ [:adjustable_type, :adjustable_id, :source_type],
15
+ name: 'index_spree_adjustments_on_adjustable_and_source_type',
16
+ if_not_exists: true
17
+
18
+ # Index for state-based filtering (open/closed adjustments)
19
+ # Used in Adjustment.not_finalized and Adjustment.finalized scopes
20
+ add_index :spree_adjustments,
21
+ [:order_id, :state],
22
+ name: 'index_spree_adjustments_on_order_id_and_state',
23
+ if_not_exists: true
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ class FixSpreePricesUniqueIndexes < ActiveRecord::Migration[7.0]
2
+ def change
3
+ remove_index :spree_prices, name: 'index_spree_prices_on_variant_currency_price_list', if_exists: true
4
+
5
+ if ActiveRecord::Base.connection.adapter_name == 'Mysql2'
6
+ # MySQL doesn't support partial indexes, so we use a composite unique index
7
+ # that includes price_list_id. NULL values are treated as distinct in MySQL unique indexes,
8
+ # so this allows multiple base prices (price_list_id = NULL) per variant/currency
9
+ # but only one price per variant/currency/price_list combination.
10
+ # Keep existing index_spree_prices_on_variant_id_and_currency as-is (non-unique, for performance).
11
+ add_index :spree_prices, [:variant_id, :currency, :price_list_id],
12
+ name: 'index_spree_prices_on_variant_currency_price_list',
13
+ unique: true
14
+ else
15
+ # PostgreSQL and SQLite support partial indexes
16
+ remove_index :spree_prices, name: 'index_spree_prices_on_variant_id_and_currency', if_exists: true
17
+
18
+ # Add unique index for base prices only (price_list_id IS NULL)
19
+ # The amount IS NOT NULL condition allows placeholder prices with nil amounts
20
+ add_index :spree_prices, [:variant_id, :currency],
21
+ name: 'index_spree_prices_on_variant_id_and_currency',
22
+ unique: true,
23
+ where: 'price_list_id IS NULL AND deleted_at IS NULL AND amount IS NOT NULL'
24
+
25
+ # Add unique index for price list prices (one price per variant/currency/price_list)
26
+ # The amount IS NOT NULL condition allows placeholder prices with nil amounts
27
+ add_index :spree_prices, [:variant_id, :currency, :price_list_id],
28
+ name: 'index_spree_prices_on_variant_currency_price_list',
29
+ unique: true,
30
+ where: 'price_list_id IS NOT NULL AND deleted_at IS NULL AND amount IS NOT NULL'
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ class CreateSpreeCustomerGroups < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :spree_customer_groups do |t|
4
+ t.references :store, null: false, index: true
5
+ t.string :name, null: false
6
+ t.text :description
7
+ t.timestamps
8
+ t.datetime :deleted_at
9
+ end
10
+
11
+ add_index :spree_customer_groups, [:store_id, :name], unique: true, where: 'deleted_at IS NULL'
12
+ add_index :spree_customer_groups, :deleted_at
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ class CreateSpreeCustomerGroupUsers < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :spree_customer_group_users do |t|
4
+ t.references :customer_group, null: false, index: true
5
+ t.references :user, polymorphic: true, null: false, index: true
6
+ t.timestamps
7
+ end
8
+
9
+ add_index :spree_customer_group_users,
10
+ [:customer_group_id, :user_id, :user_type],
11
+ unique: true,
12
+ name: 'index_spree_customer_group_users_unique'
13
+ end
14
+ end
@@ -0,0 +1,5 @@
1
+ class RemoveNotNullConstraintFromPolicyName < ActiveRecord::Migration[7.2]
2
+ def change
3
+ change_column_null :spree_policies, :name, true
4
+ end
5
+ end