spree_core 4.2.0.rc5 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
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>