solidus_core 3.0.0.rc2 → 3.1.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.

Potentially problematic release.


This version of solidus_core might be problematic. Click here for more details.

Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/base_helper.rb +1 -1
  3. data/app/helpers/spree/products_helper.rb +1 -1
  4. data/app/models/concerns/spree/active_storage_adapter/attachment.rb +11 -11
  5. data/app/models/concerns/spree/active_storage_adapter.rb +2 -0
  6. data/app/models/concerns/spree/default_price.rb +63 -10
  7. data/app/models/spree/adjustment.rb +6 -5
  8. data/app/models/spree/base.rb +0 -17
  9. data/app/models/spree/calculator.rb +4 -0
  10. data/app/models/spree/customer_return.rb +3 -2
  11. data/app/models/spree/image/active_storage_attachment.rb +11 -8
  12. data/app/models/spree/image/paperclip_attachment.rb +3 -3
  13. data/app/models/spree/line_item.rb +2 -2
  14. data/app/models/spree/order.rb +12 -7
  15. data/app/models/spree/payment_method/bogus_credit_card.rb +13 -9
  16. data/app/models/spree/payment_method/simple_bogus_credit_card.rb +4 -4
  17. data/app/models/spree/payment_method.rb +3 -0
  18. data/app/models/spree/price.rb +1 -1
  19. data/app/models/spree/product/scopes.rb +5 -5
  20. data/app/models/spree/product.rb +12 -1
  21. data/app/models/spree/promotion/rules/item_total.rb +50 -6
  22. data/app/models/spree/promotion.rb +2 -2
  23. data/app/models/spree/promotion_action.rb +3 -0
  24. data/app/models/spree/promotion_code.rb +1 -1
  25. data/app/models/spree/promotion_rule.rb +4 -0
  26. data/app/models/spree/return_item.rb +2 -3
  27. data/app/models/spree/shipping_rate_tax.rb +1 -1
  28. data/app/models/spree/stock/availability.rb +11 -3
  29. data/app/models/spree/stock/simple_coordinator.rb +6 -11
  30. data/app/models/spree/stock_location.rb +1 -1
  31. data/app/models/spree/store_credit.rb +6 -1
  32. data/app/models/spree/tax_calculator/shipping_rate.rb +1 -1
  33. data/app/models/spree/taxon/active_storage_attachment.rb +2 -2
  34. data/app/models/spree/taxon/paperclip_attachment.rb +3 -3
  35. data/app/models/spree/variant/price_selector.rb +16 -3
  36. data/app/models/spree/variant.rb +27 -17
  37. data/config/locales/en.yml +2 -0
  38. data/db/migrate/20210312061050_change_column_null_on_prices.rb +7 -0
  39. data/lib/generators/solidus/install/install_generator.rb +2 -2
  40. data/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt +3 -1
  41. data/lib/generators/solidus/update/templates/config/initializers/new_solidus_defaults.rb.tt +30 -0
  42. data/lib/generators/solidus/update/update_generator.rb +112 -0
  43. data/lib/generators/spree/dummy/templates/rails/application.rb.tt +0 -1
  44. data/lib/generators/spree/dummy/templates/rails/database.yml +78 -35
  45. data/lib/spree/app_configuration.rb +70 -0
  46. data/lib/spree/core/engine.rb +10 -11
  47. data/lib/spree/core/product_filters.rb +1 -1
  48. data/lib/spree/core/search/base.rb +1 -1
  49. data/lib/spree/core/state_machines/order.rb +1 -1
  50. data/lib/spree/core/validators/email.rb +1 -1
  51. data/lib/spree/core/version.rb +5 -1
  52. data/lib/spree/core/versioned_value.rb +75 -0
  53. data/lib/spree/core.rb +17 -0
  54. data/lib/spree/deprecation.rb +1 -1
  55. data/lib/spree/permitted_attributes.rb +8 -2
  56. data/lib/spree/preferences/configuration.rb +62 -0
  57. data/lib/spree/preferences/persistable.rb +23 -0
  58. data/lib/spree/preferences/preferable.rb +8 -0
  59. data/lib/spree/preferences/preferable_class_methods.rb +5 -3
  60. data/lib/spree/preferences/preference_differentiator.rb +28 -0
  61. data/lib/spree/testing_support/dummy_app/database.yml +42 -22
  62. data/lib/spree/testing_support/dummy_app.rb +33 -17
  63. data/lib/spree/testing_support/fixtures/file.txt +1 -0
  64. data/lib/spree/testing_support.rb +1 -1
  65. data/lib/tasks/solidus/delete_prices_with_nil_amount.rake +8 -0
  66. data/solidus_core.gemspec +20 -0
  67. metadata +29 -8
  68. data/app/models/spree/tax/shipping_rate_taxer.rb +0 -24
  69. data/lib/tasks/migrations/migrate_address_names.rake +0 -158
  70. data/lib/tasks/migrations/migrate_default_billing_addresses_to_address_book.rake +0 -38
  71. data/lib/tasks/upgrade.rake +0 -13
@@ -9,17 +9,17 @@ module Spree
9
9
 
10
10
  def authorize(_money, credit_card, _options = {})
11
11
  if VALID_CCS.include? credit_card.number
12
- ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'A' })
12
+ ActiveMerchant::Billing::Response.new(true, SUCCESS_MESSAGE, {}, test: true, authorization: AUTHORIZATION_CODE, avs_result: { code: 'A' })
13
13
  else
14
- ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', { message: 'Bogus Gateway: Forced failure' }, test: true)
14
+ ActiveMerchant::Billing::Response.new(false, FAILURE_MESSAGE, { message: FAILURE_MESSAGE }, test: true)
15
15
  end
16
16
  end
17
17
 
18
18
  def purchase(_money, credit_card, _options = {})
19
19
  if VALID_CCS.include? credit_card.number
20
- ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345', avs_result: { code: 'A' })
20
+ ActiveMerchant::Billing::Response.new(true, SUCCESS_MESSAGE, {}, test: true, authorization: AUTHORIZATION_CODE, avs_result: { code: 'A' })
21
21
  else
22
- ActiveMerchant::Billing::Response.new(false, 'Bogus Gateway: Forced failure', message: 'Bogus Gateway: Forced failure', test: true)
22
+ ActiveMerchant::Billing::Response.new(false, FAILURE_MESSAGE, message: FAILURE_MESSAGE, test: true)
23
23
  end
24
24
  end
25
25
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'spree/preferences/persistable'
3
4
  require 'spree/preferences/statically_configurable'
4
5
 
5
6
  module Spree
@@ -11,6 +12,8 @@ module Spree
11
12
  # This class is not meant to be instantiated. Please create instances of concrete payment methods.
12
13
  #
13
14
  class PaymentMethod < Spree::Base
15
+ include Spree::Preferences::Persistable
16
+
14
17
  preference :server, :string, default: 'test'
15
18
  preference :test_mode, :boolean, default: true
16
19
 
@@ -13,7 +13,7 @@ module Spree
13
13
  delegate :tax_rates, to: :variant
14
14
 
15
15
  validate :check_price
16
- validates :amount, allow_nil: true, numericality: {
16
+ validates :amount, numericality: {
17
17
  greater_than_or_equal_to: 0,
18
18
  less_than_or_equal_to: MAXIMUM_AMOUNT
19
19
  }
@@ -29,25 +29,25 @@ module Spree
29
29
  scope :descend_by_name, -> { order(name: :desc) }
30
30
 
31
31
  add_search_scope :ascend_by_master_price do
32
- joins(master: :default_price).select('spree_products.* , spree_prices.amount')
32
+ joins(master: :prices).select('spree_products.* , spree_prices.amount')
33
33
  .order(Spree::Price.arel_table[:amount].asc)
34
34
  end
35
35
 
36
36
  add_search_scope :descend_by_master_price do
37
- joins(master: :default_price).select('spree_products.* , spree_prices.amount')
37
+ joins(master: :prices).select('spree_products.* , spree_prices.amount')
38
38
  .order(Spree::Price.arel_table[:amount].desc)
39
39
  end
40
40
 
41
41
  add_search_scope :price_between do |low, high|
42
- joins(master: :default_price).where(Price.table_name => { amount: low..high })
42
+ joins(master: :prices).where(Price.table_name => { amount: low..high })
43
43
  end
44
44
 
45
45
  add_search_scope :master_price_lte do |price|
46
- joins(master: :default_price).where("#{price_table_name}.amount <= ?", price)
46
+ joins(master: :prices).where("#{price_table_name}.amount <= ?", price)
47
47
  end
48
48
 
49
49
  add_search_scope :master_price_gte do |price|
50
- joins(master: :default_price).where("#{price_table_name}.amount >= ?", price)
50
+ joins(master: :prices).where("#{price_table_name}.amount >= ?", price)
51
51
  end
52
52
 
53
53
  # This scope selects products in taxon AND all its descendants
@@ -45,7 +45,6 @@ module Spree
45
45
 
46
46
  has_many :variants,
47
47
  -> { where(is_master: false).order(:position) },
48
- inverse_of: :product,
49
48
  class_name: 'Spree::Variant'
50
49
 
51
50
  has_many :variants_including_master,
@@ -61,6 +60,17 @@ module Spree
61
60
  has_many :line_items, through: :variants_including_master
62
61
  has_many :orders, through: :line_items
63
62
 
63
+ scope :sort_by_master_default_price_amount_asc, -> {
64
+ with_default_price.order('spree_prices.amount ASC')
65
+ }
66
+ scope :sort_by_master_default_price_amount_desc, -> {
67
+ with_default_price.order('spree_prices.amount DESC')
68
+ }
69
+ scope :with_default_price, -> {
70
+ left_joins(master: :prices)
71
+ .where(master: { spree_prices: Spree::Config.default_pricing_options.desired_attributes })
72
+ }
73
+
64
74
  def find_or_build_master
65
75
  master || build_master
66
76
  end
@@ -85,6 +95,7 @@ module Spree
85
95
  :has_default_price?,
86
96
  :images,
87
97
  :price_for,
98
+ :price_for_options,
88
99
  :rebuild_vat_prices=,
89
100
  to: :find_or_build_master
90
101
 
@@ -5,12 +5,38 @@ module Spree
5
5
  module Rules
6
6
  # A rule to apply to an order greater than (or greater than or equal to)
7
7
  # a specific amount
8
+ #
9
+ # To add extra operators please override `self.operators_map` or any other helper method.
10
+ # To customize the error message you can also override `ineligible_message`.
8
11
  class ItemTotal < PromotionRule
12
+ include ActiveSupport::Deprecation::DeprecatedConstantAccessor
13
+
9
14
  preference :amount, :decimal, default: 100.00
10
15
  preference :currency, :string, default: ->{ Spree::Config[:currency] }
11
- preference :operator, :string, default: '>'
16
+ preference :operator, :string, default: 'gt'
17
+
18
+ # The list of allowed operators names mapped to their symbols.
19
+ def self.operators_map
20
+ {
21
+ gte: :>=,
22
+ gt: :>,
23
+ }
24
+ end
25
+
26
+ def self.operator_options
27
+ operators_map.map do |name, _method|
28
+ [I18n.t(name, scope: 'spree.item_total_rule.operators'), name]
29
+ end
30
+ end
12
31
 
13
- OPERATORS = ['gt', 'gte']
32
+ # @deprecated
33
+ OPERATORS = operators_map.keys.map(&:to_s)
34
+ deprecate_constant(
35
+ :OPERATORS,
36
+ :operators_map,
37
+ message: "OPERATORS is deprecated! Use `operators_map.keys.map(&:to_s)` instead.",
38
+ deprecator: Spree::Deprecation,
39
+ )
14
40
 
15
41
  def applicable?(promotable)
16
42
  promotable.is_a?(Spree::Order)
@@ -18,8 +44,8 @@ module Spree
18
44
 
19
45
  def eligible?(order, _options = {})
20
46
  return false unless order.currency == preferred_currency
21
- item_total = order.item_total
22
- unless item_total.send(preferred_operator == 'gte' ? :>= : :>, BigDecimal(preferred_amount.to_s))
47
+
48
+ unless total_for_order(order).send(operator, threshold)
23
49
  eligibility_errors.add(:base, ineligible_message, error_code: ineligible_error_code)
24
50
  end
25
51
 
@@ -28,15 +54,33 @@ module Spree
28
54
 
29
55
  private
30
56
 
57
+ def operator
58
+ self.class.operators_map.fetch(
59
+ preferred_operator.to_sym,
60
+ preferred_operator_default,
61
+ )
62
+ end
63
+
64
+ def total_for_order(order)
65
+ order.item_total
66
+ end
67
+
68
+ def threshold
69
+ BigDecimal(preferred_amount.to_s)
70
+ end
71
+
31
72
  def formatted_amount
32
73
  Spree::Money.new(preferred_amount, currency: preferred_currency).to_s
33
74
  end
34
75
 
35
76
  def ineligible_message
36
- if preferred_operator == 'gte'
77
+ case preferred_operator.to_s
78
+ when 'gte'
37
79
  eligibility_error_message(:item_total_less_than, amount: formatted_amount)
38
- else
80
+ when 'gt'
39
81
  eligibility_error_message(:item_total_less_than_or_equal, amount: formatted_amount)
82
+ else
83
+ eligibility_error_message(:item_total_doesnt_match_with_operator, amount: formatted_amount, operator: preferred_operator)
40
84
  end
41
85
  end
42
86
 
@@ -47,7 +47,7 @@ module Spree
47
47
  where(table[:expires_at].eq(nil).or(table[:expires_at].gt(time)))
48
48
  end
49
49
  scope :has_actions, -> do
50
- joins(:promotion_actions)
50
+ joins(:promotion_actions).distinct
51
51
  end
52
52
  scope :applied, -> { joins(:order_promotions).distinct }
53
53
 
@@ -192,7 +192,7 @@ module Spree
192
192
  def usage_count(excluded_orders: [])
193
193
  Spree::Adjustment.promotion.
194
194
  eligible.
195
- in_completed_orders(excluded_orders: excluded_orders).
195
+ in_completed_orders(excluded_orders: excluded_orders, exclude_canceled: true).
196
196
  where(source_id: actions).
197
197
  count(:order_id)
198
198
  end
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'spree/preferences/persistable'
4
+
3
5
  module Spree
4
6
  # Base class for all types of promotion action.
5
7
  #
6
8
  # PromotionActions perform the necessary tasks when a promotion is activated
7
9
  # by an event and determined to be eligible.
8
10
  class PromotionAction < Spree::Base
11
+ include Spree::Preferences::Persistable
9
12
  include Spree::SoftDeletable
10
13
 
11
14
  belongs_to :promotion, class_name: 'Spree::Promotion', inverse_of: :promotion_actions, optional: true
@@ -30,7 +30,7 @@ class Spree::PromotionCode < Spree::Base
30
30
  def usage_count(excluded_orders: [])
31
31
  adjustments.
32
32
  eligible.
33
- in_completed_orders(excluded_orders: excluded_orders).
33
+ in_completed_orders(excluded_orders: excluded_orders, exclude_canceled: true).
34
34
  count(:order_id)
35
35
  end
36
36
 
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'spree/preferences/persistable'
4
+
3
5
  module Spree
4
6
  # Base class for all promotion rules
5
7
  class PromotionRule < Spree::Base
8
+ include Spree::Preferences::Persistable
9
+
6
10
  belongs_to :promotion, class_name: 'Spree::Promotion', inverse_of: :promotion_rules, optional: true
7
11
 
8
12
  scope :of_type, ->(type) { where(type: type) }
@@ -254,10 +254,9 @@ module Spree
254
254
  }).where.not(id: id).first
255
255
 
256
256
  if other_return_item && (new_record? || COMPLETED_RECEPTION_STATUSES.include?(reception_status.to_sym))
257
- errors.add(:inventory_unit, :other_completed_return_item_exists, {
257
+ errors.add(:inventory_unit, :other_completed_return_item_exists,
258
258
  inventory_unit_id: inventory_unit_id,
259
- return_item_id: other_return_item.id
260
- })
259
+ return_item_id: other_return_item.id)
261
260
  end
262
261
  end
263
262
 
@@ -5,7 +5,7 @@ module Spree
5
5
  # @attr [Spree::ShippingRate] shipping_rate The shipping rate to be taxed
6
6
  # @attr [Spree::TaxRate] tax_rate The tax rate used to calculate the tax amount
7
7
  # @since 1.3.0
8
- # @see Spree::Tax::ShippingRateTaxer
8
+ # @see Spree::Stock::Estimator
9
9
  class ShippingRateTax < Spree::Base
10
10
  belongs_to :shipping_rate, class_name: "Spree::ShippingRate", optional: true
11
11
  belongs_to :tax_rate, class_name: "Spree::TaxRate", optional: true
@@ -19,7 +19,7 @@ module Spree
19
19
  # Get the on_hand stock quantities
20
20
  # @return [Hash<Integer=>Spree::StockQuantities>] A map of stock_location_ids to the stock quantities available in that location
21
21
  def on_hand_by_stock_location_id
22
- counts_on_hand.to_a.group_by do |(_, stock_location_id), _|
22
+ quantities_by_location_id = counts_on_hand.to_a.group_by do |(_, stock_location_id), _|
23
23
  stock_location_id
24
24
  end.transform_values do |values|
25
25
  Spree::StockQuantities.new(
@@ -31,12 +31,13 @@ module Spree
31
31
  end.to_h
32
32
  )
33
33
  end
34
+ restore_location_order(quantities_by_location_id)
34
35
  end
35
36
 
36
- # Get the on_hand stock quantities
37
+ # Get the backorderable stock quantities
37
38
  # @return [Hash<Integer=>Spree::StockQuantities>] A map of stock_location_ids to the stock quantities available in that location
38
39
  def backorderable_by_stock_location_id
39
- backorderables.group_by(&:second).transform_values do |variant_ids|
40
+ quantities_by_location_id = backorderables.group_by(&:second).transform_values do |variant_ids|
40
41
  Spree::StockQuantities.new(
41
42
  variant_ids.map do |variant_id, _|
42
43
  variant = @variant_map[variant_id]
@@ -44,6 +45,7 @@ module Spree
44
45
  end.to_h
45
46
  )
46
47
  end
48
+ restore_location_order(quantities_by_location_id)
47
49
  end
48
50
 
49
51
  private
@@ -67,6 +69,12 @@ module Spree
67
69
  where(variant_id: @variants).
68
70
  where(stock_location_id: @stock_locations)
69
71
  end
72
+
73
+ def restore_location_order(quantities_by_location_id)
74
+ sorted_location_ids = @stock_locations.map(&:id)
75
+
76
+ quantities_by_location_id.sort_by { |key, _value| sorted_location_ids.index(key) }.to_h
77
+ end
70
78
  end
71
79
  end
72
80
  end
@@ -73,11 +73,16 @@ module Spree
73
73
  packages = split_packages(packages)
74
74
 
75
75
  # Turn the Stock::Packages into a Shipment with rates
76
- packages.map do |package|
76
+ shipments = packages.map do |package|
77
77
  shipment = package.shipment = package.to_shipment
78
78
  shipment.shipping_rates = Spree::Config.stock.estimator_class.new.shipping_rates(package)
79
79
  shipment
80
80
  end
81
+
82
+ # Make sure we don't add the proposed shipments to the order
83
+ order.shipments = order.shipments - shipments
84
+
85
+ shipments
81
86
  end
82
87
 
83
88
  def split_packages(initial_packages)
@@ -87,16 +92,6 @@ module Spree
87
92
  end
88
93
  end
89
94
 
90
- def sort_availability(availability)
91
- sorted_availability = availability.sort_by do |stock_location_id, _|
92
- @stock_locations.find_index do |stock_location|
93
- stock_location.id == stock_location_id
94
- end
95
- end
96
-
97
- Hash[sorted_availability]
98
- end
99
-
100
95
  def get_units(quantities)
101
96
  # Change our raw quantities back into inventory units
102
97
  quantities.flat_map do |variant, quantity|
@@ -26,7 +26,7 @@ module Spree
26
26
  validates_uniqueness_of :code, allow_blank: true, case_sensitive: false
27
27
 
28
28
  scope :active, -> { where(active: true) }
29
- scope :order_default, -> { order(default: :desc, name: :asc) }
29
+ scope :order_default, -> { order(default: :desc, position: :asc) }
30
30
 
31
31
  after_create :create_stock_items, if: :propagate_all_variants?
32
32
  after_save :ensure_one_default
@@ -150,7 +150,12 @@ class Spree::StoreCredit < Spree::PaymentSource
150
150
  end
151
151
 
152
152
  def generate_authorization_code
153
- "#{id}-SC-#{Time.current.utc.strftime('%Y%m%d%H%M%S%6N')}"
153
+ [
154
+ id,
155
+ 'SC',
156
+ Time.current.utc.strftime('%Y%m%d%H%M%S%6N'),
157
+ SecureRandom.uuid
158
+ ].join('-')
154
159
  end
155
160
 
156
161
  def editable?
@@ -9,7 +9,7 @@ module Spree
9
9
  # looking to provide their own calculator should adhere to the API of this
10
10
  # class.
11
11
  #
12
- # @see Spree::Tax::ShippingRateTaxer
12
+ # @see Spree::Stock::Estimator
13
13
  class ShippingRate
14
14
  include Spree::Tax::TaxHelpers
15
15
 
@@ -6,8 +6,8 @@ module Spree::Taxon::ActiveStorageAttachment
6
6
 
7
7
  included do
8
8
  has_attachment :icon,
9
- styles: { mini: '32x32>', normal: '128x128>' },
10
- default_style: :mini
9
+ styles: Spree::Config.taxon_image_styles,
10
+ default_style: Spree::Config.taxon_image_style_default
11
11
  validate :icon_is_an_image
12
12
  end
13
13
  end
@@ -5,14 +5,14 @@ module Spree::Taxon::PaperclipAttachment
5
5
 
6
6
  included do
7
7
  has_attached_file :icon,
8
- styles: { mini: '32x32>', normal: '128x128>' },
9
- default_style: :mini,
8
+ styles: Spree::Config.taxon_image_styles,
9
+ default_style: Spree::Config.taxon_image_style_default,
10
10
  url: '/spree/taxons/:id/:style/:basename.:extension',
11
11
  path: ':rails_root/public/spree/taxons/:id/:style/:basename.:extension',
12
12
  default_url: '/assets/default_taxon.png'
13
13
 
14
14
  validates_attachment :icon,
15
- content_type: { content_type: %w[image/jpg image/jpeg image/png image/gif] }
15
+ content_type: { content_type: Spree::Config.allowed_image_mime_types }
16
16
  end
17
17
 
18
18
  def icon_present?
@@ -26,11 +26,24 @@ module Spree
26
26
  # @param [Spree::Variant::PricingOptions] price_options Pricing Options to abide by
27
27
  # @return [Spree::Money, nil] The most specific price for this set of pricing options.
28
28
  def price_for(price_options)
29
+ Spree::Deprecation.warn(
30
+ "price_for is deprecated and will be removed. The price_for method
31
+ should return a Spree::Price as described. Please use
32
+ #price_for_options and adjust your frontend code to explicitly call
33
+ &.money where required"
34
+ )
35
+ price_for_options(price_options)&.money
36
+ end
37
+
38
+ # The variant's Spree::Price record, given a set of pricing options
39
+ # @param [Spree::Variant::PricingOptions] price_options Pricing Options to abide by
40
+ # @return [Spree::Price, nil] The most specific price for this set of pricing options.
41
+ def price_for_options(price_options)
29
42
  variant.currently_valid_prices.detect do |price|
30
- ( price.country_iso == price_options.desired_attributes[:country_iso] ||
31
- price.country_iso.nil?
43
+ (price.country_iso == price_options.desired_attributes[:country_iso] ||
44
+ price.country_iso.nil?
32
45
  ) && price.currency == price_options.desired_attributes[:currency]
33
- end.try!(:money)
46
+ end
34
47
  end
35
48
  end
36
49
  end