spree_core 5.4.3 → 5.5.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 (231) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/base_helper.rb +0 -82
  3. data/app/helpers/spree/currency_helper.rb +0 -12
  4. data/app/helpers/spree/products_helper.rb +0 -8
  5. data/app/jobs/spree/base_job.rb +18 -0
  6. data/app/jobs/spree/events/subscriber_job.rb +2 -1
  7. data/app/jobs/spree/exports/generate_job.rb +11 -0
  8. data/app/jobs/spree/images/save_from_url_job.rb +23 -8
  9. data/app/jobs/spree/imports/assign_tags_job.rb +11 -0
  10. data/app/jobs/spree/imports/base_job.rb +15 -0
  11. data/app/jobs/spree/imports/create_categories_job.rb +37 -0
  12. data/app/jobs/spree/imports/create_rows_job.rb +1 -3
  13. data/app/jobs/spree/imports/process_group_job.rb +8 -6
  14. data/app/jobs/spree/imports/process_rows_job.rb +1 -3
  15. data/app/jobs/spree/media/migrate_product_assets_job.rb +83 -0
  16. data/app/jobs/spree/products/refresh_metrics_job.rb +15 -4
  17. data/app/jobs/spree/reports/generate_job.rb +11 -0
  18. data/app/jobs/spree/search_provider/index_job.rb +5 -1
  19. data/app/jobs/spree/search_provider/remove_job.rb +4 -0
  20. data/app/jobs/spree/stock_reservations/expire_job.rb +11 -0
  21. data/app/models/concerns/spree/calculated_adjustments.rb +34 -1
  22. data/app/models/concerns/spree/display_on.rb +31 -0
  23. data/app/models/concerns/spree/metafields.rb +167 -5
  24. data/app/models/concerns/spree/preference_schema.rb +191 -0
  25. data/app/models/concerns/spree/prefixed_id.rb +94 -11
  26. data/app/models/concerns/spree/product_scopes.rb +36 -17
  27. data/app/models/concerns/spree/ransackable_attributes.rb +5 -1
  28. data/app/models/concerns/spree/search_indexable.rb +8 -7
  29. data/app/models/concerns/spree/searchable.rb +11 -2
  30. data/app/models/concerns/spree/stores/channels.rb +20 -0
  31. data/app/models/concerns/spree/stores/markets.rb +21 -5
  32. data/app/models/concerns/spree/typed_associations.rb +120 -0
  33. data/app/models/concerns/spree/user_methods.rb +71 -12
  34. data/app/models/spree/ability.rb +4 -117
  35. data/app/models/spree/api_key.rb +53 -0
  36. data/app/models/spree/asset.rb +28 -5
  37. data/app/models/spree/authentication/strategy_registry.rb +72 -0
  38. data/app/models/spree/base.rb +18 -1
  39. data/app/models/spree/channel.rb +159 -0
  40. data/app/models/spree/country.rb +2 -0
  41. data/app/models/spree/current.rb +5 -1
  42. data/app/models/spree/custom_field.rb +9 -0
  43. data/app/models/spree/custom_field_definition.rb +7 -0
  44. data/app/models/spree/customer_group.rb +8 -2
  45. data/app/models/spree/export.rb +30 -3
  46. data/app/models/spree/gateway.rb +25 -0
  47. data/app/models/spree/gift_card.rb +1 -1
  48. data/app/models/spree/gift_card_batch.rb +4 -1
  49. data/app/models/spree/import.rb +5 -0
  50. data/app/models/spree/import_row.rb +12 -0
  51. data/app/models/spree/line_item.rb +6 -1
  52. data/app/models/spree/market.rb +32 -1
  53. data/app/models/spree/metafield.rb +38 -0
  54. data/app/models/spree/metafield_definition.rb +29 -6
  55. data/app/models/spree/metafields/json.rb +10 -0
  56. data/app/models/spree/newsletter_subscriber.rb +19 -3
  57. data/app/models/spree/option_type.rb +48 -7
  58. data/app/models/spree/order/checkout.rb +3 -3
  59. data/app/models/spree/order.rb +102 -6
  60. data/app/models/spree/order_approval.rb +19 -0
  61. data/app/models/spree/order_cancellation.rb +19 -0
  62. data/app/models/spree/order_routing/has_strategy_preference.rb +28 -0
  63. data/app/models/spree/order_routing/rules/default_location.rb +16 -0
  64. data/app/models/spree/order_routing/rules/minimize_splits.rb +45 -0
  65. data/app/models/spree/order_routing/rules/preferred_location.rb +22 -0
  66. data/app/models/spree/order_routing/strategy/base.rb +47 -0
  67. data/app/models/spree/order_routing/strategy/legacy.rb +33 -0
  68. data/app/models/spree/order_routing/strategy/reducer.rb +68 -0
  69. data/app/models/spree/order_routing/strategy/rules.rb +81 -0
  70. data/app/models/spree/order_routing_rule.rb +75 -0
  71. data/app/models/spree/permission_sets/configuration_management.rb +16 -0
  72. data/app/models/spree/permission_sets/product_display.rb +2 -0
  73. data/app/models/spree/permission_sets/product_management.rb +2 -0
  74. data/app/models/spree/price.rb +14 -1
  75. data/app/models/spree/price_list.rb +129 -17
  76. data/app/models/spree/price_rule.rb +11 -1
  77. data/app/models/spree/price_rules/customer_group_rule.rb +15 -1
  78. data/app/models/spree/price_rules/market_rule.rb +16 -1
  79. data/app/models/spree/price_rules/user_rule.rb +21 -2
  80. data/app/models/spree/product/channels.rb +149 -0
  81. data/app/models/spree/product/legacy_multi_store_support.rb +40 -0
  82. data/app/models/spree/product/slugs.rb +1 -1
  83. data/app/models/spree/product.rb +172 -31
  84. data/app/models/spree/product_publication.rb +43 -0
  85. data/app/models/spree/promotion/actions/create_adjustment.rb +4 -0
  86. data/app/models/spree/promotion/actions/create_item_adjustments.rb +4 -0
  87. data/app/models/spree/promotion/actions/create_line_items.rb +32 -14
  88. data/app/models/spree/promotion/rules/country.rb +40 -18
  89. data/app/models/spree/promotion/rules/customer_group.rb +10 -1
  90. data/app/models/spree/promotion/rules/product.rb +4 -0
  91. data/app/models/spree/promotion/rules/taxon.rb +24 -1
  92. data/app/models/spree/promotion/rules/user.rb +21 -0
  93. data/app/models/spree/promotion/rules/user_logged_in.rb +6 -0
  94. data/app/models/spree/promotion.rb +22 -1
  95. data/app/models/spree/promotion_action.rb +17 -11
  96. data/app/models/spree/promotion_rule.rb +17 -18
  97. data/app/models/spree/search_provider/meilisearch.rb +12 -2
  98. data/app/models/spree/stock/availability_validator.rb +1 -1
  99. data/app/models/spree/stock/quantifier.rb +89 -9
  100. data/app/models/spree/stock_item.rb +36 -0
  101. data/app/models/spree/stock_location.rb +52 -0
  102. data/app/models/spree/stock_reservation.rb +38 -0
  103. data/app/models/spree/stock_reservations/insufficient_stock_error.rb +12 -0
  104. data/app/models/spree/store.rb +18 -72
  105. data/app/models/spree/store_credit.rb +0 -8
  106. data/app/models/spree/store_product.rb +11 -23
  107. data/app/models/spree/taxon.rb +0 -5
  108. data/app/models/spree/user_identity.rb +1 -2
  109. data/app/models/spree/variant.rb +132 -18
  110. data/app/models/spree/variant_media.rb +46 -0
  111. data/app/models/spree/webhook_delivery.rb +1 -1
  112. data/app/models/spree/webhook_endpoint.rb +24 -0
  113. data/app/models/spree/wished_item.rb +0 -13
  114. data/app/presenters/spree/csv/product_variant_presenter.rb +23 -3
  115. data/app/presenters/spree/search_provider/product_presenter.rb +11 -4
  116. data/app/presenters/spree/variant_presenter.rb +4 -3
  117. data/app/services/spree/addresses/update.rb +6 -8
  118. data/app/services/spree/cart/add_item.rb +10 -0
  119. data/app/services/spree/cart/empty.rb +2 -0
  120. data/app/services/spree/cart/remove_line_item.rb +10 -0
  121. data/app/services/spree/cart/remove_out_of_stock_items.rb +1 -1
  122. data/app/services/spree/cart/set_quantity.rb +10 -0
  123. data/app/services/spree/carts/complete.rb +1 -0
  124. data/app/services/spree/carts/create.rb +1 -0
  125. data/app/services/spree/carts/update.rb +18 -2
  126. data/app/services/spree/carts/upsert_items.rb +6 -6
  127. data/app/services/spree/imports/row_processors/customer.rb +4 -1
  128. data/app/services/spree/imports/row_processors/product_variant.rb +95 -57
  129. data/app/services/spree/newsletter/link_user.rb +53 -0
  130. data/app/services/spree/newsletter/subscribe.rb +31 -9
  131. data/app/services/spree/orders/approve.rb +27 -6
  132. data/app/services/spree/orders/build_shipments.rb +29 -0
  133. data/app/services/spree/orders/cancel.rb +34 -3
  134. data/app/services/spree/orders/complete.rb +53 -0
  135. data/app/services/spree/orders/create.rb +156 -0
  136. data/app/services/spree/orders/update.rb +51 -0
  137. data/app/services/spree/orders/upsert_items.rb +70 -0
  138. data/app/services/spree/prices/bulk_upsert.rb +201 -0
  139. data/app/services/spree/products/duplicator.rb +1 -1
  140. data/app/services/spree/products/prepare_nested_attributes.rb +2 -30
  141. data/app/services/spree/sample_data/loader.rb +30 -0
  142. data/app/services/spree/stock_reservations/extend.rb +19 -0
  143. data/app/services/spree/stock_reservations/release.rb +12 -0
  144. data/app/services/spree/stock_reservations/reserve.rb +103 -0
  145. data/app/services/spree/taxons/remove_products.rb +7 -1
  146. data/app/subscribers/spree/product_metrics_subscriber.rb +3 -7
  147. data/app/views/spree/invitation_mailer/invitation_email.html.erb +4 -0
  148. data/config/locales/en.yml +27 -10
  149. data/config/routes.rb +9 -0
  150. data/db/migrate/20260429000001_create_spree_order_cancellations.rb +25 -0
  151. data/db/migrate/20260429000002_create_spree_order_approvals.rb +22 -0
  152. data/db/migrate/20260429000003_add_status_to_spree_orders.rb +6 -0
  153. data/db/migrate/20260429000004_add_scopes_to_spree_api_keys.rb +11 -0
  154. data/db/migrate/20260501000001_create_spree_stock_reservations.rb +19 -0
  155. data/db/migrate/20260507162651_create_spree_variant_media.rb +23 -0
  156. data/db/migrate/20260508175303_add_pickup_to_spree_stock_locations.rb +12 -0
  157. data/db/migrate/20260508204040_create_spree_channels.rb +18 -0
  158. data/db/migrate/20260508204041_create_spree_order_routing_rules.rb +18 -0
  159. data/db/migrate/20260508204042_add_preferred_stock_location_to_spree_orders.rb +5 -0
  160. data/db/migrate/20260508204043_add_channel_id_to_spree_orders.rb +10 -0
  161. data/db/migrate/20260511000001_backfill_status_on_spree_orders.rb +57 -0
  162. data/db/migrate/20260515000001_add_store_id_to_spree_newsletter_subscribers.rb +25 -0
  163. data/db/migrate/20260529000001_add_unique_index_to_spree_price_rules.rb +41 -0
  164. data/db/migrate/20260529000002_add_unique_index_to_spree_promotion_rules.rb +37 -0
  165. data/db/migrate/20260601000001_create_spree_product_publications.rb +14 -0
  166. data/db/migrate/20260601000002_add_store_id_to_spree_products.rb +16 -0
  167. data/db/migrate/20260602000001_add_default_to_spree_channels.rb +14 -0
  168. data/db/sample_data/channels.rb +12 -0
  169. data/db/sample_data/orders.rb +1 -1
  170. data/db/sample_data/products.csv +212 -212
  171. data/lib/generators/spree/api_resource/api_resource_generator.rb +353 -0
  172. data/lib/generators/spree/api_resource/templates/admin_controller.rb.tt +23 -0
  173. data/lib/generators/spree/api_resource/templates/admin_controller_spec.rb.tt +59 -0
  174. data/lib/generators/spree/api_resource/templates/admin_serializer.rb.tt +11 -0
  175. data/lib/generators/spree/api_resource/templates/factory.rb.tt +26 -0
  176. data/lib/generators/spree/api_resource/templates/store_aliased_serializer.rb.tt +12 -0
  177. data/lib/generators/spree/api_resource/templates/store_controller.rb.tt +31 -0
  178. data/lib/generators/spree/api_resource/templates/store_controller_spec.rb.tt +61 -0
  179. data/lib/generators/spree/api_resource/templates/store_serializer.rb.tt +14 -0
  180. data/lib/generators/spree/controller_decorator/controller_decorator_generator.rb +66 -0
  181. data/lib/generators/spree/controller_decorator/templates/controller_decorator.rb.tt +25 -0
  182. data/lib/generators/spree/model/model_generator.rb +73 -7
  183. data/lib/generators/spree/model/templates/create_table_migration.rb.tt +40 -0
  184. data/lib/generators/spree/model/templates/model.rb.tt +28 -2
  185. data/lib/spree/core/configuration.rb +7 -0
  186. data/lib/spree/core/controller_helpers/auth.rb +0 -12
  187. data/lib/spree/core/controller_helpers/currency.rb +0 -17
  188. data/lib/spree/core/controller_helpers/order.rb +0 -19
  189. data/lib/spree/core/dependencies.rb +5 -2
  190. data/lib/spree/core/engine.rb +54 -7
  191. data/lib/spree/core/permission_configuration.rb +15 -0
  192. data/lib/spree/core/preferences/masking.rb +47 -0
  193. data/lib/spree/core/preferences/preferable_class_methods.rb +7 -1
  194. data/lib/spree/core/version.rb +1 -1
  195. data/lib/spree/core.rb +56 -5
  196. data/lib/spree/permitted_attributes.rb +9 -7
  197. data/lib/spree/testing_support/factories/address_factory.rb +16 -9
  198. data/lib/spree/testing_support/factories/api_key_factory.rb +1 -0
  199. data/lib/spree/testing_support/factories/channel_factory.rb +8 -0
  200. data/lib/spree/testing_support/factories/line_item_factory.rb +2 -8
  201. data/lib/spree/testing_support/factories/newsletter_subscriber_factory.rb +2 -0
  202. data/lib/spree/testing_support/factories/product_factory.rb +16 -7
  203. data/lib/spree/testing_support/factories/product_publication_factory.rb +6 -0
  204. data/lib/spree/testing_support/factories/refresh_token_factory.rb +15 -0
  205. data/lib/spree/testing_support/factories/stock_location_factory.rb +2 -2
  206. data/lib/spree/testing_support/factories/stock_reservation_factory.rb +31 -0
  207. data/lib/spree/testing_support/factories/variant_factory.rb +3 -3
  208. data/lib/spree/testing_support/order_walkthrough.rb +1 -1
  209. data/lib/spree/testing_support/store.rb +10 -0
  210. data/lib/spree/upgrades/5_4_to_5_5/manifest.yml +53 -0
  211. data/lib/tasks/channels.rake +94 -0
  212. data/lib/tasks/core.rake +1 -0
  213. data/lib/tasks/media.rake +27 -0
  214. data/lib/tasks/products.rake +4 -6
  215. data/lib/tasks/publications.rake +60 -0
  216. data/lib/tasks/upgrade.rake +211 -0
  217. metadata +83 -18
  218. data/app/finders/spree/variants/visible_finder.rb +0 -23
  219. data/app/paginators/spree/shared/paginate.rb +0 -30
  220. data/app/presenters/spree/filters/price_presenter.rb +0 -23
  221. data/app/presenters/spree/filters/price_range_presenter.rb +0 -30
  222. data/app/presenters/spree/filters/quantified_price_range_presenter.rb +0 -45
  223. data/app/presenters/spree/product_summary_presenter.rb +0 -27
  224. data/app/presenters/spree/variants/options_presenter.rb +0 -82
  225. data/app/services/spree/classifications/reposition.rb +0 -23
  226. data/app/sorters/spree/orders/sort.rb +0 -10
  227. data/lib/spree/core/controller_helpers/common.rb +0 -14
  228. data/lib/spree/core/token_generator.rb +0 -23
  229. data/lib/spree/database_type_utilities.rb +0 -22
  230. data/lib/spree/testing_support/bar_ability.rb +0 -14
  231. data/lib/spree/testing_support/factories/store_product_factory.rb +0 -6
@@ -0,0 +1,6 @@
1
+ class AddStatusToSpreeOrders < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_column :spree_orders, :status, :string
4
+ add_index :spree_orders, :status
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ class AddScopesToSpreeApiKeys < ActiveRecord::Migration[7.2]
2
+ def change
3
+ change_table :spree_api_keys do |t|
4
+ if t.respond_to?(:jsonb)
5
+ t.jsonb :scopes
6
+ else
7
+ t.json :scopes
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ class CreateSpreeStockReservations < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :spree_stock_reservations do |t|
4
+ t.references :stock_item, null: false, index: false
5
+ t.references :line_item, null: false
6
+ t.references :order, null: false
7
+ t.integer :quantity, null: false
8
+ t.datetime :expires_at, null: false
9
+ t.timestamps
10
+ end
11
+
12
+ # Composite (stock_item_id, expires_at) is the hot-path Quantifier query;
13
+ # leading on stock_item_id makes it cover the standalone-by-stock-item lookup too.
14
+ add_index :spree_stock_reservations, [:stock_item_id, :expires_at]
15
+ add_index :spree_stock_reservations, [:stock_item_id, :line_item_id], unique: true,
16
+ name: 'idx_stock_reservations_item_line_item'
17
+ add_index :spree_stock_reservations, :expires_at
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ class CreateSpreeVariantMedia < ActiveRecord::Migration[7.2]
2
+ # `media_id` references spree_assets.id in 5.5 — the parent table renames to
3
+ # spree_media in 6.0 (see 5.4-6.0-product-media-system.md Phase 3 of the 6.0
4
+ # cleanup). Using `media_id` from the start saves a column rename in 6.0.
5
+ #
6
+ # Table name is `spree_variant_media` (not the Rails default `spree_variant_medias`)
7
+ # since "media" reads as collective; the model overrides `self.table_name`.
8
+ #
9
+ # No per-variant `position` column — gallery order comes from the asset's
10
+ # product-level position (Asset#position). Variant gallery resolution filters
11
+ # product media down to the linked subset and inherits the product's order.
12
+ def change
13
+ create_table :spree_variant_media do |t|
14
+ t.references :variant, null: false, index: false
15
+ t.bigint :media_id, null: false
16
+ t.timestamps
17
+ end
18
+
19
+ add_index :spree_variant_media, [:variant_id, :media_id], unique: true,
20
+ name: 'idx_variant_media_unique'
21
+ add_index :spree_variant_media, :media_id, name: 'idx_variant_media_media'
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ class AddPickupToSpreeStockLocations < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_column :spree_stock_locations, :kind, :string, null: false, default: 'warehouse'
4
+ add_column :spree_stock_locations, :pickup_enabled, :boolean, null: false, default: false
5
+ add_column :spree_stock_locations, :pickup_stock_policy, :string, null: false, default: 'local'
6
+ add_column :spree_stock_locations, :pickup_ready_in_minutes, :integer
7
+ add_column :spree_stock_locations, :pickup_instructions, :text
8
+
9
+ add_index :spree_stock_locations, :pickup_enabled
10
+ add_index :spree_stock_locations, :kind
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ class CreateSpreeChannels < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :spree_channels do |t|
4
+ t.references :store, null: false
5
+ t.string :name, null: false
6
+ t.string :code, null: false
7
+ t.boolean :active, null: false
8
+ t.text :preferences
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :spree_channels, %i[store_id code], unique: true
13
+
14
+ # Default-channel backfill for existing stores lives in
15
+ # +rake spree:channels:create_defaults+ (data transformations don't belong
16
+ # in migrations per Spree's guidelines).
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ class CreateSpreeOrderRoutingRules < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :spree_order_routing_rules do |t|
4
+ t.references :store, null: false
5
+ t.references :channel, null: false
6
+ t.string :type, null: false
7
+ t.integer :position, null: false
8
+ t.boolean :active, null: false
9
+ t.text :preferences
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :spree_order_routing_rules, [:channel_id, :position]
14
+ add_index :spree_order_routing_rules, [:channel_id, :active, :position],
15
+ name: 'idx_order_routing_rules_lookup'
16
+ add_index :spree_order_routing_rules, :type
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ class AddPreferredStockLocationToSpreeOrders < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_reference :spree_orders, :preferred_stock_location
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ class AddChannelIdToSpreeOrders < ActiveRecord::Migration[7.2]
2
+ # Non-destructive: adds the FK column alongside the existing `channel`
3
+ # string column. Old reads keep working until the upgrade rake task
4
+ # `spree:order_routing:backfill_channel_ids` populates `channel_id`.
5
+ # The string column is dropped in a follow-up migration once the
6
+ # backfill has run.
7
+ def change
8
+ add_reference :spree_orders, :channel
9
+ end
10
+ end
@@ -0,0 +1,57 @@
1
+ class BackfillStatusOnSpreeOrders < ActiveRecord::Migration[7.2]
2
+ BATCH_SIZE = 10_000
3
+
4
+ STATE_TO_STATUS = {
5
+ 'placed' => %w[complete resumed returned awaiting_return],
6
+ 'canceled' => %w[canceled partially_canceled]
7
+ }.freeze
8
+
9
+ def up
10
+ STATE_TO_STATUS.each do |target_status, states|
11
+ backfill_in_batches(target_status, states)
12
+ end
13
+
14
+ # Anything still NULL (carts, in-progress checkouts) becomes draft.
15
+ backfill_in_batches('draft', nil)
16
+
17
+ change_column_default :spree_orders, :status, 'draft'
18
+ change_column_null :spree_orders, :status, false
19
+ end
20
+
21
+ def down
22
+ raise ActiveRecord::IrreversibleMigration,
23
+ 'Backfilled status values cannot be safely restored: writes made after deploy ' \
24
+ 'are indistinguishable from the backfill. Roll back manually if needed.'
25
+ end
26
+
27
+ private
28
+
29
+ def backfill_in_batches(target_status, source_states)
30
+ quoted_status = connection.quote(target_status)
31
+ state_filter =
32
+ if source_states
33
+ quoted = Array(source_states).map { |s| connection.quote(s) }.join(',')
34
+ "AND state IN (#{quoted})"
35
+ else
36
+ ''
37
+ end
38
+
39
+ loop do
40
+ # The nested SELECT wrap (`SELECT id FROM (SELECT id ... LIMIT N) AS t`) is
41
+ # required for MySQL, which refuses an UPDATE that selects from the same
42
+ # table in the WHERE clause without an intermediate derived table.
43
+ affected = connection.update(<<~SQL.squish)
44
+ UPDATE spree_orders SET status = #{quoted_status}
45
+ WHERE id IN (
46
+ SELECT id FROM (
47
+ SELECT id FROM spree_orders
48
+ WHERE status IS NULL #{state_filter}
49
+ LIMIT #{BATCH_SIZE}
50
+ ) AS spree_orders_batch
51
+ )
52
+ SQL
53
+
54
+ break if affected < BATCH_SIZE
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,25 @@
1
+ class AddStoreIdToSpreeNewsletterSubscribers < ActiveRecord::Migration[7.2]
2
+ def up
3
+ add_reference :spree_newsletter_subscribers, :store
4
+
5
+ # For spree_multi_tenant we need handle the backfill and indices there
6
+ return if defined?(SpreeMultiTenant)
7
+
8
+ default_store = Spree::Store.default
9
+ Spree::NewsletterSubscriber.update_all(store_id: default_store.id) if default_store&.persisted?
10
+
11
+ change_column_null :spree_newsletter_subscribers, :store_id, false
12
+
13
+ remove_index :spree_newsletter_subscribers, :email, unique: true, if_exists: true
14
+ add_index :spree_newsletter_subscribers, [:email, :store_id], unique: true, if_not_exists: true
15
+ end
16
+
17
+ def down
18
+ unless defined?(SpreeMultiTenant)
19
+ remove_index :spree_newsletter_subscribers, [:email, :store_id], if_exists: true
20
+ add_index :spree_newsletter_subscribers, :email, unique: true, if_not_exists: true
21
+ end
22
+
23
+ remove_reference :spree_newsletter_subscribers, :store
24
+ end
25
+ end
@@ -0,0 +1,41 @@
1
+ class AddUniqueIndexToSpreePriceRules < ActiveRecord::Migration[7.2]
2
+ def up
3
+ # Remove duplicate rules (same type within the same price list),
4
+ # keeping the oldest record. Mirrors the
5
+ # `validates :type, uniqueness: { scope: :price_list_id }` model
6
+ # constraint at the DB level so concurrent writes can't race past
7
+ # the validation.
8
+ if ActiveRecord::Base.connection.adapter_name == 'Mysql2'
9
+ execute <<-SQL
10
+ DELETE FROM spree_price_rules
11
+ WHERE id NOT IN (
12
+ SELECT min_id FROM (
13
+ SELECT MIN(id) AS min_id
14
+ FROM spree_price_rules
15
+ GROUP BY price_list_id, type
16
+ ) AS keeper_ids
17
+ )
18
+ SQL
19
+ else
20
+ execute <<-SQL
21
+ DELETE FROM spree_price_rules
22
+ WHERE id NOT IN (
23
+ SELECT MIN(id)
24
+ FROM spree_price_rules
25
+ GROUP BY price_list_id, type
26
+ )
27
+ SQL
28
+ end
29
+
30
+ remove_index :spree_price_rules, [:price_list_id, :type], if_exists: true
31
+
32
+ add_index :spree_price_rules, [:price_list_id, :type], unique: true,
33
+ name: 'index_spree_price_rules_on_price_list_id_and_type'
34
+ end
35
+
36
+ def down
37
+ remove_index :spree_price_rules, name: 'index_spree_price_rules_on_price_list_id_and_type', if_exists: true
38
+
39
+ add_index :spree_price_rules, [:price_list_id, :type]
40
+ end
41
+ end
@@ -0,0 +1,37 @@
1
+ class AddUniqueIndexToSpreePromotionRules < ActiveRecord::Migration[7.2]
2
+ def up
3
+ # Remove duplicate rules (same type within the same promotion),
4
+ # keeping the oldest record. Mirrors the
5
+ # `validates :type, uniqueness: { scope: :promotion_id }` model
6
+ # constraint at the DB level so concurrent writes can't race past
7
+ # the validation.
8
+ if ActiveRecord::Base.connection.adapter_name == 'Mysql2'
9
+ execute <<-SQL
10
+ DELETE FROM spree_promotion_rules
11
+ WHERE id NOT IN (
12
+ SELECT min_id FROM (
13
+ SELECT MIN(id) AS min_id
14
+ FROM spree_promotion_rules
15
+ GROUP BY promotion_id, type
16
+ ) AS keeper_ids
17
+ )
18
+ SQL
19
+ else
20
+ execute <<-SQL
21
+ DELETE FROM spree_promotion_rules
22
+ WHERE id NOT IN (
23
+ SELECT MIN(id)
24
+ FROM spree_promotion_rules
25
+ GROUP BY promotion_id, type
26
+ )
27
+ SQL
28
+ end
29
+
30
+ add_index :spree_promotion_rules, [:promotion_id, :type], unique: true,
31
+ name: 'index_spree_promotion_rules_on_promotion_id_and_type'
32
+ end
33
+
34
+ def down
35
+ remove_index :spree_promotion_rules, name: 'index_spree_promotion_rules_on_promotion_id_and_type', if_exists: true
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ class CreateSpreeProductPublications < ActiveRecord::Migration[7.2]
2
+ def change
3
+ create_table :spree_product_publications do |t|
4
+ t.references :product, null: false
5
+ t.references :channel, null: false
6
+ t.datetime :published_at
7
+ t.datetime :unpublished_at
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :spree_product_publications, %i[product_id channel_id], unique: true,
12
+ name: 'index_spree_product_publications_on_product_and_channel'
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ class AddStoreIdToSpreeProducts < ActiveRecord::Migration[7.2]
2
+ # NOTE: After running this migration, existing products will have
3
+ # +store_id IS NULL+ and be invisible to +Product.for_store+. Operators
4
+ # upgrading from 5.4 MUST run the backfill task immediately afterwards:
5
+ #
6
+ # bundle exec rake spree:upgrade:populate_publications
7
+ #
8
+ # The backfill is also chained from +spree:channels:upgrade+ so the full
9
+ # 5.4 → 5.5 channel/publication upgrade is one command.
10
+ def change
11
+ add_reference :spree_products, :store, null: true
12
+ add_column :spree_products, :units_sold_count, :integer, default: 0, null: false
13
+ add_column :spree_products, :revenue, :decimal, precision: 16, scale: 4, default: 0, null: false
14
+ add_index :spree_products, %i[store_id units_sold_count]
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ class AddDefaultToSpreeChannels < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_column :spree_channels, :default, :boolean, null: false, default: false
4
+
5
+ # Partial unique index: one default channel per store. Supported on
6
+ # Postgres and SQLite; MySQL ignores the +where:+ option, so we fall
7
+ # back to a model-level uniqueness validation there.
8
+ unless ActiveRecord::Base.connection.adapter_name.match?(/mysql/i)
9
+ add_index :spree_channels, :store_id, unique: true,
10
+ where: '"default" = TRUE',
11
+ name: 'index_spree_channels_default_per_store'
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # Adds two extra channels alongside the seeded 'Online Store' so sample data
2
+ # exercises the channel-aware code paths (publishing, channel filters,
3
+ # channel-scoped order attribution).
4
+ store = Spree::Store.default
5
+
6
+ store.channels.find_or_create_by!(code: 'pos') do |channel|
7
+ channel.name = 'Point of Sale'
8
+ end
9
+
10
+ store.channels.find_or_create_by!(code: 'wholesale') do |channel|
11
+ channel.name = 'Wholesale'
12
+ end
@@ -78,7 +78,7 @@ end
78
78
 
79
79
  orders.each(&:create_proposed_shipments)
80
80
 
81
- Spree::Order.where(id: orders.map(&:id)).update_all(state: :complete, completed_at: Time.current - 1.day)
81
+ Spree::Order.where(id: orders.map(&:id)).update_all(state: :complete, status: 'placed', completed_at: Time.current - 1.day)
82
82
 
83
83
  # Adjustments (tax)
84
84
  tax_rate = Spree::TaxRate.find_by(name: 'California')