spree_core 4.2.0.rc2 → 4.2.0.rc3

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 (72) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/spree/base_controller.rb +0 -2
  3. data/app/finders/spree/addresses/find.rb +1 -12
  4. data/app/finders/spree/base_finder.rb +14 -0
  5. data/app/finders/spree/countries/find.rb +11 -3
  6. data/app/finders/spree/credit_cards/find.rb +2 -2
  7. data/app/finders/spree/orders/find_current.rb +1 -1
  8. data/app/helpers/spree/base_helper.rb +1 -7
  9. data/app/mailers/spree/base_mailer.rb +4 -4
  10. data/app/mailers/spree/order_mailer.rb +3 -3
  11. data/app/mailers/spree/reimbursement_mailer.rb +1 -1
  12. data/app/mailers/spree/shipment_mailer.rb +1 -1
  13. data/app/models/concerns/spree/default_price.rb +1 -5
  14. data/app/models/concerns/spree/user_methods.rb +2 -2
  15. data/app/models/spree/ability.rb +2 -6
  16. data/app/models/spree/address.rb +4 -0
  17. data/app/models/spree/app_dependencies.rb +4 -2
  18. data/app/models/spree/base.rb +5 -0
  19. data/app/models/spree/fulfilment_changer.rb +58 -16
  20. data/app/models/spree/inventory_unit.rb +2 -7
  21. data/app/models/spree/line_item.rb +1 -6
  22. data/app/models/spree/order.rb +1 -0
  23. data/app/models/spree/payment.rb +18 -4
  24. data/app/models/spree/payment/processing.rb +2 -2
  25. data/app/models/spree/payment_method.rb +3 -3
  26. data/app/models/spree/price.rb +1 -6
  27. data/app/models/spree/product.rb +12 -2
  28. data/app/models/spree/promotion/rules/product.rb +2 -1
  29. data/app/models/spree/promotion/rules/user.rb +2 -1
  30. data/app/models/spree/refund.rb +2 -2
  31. data/app/models/spree/return_item/eligibility_validator/default.rb +0 -2
  32. data/app/models/spree/return_item/eligibility_validator/{r_m_a_required.rb → rma_required.rb} +0 -0
  33. data/app/models/spree/shipment.rb +1 -1
  34. data/app/models/spree/shipping_method.rb +1 -5
  35. data/app/models/spree/shipping_rate.rb +2 -11
  36. data/app/models/spree/stock/availability_validator.rb +3 -4
  37. data/app/models/spree/stock_item.rb +1 -5
  38. data/app/models/spree/store.rb +24 -0
  39. data/app/models/spree/store_credit.rb +1 -1
  40. data/app/models/spree/variant.rb +1 -8
  41. data/app/models/spree/zone.rb +13 -4
  42. data/app/services/spree/account/addresses/create.rb +6 -1
  43. data/app/services/spree/account/addresses/{base.rb → helper.rb} +1 -3
  44. data/app/services/spree/account/addresses/update.rb +6 -1
  45. data/app/services/spree/compare_line_items.rb +4 -2
  46. data/app/sorters/spree/base_sorter.rb +35 -0
  47. data/app/sorters/spree/orders/sort.rb +1 -37
  48. data/app/sorters/spree/products/sort.rb +9 -32
  49. data/app/validators/email_validator.rb +1 -1
  50. data/config/initializers/inflections.rb +3 -0
  51. data/config/initializers/rails61_fixes.rb +3 -0
  52. data/config/locales/en.yml +25 -8
  53. data/db/migrate/20130326175857_add_stock_location_to_rma.rb +1 -1
  54. data/db/migrate/20201127084048_add_default_country_kind_to_spree_zones.rb +5 -0
  55. data/db/migrate/20210112193440_remove_contact_email_from_spree_stores.rb +5 -0
  56. data/db/migrate/20210114182625_create_spree_payment_methods_stores.rb +10 -0
  57. data/db/migrate/20210114220232_migrate_data_payment_methods_stores.rb +15 -0
  58. data/db/migrate/20210117112551_remove_store_id_from_spree_payment_methods.rb +5 -0
  59. data/lib/spree/core/product_filters.rb +3 -3
  60. data/lib/spree/core/version.rb +1 -1
  61. data/lib/spree/i18n.rb +7 -21
  62. data/lib/spree/permitted_attributes.rb +2 -2
  63. data/lib/spree/service_module.rb +6 -2
  64. data/lib/spree/testing_support/capybara_config.rb +1 -1
  65. data/lib/spree/testing_support/factories/shipment_factory.rb +7 -9
  66. data/lib/spree/testing_support/factories/zone_factory.rb +16 -13
  67. data/lib/spree/testing_support/order_walkthrough.rb +8 -3
  68. data/lib/spree/testing_support/rspec_retry_config.rb +10 -0
  69. data/spree_core.gemspec +4 -3
  70. metadata +55 -21
  71. data/lib/spree/i18n/base.rb +0 -17
  72. 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
 
@@ -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
@@ -411,8 +411,18 @@ module Spree
411
411
  # We call master.default_price here to ensure price is initialized.
412
412
  # Required to avoid Variant#check_price validation failing on create.
413
413
  unless master.default_price && master.valid?
414
- master.errors.each do |att, error|
415
- errors.add(att, error)
414
+ if Rails::VERSION::STRING >= '6.1'
415
+ master.errors.map { |error| { field: error.attribute, message: error&.message } }.each do |err|
416
+ next if err[:field].blank? || err[:message].blank?
417
+
418
+ errors.add(err[:field], err[:message])
419
+ end
420
+ else
421
+ master.errors.messages.each do |field, error|
422
+ next if field.blank? || error.empty?
423
+
424
+ errors.add(field, error.first)
425
+ end
416
426
  end
417
427
  end
418
428
  end
@@ -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
@@ -30,6 +30,8 @@ module Spree
30
30
 
31
31
  after_commit :clear_cache
32
32
 
33
+ alias_attribute :contact_email, :customer_support_email
34
+
33
35
  def self.current(domain = nil)
34
36
  current_store = domain ? Store.by_url(domain).first : nil
35
37
  current_store || Store.default
@@ -51,6 +53,28 @@ module Spree
51
53
  "#{name} (#{code})"
52
54
  end
53
55
 
56
+ def formatted_url
57
+ return if url.blank?
58
+
59
+ if url.match(/http:\/\/|https:\/\//)
60
+ url
61
+ else
62
+ "https://#{url}"
63
+ end
64
+ end
65
+
66
+ def countries_available_for_checkout
67
+ checkout_zone_or_default.try(:country_list) || Spree::Country.all
68
+ end
69
+
70
+ def states_available_for_checkout(country)
71
+ checkout_zone_or_default.try(:state_list_for, country) || country.states
72
+ end
73
+
74
+ def checkout_zone_or_default
75
+ checkout_zone || Spree::Zone.default_checkout_zone
76
+ end
77
+
54
78
  private
55
79
 
56
80
  def ensure_default_exists_and_is_unique
@@ -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,
@@ -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])
@@ -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
@@ -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)
@@ -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