spree_core 5.4.0.rc5 → 5.4.0.rc6

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/app/mailers/spree/base_mailer.rb +2 -0
  3. data/app/models/concerns/spree/stores/markets.rb +4 -2
  4. data/app/models/spree/market.rb +1 -0
  5. data/app/models/spree/option_type.rb +11 -1
  6. data/app/models/spree/option_value.rb +8 -0
  7. data/app/models/spree/order.rb +21 -1
  8. data/app/models/spree/search_provider/meilisearch.rb +41 -8
  9. data/app/models/spree/stock_location.rb +1 -1
  10. data/app/presenters/spree/search_provider/product_presenter.rb +8 -13
  11. data/app/services/spree/cart/create.rb +1 -0
  12. data/app/services/spree/carts/create.rb +1 -0
  13. data/app/services/spree/carts/update.rb +21 -0
  14. data/app/services/spree/sample_data/loader.rb +18 -20
  15. data/app/services/spree/seeds/all.rb +22 -20
  16. data/config/locales/en.yml +11 -0
  17. data/db/migrate/20250217171018_create_action_text_video_embeds.rb +11 -0
  18. data/db/migrate/20260402000001_add_kind_to_spree_option_types.rb +13 -0
  19. data/db/migrate/20260402000002_add_color_code_to_spree_option_values.rb +5 -0
  20. data/db/migrate/20260403000000_add_market_to_spree_orders.rb +5 -0
  21. data/db/sample_data/metafield_definitions.rb +2 -2
  22. data/db/sample_data/options.rb +4 -0
  23. data/db/sample_data/orders.rb +2 -2
  24. data/db/sample_data/products.csv +125 -1083
  25. data/lib/spree/core/version.rb +1 -1
  26. data/lib/spree/permitted_attributes.rb +3 -3
  27. data/lib/spree/testing_support/factories/options_factory.rb +11 -0
  28. data/lib/tasks/markets.rake +5 -2
  29. data/lib/tasks/search.rake +3 -3
  30. data/lib/tasks/variants.rake +2 -2
  31. metadata +18 -7
  32. /data/lib/tasks/{images.rake → media.rake} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d5513fa58ed4667fb34788917d1fa52e803267a9a9e43a2c76d6fa318d40645b
4
- data.tar.gz: 02b5467d26e71c5f111ffdd0677aa7e6b2732a5ff42ef75a25e7a782a7090737
3
+ metadata.gz: 10b8425a8850b155fa6d2a2d6d452e016fd142d62849ce757e00b4b2db1efcdc
4
+ data.tar.gz: 38b6187ef6b92566261d617d65e3eb59d2e760eca6d3519390189a40858a62c3
5
5
  SHA512:
6
- metadata.gz: e83cdd25848537e36b5f96b113697818cacec896a58b663415d28295d2e368c1b81dbe076baa86d7858ea8357ad1fd9b1754212c5b4d13a8a54e77c0d360ee29
7
- data.tar.gz: 6c6cf8922e2b2c242572723b176608b12a675e64f4982647aca28a0ce85269d23889ba44d5db7ad539a04f1765fde2afbb7c552b28a7e3e7163d263559b79bc6
6
+ metadata.gz: 76058003b7c03e556c6f7d035fa6b1283688927ad6102cacd955932440e81054a8ace08032fc593545b897bdc321ca5d6ae2e720170468c9e64adee5b7083a53
7
+ data.tar.gz: 1783fc54faf9da589237ac09af8f665a2d145382eb6246911eecdfc2abdb26fce3d780e47f180fa6644a534164236232b4b30e3d8392c4fb869843405dfd2eaa
@@ -1,5 +1,7 @@
1
1
  module Spree
2
2
  class BaseMailer < ActionMailer::Base
3
+ helper Spree::ImagesHelper
4
+
3
5
  def current_store
4
6
  @current_store ||= @order&.store.presence || Spree::Store.current || Spree::Store.default
5
7
  end
@@ -104,12 +104,14 @@ module Spree
104
104
  end
105
105
  end
106
106
 
107
- private
108
-
107
+ # Returns true if the store has markets, false otherwise
108
+ # @return [Boolean]
109
109
  def has_markets?
110
110
  @has_markets ||= persisted? && (markets.loaded? ? markets.any? : markets.exists?)
111
111
  end
112
112
 
113
+ private
114
+
113
115
  def legacy_supported_currencies_list
114
116
  ([default_currency] + read_attribute(:supported_currencies).to_s.split(',')).uniq.map(&:to_s).map do |code|
115
117
  ::Money::Currency.find(code.strip)
@@ -13,6 +13,7 @@ module Spree
13
13
  belongs_to :store, class_name: 'Spree::Store', touch: true
14
14
  has_many :market_countries, class_name: 'Spree::MarketCountry', dependent: :destroy
15
15
  has_many :countries, through: :market_countries, class_name: 'Spree::Country'
16
+ has_many :orders, class_name: 'Spree::Order', dependent: :nullify
16
17
 
17
18
  #
18
19
  # Validations
@@ -3,6 +3,7 @@ module Spree
3
3
  has_prefix_id :opt # Spree-specific: option type
4
4
 
5
5
  COLOR_NAMES = %w[color colour].freeze
6
+ KINDS = %w[dropdown color_swatch buttons].freeze
6
7
 
7
8
  include Spree::ParameterizableName
8
9
  include Spree::UniqueName
@@ -40,12 +41,14 @@ module Spree
40
41
  # Validations
41
42
  #
42
43
  validates :presentation, presence: true
44
+ validates :kind, presence: true, inclusion: { in: KINDS }
43
45
 
44
46
  #
45
47
  # Scopes
46
48
  #
47
49
  default_scope { order(:position) }
48
50
  scope :colors, -> { where(name: COLOR_NAMES) }
51
+ scope :color_swatches, -> { where(kind: 'color_swatch') }
49
52
  scope :filterable, -> { where(filterable: true) }
50
53
 
51
54
  #
@@ -72,8 +75,15 @@ module Spree
72
75
  colors.first
73
76
  end
74
77
 
78
+ def color_swatch?
79
+ kind == 'color_swatch'
80
+ end
81
+
75
82
  def color?
76
- name.in?(COLOR_NAMES)
83
+ Spree::Deprecation.warn(
84
+ 'Spree::OptionType#color? is deprecated. Use #color_swatch? instead. Will be removed in Spree 6.0.'
85
+ )
86
+ color_swatch?
77
87
  end
78
88
 
79
89
  private
@@ -20,6 +20,11 @@ module Spree
20
20
  acts_as_list scope: :option_type
21
21
  self.whitelisted_ransackable_attributes = ['presentation']
22
22
 
23
+ #
24
+ # Attachments
25
+ #
26
+ has_one_attached :image
27
+
23
28
  #
24
29
  # Associations
25
30
  #
@@ -39,6 +44,9 @@ module Spree
39
44
  validates :presentation
40
45
  end
41
46
 
47
+ validates :color_code, format: { with: /\A#[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?\z/, message: 'must be a valid hex color (e.g. #FF0000)' },
48
+ allow_blank: true
49
+
42
50
  #
43
51
  # Scopes
44
52
  #
@@ -140,6 +140,7 @@ module Spree
140
140
  alias_attribute :shipping_address_id, :ship_address_id
141
141
 
142
142
  belongs_to :store, class_name: 'Spree::Store'
143
+ belongs_to :market, class_name: 'Spree::Market', optional: true
143
144
 
144
145
  with_options dependent: :destroy do
145
146
  has_many :state_changes, as: :stateful, class_name: 'Spree::StateChange'
@@ -181,6 +182,8 @@ module Spree
181
182
  alias_attribute :fulfillment_status, :shipment_state
182
183
  alias_attribute :payment_status, :payment_state
183
184
 
185
+ delegate :has_markets?, to: :store, prefix: true
186
+
184
187
  accepts_nested_attributes_for :line_items
185
188
  accepts_nested_attributes_for :bill_address
186
189
  accepts_nested_attributes_for :ship_address
@@ -191,12 +194,14 @@ module Spree
191
194
 
192
195
  # Needs to happen before save_permalink is called
193
196
  before_validation :ensure_store_presence
197
+ before_validation :ensure_market_presence
194
198
  before_validation :ensure_currency_presence
195
199
  before_validation :ensure_locale_presence
200
+ before_validation :resolve_market_from_currency, if: -> { persisted? && currency_changed? && !skip_market_resolution }
196
201
 
197
202
  before_validation :clone_billing_address, if: :use_billing?
198
203
  before_validation :clone_shipping_address, if: :use_shipping?
199
- attr_accessor :use_billing, :use_shipping
204
+ attr_accessor :use_billing, :use_shipping, :skip_market_resolution
200
205
 
201
206
  before_create :link_by_email
202
207
  before_update :ensure_updated_shipments, :homogenize_line_item_currencies, if: :currency_changed?
@@ -220,6 +225,7 @@ module Spree
220
225
  validates :shipment_total, MONEY_VALIDATION
221
226
  validates :promo_total, NEGATIVE_MONEY_VALIDATION
222
227
  validates :total, MONEY_VALIDATION
228
+ validates :market, presence: true, if: :store_has_markets?
223
229
  validate :currency_must_be_supported_by_store
224
230
  validate :locale_must_be_supported_by_store
225
231
 
@@ -450,6 +456,10 @@ module Spree
450
456
  self.store ||= Spree::Store.default
451
457
  end
452
458
 
459
+ def ensure_market_presence
460
+ self.market ||= Spree::Current.market || store&.default_market
461
+ end
462
+
453
463
  def allow_cancel?
454
464
  return false if !completed? || canceled?
455
465
 
@@ -1050,6 +1060,16 @@ module Spree
1050
1060
  end
1051
1061
  end
1052
1062
 
1063
+ # When currency changes, auto-resolve the matching market.
1064
+ # Only applies when the store has markets configured.
1065
+ def resolve_market_from_currency
1066
+ return unless store_has_markets?
1067
+ return if market&.currency == currency
1068
+
1069
+ resolved = store.markets.find_by(currency: currency)
1070
+ self.market = resolved if resolved
1071
+ end
1072
+
1053
1073
  def collect_payment_methods
1054
1074
  Spree::Deprecation.warn('`Order#collect_payment_methods` is deprecated and will be removed in Spree 5.5. Use `collect_frontend_payment_methods` instead.')
1055
1075
 
@@ -103,9 +103,10 @@ module Spree
103
103
  product_prefixed_ids = ms_result['hits'].map { |h| h['product_id'] }.uniq
104
104
  raw_ids = product_prefixed_ids.filter_map { |pid| Spree::Product.decode_prefixed_id(pid) }
105
105
 
106
- # Intersect with AR scope for security/visibility.
106
+ # Intersect with AR scope for security/visibility, preserving Meilisearch sort order.
107
107
  products = if raw_ids.any?
108
- scope.where(id: raw_ids).reorder(nil)
108
+ records = scope.where(id: raw_ids).reorder(nil).index_by(&:id)
109
+ raw_ids.filter_map { |id| records[id] }
109
110
  else
110
111
  scope.none
111
112
  end
@@ -123,8 +124,9 @@ module Spree
123
124
  end
124
125
 
125
126
  def index(product)
127
+ ensure_index_settings_once!
126
128
  documents = presenter_class.new(product, store).call
127
- client.index(index_name).add_documents(documents, 'prefixed_id')
129
+ client.index(index_name).add_documents(documents, 'id')
128
130
  end
129
131
 
130
132
  def remove(product)
@@ -132,7 +134,9 @@ module Spree
132
134
  end
133
135
 
134
136
  def index_batch(documents)
135
- client.index(index_name).add_documents(documents, 'prefixed_id')
137
+ return if documents.empty?
138
+
139
+ client.index(index_name).add_documents(documents, 'id')
136
140
  end
137
141
 
138
142
  # Remove all documents for a product by its prefixed_id (e.g. 'prod_abc')
@@ -147,21 +151,44 @@ module Spree
147
151
  scope ||= store.products
148
152
  ensure_index_settings!
149
153
 
154
+ indexed = 0
150
155
  scope.reorder(id: :asc)
151
156
  .preload_associations_lazily
152
157
  .find_in_batches(batch_size: 500) do |batch|
153
158
  documents = batch.flat_map { |product| presenter_class.new(product, store).call }
159
+ next if documents.empty?
160
+
154
161
  index_batch(documents)
162
+ indexed += documents.size
163
+
164
+ Rails.logger.info { "[Meilisearch] Enqueued #{documents.size} documents (#{indexed} total) for #{index_name}" }
155
165
  end
166
+
167
+ Rails.logger.info { "[Meilisearch] Reindex complete: #{indexed} documents enqueued for #{index_name}" }
168
+ indexed
156
169
  end
157
170
 
158
171
  # Configure index settings for filtering, sorting, and faceting.
159
172
  # Called automatically by reindex, but can be called separately.
173
+ # Waits for all settings tasks to complete before returning so that
174
+ # subsequent add_documents calls use the correct filterable/sortable attributes.
160
175
  def ensure_index_settings!
161
176
  index = client.index(index_name)
162
- index.update_filterable_attributes(filterable_attributes)
163
- index.update_sortable_attributes(sortable_attributes)
164
- index.update_searchable_attributes(searchable_attributes)
177
+ tasks = []
178
+ tasks << index.update_filterable_attributes(filterable_attributes)
179
+ tasks << index.update_sortable_attributes(sortable_attributes)
180
+ tasks << index.update_searchable_attributes(searchable_attributes)
181
+ tasks.each { |task| task&.await }
182
+ @index_settings_configured = true
183
+ end
184
+
185
+ # Lightweight guard — configures index settings once per provider instance.
186
+ # Meilisearch settings updates are idempotent, so repeated calls are safe
187
+ # but we avoid the overhead by memoizing.
188
+ def ensure_index_settings_once!
189
+ return if @index_settings_configured
190
+
191
+ ensure_index_settings!
165
192
  end
166
193
 
167
194
  private
@@ -359,7 +386,12 @@ module Spree
359
386
 
360
387
  ot = ov.option_type
361
388
  by_option_type[ot] ||= []
362
- by_option_type[ot] << { id: ov.prefixed_id, name: ov.name, label: ov.label, position: ov.position, count: count }
389
+ by_option_type[ot] << {
390
+ id: ov.prefixed_id, name: ov.name, label: ov.label, position: ov.position,
391
+ color_code: ov.color_code,
392
+ image_url: ov.image.attached? ? Rails.application.routes.url_helpers.cdn_image_url(ov.image) : nil,
393
+ count: count
394
+ }
363
395
  end
364
396
 
365
397
  by_option_type.map do |option_type, values|
@@ -368,6 +400,7 @@ module Spree
368
400
  type: 'option',
369
401
  name: option_type.name,
370
402
  label: option_type.label,
403
+ kind: option_type.kind,
371
404
  options: values.sort_by { |o| o[:position] }
372
405
  }
373
406
  end
@@ -27,7 +27,7 @@ module Spree
27
27
  after_save :ensure_one_default
28
28
  after_update :conditional_touch_records
29
29
 
30
- delegate :name, :iso3, :iso, :iso_name, to: :country, prefix: true
30
+ delegate :name, :iso3, :iso, :iso_name, to: :country, prefix: true, allow_nil: true
31
31
 
32
32
  def state_text
33
33
  state.try(:abbr) || state.try(:name) || state_name
@@ -35,10 +35,15 @@ module Spree
35
35
 
36
36
  private
37
37
 
38
+ # Build a document for a given locale and currency
39
+ # @param locale [String] the locale to build the document for
40
+ # @param currency [String] the currency to build the document for
41
+ # @param fallback_locale [String] the fallback locale to use if the product has no translation for the given locale
42
+ # @return [Hash] the document
38
43
  def build_document(locale, currency, fallback_locale)
39
44
  {
40
45
  # Composite ID: product + locale + currency
41
- prefixed_id: "#{product.prefixed_id}_#{locale}_#{currency}",
46
+ id: "#{product.prefixed_id}_#{locale}_#{currency}",
42
47
  product_id: product.prefixed_id,
43
48
  locale: locale.to_s,
44
49
  currency: currency,
@@ -53,7 +58,7 @@ module Spree
53
58
  status: product.status,
54
59
  sku: product.sku,
55
60
  in_stock: product.in_stock?,
56
- store_ids: cached_store_ids,
61
+ store_ids: product.store_ids.map(&:to_s),
57
62
  discontinue_on: product.discontinue_on&.to_i || 0,
58
63
  category_ids: category_ids_with_ancestors,
59
64
  category_names: product.taxons.map { |t| translated(t, :name, fallback_locale) },
@@ -62,8 +67,7 @@ module Spree
62
67
  option_value_ids: variant_option_value_ids,
63
68
  option_values: variant_option_values_data.map { |ov| translated(ov, :presentation, fallback_locale) }.uniq,
64
69
  tags: product.tag_list || [],
65
- thumbnail_url: product.primary_media&.url(:large),
66
- units_sold_count: cached_units_sold_count,
70
+ units_sold_count: product.store_products.find { |sp| sp.store_id == store.id }&.units_sold_count || 0,
67
71
  available_on: product.available_on&.iso8601,
68
72
  created_at: product.created_at&.iso8601,
69
73
  updated_at: product.updated_at&.iso8601
@@ -111,15 +115,6 @@ module Spree
111
115
  }.uniq
112
116
  end
113
117
 
114
- # Memoized — avoids N+1 when called per document
115
- def cached_store_ids
116
- @cached_store_ids ||= product.store_ids.map(&:to_s)
117
- end
118
-
119
- def cached_units_sold_count
120
- @cached_units_sold_count ||= product.store_products.detect { |sp| sp.store_id == store.id }&.units_sold_count || 0
121
- end
122
-
123
118
  def variant_option_value_ids
124
119
  variant_option_values_data.map(&:prefixed_id).uniq
125
120
  end
@@ -11,6 +11,7 @@ module Spree
11
11
 
12
12
  default_params = {
13
13
  user: user,
14
+ market: Spree::Current.market,
14
15
  currency: currency || store.default_currency,
15
16
  token: Spree::GenerateToken.new.call(Spree::Order),
16
17
  public_metadata: public_metadata.to_h,
@@ -11,6 +11,7 @@ module Spree
11
11
 
12
12
  cart = store.carts.create!(
13
13
  user: @params.delete(:user),
14
+ market: @params.delete(:market) || Spree::Current.market,
14
15
  currency: @params.delete(:currency) || store.default_currency,
15
16
  locale: @params.delete(:locale) || Spree::Current.locale
16
17
  )
@@ -9,6 +9,7 @@ module Spree
9
9
 
10
10
  ApplicationRecord.transaction do
11
11
  assign_cart_attributes
12
+ clear_shipping_address_if_outside_market
12
13
  assign_address(:shipping_address)
13
14
  assign_address(:billing_address)
14
15
 
@@ -35,6 +36,8 @@ module Spree
35
36
  def assign_cart_attributes
36
37
  cart.email = params[:email] if params[:email].present?
37
38
  cart.customer_note = params[:customer_note] if params.key?(:customer_note)
39
+
40
+ assign_market if params[:market_id].present?
38
41
  cart.currency = params[:currency].upcase if params[:currency].present?
39
42
  cart.locale = params[:locale] if params[:locale].present?
40
43
  cart.metadata = cart.metadata.merge(params[:metadata].to_h) if params[:metadata].present?
@@ -83,6 +86,24 @@ module Spree
83
86
  decoded ? cart.user.addresses.find_by(id: decoded)&.id : nil
84
87
  end
85
88
 
89
+ def assign_market
90
+ market = cart.store.markets.find_by_prefix_id!(params[:market_id])
91
+ cart.market = market
92
+ cart.skip_market_resolution = true
93
+ end
94
+
95
+ # When the market changes, clear the shipping address if its country
96
+ # is not part of the new market. The market dictates which countries
97
+ # are available for checkout.
98
+ def clear_shipping_address_if_outside_market
99
+ return unless cart.market_id_changed? && cart.ship_address&.country
100
+
101
+ unless cart.market.country_ids.include?(cart.ship_address.country_id)
102
+ cart.ship_address = nil
103
+ revert_to_address_state if cart.has_checkout_step?('address')
104
+ end
105
+ end
106
+
86
107
  def revert_to_address_state
87
108
  return if ['cart', 'address'].include?(cart.state)
88
109
 
@@ -6,29 +6,32 @@ module Spree
6
6
  def call
7
7
  Spree::Events.disable do
8
8
  without_geocoding do
9
- ensure_seeds_loaded
9
+ ActiveRecord::Base.no_touching do
10
+ ensure_seeds_loaded
10
11
 
11
- puts 'Loading sample configuration data...'
12
- load_configuration_data
12
+ puts 'Loading sample configuration data...'
13
+ load_configuration_data
13
14
 
14
- puts 'Loading sample metafield definitions...'
15
- load_ruby_file('metafield_definitions')
15
+ puts 'Loading sample metafield definitions...'
16
+ load_ruby_file('metafield_definitions')
16
17
 
17
- puts 'Loading sample products...'
18
- load_products
18
+ puts 'Loading sample options...'
19
+ load_ruby_file('options')
19
20
 
20
- puts 'Loading sample customers...'
21
- load_customers
21
+ puts 'Loading sample products...'
22
+ load_products
22
23
 
23
- puts 'Loading sample orders...'
24
- load_ruby_file('orders')
24
+ puts 'Loading sample customers...'
25
+ load_customers
25
26
 
26
- puts 'Loading sample posts...'
27
- load_ruby_file('posts')
27
+ puts 'Loading sample orders...'
28
+ load_ruby_file('orders')
28
29
 
29
- clear_jobs_queue
30
+ puts 'Loading sample posts...'
31
+ load_ruby_file('posts')
30
32
 
31
- puts 'Sample data loaded successfully!'
33
+ puts 'Sample data loaded successfully!'
34
+ end
32
35
  end
33
36
  end
34
37
  end
@@ -75,11 +78,6 @@ module Spree
75
78
  ensure
76
79
  Spree::Config[:geocode_addresses] = previous
77
80
  end
78
-
79
- def clear_jobs_queue
80
- adapter = ActiveJob::Base.queue_adapter
81
- adapter.enqueued_jobs.clear if adapter.respond_to?(:enqueued_jobs)
82
- end
83
81
  end
84
82
  end
85
83
  end
@@ -5,30 +5,32 @@ module Spree
5
5
 
6
6
  def call
7
7
  Spree::Events.disable do
8
- # GEO
9
- Countries.call
10
- States.call
11
- Zones.call
8
+ ActiveRecord::Base.no_touching do
9
+ # GEO
10
+ Countries.call
11
+ States.call
12
+ Zones.call
12
13
 
13
- # user roles
14
- Roles.call
14
+ # user roles
15
+ Roles.call
15
16
 
16
- # additional data
17
- ReturnsEnvironment.call
18
- ShippingCategories.call
19
- StoreCreditCategories.call
20
- TaxCategories.call
21
- DigitalDelivery.call
17
+ # additional data
18
+ ReturnsEnvironment.call
19
+ ShippingCategories.call
20
+ StoreCreditCategories.call
21
+ TaxCategories.call
22
+ DigitalDelivery.call
22
23
 
23
- # store & stock location
24
- Stores.call
25
- StockLocations.call
26
- AdminUser.call
24
+ # store & stock location
25
+ Stores.call
26
+ StockLocations.call
27
+ AdminUser.call
27
28
 
28
- # add store resources
29
- PaymentMethods.call
30
- ApiKeys.call
31
- AllowedOrigins.call
29
+ # add store resources
30
+ PaymentMethods.call
31
+ ApiKeys.call
32
+ AllowedOrigins.call
33
+ end
32
34
  end
33
35
  end
34
36
  end
@@ -73,8 +73,12 @@ en:
73
73
  namespace: Namespace
74
74
  spree/option_type:
75
75
  filterable: Filterable
76
+ kind: Kind
76
77
  name: Name
77
78
  presentation: Presentation
79
+ spree/option_value:
80
+ color_code: Color Code
81
+ image: Swatch Image
78
82
  spree/order:
79
83
  checkout_complete: Checkout Complete
80
84
  completed_at: Completed At
@@ -775,6 +779,7 @@ en:
775
779
  approver: Approver
776
780
  are_you_sure: Are you sure?
777
781
  are_you_sure_delete: Are you sure you want to delete this record?
782
+ assets: Media
778
783
  associated_adjustment_closed: The associated adjustment is closed, and will not be recalculated. Do you want to open it?
779
784
  at_symbol: "@"
780
785
  attachments: Attachments
@@ -897,6 +902,7 @@ en:
897
902
  close_sidebar: Close sidebar
898
903
  closed: Closed
899
904
  code: Code
905
+ color_code: Color Code
900
906
  colors: Colors
901
907
  company: Company
902
908
  compare_at_amount: Compare at amount
@@ -1573,6 +1579,11 @@ en:
1573
1579
  option_name: Option name
1574
1580
  option_type: Option Type
1575
1581
  option_type_filterable_info: When an option type is set to Filterable, your storefront visitors are presented with the option to filter a taxon of products based on the option type. A typical example of this would be to filter clothing by size and color.<br><br><b>Please Note:</b> Filters will only be visible in the storefront taxons that contain products with this option type set.
1582
+ option_type_kind_info: Controls how the option is displayed on the storefront. Color swatches show color circles, buttons show clickable chips, and dropdown shows a select menu.
1583
+ option_type_kinds:
1584
+ buttons: Buttons
1585
+ color_swatch: Color Swatch
1586
+ dropdown: Dropdown
1576
1587
  option_type_placeholder: Choose an option type
1577
1588
  option_types: Option Types
1578
1589
  option_value: Option Value
@@ -0,0 +1,11 @@
1
+ class CreateActionTextVideoEmbeds < ActiveRecord::Migration[6.1]
2
+ def change
3
+ create_table :action_text_video_embeds, if_not_exists: true do |t|
4
+ t.string :url, null: false
5
+ t.string :thumbnail_url, null: false
6
+ t.text :raw_html, null: false
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ class AddKindToSpreeOptionTypes < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_column :spree_option_types, :kind, :string, null: false, default: 'dropdown'
4
+ add_index :spree_option_types, :kind
5
+
6
+ # Backfill: option types named 'color'/'colour' get kind 'color_swatch'
7
+ reversible do |dir|
8
+ dir.up do
9
+ execute "UPDATE spree_option_types SET kind = 'color_swatch' WHERE name IN ('color', 'colour')"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class AddColorCodeToSpreeOptionValues < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_column :spree_option_values, :color_code, :string
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddMarketToSpreeOrders < ActiveRecord::Migration[7.2]
2
+ def change
3
+ add_reference :spree_orders, :market, foreign_key: false, index: true
4
+ end
5
+ end
@@ -1,6 +1,6 @@
1
- %w[manufacturer material fit].each do |key|
1
+ %w[warranty capacity voltage wattage runtime room_coverage noise_level connectivity].each do |key|
2
2
  Spree::MetafieldDefinition.find_or_create_by!(
3
- namespace: 'properties',
3
+ namespace: 'custom',
4
4
  key: key,
5
5
  resource_type: 'Spree::Product'
6
6
  )
@@ -0,0 +1,4 @@
1
+ color = Spree::OptionType.find_or_initialize_by(name: 'color')
2
+ color.presentation = 'Color'
3
+ color.kind = 'color_swatch'
4
+ color.save!
@@ -1,8 +1,8 @@
1
1
  store = Spree::Store.default
2
2
 
3
3
  # Find products created by the product import
4
- product_1 = Spree::Product.find_by(name: 'Denim Shirt')
5
- product_2 = Spree::Product.find_by(name: 'Checked Shirt')
4
+ product_1 = Spree::Product.find_by(name: 'Automatic Espresso Machine')
5
+ product_2 = Spree::Product.find_by(name: 'Drip Coffee Maker 1.5L')
6
6
 
7
7
  unless product_1 && product_2
8
8
  puts ' Skipping orders: required products not found'