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
@@ -68,7 +68,7 @@ module Spree
68
68
  end
69
69
 
70
70
  def validate_authorization(amount, order_currency)
71
- if amount_remaining.to_d < amount.to_d
71
+ if BigDecimal(amount_remaining, 3) < BigDecimal(amount, 3)
72
72
  errors.add(:base, Spree.t('store_credit_payment_method.insufficient_funds'))
73
73
  elsif currency != order_currency
74
74
  errors.add(:base, Spree.t('store_credit_payment_method.currency_mismatch'))
@@ -3,7 +3,7 @@ module Spree
3
3
  acts_as_paranoid
4
4
  acts_as_list scope: :product
5
5
 
6
- belongs_to :product, touch: true, class_name: 'Spree::Product', inverse_of: :variants
6
+ belongs_to :product, -> { with_deleted }, touch: true, class_name: 'Spree::Product', inverse_of: :variants
7
7
  belongs_to :tax_category, class_name: 'Spree::TaxCategory', optional: true
8
8
 
9
9
  delegate :name, :name=, :description, :slug, :available_on, :shipping_category_id,
@@ -123,15 +123,15 @@ module Spree
123
123
  end
124
124
 
125
125
  def tax_category
126
- if self[:tax_category_id].nil?
127
- product.tax_category
128
- else
129
- Spree::TaxCategory.find(self[:tax_category_id])
130
- end
126
+ @tax_category ||= if self[:tax_category_id].nil?
127
+ product.tax_category
128
+ else
129
+ Spree::TaxCategory.find(self[:tax_category_id])
130
+ end
131
131
  end
132
132
 
133
133
  def options_text
134
- Spree::Variants::OptionsPresenter.new(self).to_sentence
134
+ @options_text ||= Spree::Variants::OptionsPresenter.new(self).to_sentence
135
135
  end
136
136
 
137
137
  # Default to master name
@@ -150,13 +150,6 @@ module Spree
150
150
  !!deleted_at
151
151
  end
152
152
 
153
- # Product may be created with deleted_at already set,
154
- # which would make AR's default finder return nil.
155
- # This is a stopgap for that little problem.
156
- def product
157
- Spree::Product.unscoped { super }
158
- end
159
-
160
153
  def options=(options = {})
161
154
  options.each do |option|
162
155
  set_option_value(option[:name], option[:value])
@@ -199,7 +192,7 @@ module Spree
199
192
  end
200
193
 
201
194
  def price_in(currency)
202
- prices.detect { |price| price.currency == currency } || prices.build(currency: currency)
195
+ prices.detect { |price| price.currency == currency&.upcase } || prices.build(currency: currency&.upcase)
203
196
  end
204
197
 
205
198
  def amount_in(currency)
@@ -233,7 +226,7 @@ module Spree
233
226
  end
234
227
 
235
228
  def compare_at_price
236
- price_in(cost_currency).try(:compare_at_amount)
229
+ @compare_at_price ||= price_in(cost_currency).try(:compare_at_amount)
237
230
  end
238
231
 
239
232
  def name_and_sku
@@ -101,8 +101,6 @@ module Spree
101
101
  zone_member.zoneable_id == address.country_id
102
102
  when 'Spree::State'
103
103
  zone_member.zoneable_id == address.state_id
104
- else
105
- false
106
104
  end
107
105
  end
108
106
  end
@@ -114,8 +112,6 @@ module Spree
114
112
  zoneables
115
113
  when 'state' then
116
114
  zoneables.collect(&:country)
117
- else
118
- []
119
115
  end.flatten.compact.uniq
120
116
  end
121
117
 
@@ -171,6 +167,19 @@ module Spree
171
167
  true
172
168
  end
173
169
 
170
+ def state_list
171
+ case kind
172
+ when 'country'
173
+ zoneables.map(&:states)
174
+ when 'state'
175
+ zoneables
176
+ end.flatten.compact.uniq
177
+ end
178
+
179
+ def state_list_for(country)
180
+ state_list.select { |state| state.country == country }
181
+ end
182
+
174
183
  private
175
184
 
176
185
  def remove_defunct_members
@@ -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
@@ -2,6 +2,7 @@ module Spree
2
2
  class VariantPresenter
3
3
  include Rails.application.routes.url_helpers
4
4
  include Spree::BaseHelper
5
+ include Spree::ProductsHelper
5
6
 
6
7
  attr_reader :current_currency, :current_price_options, :current_store
7
8
 
@@ -19,7 +20,7 @@ module Spree
19
20
  display_price: display_price(variant),
20
21
  price: variant.price_in(current_currency),
21
22
  display_compare_at_price: display_compare_at_price(variant),
22
- should_display_compare_at_price: should_display_compare_at_price(variant),
23
+ should_display_compare_at_price: should_display_compare_at_price?(variant),
23
24
  is_product_available_in_currency: @is_product_available_in_currency,
24
25
  backorderable: backorderable?(variant),
25
26
  in_stock: in_stock?(variant),
@@ -75,9 +76,5 @@ module Spree
75
76
  purchasable: variant.purchasable?
76
77
  }
77
78
  end
78
-
79
- def should_display_compare_at_price(variant)
80
- variant.compare_at_price.present? && variant.compare_at_price > variant.price
81
- end
82
79
  end
83
80
  end
@@ -1,7 +1,12 @@
1
1
  module Spree
2
2
  module Account
3
3
  module Addresses
4
- class Create < ::Spree::Account::Addresses::Base
4
+ class Create
5
+ prepend Spree::ServiceModule::Base
6
+ include Spree::Account::Addresses::Helper
7
+
8
+ attr_accessor :country
9
+
5
10
  def call(user:, address_params:)
6
11
  fill_country_and_state_ids(address_params)
7
12
 
@@ -1,9 +1,7 @@
1
1
  module Spree
2
2
  module Account
3
3
  module Addresses
4
- class Base
5
- prepend Spree::ServiceModule::Base
6
-
4
+ module Helper
7
5
  private
8
6
 
9
7
  attr_accessor :country
@@ -1,7 +1,12 @@
1
1
  module Spree
2
2
  module Account
3
3
  module Addresses
4
- class Update < ::Spree::Account::Addresses::Base
4
+ class Update
5
+ prepend Spree::ServiceModule::Base
6
+ include Spree::Account::Addresses::Helper
7
+
8
+ attr_accessor :country
9
+
5
10
  def call(address:, address_params:)
6
11
  address_params[:country_id] ||= address.country_id
7
12
  fill_country_and_state_ids(address_params)
@@ -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
@@ -24,7 +24,7 @@ module Spree
24
24
  if country_iso.present?
25
25
  ::Spree::Country.by_iso(country_iso)&.id
26
26
  else
27
- ::Spree::Country.default.id
27
+ Spree::Config[:default_country_id]
28
28
  end
29
29
  end
30
30
 
@@ -1,9 +1,11 @@
1
1
  # This class should be refactored
2
2
  module Spree
3
3
  class CompareLineItems
4
- prepend Spree::ServiceModule:: Base
4
+ prepend Spree::ServiceModule::Base
5
+
6
+ def call(order:, line_item:, options: {}, comparison_hooks: nil)
7
+ comparison_hooks ||= Rails.application.config.spree.line_item_comparison_hooks
5
8
 
6
- def call(order:, line_item:, options: {}, comparison_hooks: Rails.application.config.spree.line_item_comparison_hooks)
7
9
  legacy_part = comparison_hooks.all? do |hook|
8
10
  order.send(hook, line_item, options)
9
11
  end
@@ -0,0 +1,35 @@
1
+ module Spree
2
+ class BaseSorter
3
+ def initialize(scope, params = {}, allowed_sort_attributes = [])
4
+ @scope = scope
5
+ @sort = params[:sort]
6
+ @allowed_sort_attributes = allowed_sort_attributes
7
+ end
8
+
9
+ def call
10
+ by_param_attribute(scope)
11
+ end
12
+
13
+ protected
14
+
15
+ attr_reader :scope, :collection, :sort, :allowed_sort_attributes
16
+
17
+ def by_param_attribute(scope)
18
+ return scope if sort_field.blank? || !allowed_sort_attributes.include?(sort_field.to_sym)
19
+
20
+ scope.order("#{sort_field}": order_direction)
21
+ end
22
+
23
+ def desc_order
24
+ @desc_order ||= String(sort)[0] == '-'
25
+ end
26
+
27
+ def sort_field
28
+ @sort_field ||= desc_order ? sort[1..-1] : sort
29
+ end
30
+
31
+ def order_direction
32
+ desc_order ? :desc : :asc
33
+ end
34
+ end
35
+ end
@@ -1,42 +1,6 @@
1
1
  module Spree
2
2
  module Orders
3
- class Sort
4
- attr_reader :scope, :sort
5
-
6
- def initialize(scope, params)
7
- @scope = scope
8
- @sort = params[:sort]
9
- end
10
-
11
- def call
12
- orders = completed_at(scope)
13
-
14
- orders
15
- end
16
-
17
- private
18
-
19
- def desc_order
20
- @desc_order ||= String(sort)[0] == '-'
21
- end
22
-
23
- def sort_field
24
- @sort_field ||= desc_order ? sort[1..-1] : sort
25
- end
26
-
27
- def order_direction
28
- desc_order ? :asc : :desc
29
- end
30
-
31
- def completed_at?
32
- sort_field.eql?('completed_at')
33
- end
34
-
35
- def completed_at(orders)
36
- return orders unless completed_at?
37
-
38
- orders.order(completed_at: order_direction)
39
- end
3
+ class Sort < ::Spree::BaseSorter
40
4
  end
41
5
  end
42
6
  end
@@ -1,53 +1,30 @@
1
1
  module Spree
2
2
  module Products
3
- class Sort
4
- def initialize(scope, params, current_currency)
5
- @scope = scope
6
- @sort = params[:sort]
3
+ class Sort < ::Spree::BaseSorter
4
+ def initialize(scope, current_currency, params = {}, allowed_sort_attributes = [])
5
+ super(scope, params, allowed_sort_attributes)
7
6
  @currency = params[:currency] || current_currency
8
7
  end
9
8
 
10
9
  def call
11
- products = updated_at(scope)
12
- products = price(products)
10
+ products = by_param_attribute(scope)
11
+ products = by_price(products)
13
12
 
14
13
  products.distinct
15
14
  end
16
15
 
17
16
  private
18
17
 
19
- attr_reader :sort, :scope, :currency
20
-
21
- def desc_order
22
- @desc_order ||= String(sort)[0] == '-'
23
- end
24
-
25
- def sort_field
26
- @sort_field ||= desc_order ? sort[1..-1] : sort
27
- end
28
-
29
- def updated_at?
30
- sort_field == 'updated_at'
31
- end
18
+ attr_reader :sort, :scope, :currency, :allowed_sort_attributes
32
19
 
33
20
  def price?
34
21
  sort_field == 'price'
35
22
  end
36
23
 
37
- def order_direction
38
- desc_order ? :desc : :asc
39
- end
40
-
41
- def updated_at(products)
42
- return products unless updated_at?
43
-
44
- products.order(updated_at: order_direction)
45
- end
46
-
47
- def price(products)
48
- return products unless price?
24
+ def by_price(scope)
25
+ return scope unless price?
49
26
 
50
- products.joins(master: :prices).
27
+ scope.joins(master: :prices).
51
28
  select("#{Spree::Product.table_name}.*, #{Spree::Price.table_name}.amount").
52
29
  distinct.
53
30
  where(spree_prices: { currency: currency }).