spree_core 5.4.0.rc5 → 5.4.0.rc7
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.
- checksums.yaml +4 -4
- data/app/mailers/spree/base_mailer.rb +2 -0
- data/app/models/concerns/spree/default_price.rb +108 -6
- data/app/models/concerns/spree/stores/markets.rb +4 -2
- data/app/models/spree/exports/product_translations.rb +45 -0
- data/app/models/spree/import_schemas/product_translations.rb +14 -0
- data/app/models/spree/imports/product_translations.rb +17 -0
- data/app/models/spree/market.rb +1 -0
- data/app/models/spree/metafield.rb +1 -1
- data/app/models/spree/metafield_definition.rb +10 -0
- data/app/models/spree/option_type.rb +20 -2
- data/app/models/spree/option_value.rb +17 -1
- data/app/models/spree/order.rb +22 -2
- data/app/models/spree/price.rb +1 -1
- data/app/models/spree/product.rb +40 -13
- data/app/models/spree/search_provider/meilisearch.rb +41 -8
- data/app/models/spree/stock_location.rb +1 -1
- data/app/models/spree/stock_movement.rb +2 -1
- data/app/models/spree/variant.rb +13 -11
- data/app/presenters/spree/csv/product_translation_presenter.rb +31 -0
- data/app/presenters/spree/csv/product_variant_presenter.rb +16 -3
- data/app/presenters/spree/search_provider/product_presenter.rb +8 -13
- data/app/services/spree/cart/create.rb +1 -0
- data/app/services/spree/carts/create.rb +1 -0
- data/app/services/spree/carts/update.rb +21 -0
- data/app/services/spree/imports/row_processors/product_translation.rb +45 -0
- data/app/services/spree/imports/row_processors/product_variant.rb +11 -3
- data/app/services/spree/products/prepare_nested_attributes.rb +18 -1
- data/app/services/spree/sample_data/loader.rb +31 -20
- data/app/services/spree/seeds/all.rb +22 -20
- data/app/views/spree/shared/_mailer_line_item.html.erb +2 -2
- data/config/locales/en.yml +12 -0
- data/db/migrate/20250217171018_create_action_text_video_embeds.rb +11 -0
- data/db/migrate/20260402000001_add_kind_to_spree_option_types.rb +13 -0
- data/db/migrate/20260402000002_add_color_code_to_spree_option_values.rb +5 -0
- data/db/migrate/20260403000000_add_market_to_spree_orders.rb +5 -0
- data/db/sample_data/markets.rb +30 -0
- data/db/sample_data/metafield_definitions.rb +2 -2
- data/db/sample_data/options.rb +4 -0
- data/db/sample_data/orders.rb +2 -2
- data/db/sample_data/product_translations.csv +75 -0
- data/db/sample_data/products.csv +212 -1083
- data/lib/spree/core/configuration.rb +1 -0
- data/lib/spree/core/controller_helpers/order.rb +12 -10
- data/lib/spree/core/engine.rb +2 -0
- data/lib/spree/core/permission_configuration.rb +12 -0
- data/lib/spree/core/pricing/resolver.rb +5 -3
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/permitted_attributes.rb +3 -3
- data/lib/spree/testing_support/authorization_helpers.rb +16 -16
- data/lib/spree/testing_support/factories/import_factory.rb +12 -0
- data/lib/spree/testing_support/factories/options_factory.rb +11 -0
- data/lib/spree/testing_support/factories/product_factory.rb +12 -2
- data/lib/spree/testing_support/factories/variant_factory.rb +8 -2
- data/lib/tasks/markets.rake +5 -2
- data/lib/tasks/search.rake +3 -3
- data/lib/tasks/variants.rake +2 -2
- data/spec/fixtures/files/product_translations_import.csv +3 -0
- metadata +26 -7
- /data/lib/tasks/{images.rake → media.rake} +0 -0
|
@@ -31,7 +31,8 @@ module Spree
|
|
|
31
31
|
delegate :variant, :variant_id, to: :stock_item, allow_nil: true
|
|
32
32
|
delegate :product, to: :variant
|
|
33
33
|
|
|
34
|
-
self.whitelisted_ransackable_attributes = [
|
|
34
|
+
self.whitelisted_ransackable_attributes = %w[quantity action created_at stock_item_id originator_type]
|
|
35
|
+
self.whitelisted_ransackable_associations = %w[stock_item]
|
|
35
36
|
|
|
36
37
|
def readonly?
|
|
37
38
|
persisted?
|
data/app/models/spree/variant.rb
CHANGED
|
@@ -62,14 +62,13 @@ module Spree
|
|
|
62
62
|
|
|
63
63
|
before_validation :set_cost_currency
|
|
64
64
|
|
|
65
|
-
validate :check_price
|
|
65
|
+
validate :check_price, if: -> { Spree::Config.enable_legacy_default_price }
|
|
66
66
|
|
|
67
67
|
validates :option_value_variants, presence: true, unless: :is_master?
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
end
|
|
69
|
+
validates :cost_price, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
|
|
70
|
+
validates :price, numericality: { greater_than_or_equal_to: 0, allow_nil: true },
|
|
71
|
+
if: -> { Spree::Config.enable_legacy_default_price }
|
|
73
72
|
validates :sku, uniqueness: { conditions: -> { where(deleted_at: nil) }, case_sensitive: false, scope: spree_base_uniqueness_scope },
|
|
74
73
|
allow_blank: true, unless: :disable_sku_validation?
|
|
75
74
|
|
|
@@ -447,8 +446,8 @@ module Spree
|
|
|
447
446
|
def set_price(currency, amount, compare_at_amount = nil)
|
|
448
447
|
price = prices.base_prices.find_or_initialize_by(currency: currency)
|
|
449
448
|
price.amount = amount
|
|
450
|
-
price.compare_at_amount = compare_at_amount
|
|
451
|
-
price.save!
|
|
449
|
+
price.compare_at_amount = compare_at_amount
|
|
450
|
+
price.save! if persisted?
|
|
452
451
|
end
|
|
453
452
|
|
|
454
453
|
# Returns the price for the given context or options.
|
|
@@ -607,18 +606,21 @@ module Spree
|
|
|
607
606
|
|
|
608
607
|
# Ensures a new variant takes the product master price when price is not supplied
|
|
609
608
|
def check_price
|
|
610
|
-
return if
|
|
609
|
+
return if prices.any?
|
|
611
610
|
|
|
612
611
|
infer_price_from_default_variant_if_needed
|
|
613
|
-
self.currency = Spree::Store.default.default_currency if price.present? && currency.nil?
|
|
614
612
|
end
|
|
615
613
|
|
|
616
614
|
def infer_price_from_default_variant_if_needed
|
|
617
|
-
|
|
615
|
+
default_currency = Spree::Store.default.default_currency
|
|
616
|
+
current_price = price_in(default_currency).amount
|
|
617
|
+
|
|
618
|
+
if current_price.nil?
|
|
618
619
|
return errors.add(:base, :no_master_variant_found_to_infer_price) unless product&.master
|
|
619
620
|
|
|
620
621
|
# At this point, master can have or have no price, so let's use price from the default variant
|
|
621
|
-
|
|
622
|
+
inferred_price = product.default_variant.price_in(default_currency).amount
|
|
623
|
+
set_price(default_currency, inferred_price) if inferred_price.present?
|
|
622
624
|
end
|
|
623
625
|
end
|
|
624
626
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module CSV
|
|
3
|
+
class ProductTranslationPresenter
|
|
4
|
+
CSV_HEADERS = %w[
|
|
5
|
+
slug
|
|
6
|
+
locale
|
|
7
|
+
name
|
|
8
|
+
description
|
|
9
|
+
meta_title
|
|
10
|
+
meta_description
|
|
11
|
+
].freeze
|
|
12
|
+
|
|
13
|
+
TRANSLATABLE_FIELDS = %i[name description meta_title meta_description].freeze
|
|
14
|
+
|
|
15
|
+
def initialize(product, locale)
|
|
16
|
+
@product = product
|
|
17
|
+
@locale = locale.to_s
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
attr_reader :product, :locale
|
|
21
|
+
|
|
22
|
+
def call
|
|
23
|
+
[
|
|
24
|
+
product.slug,
|
|
25
|
+
locale,
|
|
26
|
+
*TRANSLATABLE_FIELDS.map { |field| product.get_field_with_locale(locale, field) }
|
|
27
|
+
]
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -47,18 +47,19 @@ module Spree
|
|
|
47
47
|
'category3',
|
|
48
48
|
].freeze
|
|
49
49
|
|
|
50
|
-
def initialize(product, variant, index = 0, properties = [], taxons = [], store = nil, metafields = [])
|
|
50
|
+
def initialize(product, variant, index = 0, properties = [], taxons = [], store = nil, metafields = [], currency = nil)
|
|
51
51
|
@product = product
|
|
52
52
|
@variant = variant
|
|
53
53
|
@index = index
|
|
54
54
|
@properties = properties
|
|
55
55
|
@taxons = taxons
|
|
56
56
|
@store = store || product.stores.first
|
|
57
|
-
@currency = @store.default_currency
|
|
57
|
+
@currency = currency || @store.default_currency
|
|
58
|
+
@price_only = @currency != @store.default_currency
|
|
58
59
|
@metafields = metafields
|
|
59
60
|
end
|
|
60
61
|
|
|
61
|
-
attr_accessor :product, :variant, :index, :properties, :taxons, :store, :currency, :metafields
|
|
62
|
+
attr_accessor :product, :variant, :index, :properties, :taxons, :store, :currency, :price_only, :metafields
|
|
62
63
|
|
|
63
64
|
##
|
|
64
65
|
# Generates an array representing a CSV row of product variant data.
|
|
@@ -72,6 +73,8 @@ module Spree
|
|
|
72
73
|
#
|
|
73
74
|
# @return [Array] An array containing the combined product and variant CSV data.
|
|
74
75
|
def call
|
|
76
|
+
return price_only_row if price_only
|
|
77
|
+
|
|
75
78
|
total_on_hand = variant.total_on_hand
|
|
76
79
|
|
|
77
80
|
csv = [
|
|
@@ -134,6 +137,16 @@ module Spree
|
|
|
134
137
|
|
|
135
138
|
private
|
|
136
139
|
|
|
140
|
+
def price_only_row
|
|
141
|
+
csv = Array.new(CSV_HEADERS.size)
|
|
142
|
+
csv[CSV_HEADERS.index('sku')] = variant.sku
|
|
143
|
+
csv[CSV_HEADERS.index('slug')] = product.slug
|
|
144
|
+
csv[CSV_HEADERS.index('price')] = variant.amount_in(currency)&.to_f
|
|
145
|
+
csv[CSV_HEADERS.index('compare_at_price')] = variant.compare_at_amount_in(currency)&.to_f
|
|
146
|
+
csv[CSV_HEADERS.index('currency')] = currency
|
|
147
|
+
csv
|
|
148
|
+
end
|
|
149
|
+
|
|
137
150
|
def image_url_options
|
|
138
151
|
{ variant: :xlarge }
|
|
139
152
|
end
|
|
@@ -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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
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
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Imports
|
|
3
|
+
module RowProcessors
|
|
4
|
+
class ProductTranslation < Base
|
|
5
|
+
TRANSLATABLE_FIELDS = %w[name description meta_title meta_description].freeze
|
|
6
|
+
|
|
7
|
+
def initialize(row)
|
|
8
|
+
super
|
|
9
|
+
@store = row.store
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
attr_reader :store
|
|
13
|
+
|
|
14
|
+
def process!
|
|
15
|
+
locale = attributes['locale'].to_s.strip
|
|
16
|
+
raise ArgumentError, 'Locale is required' if locale.blank?
|
|
17
|
+
|
|
18
|
+
slug = attributes['slug'].to_s.strip
|
|
19
|
+
raise ArgumentError, 'Slug is required' if slug.blank?
|
|
20
|
+
|
|
21
|
+
product = product_scope.find_by!(slug: slug)
|
|
22
|
+
|
|
23
|
+
translation_attrs = TRANSLATABLE_FIELDS.each_with_object({}) do |field, hash|
|
|
24
|
+
value = attributes[field]
|
|
25
|
+
hash[field.to_sym] = value.to_s.strip if value.present?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
return product if translation_attrs.empty?
|
|
29
|
+
|
|
30
|
+
Mobility.with_locale(locale) do
|
|
31
|
+
product.update!(translation_attrs)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
product
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def product_scope
|
|
40
|
+
Spree::Product.accessible_by(import.current_ability, :manage)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -32,7 +32,7 @@ module Spree
|
|
|
32
32
|
variant.width = attributes['width'] if attributes['width'].present?
|
|
33
33
|
variant.depth = attributes['depth'] if attributes['depth'].present?
|
|
34
34
|
variant.track_inventory = attributes['track_inventory'] if attributes['track_inventory'].present?
|
|
35
|
-
variant.option_value_variants = prepare_option_value_variants
|
|
35
|
+
variant.option_value_variants = prepare_option_value_variants if options.any?
|
|
36
36
|
|
|
37
37
|
if attributes['tax_category'].present?
|
|
38
38
|
tax_category = prepare_tax_category
|
|
@@ -68,7 +68,7 @@ module Spree
|
|
|
68
68
|
|
|
69
69
|
product = assign_attributes_to_product(product)
|
|
70
70
|
product.save!
|
|
71
|
-
handle_metafields(product)
|
|
71
|
+
handle_metafields(product) if has_product_attributes?
|
|
72
72
|
product
|
|
73
73
|
else
|
|
74
74
|
# For non-master variants, only look up the product
|
|
@@ -105,7 +105,11 @@ module Spree
|
|
|
105
105
|
shipping_category = prepare_shipping_category
|
|
106
106
|
product.shipping_category = shipping_category if shipping_category.present?
|
|
107
107
|
end
|
|
108
|
-
|
|
108
|
+
|
|
109
|
+
taxons = prepare_taxons
|
|
110
|
+
# Full product rows (with name/status/description) clear taxons when categories are blank.
|
|
111
|
+
# Price-only rows (no product attributes) never touch taxons.
|
|
112
|
+
product.taxons = taxons if taxons.any? || has_product_attributes?
|
|
109
113
|
end
|
|
110
114
|
|
|
111
115
|
product
|
|
@@ -197,6 +201,10 @@ module Spree
|
|
|
197
201
|
end
|
|
198
202
|
end
|
|
199
203
|
|
|
204
|
+
def has_product_attributes?
|
|
205
|
+
%w[name status description category1 category2 category3].any? { |key| attributes[key].present? }
|
|
206
|
+
end
|
|
207
|
+
|
|
200
208
|
def handle_metafields(product)
|
|
201
209
|
return unless product.class.included_modules.include?(Spree::Metafields)
|
|
202
210
|
|
|
@@ -33,8 +33,11 @@ module Spree
|
|
|
33
33
|
existing_variant = variant_params[:id].presence && @product.variants.find_by(id: variant_params[:id])
|
|
34
34
|
variants_to_remove.delete(variant_params[:id]) if variant_params[:id].present?
|
|
35
35
|
|
|
36
|
+
variant_params.delete(:price) # remove legacy price param
|
|
37
|
+
|
|
36
38
|
if can_update_prices?
|
|
37
|
-
|
|
39
|
+
backfill_price_ids!(variant_params, existing_variant)
|
|
40
|
+
|
|
38
41
|
variant_params[:prices_attributes]&.each do |price_key, price_params|
|
|
39
42
|
variant_params[:prices_attributes][price_key]['_destroy'] = '1' if price_params[:amount].blank?
|
|
40
43
|
end
|
|
@@ -95,6 +98,20 @@ module Spree
|
|
|
95
98
|
|
|
96
99
|
delegate :can?, :cannot?, to: :ability
|
|
97
100
|
|
|
101
|
+
# Backfill IDs for prices_attributes entries that reference existing prices
|
|
102
|
+
# so that ActiveRecord updates them instead of inserting duplicates
|
|
103
|
+
def backfill_price_ids!(variant_params, existing_variant)
|
|
104
|
+
return unless existing_variant && variant_params[:prices_attributes]
|
|
105
|
+
|
|
106
|
+
variant_params[:prices_attributes].each do |_key, price_params|
|
|
107
|
+
next if price_params[:id].present?
|
|
108
|
+
next if price_params[:currency].blank?
|
|
109
|
+
|
|
110
|
+
existing_price = existing_variant.prices.base_prices.find_by(currency: price_params[:currency])
|
|
111
|
+
price_params[:id] = existing_price.id if existing_price
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
98
115
|
def product_option_types_params
|
|
99
116
|
@product_option_types_params ||= {}
|
|
100
117
|
end
|
|
@@ -6,29 +6,38 @@ module Spree
|
|
|
6
6
|
def call
|
|
7
7
|
Spree::Events.disable do
|
|
8
8
|
without_geocoding do
|
|
9
|
-
|
|
9
|
+
ActiveRecord::Base.no_touching do
|
|
10
|
+
ensure_seeds_loaded
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
puts 'Loading sample configuration data...'
|
|
13
|
+
load_configuration_data
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
puts 'Loading sample markets...'
|
|
16
|
+
load_ruby_file('markets')
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
puts 'Loading sample metafield definitions...'
|
|
19
|
+
load_ruby_file('metafield_definitions')
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
21
|
+
puts 'Loading sample options...'
|
|
22
|
+
load_ruby_file('options')
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
puts 'Loading sample products...'
|
|
25
|
+
load_products
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
puts 'Loading sample product translations...'
|
|
28
|
+
load_product_translations
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
puts 'Loading sample customers...'
|
|
31
|
+
load_customers
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
puts 'Loading sample orders...'
|
|
34
|
+
load_ruby_file('orders')
|
|
35
|
+
|
|
36
|
+
puts 'Loading sample posts...'
|
|
37
|
+
load_ruby_file('posts')
|
|
38
|
+
|
|
39
|
+
puts 'Sample data loaded successfully!'
|
|
40
|
+
end
|
|
32
41
|
end
|
|
33
42
|
end
|
|
34
43
|
end
|
|
@@ -58,6 +67,13 @@ module Spree
|
|
|
58
67
|
Spree::SampleData::ImportRunner.call(csv_path: csv_path, import_class: Spree::Imports::Products)
|
|
59
68
|
end
|
|
60
69
|
|
|
70
|
+
def load_product_translations
|
|
71
|
+
csv_path = sample_data_path.join('product_translations.csv')
|
|
72
|
+
return unless csv_path.exist?
|
|
73
|
+
|
|
74
|
+
Spree::SampleData::ImportRunner.call(csv_path: csv_path, import_class: Spree::Imports::ProductTranslations)
|
|
75
|
+
end
|
|
76
|
+
|
|
61
77
|
def load_customers
|
|
62
78
|
csv_path = sample_data_path.join('customers.csv')
|
|
63
79
|
Spree::SampleData::ImportRunner.call(csv_path: csv_path, import_class: Spree::Imports::Customers)
|
|
@@ -75,11 +91,6 @@ module Spree
|
|
|
75
91
|
ensure
|
|
76
92
|
Spree::Config[:geocode_addresses] = previous
|
|
77
93
|
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
94
|
end
|
|
84
95
|
end
|
|
85
96
|
end
|
|
@@ -5,30 +5,32 @@ module Spree
|
|
|
5
5
|
|
|
6
6
|
def call
|
|
7
7
|
Spree::Events.disable do
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
ActiveRecord::Base.no_touching do
|
|
9
|
+
# GEO
|
|
10
|
+
Countries.call
|
|
11
|
+
States.call
|
|
12
|
+
Zones.call
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
# user roles
|
|
15
|
+
Roles.call
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
# additional data
|
|
18
|
+
ReturnsEnvironment.call
|
|
19
|
+
ShippingCategories.call
|
|
20
|
+
StoreCreditCategories.call
|
|
21
|
+
TaxCategories.call
|
|
22
|
+
DigitalDelivery.call
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
# store & stock location
|
|
25
|
+
Stores.call
|
|
26
|
+
StockLocations.call
|
|
27
|
+
AdminUser.call
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
# add store resources
|
|
30
|
+
PaymentMethods.call
|
|
31
|
+
ApiKeys.call
|
|
32
|
+
AllowedOrigins.call
|
|
33
|
+
end
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
<tr>
|
|
2
2
|
<td class="six sub-columns">
|
|
3
3
|
<strong>
|
|
4
|
-
<%= link_to
|
|
4
|
+
<%= link_to line_item.variant.product.name, spree_storefront_resource_url(line_item.variant.product) %>
|
|
5
5
|
</strong>
|
|
6
|
-
<%=
|
|
6
|
+
<%= line_item.variant.options_text -%>
|
|
7
7
|
(<%= line_item.variant.sku %>)
|
|
8
8
|
</td>
|
|
9
9
|
<td class="six sub-columns last right">
|
data/config/locales/en.yml
CHANGED
|
@@ -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
|
|
@@ -1826,6 +1837,7 @@ en:
|
|
|
1826
1837
|
group: From product group
|
|
1827
1838
|
manual: Manually choose
|
|
1828
1839
|
product_sold_out: This item is sold out
|
|
1840
|
+
product_translations: Product Translations
|
|
1829
1841
|
products: Products
|
|
1830
1842
|
products_added: Products added
|
|
1831
1843
|
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.
|
|
@@ -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,30 @@
|
|
|
1
|
+
store = Spree::Store.default
|
|
2
|
+
|
|
3
|
+
us = Spree::Country.find_by(iso: 'US')
|
|
4
|
+
ca = Spree::Country.find_by(iso: 'CA')
|
|
5
|
+
|
|
6
|
+
if us
|
|
7
|
+
# Store auto-creates a default market on creation — update it rather than creating a new one
|
|
8
|
+
us_market = store.markets.default.first || store.markets.order(:position).first || store.markets.new
|
|
9
|
+
us_market.name = 'US'
|
|
10
|
+
us_market.currency = 'USD'
|
|
11
|
+
us_market.default_locale = 'en'
|
|
12
|
+
us_market.default = true
|
|
13
|
+
us_market.countries = [us, ca].compact
|
|
14
|
+
us_market.save!
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
eu_zone = Spree::Zone.find_by(name: 'EU_VAT')
|
|
18
|
+
|
|
19
|
+
if eu_zone
|
|
20
|
+
eu_countries = eu_zone.zone_members.where(zoneable_type: 'Spree::Country').map(&:zoneable)
|
|
21
|
+
|
|
22
|
+
if eu_countries.any?
|
|
23
|
+
eu_market = store.markets.find_or_initialize_by(name: 'Europe')
|
|
24
|
+
eu_market.currency = 'EUR'
|
|
25
|
+
eu_market.default_locale = 'de'
|
|
26
|
+
eu_market.supported_locales = 'de,fr,es,it'
|
|
27
|
+
eu_market.countries = eu_countries
|
|
28
|
+
eu_market.save!
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
%w[
|
|
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: '
|
|
3
|
+
namespace: 'custom',
|
|
4
4
|
key: key,
|
|
5
5
|
resource_type: 'Spree::Product'
|
|
6
6
|
)
|