spree_core 4.2.0.rc4 → 4.2.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 (62) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/spree.js +19 -0
  3. data/app/controllers/spree/base_controller.rb +2 -1
  4. data/app/controllers/spree/errors_controller.rb +11 -0
  5. data/app/finders/spree/orders/find_current.rb +1 -1
  6. data/app/finders/spree/products/find.rb +14 -3
  7. data/app/helpers/spree/base_helper.rb +2 -1
  8. data/app/helpers/spree/currency_helper.rb +34 -0
  9. data/app/helpers/spree/locale_helper.rb +31 -0
  10. data/app/helpers/spree/products_helper.rb +37 -12
  11. data/app/models/concerns/spree/product_scopes.rb +1 -1
  12. data/app/models/concerns/spree/user_payment_source.rb +1 -1
  13. data/app/models/spree/address.rb +2 -0
  14. data/app/models/spree/app_dependencies.rb +3 -1
  15. data/app/models/spree/credit_card.rb +4 -0
  16. data/app/models/spree/image.rb +14 -14
  17. data/app/models/spree/price.rb +1 -1
  18. data/app/models/spree/product.rb +29 -15
  19. data/app/models/spree/promotion/rules/option_value.rb +1 -1
  20. data/app/models/spree/store.rb +38 -9
  21. data/app/models/spree/variant.rb +8 -8
  22. data/app/paginators/spree/shared/paginate.rb +8 -1
  23. data/app/presenters/spree/variant_presenter.rb +2 -5
  24. data/app/services/spree/build_localized_redirect_url.rb +101 -0
  25. data/app/services/spree/cart/estimate_shipping_rates.rb +1 -1
  26. data/app/views/spree/errors/forbidden.html.erb +0 -0
  27. data/app/views/spree/errors/unauthorized.html.erb +0 -0
  28. data/app/views/spree/shared/_base_mailer_stylesheets.html.erb +13 -2
  29. data/app/views/spree/shared/_purchased_items_table.html.erb +15 -6
  30. data/app/views/spree/shared/purchased_items_table/_adjustment.html.erb +2 -2
  31. data/app/views/spree/shared/purchased_items_table/_line_item.html.erb +2 -2
  32. data/config/locales/en.yml +7 -53
  33. data/config/routes.rb +2 -1
  34. data/db/default/spree/stores.rb +1 -0
  35. data/db/migrate/20191017121054_add_supported_currencies_to_store.rb +1 -0
  36. data/db/migrate/20201012091259_add_filterable_column_to_spree_option_types.rb +6 -2
  37. data/db/migrate/20210120142527_ensure_default_locale_in_spree_stores.rb +5 -0
  38. data/db/migrate/20210205211040_add_supported_locales_to_spree_stores.rb +11 -0
  39. data/db/migrate/20210215202602_migrate_spree_i18n_globalize_config.rb +22 -0
  40. data/lib/generators/spree/install/install_generator.rb +9 -6
  41. data/lib/spree/core.rb +2 -1
  42. data/lib/spree/core/controller_helpers/auth.rb +3 -1
  43. data/lib/spree/core/controller_helpers/common.rb +6 -8
  44. data/lib/spree/core/controller_helpers/currency.rb +54 -0
  45. data/lib/spree/core/controller_helpers/locale.rb +58 -0
  46. data/lib/spree/core/controller_helpers/search.rb +1 -1
  47. data/lib/spree/core/controller_helpers/store.rb +0 -16
  48. data/lib/spree/core/version.rb +3 -1
  49. data/lib/spree/i18n.rb +12 -0
  50. data/lib/spree/permitted_attributes.rb +1 -1
  51. data/lib/spree/service_module.rb +2 -2
  52. data/lib/spree/testing_support/common_rake.rb +1 -1
  53. data/lib/spree/testing_support/controller_requests.rb +10 -10
  54. data/lib/spree/testing_support/factories/stock_location_factory.rb +2 -2
  55. data/lib/spree/testing_support/factories/store_factory.rb +1 -0
  56. data/lib/spree/testing_support/flatpickr_capybara.rb +101 -0
  57. data/lib/spree/testing_support/locale_helpers.rb +78 -0
  58. data/lib/spree/testing_support/next_instance_of.rb +38 -0
  59. metadata +19 -8
  60. data/lib/generators/spree/install/templates/config/initializers/spree_storefront.rb +0 -1
  61. data/lib/generators/spree/install/templates/config/spree_storefront.yml +0 -67
  62. data/lib/spree/core/controller_helpers/currency_helpers.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 661697daa0303090a2d39f0b439929bf8b54e59722ad738cee547487ba27aaf2
4
- data.tar.gz: 8de83da0c8c3aa629f8389e40c63c178391d5a51a370da3595f0e1ce6edf431f
3
+ metadata.gz: 8c152a93c7f008f29d4924552002413386a57ffad6889e735b020e5cb59ec7e0
4
+ data.tar.gz: 973a4f63078cab9a925ef921323df6a8207d2d2c49b5a1770cae4dd725db42a2
5
5
  SHA512:
6
- metadata.gz: 1b2cffe1a5872d559bb2ff7b0361e99c3d2cf6436a57e6ba1779550fb587651316c6c031c019b8e2150feae8df15f5d04ec500ae245585b9886f5584cb89ae2c
7
- data.tar.gz: 83bba2eb568b84622dd523b4b943bcfee3cba206bce126ba9cf2a094b671e53d6094e8129edc4b8d620630fccf0783f962e3311fa1d41baf149575155bd7cf4e
6
+ metadata.gz: fd78758868388f04c61383131a2510e6253d3b8e334f9d74dca40f83c064de6b9e2afd5a5a3944b1114a70b53e846e7d33c690c6ee846319e7ef951458367aaa
7
+ data.tar.gz: 796583a22125857aa1c5e9dc883030e47c1b92b55d2118bb0b8bcef73fa7264c3af1bbc0ad55cb000bf3da11246369dc28c02e7b152ff1eb663b499e51b14548
@@ -17,9 +17,28 @@ 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' && typeof (SPREE_CURRENCY) !== 'undefined') {
26
+ var fullUrl = new URL(Spree.pathFor(path))
27
+ var params = fullUrl.searchParams
28
+ var pathName = fullUrl.pathname
29
+
30
+ params.set('currency', SPREE_CURRENCY)
31
+
32
+ if (pathName.match(/api\/v/)) {
33
+ params.set('locale', SPREE_LOCALE)
34
+ } else {
35
+ pathName = SPREE_LOCALE + '/' + path
36
+ }
37
+ return fullUrl.origin + (this.mountedAt()) + pathName + '?' + params.toString()
38
+ }
39
+ return Spree.pathFor(path)
40
+ }
41
+
23
42
  Spree.adminPathFor = function (path) {
24
43
  return this.pathFor('' + (this.adminPath()) + path)
25
44
  }
@@ -7,7 +7,8 @@ class Spree::BaseController < ApplicationController
7
7
  include Spree::Core::ControllerHelpers::Search
8
8
  include Spree::Core::ControllerHelpers::Store
9
9
  include Spree::Core::ControllerHelpers::StrongParameters
10
- include Spree::Core::ControllerHelpers::CurrencyHelpers
10
+ include Spree::Core::ControllerHelpers::Locale
11
+ include Spree::Core::ControllerHelpers::Currency
11
12
 
12
13
  respond_to :html
13
14
  end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ class ErrorsController < BaseController
3
+ def forbidden
4
+ render status: 403
5
+ end
6
+
7
+ def unauthorized
8
+ render status: 401
9
+ end
10
+ end
11
+ end
@@ -9,7 +9,7 @@ module Spree
9
9
  return order unless order.nil?
10
10
  return if user.nil?
11
11
 
12
- incomplete_orders.order(created_at: :desc).find_by(store: store, user: user)
12
+ incomplete_orders.order(created_at: :desc).find_by(store: store, user: user, currency: params[:currency])
13
13
  end
14
14
 
15
15
  private
@@ -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)
@@ -22,6 +22,7 @@ module Spree
22
22
  products = by_ids(scope)
23
23
  products = by_skus(products)
24
24
  products = by_price(products)
25
+ products = by_currency(products)
25
26
  products = by_taxons(products)
26
27
  products = by_concat_taxons(products)
27
28
  products = by_name(products)
@@ -51,6 +52,10 @@ module Spree
51
52
  price.present?
52
53
  end
53
54
 
55
+ def currency?
56
+ currency.present?
57
+ end
58
+
54
59
  def taxons?
55
60
  taxons.present?
56
61
  end
@@ -94,15 +99,21 @@ module Spree
94
99
  def by_price(products)
95
100
  return products unless price?
96
101
 
97
- products.joins(master: :default_price).
102
+ products.joins(master: :prices).
98
103
  where(
99
104
  spree_prices: {
100
105
  amount: price.min..price.max,
101
- currency: currency
106
+ currency: currency&.upcase
102
107
  }
103
108
  )
104
109
  end
105
110
 
111
+ def by_currency(products)
112
+ return products unless currency?
113
+
114
+ products.joins(master: :prices).where(spree_prices: { currency: currency.upcase })
115
+ end
116
+
106
117
  def by_taxons(products)
107
118
  return products unless taxons?
108
119
 
@@ -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,34 @@
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).map { |currency| currency_presentation(currency) }
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
+
24
+ def currency_symbol(currency)
25
+ ::Money::Currency.find(currency).symbol
26
+ end
27
+
28
+ def currency_presentation(currency)
29
+ label = [currency_symbol(currency), currency].compact.join(' ')
30
+
31
+ [label, currency]
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,31 @@
1
+ module Spree
2
+ module LocaleHelper
3
+ def all_locales_options
4
+ supported_locales_for_all_stores.map { |locale| locale_presentation(locale) }
5
+ end
6
+
7
+ def available_locales_options
8
+ available_locales.map { |locale| locale_presentation(locale) }
9
+ end
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
+
17
+ def locale_presentation(locale)
18
+ if I18n.exists?('spree.i18n.this_file_language', locale: locale)
19
+ [Spree.t('i18n.this_file_language', locale: locale), locale.to_s]
20
+ else
21
+ locale.to_s == 'en' ? ['English (US)', 'en'] : [locale, locale.to_s]
22
+ end
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
30
+ end
31
+ end
@@ -35,6 +35,11 @@ module Spree
35
35
  variants_option_types_presenter(variants, product).default_variant || product.default_variant
36
36
  end
37
37
 
38
+ def should_display_compare_at_price?(default_variant)
39
+ default_variant_price = default_variant.price_in(current_currency)
40
+ default_variant_price.compare_at_amount.present? && (default_variant_price.compare_at_amount > default_variant_price.amount)
41
+ end
42
+
38
43
  def used_variants_options(variants, product)
39
44
  variants_option_types_presenter(variants, product).options
40
45
  end
@@ -115,19 +120,39 @@ module Spree
115
120
  ).call.to_json
116
121
  end
117
122
 
123
+ def product_relation_types
124
+ @product_relation_types ||= @product.respond_to?(:relation_types) ? @product.relation_types : []
125
+ end
126
+
127
+ def product_relations_by_type(relation_type)
128
+ return [] if product_relation_types.none? || !@product.respond_to?(:relations)
129
+
130
+ product_ids = @product.relations.where(relation_type: relation_type).pluck(:related_to_id).uniq
131
+
132
+ return [] if product_ids.empty?
133
+
134
+ Spree::Product.
135
+ available.not_discontinued.distinct.
136
+ where(id: product_ids).
137
+ includes(
138
+ :tax_category,
139
+ master: [
140
+ :prices,
141
+ { images: { attachment_attachment: :blob } },
142
+ ]
143
+ ).
144
+ limit(Spree::Config[:products_per_page])
145
+ end
146
+
118
147
  def related_products
119
- return [] unless @product.respond_to?(:has_related_products?) && @product.has_related_products?(:related_products)
120
-
121
- @related_products ||= @product.
122
- related_products.
123
- includes(
124
- :tax_category,
125
- master: [
126
- :prices,
127
- images: { attachment_attachment: :blob },
128
- ]
129
- ).
130
- limit(Spree::Config[:products_per_page])
148
+ ActiveSupport::Deprecation.warn(<<-DEPRECATION, caller)
149
+ ProductsHelper#related_products is deprecated and will be removed in Spree 5.0.
150
+ Please use ProductsHelper#relations from now on.
151
+ DEPRECATION
152
+
153
+ return [] unless @product.respond_to?(:has_related_products?)
154
+
155
+ @related_products ||= relations_by_type('related_products')
131
156
  end
132
157
 
133
158
  def product_available_in_currency?
@@ -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
 
@@ -9,7 +9,7 @@ module Spree
9
9
  end
10
10
 
11
11
  def payment_sources
12
- credit_cards.with_payment_profile.not_expired
12
+ credit_cards.with_payment_profile.not_expired.where(payment_method: Spree::PaymentMethod.active)
13
13
  end
14
14
 
15
15
  def drop_payment_source(source)
@@ -25,6 +25,8 @@ module Spree
25
25
  ADDRESS_FIELDS = %w(firstname lastname company address1 address2 city state zipcode country phone)
26
26
  EXCLUDED_KEYS_FOR_COMPARISION = %w(id updated_at created_at deleted_at label user_id)
27
27
 
28
+ scope :not_deleted, -> { where(deleted_at: nil) }
29
+
28
30
  belongs_to :country, class_name: 'Spree::Country'
29
31
  belongs_to :state, class_name: 'Spree::State', optional: true
30
32
  belongs_to :user, class_name: Spree.user_class.name, optional: true
@@ -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
@@ -15,13 +15,13 @@ module Spree
15
15
  width, height = size.chop.split('x')
16
16
 
17
17
  {
18
- url: polymorphic_path(attachment.variant(combine_options: {
19
- gravity: 'center',
20
- resize: size,
21
- extent: size,
22
- background: 'snow2',
23
- quality: 80
24
- }), only_path: true),
18
+ url: polymorphic_path(attachment.variant(
19
+ gravity: 'center',
20
+ resize: size,
21
+ extent: size,
22
+ background: 'snow2',
23
+ quality: 80
24
+ ), only_path: true),
25
25
  width: width,
26
26
  height: height
27
27
  }
@@ -35,13 +35,13 @@ module Spree
35
35
  width, height = size.chop.split('x')
36
36
 
37
37
  {
38
- url: polymorphic_path(attachment.variant(combine_options: {
39
- gravity: 'center',
40
- resize: size,
41
- extent: size,
42
- background: 'snow2',
43
- quality: 80
44
- }), only_path: true),
38
+ url: polymorphic_path(attachment.variant(
39
+ gravity: 'center',
40
+ resize: size,
41
+ extent: size,
42
+ background: 'snow2',
43
+ quality: 80
44
+ ), only_path: true),
45
45
  size: size,
46
46
  width: width,
47
47
  height: height
@@ -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?)
@@ -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
@@ -440,12 +448,12 @@ module Spree
440
448
  end
441
449
 
442
450
  def taxon_and_ancestors
443
- taxons.map(&:self_and_ancestors).flatten.uniq
451
+ @taxon_and_ancestors ||= taxons.map(&:self_and_ancestors).flatten.uniq
444
452
  end
445
453
 
446
454
  # Get the taxonomy ids of all taxons assigned to this product and their ancestors.
447
455
  def taxonomy_ids
448
- taxon_and_ancestors.map(&:taxonomy_id).flatten.uniq
456
+ @taxonomy_ids ||= taxon_and_ancestors.map(&:taxonomy_id).flatten.uniq
449
457
  end
450
458
 
451
459
  # Iterate through this products taxons and taxonomies and touch their timestamps in a batch
@@ -471,5 +479,11 @@ module Spree
471
479
  errors.add(:discontinue_on, :invalid_date_range)
472
480
  end
473
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
474
488
  end
475
489
  end