spree_core 4.2.0.rc2 → 4.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/spree.js +20 -0
  3. data/app/controllers/spree/base_controller.rb +2 -3
  4. data/app/controllers/spree/errors_controller.rb +11 -0
  5. data/app/finders/spree/addresses/find.rb +1 -12
  6. data/app/finders/spree/base_finder.rb +14 -0
  7. data/app/finders/spree/countries/find.rb +11 -3
  8. data/app/finders/spree/credit_cards/find.rb +2 -2
  9. data/app/finders/spree/orders/find_current.rb +2 -2
  10. data/app/finders/spree/products/find.rb +14 -3
  11. data/app/helpers/spree/base_helper.rb +3 -8
  12. data/app/helpers/spree/currency_helper.rb +34 -0
  13. data/app/helpers/spree/locale_helper.rb +31 -0
  14. data/app/helpers/spree/products_helper.rb +37 -12
  15. data/app/mailers/spree/base_mailer.rb +4 -4
  16. data/app/mailers/spree/order_mailer.rb +3 -3
  17. data/app/mailers/spree/reimbursement_mailer.rb +1 -1
  18. data/app/mailers/spree/shipment_mailer.rb +1 -1
  19. data/app/models/concerns/spree/default_price.rb +1 -5
  20. data/app/models/concerns/spree/product_scopes.rb +1 -1
  21. data/app/models/concerns/spree/user_methods.rb +2 -2
  22. data/app/models/concerns/spree/user_payment_source.rb +1 -1
  23. data/app/models/spree/ability.rb +45 -34
  24. data/app/models/spree/address.rb +4 -0
  25. data/app/models/spree/app_configuration.rb +2 -2
  26. data/app/models/spree/app_dependencies.rb +6 -2
  27. data/app/models/spree/base.rb +5 -0
  28. data/app/models/spree/credit_card.rb +4 -0
  29. data/app/models/spree/fulfilment_changer.rb +58 -16
  30. data/app/models/spree/image.rb +14 -14
  31. data/app/models/spree/inventory_unit.rb +2 -7
  32. data/app/models/spree/line_item.rb +7 -15
  33. data/app/models/spree/order.rb +1 -0
  34. data/app/models/spree/payment.rb +18 -4
  35. data/app/models/spree/payment/processing.rb +2 -2
  36. data/app/models/spree/payment_method.rb +3 -3
  37. data/app/models/spree/price.rb +2 -7
  38. data/app/models/spree/product.rb +41 -17
  39. data/app/models/spree/promotion/rules/option_value.rb +1 -1
  40. data/app/models/spree/promotion/rules/product.rb +2 -1
  41. data/app/models/spree/promotion/rules/user.rb +2 -1
  42. data/app/models/spree/refund.rb +2 -2
  43. data/app/models/spree/return_item/eligibility_validator/default.rb +0 -2
  44. data/app/models/spree/return_item/eligibility_validator/{r_m_a_required.rb → rma_required.rb} +0 -0
  45. data/app/models/spree/shipment.rb +1 -1
  46. data/app/models/spree/shipping_method.rb +1 -5
  47. data/app/models/spree/shipping_rate.rb +2 -11
  48. data/app/models/spree/stock/availability_validator.rb +3 -4
  49. data/app/models/spree/stock_item.rb +1 -5
  50. data/app/models/spree/store.rb +55 -2
  51. data/app/models/spree/store_credit.rb +1 -1
  52. data/app/models/spree/variant.rb +9 -16
  53. data/app/models/spree/zone.rb +13 -4
  54. data/app/paginators/spree/shared/paginate.rb +8 -1
  55. data/app/presenters/spree/variant_presenter.rb +2 -5
  56. data/app/services/spree/account/addresses/create.rb +6 -1
  57. data/app/services/spree/account/addresses/{base.rb → helper.rb} +1 -3
  58. data/app/services/spree/account/addresses/update.rb +6 -1
  59. data/app/services/spree/build_localized_redirect_url.rb +101 -0
  60. data/app/services/spree/cart/estimate_shipping_rates.rb +1 -1
  61. data/app/services/spree/compare_line_items.rb +4 -2
  62. data/app/sorters/spree/base_sorter.rb +35 -0
  63. data/app/sorters/spree/orders/sort.rb +1 -37
  64. data/app/sorters/spree/products/sort.rb +9 -32
  65. data/app/validators/email_validator.rb +1 -1
  66. data/app/views/spree/errors/forbidden.html.erb +0 -0
  67. data/app/views/spree/errors/unauthorized.html.erb +0 -0
  68. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +13 -2
  69. data/app/views/spree/shared/_purchased_items_table.html.erb +15 -6
  70. data/app/views/spree/shared/purchased_items_table/_adjustment.html.erb +2 -2
  71. data/app/views/spree/shared/purchased_items_table/_line_item.html.erb +2 -2
  72. data/config/initializers/inflections.rb +3 -0
  73. data/config/initializers/rails61_fixes.rb +3 -0
  74. data/config/locales/en.yml +31 -60
  75. data/config/routes.rb +2 -1
  76. data/db/default/spree/stores.rb +1 -0
  77. data/db/default/spree/zones.rb +4 -1
  78. data/db/migrate/20130326175857_add_stock_location_to_rma.rb +1 -1
  79. data/db/migrate/20191017121054_add_supported_currencies_to_store.rb +1 -0
  80. data/db/migrate/20201012091259_add_filterable_column_to_spree_option_types.rb +6 -2
  81. data/db/migrate/20201127084048_add_default_country_kind_to_spree_zones.rb +5 -0
  82. data/db/migrate/20210112193440_remove_contact_email_from_spree_stores.rb +5 -0
  83. data/db/migrate/20210114182625_create_spree_payment_methods_stores.rb +10 -0
  84. data/db/migrate/20210114220232_migrate_data_payment_methods_stores.rb +15 -0
  85. data/db/migrate/20210117112551_remove_store_id_from_spree_payment_methods.rb +5 -0
  86. data/db/migrate/20210120142527_ensure_default_locale_in_spree_stores.rb +5 -0
  87. data/db/migrate/20210205211040_add_supported_locales_to_spree_stores.rb +11 -0
  88. data/db/migrate/20210215202602_migrate_spree_i18n_globalize_config.rb +22 -0
  89. data/lib/generators/spree/install/install_generator.rb +9 -6
  90. data/lib/spree/core.rb +2 -1
  91. data/lib/spree/core/controller_helpers/auth.rb +3 -1
  92. data/lib/spree/core/controller_helpers/common.rb +6 -8
  93. data/lib/spree/core/controller_helpers/currency.rb +54 -0
  94. data/lib/spree/core/controller_helpers/locale.rb +58 -0
  95. data/lib/spree/core/controller_helpers/search.rb +1 -1
  96. data/lib/spree/core/controller_helpers/store.rb +4 -16
  97. data/lib/spree/core/product_filters.rb +3 -3
  98. data/lib/spree/core/version.rb +3 -1
  99. data/lib/spree/i18n.rb +17 -19
  100. data/lib/spree/permitted_attributes.rb +2 -2
  101. data/lib/spree/service_module.rb +8 -4
  102. data/lib/spree/testing_support/capybara_config.rb +1 -1
  103. data/lib/spree/testing_support/common_rake.rb +1 -1
  104. data/lib/spree/testing_support/controller_requests.rb +10 -10
  105. data/lib/spree/testing_support/factories/shipment_factory.rb +7 -9
  106. data/lib/spree/testing_support/factories/stock_location_factory.rb +2 -2
  107. data/lib/spree/testing_support/factories/store_factory.rb +1 -0
  108. data/lib/spree/testing_support/factories/zone_factory.rb +16 -13
  109. data/lib/spree/testing_support/flatpickr_capybara.rb +101 -0
  110. data/lib/spree/testing_support/locale_helpers.rb +78 -0
  111. data/lib/spree/testing_support/next_instance_of.rb +38 -0
  112. data/lib/spree/testing_support/order_walkthrough.rb +8 -3
  113. data/lib/spree/testing_support/rspec_retry_config.rb +10 -0
  114. data/spree_core.gemspec +5 -4
  115. metadata +71 -26
  116. data/lib/generators/spree/install/templates/config/initializers/spree_storefront.rb +0 -1
  117. data/lib/generators/spree/install/templates/config/spree_storefront.yml +0 -67
  118. data/lib/spree/core/controller_helpers/currency_helpers.rb +0 -15
  119. data/lib/spree/i18n/base.rb +0 -17
  120. data/lib/spree/i18n/initializer.rb +0 -1
@@ -157,6 +157,7 @@ module Spree
157
157
  scope :completed_between, ->(start_date, end_date) { where(completed_at: start_date..end_date) }
158
158
  scope :complete, -> { where.not(completed_at: nil) }
159
159
  scope :incomplete, -> { where(completed_at: nil) }
160
+ scope :not_canceled, -> { where.not(state: 'canceled') }
160
161
 
161
162
  # shows completed orders first, by their completed_at date, then uncompleted orders by their created_at
162
163
  scope :reverse_chronological, -> { order(Arel.sql('spree_orders.completed_at IS NULL'), completed_at: :desc, created_at: :desc) }
@@ -14,7 +14,7 @@ module Spree
14
14
 
15
15
  with_options inverse_of: :payments do
16
16
  belongs_to :order, class_name: 'Spree::Order', touch: true
17
- belongs_to :payment_method, class_name: 'Spree::PaymentMethod'
17
+ belongs_to :payment_method, -> { with_deleted }, class_name: 'Spree::PaymentMethod'
18
18
  end
19
19
  belongs_to :source, polymorphic: true
20
20
 
@@ -195,14 +195,28 @@ module Spree
195
195
 
196
196
  def validate_source
197
197
  if source && !source.valid?
198
- source.errors.each do |field, error|
199
- field_name = I18n.t("activerecord.attributes.#{source.class.to_s.underscore}.#{field}")
200
- errors.add(Spree.t(source.class.to_s.demodulize.underscore), "#{field_name} #{error}")
198
+ if Rails::VERSION::STRING >= '6.1'
199
+ source.errors.map { |error| { field: error.attribute, message: error&.message } }.each do |err|
200
+ next if err[:field].blank? || err[:message].blank?
201
+
202
+ add_source_error(err[:field], err[:message])
203
+ end
204
+ else
205
+ source.errors.messages.each do |field, error|
206
+ next if field.blank? || error.empty?
207
+
208
+ add_source_error(field, error.first)
209
+ end
201
210
  end
202
211
  end
203
212
  !errors.present?
204
213
  end
205
214
 
215
+ def add_source_error(field, message)
216
+ field_name = I18n.t("activerecord.attributes.#{source.class.to_s.underscore}.#{field}")
217
+ errors.add(Spree.t(source.class.to_s.demodulize.underscore), "#{field_name} #{message}")
218
+ end
219
+
206
220
  def profiles_supported?
207
221
  payment_method.respond_to?(:payment_profiles_supported?) && payment_method.payment_profiles_supported?
208
222
  end
@@ -163,8 +163,8 @@ module Spree
163
163
  else
164
164
  error.to_s
165
165
  end
166
- logger.error(Spree.t(:gateway_error))
167
- logger.error(" #{error.to_yaml}")
166
+ Rails.logger.error(Spree.t(:gateway_error))
167
+ Rails.logger.error(" #{error.to_yaml}")
168
168
  raise Core::GatewayError, text
169
169
  end
170
170
 
@@ -12,7 +12,7 @@ module Spree
12
12
 
13
13
  validates :name, presence: true
14
14
 
15
- belongs_to :store
15
+ has_and_belongs_to_many :stores
16
16
 
17
17
  with_options dependent: :restrict_with_error do
18
18
  has_many :payments, class_name: 'Spree::Payment', inverse_of: :payment_method
@@ -79,9 +79,9 @@ module Spree
79
79
  end
80
80
 
81
81
  def available_for_store?(store)
82
- return true if store.blank? || store_id.blank?
82
+ return true if store.blank?
83
83
 
84
- store_id == store.id
84
+ store_ids.include?(store.id)
85
85
  end
86
86
  end
87
87
  end
@@ -6,7 +6,7 @@ module Spree
6
6
 
7
7
  MAXIMUM_AMOUNT = BigDecimal('99_999_999.99')
8
8
 
9
- belongs_to :variant, class_name: 'Spree::Variant', inverse_of: :prices, touch: true
9
+ belongs_to :variant, -> { with_deleted }, class_name: 'Spree::Variant', inverse_of: :prices, touch: true
10
10
 
11
11
  before_validation :ensure_currency
12
12
 
@@ -27,7 +27,7 @@ module Spree
27
27
  self.whitelisted_ransackable_attributes = ['amount', 'compare_at_amount']
28
28
 
29
29
  def money
30
- Spree::Money.new(amount || 0, currency: currency)
30
+ Spree::Money.new(amount || 0, currency: currency.upcase)
31
31
  end
32
32
 
33
33
  def amount=(amount)
@@ -63,11 +63,6 @@ module Spree
63
63
  Spree::Money.new(compare_at_price_including_vat_for(price_options), currency: currency)
64
64
  end
65
65
 
66
- # Remove variant default_scope `deleted_at: nil`
67
- def variant
68
- Spree::Variant.unscoped { super }
69
- end
70
-
71
66
  private
72
67
 
73
68
  def ensure_currency
@@ -90,6 +90,10 @@ module Spree
90
90
  after_save :reset_nested_changes
91
91
  after_touch :touch_taxons
92
92
 
93
+ # reset cache on save inside trasaction and transaction commit
94
+ after_save :reset_memoized_data
95
+ after_commit :reset_memoized_data
96
+
93
97
  before_validation :normalize_slug, on: :update
94
98
  before_validation :validate_master
95
99
 
@@ -127,6 +131,13 @@ module Spree
127
131
 
128
132
  alias master_images images
129
133
 
134
+ def reload
135
+ %w(total_on_hand taxonomy_ids taxon_and_ancestors category category default_variant_id tax_category default_variant).each do |v|
136
+ instance_variable_set(:"@#{v}", nil)
137
+ end
138
+ super
139
+ end
140
+
130
141
  # Cant use short form block syntax due to https://github.com/Netflix/fast_jsonapi/issues/259
131
142
  def purchasable?
132
143
  variants_including_master.any?(&:purchasable?)
@@ -160,7 +171,7 @@ module Spree
160
171
  #
161
172
  # @return [Spree::Variant]
162
173
  def default_variant
163
- Rails.cache.fetch(default_variant_cache_key) do
174
+ @default_variant ||= Rails.cache.fetch(default_variant_cache_key) do
164
175
  if Spree::Config[:track_inventory_levels] && variants.in_stock_or_backorderable.any?
165
176
  variants.in_stock_or_backorderable.first
166
177
  else
@@ -172,11 +183,11 @@ module Spree
172
183
  # Returns default Variant ID for Product
173
184
  # @return [Integer]
174
185
  def default_variant_id
175
- default_variant.id
186
+ @default_variant_id ||= default_variant.id
176
187
  end
177
188
 
178
189
  def tax_category
179
- super || TaxCategory.find_by(is_default: true)
190
+ @tax_category ||= super || TaxCategory.find_by(is_default: true)
180
191
  end
181
192
 
182
193
  # Adding properties and option types on creation based on a chosen prototype
@@ -288,11 +299,11 @@ module Spree
288
299
  end
289
300
 
290
301
  def total_on_hand
291
- if any_variants_not_track_inventory?
292
- Float::INFINITY
293
- else
294
- stock_items.sum(:count_on_hand)
295
- end
302
+ @total_on_hand ||= if any_variants_not_track_inventory?
303
+ Float::INFINITY
304
+ else
305
+ stock_items.sum(:count_on_hand)
306
+ end
296
307
  end
297
308
 
298
309
  # Master variant may be deleted (i.e. when the product is deleted)
@@ -303,14 +314,11 @@ module Spree
303
314
  end
304
315
 
305
316
  def brand
306
- taxons.joins(:taxonomy).find_by(spree_taxonomies: { name: Spree.t(:taxonomy_brands_name) })
317
+ @brand ||= taxons.joins(:taxonomy).find_by(spree_taxonomies: { name: Spree.t(:taxonomy_brands_name) })
307
318
  end
308
319
 
309
320
  def category
310
- taxons.joins(:taxonomy).
311
- where(spree_taxonomies: { name: Spree.t(:taxonomy_categories_name) }).
312
- order(depth: :desc).
313
- first
321
+ @category ||= taxons.joins(:taxonomy).order(depth: :desc).find_by(spree_taxonomies: { name: Spree.t(:taxonomy_categories_name) })
314
322
  end
315
323
 
316
324
  private
@@ -411,8 +419,18 @@ module Spree
411
419
  # We call master.default_price here to ensure price is initialized.
412
420
  # Required to avoid Variant#check_price validation failing on create.
413
421
  unless master.default_price && master.valid?
414
- master.errors.each do |att, error|
415
- errors.add(att, error)
422
+ if Rails::VERSION::STRING >= '6.1'
423
+ master.errors.map { |error| { field: error.attribute, message: error&.message } }.each do |err|
424
+ next if err[:field].blank? || err[:message].blank?
425
+
426
+ errors.add(err[:field], err[:message])
427
+ end
428
+ else
429
+ master.errors.messages.each do |field, error|
430
+ next if field.blank? || error.empty?
431
+
432
+ errors.add(field, error.first)
433
+ end
416
434
  end
417
435
  end
418
436
  end
@@ -430,12 +448,12 @@ module Spree
430
448
  end
431
449
 
432
450
  def taxon_and_ancestors
433
- taxons.map(&:self_and_ancestors).flatten.uniq
451
+ @taxon_and_ancestors ||= taxons.map(&:self_and_ancestors).flatten.uniq
434
452
  end
435
453
 
436
454
  # Get the taxonomy ids of all taxons assigned to this product and their ancestors.
437
455
  def taxonomy_ids
438
- taxon_and_ancestors.map(&:taxonomy_id).flatten.uniq
456
+ @taxonomy_ids ||= taxon_and_ancestors.map(&:taxonomy_id).flatten.uniq
439
457
  end
440
458
 
441
459
  # Iterate through this products taxons and taxonomies and touch their timestamps in a batch
@@ -461,5 +479,11 @@ module Spree
461
479
  errors.add(:discontinue_on, :invalid_date_range)
462
480
  end
463
481
  end
482
+
483
+ def reset_memoized_data
484
+ %w(total_on_hand taxonomy_ids taxon_and_ancestors category default_variant_id tax_category default_variant).each do |v|
485
+ instance_variable_set(:"@#{v}", nil)
486
+ end
487
+ end
464
488
  end
465
489
  end
@@ -31,7 +31,7 @@ module Spree
31
31
  end
32
32
 
33
33
  def actionable?(line_item)
34
- product_id = line_item.product.id
34
+ product_id = line_item.product_id
35
35
  option_values_ids = line_item.variant.option_value_ids
36
36
  eligible_product_ids = preferred_eligible_values.keys
37
37
  eligible_value_ids = preferred_eligible_values[product_id]
@@ -58,7 +58,8 @@ module Spree
58
58
  end
59
59
 
60
60
  def product_ids_string=(s)
61
- self.product_ids = s.to_s.split(',').map(&:strip)
61
+ # check this
62
+ self.product_ids = s
62
63
  end
63
64
  end
64
65
  end
@@ -22,7 +22,8 @@ module Spree
22
22
  end
23
23
 
24
24
  def user_ids_string=(s)
25
- self.user_ids = s.to_s.split(',').map(&:strip)
25
+ # check this
26
+ self.user_ids = s
26
27
  end
27
28
  end
28
29
  end
@@ -61,14 +61,14 @@ module Spree
61
61
  end
62
62
 
63
63
  unless response.success?
64
- logger.error(Spree.t(:gateway_error) + " #{response.to_yaml}")
64
+ Rails.logger.error(Spree.t(:gateway_error) + " #{response.to_yaml}")
65
65
  text = response.params['message'] || response.params['response_reason_text'] || response.message
66
66
  raise Core::GatewayError, text
67
67
  end
68
68
 
69
69
  response
70
70
  rescue ActiveMerchant::ConnectionError => e
71
- logger.error(Spree.t(:gateway_error) + " #{e.inspect}")
71
+ Rails.logger.error(Spree.t(:gateway_error) + " #{e.inspect}")
72
72
  raise Core::GatewayError, Spree.t(:unable_to_connect_to_gateway)
73
73
  end
74
74
 
@@ -1,5 +1,3 @@
1
- require_relative 'r_m_a_required'
2
-
3
1
  module Spree
4
2
  class ReturnItem::EligibilityValidator::Default < Spree::ReturnItem::EligibilityValidator::BaseValidator
5
3
  class_attribute :permitted_eligibility_validators
@@ -362,7 +362,7 @@ module Spree
362
362
  desired_shipment: shipment_to_transfer_to,
363
363
  variant: variant,
364
364
  quantity: quantity
365
- ).run!
365
+ )
366
366
  end
367
367
 
368
368
  private
@@ -19,7 +19,7 @@ module Spree
19
19
  foreign_key: 'shipping_method_id'
20
20
  has_many :zones, through: :shipping_method_zones, class_name: 'Spree::Zone'
21
21
 
22
- belongs_to :tax_category, class_name: 'Spree::TaxCategory', optional: true
22
+ belongs_to :tax_category, -> { with_deleted }, class_name: 'Spree::TaxCategory', optional: true
23
23
 
24
24
  validates :name, :display_on, presence: true
25
25
 
@@ -44,10 +44,6 @@ module Spree
44
44
  select { |c| c.to_s.constantize < Spree::ShippingCalculator }
45
45
  end
46
46
 
47
- def tax_category
48
- Spree::TaxCategory.unscoped { super }
49
- end
50
-
51
47
  def available_to_display?(display_filter)
52
48
  (frontend? && display_filter == DISPLAY_ON_FRONT_END) ||
53
49
  (backend? && display_filter == DISPLAY_ON_BACK_END)
@@ -1,9 +1,8 @@
1
1
  module Spree
2
2
  class ShippingRate < Spree::Base
3
3
  belongs_to :shipment, class_name: 'Spree::Shipment'
4
- belongs_to :tax_rate, class_name: 'Spree::TaxRate'
5
- belongs_to :shipping_method, class_name: 'Spree::ShippingMethod', inverse_of: :shipping_rates
6
-
4
+ belongs_to :tax_rate, -> { with_deleted }, class_name: 'Spree::TaxRate'
5
+ belongs_to :shipping_method, -> { with_deleted }, class_name: 'Spree::ShippingMethod', inverse_of: :shipping_rates
7
6
  extend Spree::DisplayMoney
8
7
 
9
8
  money_methods :base_price, :final_price, :tax_amount
@@ -32,14 +31,6 @@ module Spree
32
31
  @tax_amount ||= tax_rate&.calculator&.compute_shipping_rate(self) || BigDecimal(0)
33
32
  end
34
33
 
35
- def shipping_method
36
- Spree::ShippingMethod.unscoped { super }
37
- end
38
-
39
- def tax_rate
40
- Spree::TaxRate.unscoped { super }
41
- end
42
-
43
34
  # returns base price - any available discounts for this Shipment
44
35
  # useful when you want to present a list of available shipping rates
45
36
  def final_price
@@ -14,10 +14,9 @@ module Spree
14
14
  display_name = variant.name.to_s
15
15
  display_name += " (#{variant.options_text})" unless variant.options_text.blank?
16
16
 
17
- line_item.errors[:quantity] << Spree.t(
18
- :selected_quantity_not_available,
19
- item: display_name.inspect
20
- )
17
+ line_item.errors.add(:quantity,
18
+ :selected_quantity_not_available,
19
+ message: Spree.t(:selected_quantity_not_available, item: display_name.inspect))
21
20
  end
22
21
 
23
22
  private
@@ -4,7 +4,7 @@ module Spree
4
4
 
5
5
  with_options inverse_of: :stock_items do
6
6
  belongs_to :stock_location, class_name: 'Spree::StockLocation'
7
- belongs_to :variant, class_name: 'Spree::Variant'
7
+ belongs_to :variant, -> { with_deleted }, class_name: 'Spree::Variant'
8
8
  end
9
9
  has_many :stock_movements, inverse_of: :stock_item
10
10
 
@@ -56,10 +56,6 @@ module Spree
56
56
  in_stock? || backorderable?
57
57
  end
58
58
 
59
- def variant
60
- Spree::Variant.unscoped { super }
61
- end
62
-
63
59
  def reduce_count_on_hand_to_zero
64
60
  set_count_on_hand(0) if count_on_hand > 0
65
61
  end
@@ -24,12 +24,15 @@ module Spree
24
24
  validates :mailer_logo, content_type: ['image/png', 'image/jpg', 'image/jpeg']
25
25
 
26
26
  before_save :ensure_default_exists_and_is_unique
27
+ before_save :ensure_supported_currencies, :ensure_supported_locales
27
28
  before_destroy :validate_not_default
28
29
 
29
30
  scope :by_url, ->(url) { where('url like ?', "%#{url}%") }
30
31
 
31
32
  after_commit :clear_cache
32
33
 
34
+ alias_attribute :contact_email, :customer_support_email
35
+
33
36
  def self.current(domain = nil)
34
37
  current_store = domain ? Store.by_url(domain).first : nil
35
38
  current_store || Store.default
@@ -41,14 +44,47 @@ module Spree
41
44
  end
42
45
  end
43
46
 
47
+ def self.available_locales
48
+ Rails.cache.fetch('stores_available_locales') do
49
+ Spree::Store.all.map(&:supported_locales_list).flatten.uniq
50
+ end
51
+ end
52
+
44
53
  def supported_currencies_list
45
- (read_attribute(:supported_currencies).to_s.split(',') << default_currency).map(&:to_s).map do |code|
54
+ @supported_currencies_list ||= (read_attribute(:supported_currencies).to_s.split(',') << default_currency).sort.map(&:to_s).map do |code|
46
55
  ::Money::Currency.find(code.strip)
47
56
  end.uniq.compact
48
57
  end
49
58
 
59
+ def supported_locales_list
60
+ # TODO: add support of multiple supported languages to a single Store
61
+ @supported_locales_list ||= (read_attribute(:supported_locales).to_s.split(',') << default_locale).compact.uniq.sort
62
+ end
63
+
50
64
  def unique_name
51
- "#{name} (#{code})"
65
+ @unique_name ||= "#{name} (#{code})"
66
+ end
67
+
68
+ def formatted_url
69
+ return if url.blank?
70
+
71
+ @formatted_url ||= if url.match(/http:\/\/|https:\/\//)
72
+ url
73
+ else
74
+ Rails.env.development? ? "http://#{url}" : "https://#{url}"
75
+ end
76
+ end
77
+
78
+ def countries_available_for_checkout
79
+ @countries_available_for_checkout ||= checkout_zone_or_default.try(:country_list) || Spree::Country.all
80
+ end
81
+
82
+ def states_available_for_checkout(country)
83
+ checkout_zone_or_default.try(:state_list_for, country) || country.states
84
+ end
85
+
86
+ def checkout_zone_or_default
87
+ @checkout_zone_or_default ||= checkout_zone || Spree::Zone.default_checkout_zone
52
88
  end
53
89
 
54
90
  private
@@ -61,6 +97,22 @@ module Spree
61
97
  end
62
98
  end
63
99
 
100
+ def ensure_supported_locales
101
+ return unless attributes.keys.include?('supported_locales')
102
+ return if supported_locales.present?
103
+ return if default_locale.blank?
104
+
105
+ self.supported_locales = default_locale
106
+ end
107
+
108
+ def ensure_supported_currencies
109
+ return unless attributes.keys.include?('supported_currencies')
110
+ return if supported_currencies.present?
111
+ return if default_currency.blank?
112
+
113
+ self.supported_currencies = default_currency
114
+ end
115
+
64
116
  def validate_not_default
65
117
  if default
66
118
  errors.add(:base, :cannot_destroy_default_store)
@@ -70,6 +122,7 @@ module Spree
70
122
 
71
123
  def clear_cache
72
124
  Rails.cache.delete('default_store')
125
+ Rails.cache.delete('stores_available_locales')
73
126
  end
74
127
  end
75
128
  end