spree_core 4.2.0.rc5 → 4.2.0

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 (42) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/spree.js +16 -0
  3. data/app/finders/spree/products/find.rb +3 -3
  4. data/app/helpers/spree/base_helper.rb +2 -1
  5. data/app/helpers/spree/currency_helper.rb +24 -0
  6. data/app/helpers/spree/locale_helper.rb +15 -3
  7. data/app/models/concerns/spree/product_scopes.rb +1 -1
  8. data/app/models/spree/app_configuration.rb +1 -1
  9. data/app/models/spree/app_dependencies.rb +3 -1
  10. data/app/models/spree/credit_card.rb +4 -0
  11. data/app/models/spree/price.rb +1 -1
  12. data/app/models/spree/product.rb +17 -0
  13. data/app/models/spree/store.rb +19 -2
  14. data/app/models/spree/variant.rb +1 -1
  15. data/app/paginators/spree/shared/paginate.rb +8 -1
  16. data/app/services/spree/build_localized_redirect_url.rb +101 -0
  17. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +13 -0
  18. data/app/views/spree/shared/_purchased_items_table.html.erb +15 -6
  19. data/app/views/spree/shared/purchased_items_table/_adjustment.html.erb +2 -2
  20. data/app/views/spree/shared/purchased_items_table/_line_item.html.erb +1 -1
  21. data/config/locales/en.yml +7 -6
  22. data/db/migrate/20191017121054_add_supported_currencies_to_store.rb +1 -0
  23. data/db/migrate/20201012091259_add_filterable_column_to_spree_option_types.rb +6 -2
  24. data/db/migrate/20210205211040_add_supported_locales_to_spree_stores.rb +11 -0
  25. data/db/migrate/20210215202602_migrate_spree_i18n_globalize_config.rb +22 -0
  26. data/lib/generators/spree/install/install_generator.rb +9 -6
  27. data/lib/spree/core/controller_helpers/currency.rb +8 -6
  28. data/lib/spree/core/controller_helpers/locale.rb +13 -12
  29. data/lib/spree/core/controller_helpers/search.rb +1 -1
  30. data/lib/spree/core/version.rb +1 -1
  31. data/lib/spree/i18n.rb +12 -0
  32. data/lib/spree/permitted_attributes.rb +1 -1
  33. data/lib/spree/service_module.rb +2 -2
  34. data/lib/spree/testing_support/common_rake.rb +1 -1
  35. data/lib/spree/testing_support/controller_requests.rb +10 -10
  36. data/lib/spree/testing_support/factories/store_factory.rb +1 -0
  37. data/lib/spree/testing_support/flatpickr_capybara.rb +101 -0
  38. data/lib/spree/testing_support/locale_helpers.rb +71 -0
  39. data/lib/spree/testing_support/next_instance_of.rb +38 -0
  40. metadata +11 -6
  41. data/lib/generators/spree/install/templates/config/initializers/spree_storefront.rb +0 -1
  42. data/lib/generators/spree/install/templates/config/spree_storefront.yml +0 -67
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d0f9d56b3e68e0ff69cfa46d00d61872b775e2e92ddf76a3eccd85899ca863b
4
- data.tar.gz: 80879be83858cb06f69c5e78f975b9cbf1846941d62282290c8403bd8b509363
3
+ metadata.gz: a59c1e362e580d2884f8c82e1c15d2cdf057e21b1efc66e1ede10ef4e41883a8
4
+ data.tar.gz: cfe5bc95ed8d23c91c0c19dace8fe3d96b015cbcd97af1c38524b73c94797cac
5
5
  SHA512:
6
- metadata.gz: 82e15e51f5847c472eac8cd4e24e549dc28705b328f082860009c638277f203adbfe42ff898105862e3385b4ed5935e102200cd247ee998b09b4b2f3912b77d3
7
- data.tar.gz: c843177415698e0d50d2d9d81b2803f84713db0dfe7d9098c41a7de69cb9c1c15158d97b93a2ecc97dc62752078797adbd03436bad2a8674c87eef07ecb97e35
6
+ metadata.gz: 420dbd7fdd44e2a4fa1d68d4d9e78a2fc42062757fc9470e57277bb7599fc3883b9e01a8052e5d0c34fb784361a5bc2b1b0b8e0e7bd7342860412e09e5799e70
7
+ data.tar.gz: 6b45c3c66e49701c0830b59e51ecb1776563abf078fc0728080479799dbbda443b4191c5f3af934cb663cdda6e824564b5891ec805950e87f949f0011b34430c
@@ -17,9 +17,25 @@ Spree.adminPath = function () {
17
17
 
18
18
  Spree.pathFor = function (path) {
19
19
  var locationOrigin = (window.location.protocol + '//' + window.location.hostname) + (window.location.port ? ':' + window.location.port : '')
20
+
20
21
  return this.url('' + locationOrigin + (this.mountedAt()) + path, this.url_params).toString()
21
22
  }
22
23
 
24
+ Spree.localizedPathFor = function(path) {
25
+ if (typeof (SPREE_LOCALE) !== 'undefined') {
26
+ if (path.match(/api\/v/)) {
27
+ if (path.match(/\?/)) {
28
+ path = path + '&locale=' + SPREE_LOCALE
29
+ } else {
30
+ path = path + '?locale=' + SPREE_LOCALE
31
+ }
32
+ } else {
33
+ path = SPREE_LOCALE + '/' + path
34
+ }
35
+ }
36
+ return Spree.pathFor(path)
37
+ }
38
+
23
39
  Spree.adminPathFor = function (path) {
24
40
  return this.pathFor('' + (this.adminPath()) + path)
25
41
  }
@@ -7,7 +7,7 @@ module Spree
7
7
  @ids = String(params.dig(:filter, :ids)).split(',')
8
8
  @skus = String(params.dig(:filter, :skus)).split(',')
9
9
  @price = String(params.dig(:filter, :price)).split(',').map(&:to_f)
10
- @currency = params[:currency] || current_currency
10
+ @currency = current_currency
11
11
  @taxons = taxon_ids(params.dig(:filter, :taxons))
12
12
  @concat_taxons = taxon_ids(params.dig(:filter, :concat_taxons))
13
13
  @name = params.dig(:filter, :name)
@@ -103,7 +103,7 @@ module Spree
103
103
  where(
104
104
  spree_prices: {
105
105
  amount: price.min..price.max,
106
- currency: currency
106
+ currency: currency&.upcase
107
107
  }
108
108
  )
109
109
  end
@@ -111,7 +111,7 @@ module Spree
111
111
  def by_currency(products)
112
112
  return products unless currency?
113
113
 
114
- products.joins(master: :prices).where(spree_prices: { currency: currency })
114
+ products.joins(master: :prices).where(spree_prices: { currency: currency.upcase })
115
115
  end
116
116
 
117
117
  def by_taxons(products)
@@ -134,7 +134,8 @@ module Spree
134
134
  [I18n.l(date.to_date, format: :long)].join(' ')
135
135
  end
136
136
 
137
- def seo_url(taxon, options = nil)
137
+ def seo_url(taxon, options = {})
138
+ options.merge(locale: locale_param)
138
139
  spree.nested_taxons_path(taxon.permalink, options)
139
140
  end
140
141
 
@@ -0,0 +1,24 @@
1
+ module Spree
2
+ module CurrencyHelper
3
+ def currency_options(selected_value = nil)
4
+ selected_value ||= Spree::Config[:currency]
5
+ currencies = ::Money::Currency.table.map do |_code, details|
6
+ iso = details[:iso_code]
7
+ [iso, "#{details[:name]} (#{iso})"]
8
+ end
9
+ options_from_collection_for_select(currencies, :first, :last, selected_value)
10
+ end
11
+
12
+ def supported_currency_options
13
+ return if current_store.nil?
14
+
15
+ current_store.supported_currencies_list.map(&:iso_code)
16
+ end
17
+
18
+ def should_render_currency_dropdown?
19
+ return false if current_store.nil?
20
+
21
+ current_store.supported_currencies_list.size > 1
22
+ end
23
+ end
24
+ end
@@ -8,12 +8,24 @@ module Spree
8
8
  available_locales.map { |locale| locale_presentation(locale) }
9
9
  end
10
10
 
11
+ def supported_locales_options
12
+ return if current_store.nil?
13
+
14
+ current_store.supported_locales_list.map { |locale| locale_presentation(locale) }
15
+ end
16
+
11
17
  def locale_presentation(locale)
12
- if defined?(SpreeI18n)
13
- [Spree.t('i18n.this_file_language', locale: locale), locale]
18
+ if I18n.exists?('spree.i18n.this_file_language', locale: locale)
19
+ [Spree.t('i18n.this_file_language', locale: locale), locale.to_s]
14
20
  else
15
- locale.to_s == 'en' ? ['English (US)', :en] : [locale, locale]
21
+ locale.to_s == 'en' ? ['English (US)', 'en'] : [locale, locale.to_s]
16
22
  end
17
23
  end
24
+
25
+ def should_render_locale_dropdown?
26
+ return false if current_store.nil?
27
+
28
+ current_store.supported_locales_list.size > 1
29
+ end
18
30
  end
19
31
  end
@@ -206,7 +206,7 @@ module Spree
206
206
  search_scopes << :available
207
207
 
208
208
  def self.active(currency = nil)
209
- available(nil, currency)
209
+ available(nil, currency&.upcase)
210
210
  end
211
211
  search_scopes << :active
212
212
 
@@ -69,7 +69,7 @@ module Spree
69
69
  preference :credit_to_new_allocation, :boolean, default: false
70
70
 
71
71
  # Multi store configurations
72
- preference :show_store_selector, :boolean, default: true
72
+ preference :show_store_selector, :boolean, default: false
73
73
 
74
74
  # searcher_class allows spree extension writers to provide their own Search class
75
75
  def searcher_class
@@ -13,7 +13,7 @@ module Spree
13
13
  :completed_order_finder, :order_sorter, :cart_compare_line_items_service, :collection_paginator, :products_sorter,
14
14
  :products_finder, :taxon_finder, :line_item_by_variant_finder, :cart_estimate_shipping_rates_service,
15
15
  :account_create_address_service, :account_update_address_service, :address_finder,
16
- :collection_sorter
16
+ :collection_sorter, :error_handler
17
17
  ].freeze
18
18
 
19
19
  attr_accessor *INJECTION_POINTS
@@ -66,6 +66,8 @@ module Spree
66
66
  # account
67
67
  @account_create_address_service = 'Spree::Account::Addresses::Create'
68
68
  @account_update_address_service = 'Spree::Account::Addresses::Update'
69
+
70
+ @error_handler = 'Spree::ErrorReporter'
69
71
  end
70
72
 
71
73
  def set_default_finders
@@ -127,6 +127,10 @@ module Spree
127
127
  "XXXX-XXXX-XXXX-#{last_digits}"
128
128
  end
129
129
 
130
+ def display_brand
131
+ brand.present? ? brand.upcase : Spree.t(:no_cc_type)
132
+ end
133
+
130
134
  def actions
131
135
  %w{capture void credit}
132
136
  end
@@ -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)
@@ -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?)
@@ -468,5 +479,11 @@ module Spree
468
479
  errors.add(:discontinue_on, :invalid_date_range)
469
480
  end
470
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
471
488
  end
472
489
  end
@@ -24,6 +24,7 @@ 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}%") }
@@ -50,14 +51,14 @@ module Spree
50
51
  end
51
52
 
52
53
  def supported_currencies_list
53
- @supported_currencies_list ||= (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|
54
55
  ::Money::Currency.find(code.strip)
55
56
  end.uniq.compact
56
57
  end
57
58
 
58
59
  def supported_locales_list
59
60
  # TODO: add support of multiple supported languages to a single Store
60
- @supported_locales_list ||= [default_locale].compact.uniq
61
+ @supported_locales_list ||= (read_attribute(:supported_locales).to_s.split(',') << default_locale).compact.uniq.sort
61
62
  end
62
63
 
63
64
  def unique_name
@@ -96,6 +97,22 @@ module Spree
96
97
  end
97
98
  end
98
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
+
99
116
  def validate_not_default
100
117
  if default
101
118
  errors.add(:base, :cannot_destroy_default_store)
@@ -192,7 +192,7 @@ module Spree
192
192
  end
193
193
 
194
194
  def price_in(currency)
195
- prices.detect { |price| price.currency == currency } || prices.build(currency: currency)
195
+ prices.detect { |price| price.currency == currency&.upcase } || prices.build(currency: currency&.upcase)
196
196
  end
197
197
 
198
198
  def amount_in(currency)
@@ -4,7 +4,14 @@ module Spree
4
4
  def initialize(collection, params)
5
5
  @collection = collection
6
6
  @page = params[:page]
7
- @per_page = params[:per_page]
7
+
8
+ per_page_limit = Spree::Api::Config[:api_v2_per_page_limit]
9
+
10
+ @per_page = if params[:per_page].to_i.between?(1, per_page_limit)
11
+ params[:per_page]
12
+ else
13
+ Kaminari.config.default_per_page
14
+ end
8
15
  end
9
16
 
10
17
  def call
@@ -0,0 +1,101 @@
1
+ require 'uri'
2
+
3
+ module Spree
4
+ class BuildLocalizedRedirectUrl
5
+ prepend Spree::ServiceModule::Base
6
+
7
+ LOCALE_REGEX = /^\/[A-Za-z]{2}\/|^\/[A-Za-z]{2}-[A-Za-z]{2}\/|^\/[A-Za-z]{2}$|^\/[A-Za-z]{2}-[A-Za-z]{2}$/.freeze
8
+
9
+ SUPPORTED_PATHS_REGEX = /\/(products|t\/|cart|checkout|addresses|content)/.freeze
10
+
11
+ # rubocop:disable Lint/UnusedMethodArgument
12
+ def call(url:, locale:, default_locale: nil)
13
+ run :initialize_url_object
14
+ run :generate_new_path
15
+ run :append_locale_param
16
+ run :build_url
17
+ end
18
+ # rubocop:enable Lint/UnusedMethodArgument
19
+
20
+ protected
21
+
22
+ def initialize_url_object(url:, locale:, default_locale:)
23
+ success(
24
+ url: URI(url),
25
+ locale: locale,
26
+ default_locale_supplied: default_locale_supplied?(locale, default_locale)
27
+ )
28
+ end
29
+
30
+ def generate_new_path(url:, locale:, default_locale_supplied:)
31
+ unless supported_path?(url.path)
32
+ return success(
33
+ url: url,
34
+ locale: locale,
35
+ path: cleanup_path(url.path),
36
+ default_locale_supplied: default_locale_supplied,
37
+ locale_added_to_path: false
38
+ )
39
+ end
40
+
41
+ new_path = if default_locale_supplied
42
+ maches_locale_regex?(url.path) ? url.path.gsub(LOCALE_REGEX, '/') : url.path
43
+ else
44
+ maches_locale_regex?(url.path) ? url.path.gsub(LOCALE_REGEX, "/#{locale}/") : "/#{locale}#{url.path}"
45
+ end
46
+
47
+ success(
48
+ url: url,
49
+ locale: locale,
50
+ path: cleanup_path(new_path),
51
+ default_locale_supplied: default_locale_supplied,
52
+ locale_added_to_path: true
53
+ )
54
+ end
55
+
56
+ def append_locale_param(url:, locale:, path:, default_locale_supplied:, locale_added_to_path:)
57
+ return success(url: url, path: path, query: url.query) if locale_added_to_path
58
+
59
+ query_params = Rack::Utils.parse_nested_query(url.query)
60
+
61
+ if default_locale_supplied
62
+ query_params.delete('locale')
63
+ else
64
+ query_params.merge!('locale' => locale)
65
+ end
66
+
67
+ query_string = query_params.any? ? query_params.to_query : nil
68
+
69
+ success(url: url, path: path, query: query_string)
70
+ end
71
+
72
+ def build_url(url:, path:, query:)
73
+ localized_url = builder_class(url).build(host: url.host, port: url.port, path: path, query: query).to_s
74
+ success(localized_url)
75
+ end
76
+
77
+ private
78
+
79
+ def supported_path?(path)
80
+ return true if path.blank? || path == '/' || maches_locale_regex?(path)
81
+
82
+ path.match(SUPPORTED_PATHS_REGEX)
83
+ end
84
+
85
+ def maches_locale_regex?(path)
86
+ path.match(LOCALE_REGEX)[0].gsub('/', '') if path.match(LOCALE_REGEX)
87
+ end
88
+
89
+ def default_locale_supplied?(locale, default_locale)
90
+ default_locale.present? && default_locale.to_s == locale.to_s
91
+ end
92
+
93
+ def cleanup_path(path)
94
+ path.chomp('/').gsub('//', '/')
95
+ end
96
+
97
+ def builder_class(url)
98
+ url.scheme == 'http' ? URI::HTTP : URI::HTTPS
99
+ end
100
+ end
101
+ end
@@ -287,10 +287,20 @@
287
287
  color: #333333;
288
288
  }
289
289
 
290
+ .purchase_total--name {
291
+ margin: 0;
292
+ text-align: right;
293
+ color: #333333;
294
+ }
295
+
290
296
  .purchase_total--label {
291
297
  padding: 0 15px 0 0;
292
298
  }
293
299
 
300
+ .purchase_total-col {
301
+ vertical-align: bottom;
302
+ }
303
+
294
304
  body {
295
305
  background-color: #F2F4F6;
296
306
  color: #51545E;
@@ -400,6 +410,9 @@
400
410
  .email-footer {
401
411
  width: 100% !important;
402
412
  }
413
+ .content-cell {
414
+ padding: 40px 15px !important;
415
+ }
403
416
  }
404
417
 
405
418
  @media (prefers-color-scheme: dark) {
@@ -8,10 +8,13 @@
8
8
  <td></td>
9
9
  <td>
10
10
  <p class="f-fallback purchase_total purchase_total--label">
11
- <%= Spree.t(:promotion) %> <%= label %>:
11
+ <%= Spree.t(:promotion) %>:
12
+ </p>
13
+ <p class="f-fallback purchase_total--name purchase_total--label">
14
+ <%= label %>
12
15
  </p>
13
16
  </td>
14
- <td>
17
+ <td class="purchase_total-col">
15
18
  <p class="f-fallback purchase_total">
16
19
  <%= Spree::Money.new(adjustments.sum(&:amount), currency: order.currency) %>
17
20
  </p>
@@ -25,10 +28,13 @@
25
28
  <td></td>
26
29
  <td>
27
30
  <p class="f-fallback purchase_total purchase_total--label">
28
- <%= Spree.t(:shipping) %> <%= name %>:
31
+ <%= Spree.t(:shipping) %>:
32
+ </p>
33
+ <p class="f-fallback purchase_total--name purchase_total--label">
34
+ <%= name %>
29
35
  </p>
30
36
  </td>
31
- <td>
37
+ <td class="purchase_total-col">
32
38
  <p class="f-fallback purchase_total">
33
39
  <%= Spree::Money.new(shipments.sum(&:discounted_cost), currency: order.currency) %>
34
40
  </p>
@@ -41,10 +47,13 @@
41
47
  <td></td>
42
48
  <td>
43
49
  <p class="f-fallback purchase_total purchase_total--label">
44
- <%= Spree.t(:tax) %> <%= label %>:
50
+ <%= Spree.t(:tax) %>:
51
+ </p>
52
+ <p class="f-fallback purchase_total--name purchase_total--label">
53
+ <%= label %>
45
54
  </p>
46
55
  </td>
47
- <td>
56
+ <td class="purchase_total-col">
48
57
  <p class="f-fallback purchase_total">
49
58
  <%= Spree::Money.new(adjustments.sum(&:amount), currency: order.currency) %>
50
59
  </p>