spree_core 4.5.0 → 4.7.3
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/finders/spree/countries/find.rb +1 -1
- data/app/finders/spree/option_values/find_available.rb +1 -1
- data/app/finders/spree/product_properties/find_available.rb +1 -1
- data/app/finders/spree/products/find.rb +21 -13
- data/app/finders/spree/taxons/find.rb +7 -10
- data/app/helpers/spree/base_helper.rb +3 -7
- data/app/helpers/spree/products_helper.rb +3 -3
- data/app/models/concerns/spree/metadata.rb +7 -2
- data/app/models/concerns/spree/named_type.rb +1 -1
- data/app/models/concerns/spree/product_scopes.rb +27 -23
- data/app/models/concerns/spree/translatable_resource.rb +25 -0
- data/app/models/concerns/spree/translatable_resource_scopes.rb +24 -0
- data/app/models/concerns/spree/translatable_resource_slug.rb +15 -0
- data/app/models/concerns/spree/user_roles.rb +3 -3
- data/app/models/spree/address.rb +6 -4
- data/app/models/spree/asset.rb +1 -1
- data/app/models/spree/base.rb +6 -1
- data/app/models/spree/calculator/default_tax.rb +1 -1
- data/app/models/spree/cms/sections/image_gallery.rb +1 -7
- data/app/models/spree/cms/sections/side_by_side_images.rb +1 -7
- data/app/models/spree/cms_page.rb +2 -2
- data/app/models/spree/cms_section.rb +1 -1
- data/app/models/spree/country.rb +2 -2
- data/app/models/spree/credit_card.rb +1 -1
- data/app/models/spree/customer_return.rb +2 -2
- data/app/models/spree/data_feed/google.rb +15 -0
- data/app/models/spree/data_feed.rb +40 -0
- data/app/models/spree/digital_link.rb +5 -1
- data/app/models/spree/image.rb +2 -2
- data/app/models/spree/legacy_user.rb +3 -3
- data/app/models/spree/line_item.rb +1 -1
- data/app/models/spree/log_entry.rb +5 -1
- data/app/models/spree/menu.rb +1 -1
- data/app/models/spree/option_type.rb +6 -2
- data/app/models/spree/option_value.rb +5 -1
- data/app/models/spree/order/store_credit.rb +8 -0
- data/app/models/spree/order.rb +9 -9
- data/app/models/spree/order_contents.rb +31 -0
- data/app/models/spree/payment.rb +7 -15
- data/app/models/spree/payment_method.rb +1 -1
- data/app/models/spree/payment_source.rb +1 -1
- data/app/models/spree/preference.rb +5 -1
- data/app/models/spree/price.rb +1 -1
- data/app/models/spree/product.rb +64 -25
- data/app/models/spree/product_property.rb +13 -4
- data/app/models/spree/promotion.rb +2 -2
- data/app/models/spree/property.rb +8 -2
- data/app/models/spree/prototype.rb +1 -1
- data/app/models/spree/refund.rb +1 -1
- data/app/models/spree/reimbursement.rb +1 -1
- data/app/models/spree/return_authorization.rb +1 -1
- data/app/models/spree/return_item.rb +5 -1
- data/app/models/spree/role.rb +1 -1
- data/app/models/spree/shipment.rb +5 -5
- data/app/models/spree/shipping_category.rb +1 -1
- data/app/models/spree/shipping_method.rb +1 -1
- data/app/models/spree/stock/estimator.rb +1 -1
- data/app/models/spree/stock/splitter/weight.rb +1 -1
- data/app/models/spree/stock_item.rb +1 -1
- data/app/models/spree/stock_location.rb +1 -1
- data/app/models/spree/stock_transfer.rb +3 -3
- data/app/models/spree/store.rb +22 -3
- data/app/models/spree/store_credit.rb +2 -2
- data/app/models/spree/taxon.rb +47 -7
- data/app/models/spree/taxon_image.rb +2 -2
- data/app/models/spree/taxonomy.rb +5 -1
- data/app/models/spree/variant.rb +6 -9
- data/app/models/spree/wishlist.rb +6 -2
- data/app/models/spree/zone.rb +2 -2
- data/app/services/spree/data_feeds/google/optional_attributes.rb +23 -0
- data/app/services/spree/data_feeds/google/optional_sub_attributes.rb +21 -0
- data/app/services/spree/data_feeds/google/products_list.rb +14 -0
- data/app/services/spree/data_feeds/google/required_attributes.rb +67 -0
- data/app/services/spree/data_feeds/google/rss.rb +107 -0
- data/app/services/spree/locales/set_fallback_locale_for_store.rb +16 -0
- data/app/services/spree/seeds/countries.rb +12 -27
- data/app/services/spree/seeds/states.rb +8 -17
- data/app/services/spree/stock_locations/stock_items/create.rb +12 -18
- data/app/sorters/spree/products/sort.rb +23 -0
- data/app/validators/db_maximum_length_validator.rb +2 -6
- data/brakeman.ignore +326 -18
- data/config/initializers/friendly_id.rb +2 -0
- data/config/initializers/mobility.rb +16 -0
- data/config/initializers/rails61_fixes.rb +1 -3
- data/config/locales/en.yml +7 -0
- data/db/migrate/20220706112554_create_product_name_and_description_translations_for_mobility_table_backend.rb +27 -0
- data/db/migrate/20220715083542_create_spree_product_translations_for_mobility.rb +7 -0
- data/db/migrate/20220715120222_change_product_name_null_to_true.rb +5 -0
- data/db/migrate/20220718100743_create_spree_taxon_name_and_description_translations_for_mobility_table_backend.rb +27 -0
- data/db/migrate/20220718100948_change_taxon_name_null_to_true.rb +5 -0
- data/db/migrate/20220802070609_add_locale_to_friendly_id_slugs.rb +11 -0
- data/db/migrate/20220802073225_create_spree_product_slug_translations_for_mobility_table_backend.rb +5 -0
- data/db/migrate/20220804073928_transfer_data_to_translatable_tables.rb +66 -0
- data/db/migrate/20221215151408_add_selected_locale_to_spree_users.rb +8 -0
- data/db/migrate/20221219123957_add_deleted_at_to_product_translations.rb +6 -0
- data/db/migrate/20221220133432_add_uniqueness_constraint_to_product_translations.rb +5 -0
- data/db/migrate/20221229132350_create_spree_data_feed_settings.rb +14 -0
- data/db/migrate/20230103144439_create_option_type_translations.rb +26 -0
- data/db/migrate/20230103151034_create_option_value_translations.rb +26 -0
- data/db/migrate/20230109084253_create_product_property_translations.rb +25 -0
- data/db/migrate/20230109094907_transfer_options_data_to_translatable_tables.rb +58 -0
- data/db/migrate/20230109105943_create_property_translations.rb +26 -0
- data/db/migrate/20230109110840_transfer_property_data_to_translatable_tables.rb +59 -0
- data/db/migrate/20230110142344_backfill_friendly_id_slug_locale.rb +15 -0
- data/db/migrate/20230111121534_add_additional_taxon_translation_fields.rb +8 -0
- data/db/migrate/20230111122511_transfer_product_and_taxon_data_to_translatable_tables.rb +82 -0
- data/db/migrate/20230117115531_create_taxonomy_translations.rb +24 -0
- data/db/migrate/20230117120430_allow_null_taxonomy_name.rb +5 -0
- data/db/migrate/20230117121303_transfer_taxonomy_data_to_translatable_tables.rb +11 -0
- data/db/migrate/20230210142732_create_store_translations.rb +50 -0
- data/db/migrate/20230210142849_transfer_store_data_to_translatable_tables.rb +11 -0
- data/db/migrate/20230210230434_add_deleted_at_to_store_translations.rb +6 -0
- data/db/migrate/20230415155958_rename_data_feed_settings_table.rb +5 -0
- data/db/migrate/20230415160828_rename_data_feed_table_columns.rb +7 -0
- data/db/migrate/20230415161226_add_indexes_to_data_feeds_table.rb +5 -0
- data/db/migrate/20230512094803_rename_data_feeds_column_provider_to_type.rb +5 -0
- data/db/migrate/20230514162157_add_index_on_locale_and_permalink_to_spree_taxons.rb +5 -0
- data/lib/mobility/plugins/store_based_fallbacks.rb +55 -0
- data/lib/spree/core/configuration.rb +2 -1
- data/lib/spree/core/controller_helpers/auth.rb +1 -1
- data/lib/spree/core/controller_helpers/common.rb +5 -5
- data/lib/spree/core/controller_helpers/locale.rb +33 -2
- data/lib/spree/core/dependencies.rb +70 -94
- data/lib/spree/core/dependencies_helper.rb +19 -0
- data/lib/spree/core/engine.rb +7 -1
- data/lib/spree/core/preferences/preferable_class_methods.rb +1 -1
- data/lib/spree/core/product_duplicator.rb +4 -1
- data/lib/spree/core/product_filters.rb +7 -4
- data/lib/spree/core/search/base.rb +1 -1
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/core.rb +2 -0
- data/lib/spree/permitted_attributes.rb +1 -1
- data/lib/spree/testing_support/controller_requests.rb +10 -10
- data/lib/spree/testing_support/factories/google_data_feed_factory.rb +8 -0
- data/lib/spree/testing_support/factories/product_factory.rb +6 -0
- data/lib/spree/testing_support/factories/product_translation_factory.rb +6 -0
- data/lib/spree/testing_support/factories/store_factory.rb +3 -0
- data/lib/spree/testing_support/factories/variant_factory.rb +4 -0
- data/lib/spree/translation_migrations.rb +40 -0
- data/lib/spree_core.rb +1 -0
- data/spree_core.gemspec +6 -3
- metadata +147 -14
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class StockTransfer < Spree::Base
|
|
3
3
|
include Spree::Core::NumberGenerator.new(prefix: 'T')
|
|
4
|
-
include NumberIdentifier
|
|
5
|
-
include NumberAsParam
|
|
6
|
-
include Metadata
|
|
4
|
+
include Spree::NumberIdentifier
|
|
5
|
+
include Spree::NumberAsParam
|
|
6
|
+
include Spree::Metadata
|
|
7
7
|
if defined?(Spree::Webhooks)
|
|
8
8
|
include Spree::Webhooks::HasWebhooks
|
|
9
9
|
end
|
data/app/models/spree/store.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class Store < Spree::Base
|
|
3
|
+
include Spree::TranslatableResource
|
|
3
4
|
if defined?(Spree::Webhooks)
|
|
4
5
|
include Spree::Webhooks::HasWebhooks
|
|
5
6
|
end
|
|
@@ -7,6 +8,17 @@ module Spree
|
|
|
7
8
|
include Spree::Security::Stores
|
|
8
9
|
end
|
|
9
10
|
|
|
11
|
+
TRANSLATABLE_FIELDS = %i[name meta_description meta_keywords seo_title facebook
|
|
12
|
+
twitter instagram customer_support_email description
|
|
13
|
+
address contact_phone new_order_notifications_email].freeze
|
|
14
|
+
translates(*TRANSLATABLE_FIELDS)
|
|
15
|
+
|
|
16
|
+
self::Translation.class_eval do
|
|
17
|
+
acts_as_paranoid
|
|
18
|
+
# deleted translation values still need to be accessible - remove deleted_at scope
|
|
19
|
+
default_scope { unscope(where: :deleted_at) }
|
|
20
|
+
end
|
|
21
|
+
|
|
10
22
|
typed_store :settings, coder: ActiveRecord::TypedStore::IdentityCoder do |s|
|
|
11
23
|
# Spree Digital Asset Configurations
|
|
12
24
|
s.boolean :limit_digital_download_count, default: true, null: false
|
|
@@ -55,6 +67,8 @@ module Spree
|
|
|
55
67
|
|
|
56
68
|
has_many :wishlists, class_name: 'Spree::Wishlist'
|
|
57
69
|
|
|
70
|
+
has_many :data_feeds, class_name: 'Spree::DataFeed'
|
|
71
|
+
|
|
58
72
|
belongs_to :default_country, class_name: 'Spree::Country'
|
|
59
73
|
belongs_to :checkout_zone, class_name: 'Spree::Zone'
|
|
60
74
|
|
|
@@ -98,7 +112,7 @@ module Spree
|
|
|
98
112
|
alias_attribute :contact_email, :customer_support_email
|
|
99
113
|
|
|
100
114
|
def self.current(url = nil)
|
|
101
|
-
|
|
115
|
+
Spree::Deprecation.warn(<<-DEPRECATION, caller)
|
|
102
116
|
`Spree::Store.current` is deprecated and will be removed in Spree 5.0
|
|
103
117
|
Please use `Spree::Stores::FindCurrent.new(url: "https://example.com").execute` instead
|
|
104
118
|
DEPRECATION
|
|
@@ -109,7 +123,12 @@ module Spree
|
|
|
109
123
|
# this behaviour is very buggy and unpredictable
|
|
110
124
|
def self.default
|
|
111
125
|
Rails.cache.fetch('default_store') do
|
|
112
|
-
|
|
126
|
+
# workaround for Mobility bug with first_or_initialize
|
|
127
|
+
if where(default: true).any?
|
|
128
|
+
where(default: true).first
|
|
129
|
+
else
|
|
130
|
+
new(default: true)
|
|
131
|
+
end
|
|
113
132
|
end
|
|
114
133
|
end
|
|
115
134
|
|
|
@@ -175,7 +194,7 @@ module Spree
|
|
|
175
194
|
end
|
|
176
195
|
|
|
177
196
|
def checkout_zone_or_default
|
|
178
|
-
|
|
197
|
+
Spree::Deprecation.warn('Store#checkout_zone_or_default is deprecated and will be removed in Spree 5')
|
|
179
198
|
|
|
180
199
|
@checkout_zone_or_default ||= checkout_zone || Spree::Zone.default_checkout_zone
|
|
181
200
|
end
|
data/app/models/spree/taxon.rb
CHANGED
|
@@ -3,7 +3,9 @@ require 'stringex'
|
|
|
3
3
|
|
|
4
4
|
module Spree
|
|
5
5
|
class Taxon < Spree::Base
|
|
6
|
-
include
|
|
6
|
+
include Spree::TranslatableResource
|
|
7
|
+
include Spree::TranslatableResourceSlug
|
|
8
|
+
include Spree::Metadata
|
|
7
9
|
if defined?(Spree::Webhooks)
|
|
8
10
|
include Spree::Webhooks::HasWebhooks
|
|
9
11
|
end
|
|
@@ -11,6 +13,7 @@ module Spree
|
|
|
11
13
|
extend FriendlyId
|
|
12
14
|
friendly_id :permalink, slug_column: :permalink, use: :history
|
|
13
15
|
before_validation :set_permalink, on: :create, if: :name
|
|
16
|
+
alias_attribute :slug, :permalink
|
|
14
17
|
|
|
15
18
|
acts_as_nested_set dependent: :destroy
|
|
16
19
|
|
|
@@ -27,7 +30,7 @@ module Spree
|
|
|
27
30
|
has_many :promotion_rule_taxons, class_name: 'Spree::PromotionRuleTaxon', dependent: :destroy
|
|
28
31
|
has_many :promotion_rules, through: :promotion_rule_taxons, class_name: 'Spree::PromotionRule'
|
|
29
32
|
|
|
30
|
-
validates :name, presence: true, uniqueness: { scope: [:parent_id, :taxonomy_id],
|
|
33
|
+
validates :name, presence: true, uniqueness: { scope: [:parent_id, :taxonomy_id], case_sensitive: false }
|
|
31
34
|
validates :taxonomy, presence: true
|
|
32
35
|
validates :permalink, uniqueness: { case_sensitive: false, scope: [:parent_id, :taxonomy_id] }
|
|
33
36
|
validates :hide_from_nav, inclusion: { in: [true, false] }
|
|
@@ -54,6 +57,47 @@ module Spree
|
|
|
54
57
|
|
|
55
58
|
scope :for_stores, ->(stores) { joins(:taxonomy).where(spree_taxonomies: { store_id: stores.ids }) }
|
|
56
59
|
|
|
60
|
+
TRANSLATABLE_FIELDS = %i[name description permalink].freeze
|
|
61
|
+
translates(*TRANSLATABLE_FIELDS)
|
|
62
|
+
|
|
63
|
+
self::Translation.class_eval do
|
|
64
|
+
alias_attribute :slug, :permalink
|
|
65
|
+
before_save :set_permalink
|
|
66
|
+
|
|
67
|
+
def set_permalink
|
|
68
|
+
self.permalink = generate_slug
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def generate_slug
|
|
74
|
+
if parent.present?
|
|
75
|
+
generate_permalink_including_parent
|
|
76
|
+
elsif permalink.blank?
|
|
77
|
+
name_with_fallback.to_url
|
|
78
|
+
else
|
|
79
|
+
permalink.to_url
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def generate_permalink_including_parent
|
|
84
|
+
[parent_permalink_with_fallback, (permalink.blank? ? name_with_fallback.to_url : permalink.split('/').last.to_url)].join('/')
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def name_with_fallback
|
|
88
|
+
name.blank? ? translated_model.name : name
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def parent
|
|
92
|
+
translated_model.parent
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def parent_permalink_with_fallback
|
|
96
|
+
localized_parent = parent.translations.find_by(locale: locale)
|
|
97
|
+
localized_parent.present? ? localized_parent.permalink : parent.permalink
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
57
101
|
# indicate which filters should be used for a taxon
|
|
58
102
|
# this method should be customized to your own site
|
|
59
103
|
def applicable_filters
|
|
@@ -73,11 +117,7 @@ module Spree
|
|
|
73
117
|
|
|
74
118
|
# Creates permalink base for friendly_id
|
|
75
119
|
def set_permalink
|
|
76
|
-
|
|
77
|
-
self.permalink = [parent.permalink, (permalink.blank? ? name.to_url : permalink.split('/').last)].join('/')
|
|
78
|
-
else
|
|
79
|
-
self.permalink = name.to_url if permalink.blank?
|
|
80
|
-
end
|
|
120
|
+
translations.each(&:set_permalink)
|
|
81
121
|
end
|
|
82
122
|
|
|
83
123
|
def active_products
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class TaxonImage < Asset
|
|
3
|
-
include Configuration::ActiveStorage
|
|
3
|
+
include Spree::TaxonImage::Configuration::ActiveStorage
|
|
4
4
|
include Rails.application.routes.url_helpers
|
|
5
|
-
include
|
|
5
|
+
include Spree::ImageMethods
|
|
6
6
|
|
|
7
7
|
def styles
|
|
8
8
|
self.class.styles.map do |_, size|
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class Taxonomy < Spree::Base
|
|
3
|
-
include
|
|
3
|
+
include Spree::TranslatableResource
|
|
4
|
+
include Spree::Metadata
|
|
4
5
|
if defined?(Spree::Webhooks)
|
|
5
6
|
include Spree::Webhooks::HasWebhooks
|
|
6
7
|
end
|
|
7
8
|
|
|
9
|
+
TRANSLATABLE_FIELDS = %i[name].freeze
|
|
10
|
+
translates(*TRANSLATABLE_FIELDS)
|
|
11
|
+
|
|
8
12
|
acts_as_list
|
|
9
13
|
|
|
10
14
|
validates :name, presence: true, uniqueness: { case_sensitive: false, allow_blank: true, scope: :store_id }
|
data/app/models/spree/variant.rb
CHANGED
|
@@ -3,8 +3,8 @@ module Spree
|
|
|
3
3
|
acts_as_paranoid
|
|
4
4
|
acts_as_list scope: :product
|
|
5
5
|
|
|
6
|
-
include MemoizedData
|
|
7
|
-
include Metadata
|
|
6
|
+
include Spree::MemoizedData
|
|
7
|
+
include Spree::Metadata
|
|
8
8
|
if defined?(Spree::Webhooks)
|
|
9
9
|
include Spree::Webhooks::HasWebhooks
|
|
10
10
|
end
|
|
@@ -122,16 +122,13 @@ module Spree
|
|
|
122
122
|
self.whitelisted_ransackable_scopes = %i(product_name_or_sku_cont search_by_product_name_or_sku)
|
|
123
123
|
|
|
124
124
|
def self.product_name_or_sku_cont(query)
|
|
125
|
-
joins(:product).
|
|
125
|
+
joins(:product).join_translation_table(Product).
|
|
126
|
+
where("LOWER(#{Product.translation_table_alias}.name) LIKE LOWER(:query)
|
|
127
|
+
OR LOWER(sku) LIKE LOWER(:query)", query: "%#{query}%")
|
|
126
128
|
end
|
|
127
129
|
|
|
128
130
|
def self.search_by_product_name_or_sku(query)
|
|
129
|
-
|
|
130
|
-
joins(product: :translations).where("LOWER(#{Product::Translation.table_name}.name) LIKE LOWER(:query) OR LOWER(sku) LIKE LOWER(:query)",
|
|
131
|
-
query: "%#{query}%")
|
|
132
|
-
else
|
|
133
|
-
product_name_or_sku_cont(query)
|
|
134
|
-
end
|
|
131
|
+
product_name_or_sku_cont(query)
|
|
135
132
|
end
|
|
136
133
|
|
|
137
134
|
def available?
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class Wishlist < Spree::Base
|
|
3
|
-
include SingleStoreResource
|
|
3
|
+
include Spree::SingleStoreResource
|
|
4
4
|
if defined?(Spree::Webhooks)
|
|
5
5
|
include Spree::Webhooks::HasWebhooks
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
if Rails::VERSION::STRING >= '7.1.0'
|
|
9
|
+
has_secure_token on: :save
|
|
10
|
+
else
|
|
11
|
+
has_secure_token
|
|
12
|
+
end
|
|
9
13
|
|
|
10
14
|
belongs_to :user, class_name: "::#{Spree.user_class}", touch: true
|
|
11
15
|
belongs_to :store, class_name: 'Spree::Store'
|
data/app/models/spree/zone.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Spree
|
|
2
2
|
class Zone < Spree::Base
|
|
3
|
-
include UniqueName
|
|
3
|
+
include Spree::UniqueName
|
|
4
4
|
if defined?(Spree::Webhooks)
|
|
5
5
|
include Spree::Webhooks::HasWebhooks
|
|
6
6
|
end
|
|
@@ -74,7 +74,7 @@ module Spree
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def self.default_checkout_zone
|
|
77
|
-
|
|
77
|
+
Spree::Deprecation.warn('Spree::Zone.default_checkout_zone is deprecated and will be removed in Spree 5')
|
|
78
78
|
|
|
79
79
|
first
|
|
80
80
|
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module DataFeeds
|
|
3
|
+
module Google
|
|
4
|
+
class OptionalAttributes
|
|
5
|
+
prepend Spree::ServiceModule::Base
|
|
6
|
+
|
|
7
|
+
def call(input)
|
|
8
|
+
information = {}
|
|
9
|
+
|
|
10
|
+
input[:product].property_ids.each do |key|
|
|
11
|
+
name = Spree::Property.find(key)&.name
|
|
12
|
+
value = input[:product].property(name)
|
|
13
|
+
unless value.nil?
|
|
14
|
+
information[name] = value
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
success(information: information)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module DataFeeds
|
|
3
|
+
module Google
|
|
4
|
+
class OptionalSubAttributes
|
|
5
|
+
prepend Spree::ServiceModule::Base
|
|
6
|
+
|
|
7
|
+
def call(input)
|
|
8
|
+
information = {}
|
|
9
|
+
|
|
10
|
+
# This is a place where you can put attributes that have sub-attributes, example for shipping:
|
|
11
|
+
#
|
|
12
|
+
# information['shipping'] = {}
|
|
13
|
+
# information['shipping']['price'] = calculate_shipping(input[:product])
|
|
14
|
+
# information['shipping']['country'] = input[:store].default_country
|
|
15
|
+
|
|
16
|
+
success(information: information)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module DataFeeds
|
|
3
|
+
module Google
|
|
4
|
+
class RequiredAttributes
|
|
5
|
+
prepend Spree::ServiceModule::Base
|
|
6
|
+
|
|
7
|
+
def call(input)
|
|
8
|
+
information = {}
|
|
9
|
+
|
|
10
|
+
return failure(nil, error: 'No image link') if get_image_link(input[:variant], input[:product]).nil?
|
|
11
|
+
|
|
12
|
+
information['id'] = input[:variant].id
|
|
13
|
+
information['title'] = format_title(input[:product], input[:variant])
|
|
14
|
+
information['description'] = get_description(input[:product], input[:variant])
|
|
15
|
+
information['link'] = "#{input[:store].url}/#{input[:product].slug}"
|
|
16
|
+
information['image_link'] = get_image_link(input[:variant], input[:product])
|
|
17
|
+
information['price'] = format_price(input[:variant])
|
|
18
|
+
information['availability'] = get_availability(input[:product])
|
|
19
|
+
information['availability_date'] = input[:product].available_on&.xmlschema unless input[:product].available_on.nil?
|
|
20
|
+
|
|
21
|
+
success(information: information)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def format_title(product, variant)
|
|
27
|
+
# Title of a variant is created by joining title of a product and variant's option_values, as they are
|
|
28
|
+
# what differentiates it from other variants.
|
|
29
|
+
title = product.name
|
|
30
|
+
variant.option_values.find_each do |option_value|
|
|
31
|
+
title << " - #{option_value.name}"
|
|
32
|
+
end
|
|
33
|
+
title
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def get_description(product, variant)
|
|
37
|
+
return product.description unless product.description.nil?
|
|
38
|
+
|
|
39
|
+
format_title(product, variant)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get_image_link(variant, product)
|
|
43
|
+
# try getting image from variant
|
|
44
|
+
img = variant.images.first&.plp_url
|
|
45
|
+
|
|
46
|
+
# if no image specified for variant try getting product image
|
|
47
|
+
if img.nil?
|
|
48
|
+
img = product.images.first&.plp_url
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
img
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def format_price(variant)
|
|
55
|
+
"#{variant.price} #{variant.cost_currency}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def get_availability(product)
|
|
59
|
+
return 'in stock' if product.available? && product.available_on&.past?
|
|
60
|
+
return 'backorder' if product.backorderable? && product.backordered? && product.available_on&.future?
|
|
61
|
+
|
|
62
|
+
'out of stock'
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
require 'nokogiri'
|
|
2
|
+
|
|
3
|
+
module Spree
|
|
4
|
+
module DataFeeds
|
|
5
|
+
module Google
|
|
6
|
+
class Rss
|
|
7
|
+
prepend Spree::ServiceModule::Base
|
|
8
|
+
|
|
9
|
+
def call(settings)
|
|
10
|
+
@settings = settings
|
|
11
|
+
|
|
12
|
+
return failure(store, error: "Store with id: #{settings.store_id} does not exist.") if store.nil?
|
|
13
|
+
|
|
14
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
|
15
|
+
xml.rss('xmlns:g' => 'http://base.google.com/ns/1.0', 'version' => '2.0') do
|
|
16
|
+
xml.channel do
|
|
17
|
+
add_store_information_to_xml(xml)
|
|
18
|
+
result = products_list.call(store)
|
|
19
|
+
if result.success?
|
|
20
|
+
result.value[:products].find_each do |product|
|
|
21
|
+
product.variants.active.find_each do |variant|
|
|
22
|
+
add_variant_information_to_xml(xml, product, variant)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
success(file: builder.to_xml)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def store
|
|
36
|
+
return @store if defined? @store
|
|
37
|
+
|
|
38
|
+
@store ||= Spree::Store.find_by(id: @settings.store_id)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def add_store_information_to_xml(xml)
|
|
42
|
+
xml.title store.name
|
|
43
|
+
xml.link store.url
|
|
44
|
+
xml.description store.meta_description
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def add_variant_information_to_xml(xml, product, variant)
|
|
48
|
+
input = { product: product, variant: variant, settings: @settings, store: store }
|
|
49
|
+
result = required_attributes.call(input)
|
|
50
|
+
|
|
51
|
+
if result.success
|
|
52
|
+
xml.item do
|
|
53
|
+
result.value[:information]&.each do |key, value|
|
|
54
|
+
xml['g'].send(key, value)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
add_optional_information(xml, product, variant)
|
|
58
|
+
add_optional_sub_attributes(xml, product, variant)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def add_optional_information(xml, product, variant)
|
|
64
|
+
input = { product: product, variant: variant, settings: @settings, store: store }
|
|
65
|
+
result = optional_attributes.call(input)
|
|
66
|
+
if result.success?
|
|
67
|
+
information = result.value[:information]
|
|
68
|
+
information.each do |key, value|
|
|
69
|
+
xml['g'].send(key, value)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def add_optional_sub_attributes(xml, product, variant)
|
|
75
|
+
input = { product: product, variant: variant, settings: @settings, store: store }
|
|
76
|
+
result = optional_sub_attributes.call(input)
|
|
77
|
+
if result.success?
|
|
78
|
+
information = result.value[:information]
|
|
79
|
+
information.each do |key, value|
|
|
80
|
+
xml['g'].send(key) do
|
|
81
|
+
value.each do |subkey, subvalue|
|
|
82
|
+
xml['g'].send(subkey, subvalue)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def optional_attributes
|
|
90
|
+
Spree::Dependencies.data_feeds_google_optional_attributes_service.constantize.new
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def required_attributes
|
|
94
|
+
Spree::Dependencies.data_feeds_google_required_attributes_service.constantize.new
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def optional_sub_attributes
|
|
98
|
+
Spree::Dependencies.data_feeds_google_optional_sub_attributes_service.constantize.new
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def products_list
|
|
102
|
+
Spree::Dependencies.data_feeds_google_products_list.constantize.new
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Spree
|
|
2
|
+
module Locales
|
|
3
|
+
class SetFallbackLocaleForStore
|
|
4
|
+
def call(store:)
|
|
5
|
+
store_default_locale = store.default_locale
|
|
6
|
+
fallbacks = store.supported_locales_list.each_with_object({}) do |locale, object|
|
|
7
|
+
object[locale] = [store_default_locale]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
fallbacks_instance = I18n::Locale::Fallbacks.new(fallbacks)
|
|
11
|
+
|
|
12
|
+
Mobility.store_based_fallbacks = fallbacks_instance
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -9,34 +9,19 @@ module Spree
|
|
|
9
9
|
|
|
10
10
|
def call
|
|
11
11
|
ApplicationRecord.transaction do
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
next if EXCLUDED_COUNTRIES.include?(country.alpha_2_code)
|
|
12
|
+
new_countries = Carmen::Country.all.map do |country|
|
|
13
|
+
# Skip the creation of some territories, uninhabited islands and the Antarctic.
|
|
14
|
+
next if EXCLUDED_COUNTRIES.include?(country.alpha_2_code)
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
else
|
|
27
|
-
Carmen::Country.all.each do |country|
|
|
28
|
-
# Skip the creation of some territories, uninhabited islands and the Antarctic.
|
|
29
|
-
next if EXCLUDED_COUNTRIES.include?(country.alpha_2_code)
|
|
30
|
-
|
|
31
|
-
Spree::Country.where(
|
|
32
|
-
name: country.name,
|
|
33
|
-
iso3: country.alpha_3_code,
|
|
34
|
-
iso: country.alpha_2_code,
|
|
35
|
-
iso_name: country.name.upcase,
|
|
36
|
-
numcode: country.numeric_code
|
|
37
|
-
).first_or_create!
|
|
38
|
-
end
|
|
39
|
-
end
|
|
16
|
+
Hash[
|
|
17
|
+
'name': country.name,
|
|
18
|
+
'iso3': country.alpha_3_code,
|
|
19
|
+
'iso': country.alpha_2_code,
|
|
20
|
+
'iso_name': country.name.upcase,
|
|
21
|
+
'numcode': country.numeric_code,
|
|
22
|
+
]
|
|
23
|
+
end.compact.uniq
|
|
24
|
+
Spree::Country.insert_all(new_countries)
|
|
40
25
|
|
|
41
26
|
# Find countries that do not use postal codes (by iso) and set 'zipcode_required' to false for them.
|
|
42
27
|
Spree::Country.where(iso: Spree::Address::NO_ZIPCODE_ISO_CODES).update_all(zipcode_required: false)
|
|
@@ -49,23 +49,14 @@ module Spree
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def province_level(country, subregion)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Spree::State.insert_all(new_states)
|
|
61
|
-
else
|
|
62
|
-
subregion.subregions.each do |province|
|
|
63
|
-
country.states.where(
|
|
64
|
-
name: province.name,
|
|
65
|
-
abbr: province.code
|
|
66
|
-
).first_or_create!
|
|
67
|
-
end
|
|
68
|
-
end
|
|
52
|
+
new_states = subregion.subregions.map do |province|
|
|
53
|
+
Hash[
|
|
54
|
+
name: province.name,
|
|
55
|
+
abbr: province.code,
|
|
56
|
+
country_id: country.id
|
|
57
|
+
]
|
|
58
|
+
end.compact.uniq
|
|
59
|
+
Spree::State.insert_all(new_states)
|
|
69
60
|
end
|
|
70
61
|
end
|
|
71
62
|
end
|
|
@@ -5,24 +5,18 @@ module Spree
|
|
|
5
5
|
prepend Spree::ServiceModule::Base
|
|
6
6
|
|
|
7
7
|
def call(stock_location:, variants_scope: Spree::Variant)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
variants_scope.touch_all
|
|
21
|
-
end
|
|
22
|
-
else
|
|
23
|
-
variants_scope.find_each do |variant|
|
|
24
|
-
stock_location.propagate_variant(variant)
|
|
25
|
-
end
|
|
8
|
+
prepared_stock_items = variants_scope.ids.map do |variant_id|
|
|
9
|
+
Hash[
|
|
10
|
+
'stock_location_id', stock_location.id,
|
|
11
|
+
'variant_id', variant_id,
|
|
12
|
+
'backorderable', stock_location.backorderable_default,
|
|
13
|
+
'created_at', Time.current,
|
|
14
|
+
'updated_at', Time.current
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
if prepared_stock_items.any?
|
|
18
|
+
stock_location.stock_items.insert_all(prepared_stock_items)
|
|
19
|
+
variants_scope.touch_all
|
|
26
20
|
end
|
|
27
21
|
end
|
|
28
22
|
end
|