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.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/app/finders/spree/countries/find.rb +1 -1
  3. data/app/finders/spree/option_values/find_available.rb +1 -1
  4. data/app/finders/spree/product_properties/find_available.rb +1 -1
  5. data/app/finders/spree/products/find.rb +21 -13
  6. data/app/finders/spree/taxons/find.rb +7 -10
  7. data/app/helpers/spree/base_helper.rb +3 -7
  8. data/app/helpers/spree/products_helper.rb +3 -3
  9. data/app/models/concerns/spree/metadata.rb +7 -2
  10. data/app/models/concerns/spree/named_type.rb +1 -1
  11. data/app/models/concerns/spree/product_scopes.rb +27 -23
  12. data/app/models/concerns/spree/translatable_resource.rb +25 -0
  13. data/app/models/concerns/spree/translatable_resource_scopes.rb +24 -0
  14. data/app/models/concerns/spree/translatable_resource_slug.rb +15 -0
  15. data/app/models/concerns/spree/user_roles.rb +3 -3
  16. data/app/models/spree/address.rb +6 -4
  17. data/app/models/spree/asset.rb +1 -1
  18. data/app/models/spree/base.rb +6 -1
  19. data/app/models/spree/calculator/default_tax.rb +1 -1
  20. data/app/models/spree/cms/sections/image_gallery.rb +1 -7
  21. data/app/models/spree/cms/sections/side_by_side_images.rb +1 -7
  22. data/app/models/spree/cms_page.rb +2 -2
  23. data/app/models/spree/cms_section.rb +1 -1
  24. data/app/models/spree/country.rb +2 -2
  25. data/app/models/spree/credit_card.rb +1 -1
  26. data/app/models/spree/customer_return.rb +2 -2
  27. data/app/models/spree/data_feed/google.rb +15 -0
  28. data/app/models/spree/data_feed.rb +40 -0
  29. data/app/models/spree/digital_link.rb +5 -1
  30. data/app/models/spree/image.rb +2 -2
  31. data/app/models/spree/legacy_user.rb +3 -3
  32. data/app/models/spree/line_item.rb +1 -1
  33. data/app/models/spree/log_entry.rb +5 -1
  34. data/app/models/spree/menu.rb +1 -1
  35. data/app/models/spree/option_type.rb +6 -2
  36. data/app/models/spree/option_value.rb +5 -1
  37. data/app/models/spree/order/store_credit.rb +8 -0
  38. data/app/models/spree/order.rb +9 -9
  39. data/app/models/spree/order_contents.rb +31 -0
  40. data/app/models/spree/payment.rb +7 -15
  41. data/app/models/spree/payment_method.rb +1 -1
  42. data/app/models/spree/payment_source.rb +1 -1
  43. data/app/models/spree/preference.rb +5 -1
  44. data/app/models/spree/price.rb +1 -1
  45. data/app/models/spree/product.rb +64 -25
  46. data/app/models/spree/product_property.rb +13 -4
  47. data/app/models/spree/promotion.rb +2 -2
  48. data/app/models/spree/property.rb +8 -2
  49. data/app/models/spree/prototype.rb +1 -1
  50. data/app/models/spree/refund.rb +1 -1
  51. data/app/models/spree/reimbursement.rb +1 -1
  52. data/app/models/spree/return_authorization.rb +1 -1
  53. data/app/models/spree/return_item.rb +5 -1
  54. data/app/models/spree/role.rb +1 -1
  55. data/app/models/spree/shipment.rb +5 -5
  56. data/app/models/spree/shipping_category.rb +1 -1
  57. data/app/models/spree/shipping_method.rb +1 -1
  58. data/app/models/spree/stock/estimator.rb +1 -1
  59. data/app/models/spree/stock/splitter/weight.rb +1 -1
  60. data/app/models/spree/stock_item.rb +1 -1
  61. data/app/models/spree/stock_location.rb +1 -1
  62. data/app/models/spree/stock_transfer.rb +3 -3
  63. data/app/models/spree/store.rb +22 -3
  64. data/app/models/spree/store_credit.rb +2 -2
  65. data/app/models/spree/taxon.rb +47 -7
  66. data/app/models/spree/taxon_image.rb +2 -2
  67. data/app/models/spree/taxonomy.rb +5 -1
  68. data/app/models/spree/variant.rb +6 -9
  69. data/app/models/spree/wishlist.rb +6 -2
  70. data/app/models/spree/zone.rb +2 -2
  71. data/app/services/spree/data_feeds/google/optional_attributes.rb +23 -0
  72. data/app/services/spree/data_feeds/google/optional_sub_attributes.rb +21 -0
  73. data/app/services/spree/data_feeds/google/products_list.rb +14 -0
  74. data/app/services/spree/data_feeds/google/required_attributes.rb +67 -0
  75. data/app/services/spree/data_feeds/google/rss.rb +107 -0
  76. data/app/services/spree/locales/set_fallback_locale_for_store.rb +16 -0
  77. data/app/services/spree/seeds/countries.rb +12 -27
  78. data/app/services/spree/seeds/states.rb +8 -17
  79. data/app/services/spree/stock_locations/stock_items/create.rb +12 -18
  80. data/app/sorters/spree/products/sort.rb +23 -0
  81. data/app/validators/db_maximum_length_validator.rb +2 -6
  82. data/brakeman.ignore +326 -18
  83. data/config/initializers/friendly_id.rb +2 -0
  84. data/config/initializers/mobility.rb +16 -0
  85. data/config/initializers/rails61_fixes.rb +1 -3
  86. data/config/locales/en.yml +7 -0
  87. data/db/migrate/20220706112554_create_product_name_and_description_translations_for_mobility_table_backend.rb +27 -0
  88. data/db/migrate/20220715083542_create_spree_product_translations_for_mobility.rb +7 -0
  89. data/db/migrate/20220715120222_change_product_name_null_to_true.rb +5 -0
  90. data/db/migrate/20220718100743_create_spree_taxon_name_and_description_translations_for_mobility_table_backend.rb +27 -0
  91. data/db/migrate/20220718100948_change_taxon_name_null_to_true.rb +5 -0
  92. data/db/migrate/20220802070609_add_locale_to_friendly_id_slugs.rb +11 -0
  93. data/db/migrate/20220802073225_create_spree_product_slug_translations_for_mobility_table_backend.rb +5 -0
  94. data/db/migrate/20220804073928_transfer_data_to_translatable_tables.rb +66 -0
  95. data/db/migrate/20221215151408_add_selected_locale_to_spree_users.rb +8 -0
  96. data/db/migrate/20221219123957_add_deleted_at_to_product_translations.rb +6 -0
  97. data/db/migrate/20221220133432_add_uniqueness_constraint_to_product_translations.rb +5 -0
  98. data/db/migrate/20221229132350_create_spree_data_feed_settings.rb +14 -0
  99. data/db/migrate/20230103144439_create_option_type_translations.rb +26 -0
  100. data/db/migrate/20230103151034_create_option_value_translations.rb +26 -0
  101. data/db/migrate/20230109084253_create_product_property_translations.rb +25 -0
  102. data/db/migrate/20230109094907_transfer_options_data_to_translatable_tables.rb +58 -0
  103. data/db/migrate/20230109105943_create_property_translations.rb +26 -0
  104. data/db/migrate/20230109110840_transfer_property_data_to_translatable_tables.rb +59 -0
  105. data/db/migrate/20230110142344_backfill_friendly_id_slug_locale.rb +15 -0
  106. data/db/migrate/20230111121534_add_additional_taxon_translation_fields.rb +8 -0
  107. data/db/migrate/20230111122511_transfer_product_and_taxon_data_to_translatable_tables.rb +82 -0
  108. data/db/migrate/20230117115531_create_taxonomy_translations.rb +24 -0
  109. data/db/migrate/20230117120430_allow_null_taxonomy_name.rb +5 -0
  110. data/db/migrate/20230117121303_transfer_taxonomy_data_to_translatable_tables.rb +11 -0
  111. data/db/migrate/20230210142732_create_store_translations.rb +50 -0
  112. data/db/migrate/20230210142849_transfer_store_data_to_translatable_tables.rb +11 -0
  113. data/db/migrate/20230210230434_add_deleted_at_to_store_translations.rb +6 -0
  114. data/db/migrate/20230415155958_rename_data_feed_settings_table.rb +5 -0
  115. data/db/migrate/20230415160828_rename_data_feed_table_columns.rb +7 -0
  116. data/db/migrate/20230415161226_add_indexes_to_data_feeds_table.rb +5 -0
  117. data/db/migrate/20230512094803_rename_data_feeds_column_provider_to_type.rb +5 -0
  118. data/db/migrate/20230514162157_add_index_on_locale_and_permalink_to_spree_taxons.rb +5 -0
  119. data/lib/mobility/plugins/store_based_fallbacks.rb +55 -0
  120. data/lib/spree/core/configuration.rb +2 -1
  121. data/lib/spree/core/controller_helpers/auth.rb +1 -1
  122. data/lib/spree/core/controller_helpers/common.rb +5 -5
  123. data/lib/spree/core/controller_helpers/locale.rb +33 -2
  124. data/lib/spree/core/dependencies.rb +70 -94
  125. data/lib/spree/core/dependencies_helper.rb +19 -0
  126. data/lib/spree/core/engine.rb +7 -1
  127. data/lib/spree/core/preferences/preferable_class_methods.rb +1 -1
  128. data/lib/spree/core/product_duplicator.rb +4 -1
  129. data/lib/spree/core/product_filters.rb +7 -4
  130. data/lib/spree/core/search/base.rb +1 -1
  131. data/lib/spree/core/version.rb +1 -1
  132. data/lib/spree/core.rb +2 -0
  133. data/lib/spree/permitted_attributes.rb +1 -1
  134. data/lib/spree/testing_support/controller_requests.rb +10 -10
  135. data/lib/spree/testing_support/factories/google_data_feed_factory.rb +8 -0
  136. data/lib/spree/testing_support/factories/product_factory.rb +6 -0
  137. data/lib/spree/testing_support/factories/product_translation_factory.rb +6 -0
  138. data/lib/spree/testing_support/factories/store_factory.rb +3 -0
  139. data/lib/spree/testing_support/factories/variant_factory.rb +4 -0
  140. data/lib/spree/translation_migrations.rb +40 -0
  141. data/lib/spree_core.rb +1 -0
  142. data/spree_core.gemspec +6 -3
  143. metadata +147 -14
@@ -2,7 +2,7 @@ module Spree
2
2
  class StockItem < Spree::Base
3
3
  acts_as_paranoid
4
4
 
5
- include Metadata
5
+ include Spree::Metadata
6
6
  if defined?(Spree::Webhooks)
7
7
  include Spree::Webhooks::HasWebhooks
8
8
  end
@@ -1,6 +1,6 @@
1
1
  module Spree
2
2
  class StockLocation < Spree::Base
3
- include UniqueName
3
+ include Spree::UniqueName
4
4
  if defined?(Spree::Webhooks)
5
5
  include Spree::Webhooks::HasWebhooks
6
6
  end
@@ -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
@@ -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
- ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
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
- where(default: true).first_or_initialize
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
- ActiveSupport::Deprecation.warn('Store#checkout_zone_or_default is deprecated and will be removed in Spree 5')
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
@@ -1,7 +1,7 @@
1
1
  module Spree
2
2
  class StoreCredit < Spree::Base
3
- include SingleStoreResource
4
- include Metadata
3
+ include Spree::SingleStoreResource
4
+ include Spree::Metadata
5
5
  if defined?(Spree::Webhooks)
6
6
  include Spree::Webhooks::HasWebhooks
7
7
  end
@@ -3,7 +3,9 @@ require 'stringex'
3
3
 
4
4
  module Spree
5
5
  class Taxon < Spree::Base
6
- include Metadata
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], allow_blank: true, case_sensitive: false }
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
- if parent.present?
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 ::Spree::ImageMethods
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 Metadata
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 }
@@ -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).where("LOWER(#{Product.table_name}.name) LIKE LOWER(:query) OR LOWER(sku) LIKE LOWER(:query)", query: "%#{query}%")
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
- if defined?(SpreeGlobalize)
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
- has_secure_token
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'
@@ -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
- ActiveSupport::Deprecation.warn('Spree::Zone.default_checkout_zone is deprecated and will be removed in Spree 5')
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,14 @@
1
+ module Spree
2
+ module DataFeeds
3
+ module Google
4
+ class ProductsList
5
+ prepend Spree::ServiceModule::Base
6
+
7
+ def call(store)
8
+ products = store.products.active
9
+ success(products: products)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ 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
- if Rails::VERSION::MAJOR >= 6
13
- new_countries = Carmen::Country.all.map do |country|
14
- # Skip the creation of some territories, uninhabited islands and the Antarctic.
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
- Hash[
18
- 'name': country.name,
19
- 'iso3': country.alpha_3_code,
20
- 'iso': country.alpha_2_code,
21
- 'iso_name': country.name.upcase,
22
- 'numcode': country.numeric_code,
23
- ]
24
- end.compact.uniq
25
- Spree::Country.insert_all(new_countries)
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
- if Rails::VERSION::MAJOR >= 6
53
- new_states = subregion.subregions.map do |province|
54
- Hash[
55
- name: province.name,
56
- abbr: province.code,
57
- country_id: country.id
58
- ]
59
- end.compact.uniq
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
- if Rails::VERSION::MAJOR >= 6
9
- prepared_stock_items = variants_scope.ids.map do |variant_id|
10
- Hash[
11
- 'stock_location_id', stock_location.id,
12
- 'variant_id', variant_id,
13
- 'backorderable', stock_location.backorderable_default,
14
- 'created_at', Time.current,
15
- 'updated_at', Time.current
16
- ]
17
- end
18
- if prepared_stock_items.any?
19
- stock_location.stock_items.insert_all(prepared_stock_items)
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