solidus_core 3.1.5 → 3.2.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/products_helper.rb +1 -1
  3. data/app/models/concerns/spree/active_storage_adapter/attachment.rb +26 -11
  4. data/app/models/concerns/spree/active_storage_adapter.rb +1 -1
  5. data/app/models/concerns/spree/user_address_book.rb +11 -1
  6. data/app/models/concerns/spree/user_methods.rb +20 -1
  7. data/app/models/spree/adjustment.rb +1 -0
  8. data/app/models/spree/carton.rb +1 -1
  9. data/app/models/spree/log_entry.rb +74 -1
  10. data/app/models/spree/option_value.rb +9 -0
  11. data/app/models/spree/order.rb +68 -29
  12. data/app/models/spree/order_contents.rb +2 -1
  13. data/app/models/spree/order_inventory.rb +1 -1
  14. data/app/models/spree/order_merger.rb +2 -2
  15. data/app/models/spree/order_shipping.rb +6 -9
  16. data/app/models/spree/order_taxation.rb +6 -4
  17. data/app/models/spree/order_updater.rb +4 -3
  18. data/app/models/spree/payment_method.rb +11 -0
  19. data/app/models/spree/product/scopes.rb +21 -3
  20. data/app/models/spree/product.rb +2 -1
  21. data/app/models/spree/promotion/actions/create_adjustment.rb +4 -0
  22. data/app/models/spree/promotion/actions/create_item_adjustments.rb +5 -6
  23. data/app/models/spree/promotion/rules/product.rb +20 -8
  24. data/app/models/spree/promotion/rules/store.rb +4 -0
  25. data/app/models/spree/promotion/rules/taxon.rb +4 -0
  26. data/app/models/spree/promotion/rules/user.rb +4 -0
  27. data/app/models/spree/promotion.rb +34 -23
  28. data/app/models/spree/promotion_action.rb +4 -0
  29. data/app/models/spree/promotion_code.rb +10 -6
  30. data/app/models/spree/promotion_handler/cart.rb +26 -6
  31. data/app/models/spree/promotion_rule.rb +5 -0
  32. data/app/models/spree/refund.rb +8 -0
  33. data/app/models/spree/reimbursement.rb +2 -2
  34. data/app/models/spree/return_item.rb +1 -2
  35. data/app/models/spree/stock/allocator/on_hand_first.rb +2 -2
  36. data/app/models/spree/stock/quantifier.rb +12 -8
  37. data/app/models/spree/stock/simple_coordinator.rb +2 -1
  38. data/app/models/spree/store_credit.rb +8 -0
  39. data/app/models/spree/tax/item_tax.rb +3 -2
  40. data/app/models/spree/tax/order_tax.rb +3 -1
  41. data/app/models/spree/tax/tax_location.rb +4 -7
  42. data/app/models/spree/tax_rate.rb +2 -0
  43. data/app/models/spree/variant.rb +1 -1
  44. data/app/subscribers/spree/mailer_subscriber.rb +4 -0
  45. data/app/subscribers/spree/order_mailer_subscriber.rb +35 -0
  46. data/config/locales/en.yml +9 -2
  47. data/db/migrate/20201127212108_add_type_before_removal_to_spree_payment_methods.rb +7 -0
  48. data/db/migrate/20220317165036_set_promotions_with_any_policy_to_all_if_possible.rb +20 -0
  49. data/lib/generators/solidus/install/install_generator/bundler_context.rb +97 -0
  50. data/lib/generators/solidus/install/install_generator/install_frontend.rb +50 -0
  51. data/lib/generators/solidus/install/install_generator/support_solidus_frontend_extraction.rb +48 -0
  52. data/lib/generators/solidus/install/install_generator.rb +58 -50
  53. data/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt +6 -16
  54. data/lib/generators/solidus/install/templates/vendor/assets/javascripts/spree/backend/all.js +2 -2
  55. data/lib/spree/app_configuration.rb +43 -0
  56. data/lib/spree/bus.rb +20 -0
  57. data/lib/spree/core/controller_helpers/auth.rb +9 -1
  58. data/lib/spree/core/controller_helpers/current_host.rb +1 -3
  59. data/lib/spree/core/controller_helpers/order.rb +10 -10
  60. data/lib/spree/core/controller_helpers/search.rb +1 -1
  61. data/lib/spree/core/engine.rb +39 -8
  62. data/lib/spree/core/state_machines/order.rb +1 -1
  63. data/lib/spree/core/stock_configuration.rb +18 -0
  64. data/lib/spree/core/validators/email.rb +3 -1
  65. data/lib/spree/core/version.rb +1 -1
  66. data/lib/spree/core.rb +20 -0
  67. data/lib/spree/event/subscriber_registry.rb +4 -6
  68. data/lib/spree/event.rb +1 -1
  69. data/lib/spree/migrations.rb +1 -1
  70. data/lib/spree/permission_sets/default_customer.rb +8 -1
  71. data/lib/spree/permitted_attributes.rb +4 -4
  72. data/lib/spree/preferences/configuration.rb +34 -12
  73. data/lib/spree/preferences/preferable_class_methods.rb +1 -1
  74. data/lib/spree/preferences/preference_differentiator.rb +2 -1
  75. data/lib/spree/preferences/static_model_preferences.rb +0 -2
  76. data/lib/spree/rails_compatibility.rb +99 -0
  77. data/lib/spree/testing_support/bus_helpers.rb +101 -0
  78. data/lib/spree/testing_support/common_rake.rb +47 -19
  79. data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/backend/all.js +1 -1
  80. data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/frontend/all.js +1 -1
  81. data/lib/spree/testing_support/dummy_app.rb +6 -1
  82. data/lib/spree/testing_support/factories/address_factory.rb +7 -2
  83. data/lib/spree/testing_support/factories/inventory_unit_factory.rb +1 -1
  84. data/lib/spree/testing_support/factories/order_factory.rb +8 -4
  85. data/lib/spree/testing_support/factories/product_factory.rb +4 -1
  86. data/lib/spree/testing_support/factories/store_credit_factory.rb +4 -4
  87. data/lib/spree/testing_support/factories/user_factory.rb +6 -0
  88. data/lib/spree/testing_support/factory_bot.rb +1 -1
  89. data/lib/spree/testing_support/order_walkthrough.rb +5 -4
  90. data/lib/spree/testing_support/silence_deprecations.rb +9 -0
  91. data/lib/tasks/payment_method.rake +29 -0
  92. data/lib/tasks/solidus/delete_prices_with_nil_amount.rake +2 -2
  93. data/lib/tasks/solidus/split_promotions_with_any_match_policy.rake +33 -0
  94. data/solidus_core.gemspec +7 -2
  95. metadata +88 -23
  96. data/lib/generators/solidus/install/templates/vendor/assets/javascripts/spree/frontend/all.js +0 -10
  97. data/lib/generators/solidus/install/templates/vendor/assets/stylesheets/spree/frontend/all.css +0 -9
@@ -15,6 +15,10 @@ module Spree
15
15
  before_destroy :remove_adjustments_from_incomplete_orders
16
16
  before_discard :remove_adjustments_from_incomplete_orders
17
17
 
18
+ def preload_relations
19
+ [:calculator]
20
+ end
21
+
18
22
  # Creates the adjustment related to a promotion for the order passed
19
23
  # through options hash
20
24
  #
@@ -15,6 +15,10 @@ module Spree
15
15
  before_destroy :remove_adjustments_from_incomplete_orders
16
16
  before_discard :remove_adjustments_from_incomplete_orders
17
17
 
18
+ def preload_relations
19
+ [:calculator]
20
+ end
21
+
18
22
  def perform(payload = {})
19
23
  order = payload[:order]
20
24
  promotion = payload[:promotion]
@@ -83,13 +87,8 @@ module Spree
83
87
  end
84
88
 
85
89
  def line_items_to_adjust(promotion, order)
86
- excluded_ids = adjustments.
87
- where(adjustable_id: order.line_items.pluck(:id), adjustable_type: 'Spree::LineItem').
88
- pluck(:adjustable_id).
89
- to_set
90
-
91
90
  order.line_items.select do |line_item|
92
- !excluded_ids.include?(line_item.id) &&
91
+ line_item.adjustments.none? { |adjustment| adjustment.source == self } &&
93
92
  promotion.line_item_actionable?(order, line_item)
94
93
  end
95
94
  end
@@ -12,6 +12,10 @@ module Spree
12
12
  class_name: 'Spree::ProductPromotionRule'
13
13
  has_many :products, class_name: 'Spree::Product', through: :product_promotion_rules
14
14
 
15
+ def preload_relations
16
+ [:products]
17
+ end
18
+
15
19
  MATCH_POLICIES = %w(any all none)
16
20
 
17
21
  validates_inclusion_of :preferred_match_policy, in: MATCH_POLICIES
@@ -31,17 +35,19 @@ module Spree
31
35
  return true if eligible_products.empty?
32
36
 
33
37
  case preferred_match_policy
34
- when 'all'
35
- unless eligible_products.all? { |product| order.products.include?(product) }
38
+ when "all"
39
+ unless eligible_products.all? { |product| order_products(order).include?(product) }
36
40
  eligibility_errors.add(:base, eligibility_error_message(:missing_product), error_code: :missing_product)
37
41
  end
38
- when 'any'
39
- unless order.products.any? { |product| eligible_products.include?(product) }
40
- eligibility_errors.add(:base, eligibility_error_message(:no_applicable_products), error_code: :no_applicable_products)
42
+ when "any"
43
+ unless order_products(order).any? { |product| eligible_products.include?(product) }
44
+ eligibility_errors.add(:base, eligibility_error_message(:no_applicable_products),
45
+ error_code: :no_applicable_products)
41
46
  end
42
- when 'none'
43
- unless order.products.none? { |product| eligible_products.include?(product) }
44
- eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product), error_code: :has_excluded_product)
47
+ when "none"
48
+ unless order_products(order).none? { |product| eligible_products.include?(product) }
49
+ eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product),
50
+ error_code: :has_excluded_product)
45
51
  end
46
52
  else
47
53
  raise "unexpected match policy: #{preferred_match_policy.inspect}"
@@ -68,6 +74,12 @@ module Spree
68
74
  def product_ids_string=(product_ids)
69
75
  self.product_ids = product_ids.to_s.split(',').map(&:strip)
70
76
  end
77
+
78
+ private
79
+
80
+ def order_products(order)
81
+ order.line_items.map(&:variant).map(&:product)
82
+ end
71
83
  end
72
84
  end
73
85
  end
@@ -9,6 +9,10 @@ module Spree
9
9
  dependent: :destroy
10
10
  has_many :stores, through: :promotion_rule_stores, class_name: "Spree::Store"
11
11
 
12
+ def preload_relations
13
+ [:stores]
14
+ end
15
+
12
16
  def applicable?(promotable)
13
17
  promotable.is_a?(Spree::Order)
14
18
  end
@@ -8,6 +8,10 @@ module Spree
8
8
  dependent: :destroy
9
9
  has_many :taxons, through: :promotion_rule_taxons, class_name: 'Spree::Taxon'
10
10
 
11
+ def preload_relations
12
+ [:taxons]
13
+ end
14
+
11
15
  MATCH_POLICIES = %w(any all none)
12
16
 
13
17
  validates_inclusion_of :preferred_match_policy, in: MATCH_POLICIES
@@ -9,6 +9,10 @@ module Spree
9
9
  dependent: :destroy
10
10
  has_many :users, through: :promotion_rule_users, class_name: Spree::UserClassHandle.new
11
11
 
12
+ def preload_relations
13
+ [:users]
14
+ end
15
+
12
16
  def applicable?(promotable)
13
17
  promotable.is_a?(Spree::Order)
14
18
  end
@@ -3,6 +3,7 @@
3
3
  module Spree
4
4
  class Promotion < Spree::Base
5
5
  MATCH_POLICIES = %w(all any)
6
+
6
7
  UNACTIVATABLE_ORDER_STATES = ["complete", "awaiting_return", "returned"]
7
8
 
8
9
  attr_reader :eligibility_errors
@@ -67,6 +68,19 @@ module Spree
67
68
  ).first
68
69
  end
69
70
 
71
+ # All orders that have been discounted using this promotion
72
+ def discounted_orders
73
+ Spree::Order.
74
+ joins(:all_adjustments).
75
+ where(
76
+ spree_adjustments: {
77
+ source_type: "Spree::PromotionAction",
78
+ source_id: actions.map(&:id),
79
+ eligible: true
80
+ }
81
+ ).distinct
82
+ end
83
+
70
84
  def as_json(options = {})
71
85
  options[:except] ||= :code
72
86
  super
@@ -151,7 +165,7 @@ module Spree
151
165
  return [] if rules.none?
152
166
 
153
167
  eligible = lambda { |rule| rule.eligible?(promotable, options) }
154
- specific_rules = rules.for(promotable)
168
+ specific_rules = rules.select { |rule| rule.applicable?(promotable) }
155
169
  return [] if specific_rules.none?
156
170
 
157
171
  if match_all?
@@ -163,6 +177,12 @@ module Spree
163
177
  end
164
178
  specific_rules
165
179
  else
180
+ Spree::Deprecation.warn(
181
+ <<~WARN
182
+ Your promotion "#{name}" with ID #{id} has a match_policy of 'any'.
183
+ This is deprecated, please split the promotion into separate promotions for each rule.
184
+ WARN
185
+ )
166
186
  unless specific_rules.any?(&eligible)
167
187
  @eligibility_errors = specific_rules.map(&:eligibility_errors).detect(&:present?)
168
188
  return nil
@@ -190,11 +210,11 @@ module Spree
190
210
  # @param excluded_orders [Array<Spree::Order>] Orders to exclude from usage count
191
211
  # @return [Integer] usage count
192
212
  def usage_count(excluded_orders: [])
193
- Spree::Adjustment.promotion.
194
- eligible.
195
- in_completed_orders(excluded_orders: excluded_orders, exclude_canceled: true).
196
- where(source_id: actions).
197
- count(:order_id)
213
+ discounted_orders.
214
+ complete.
215
+ where.not(id: [excluded_orders.map(&:id)]).
216
+ where.not(spree_orders: { state: :canceled }).
217
+ count
198
218
  end
199
219
 
200
220
  def line_item_actionable?(order, line_item, promotion_code: nil)
@@ -215,21 +235,12 @@ module Spree
215
235
  end
216
236
 
217
237
  def used_by?(user, excluded_orders = [])
218
- [
219
- :adjustments,
220
- :line_item_adjustments,
221
- :shipment_adjustments
222
- ].any? do |adjustment_type|
223
- user.orders.complete.joins(adjustment_type).where(
224
- spree_adjustments: {
225
- source_type: "Spree::PromotionAction",
226
- source_id: actions.map(&:id),
227
- eligible: true
228
- }
229
- ).where.not(
230
- id: excluded_orders.map(&:id)
231
- ).any?
232
- end
238
+ discounted_orders.
239
+ complete.
240
+ where.not(id: excluded_orders.map(&:id)).
241
+ where(user: user).
242
+ where.not(spree_orders: { state: :canceled }).
243
+ exists?
233
244
  end
234
245
 
235
246
  # Removes a promotion and any adjustments or other side effects from an
@@ -251,9 +262,9 @@ module Spree
251
262
  def blacklisted?(promotable)
252
263
  case promotable
253
264
  when Spree::LineItem
254
- !promotable.product.promotionable?
265
+ !promotable.variant.product.promotionable?
255
266
  when Spree::Order
256
- promotable.line_items.joins(:product).where(spree_products: { promotionable: false }).exists?
267
+ promotable.line_items.any? { |line_item| !line_item.variant.product.promotionable? }
257
268
  end
258
269
  end
259
270
 
@@ -16,6 +16,10 @@ module Spree
16
16
  scope :of_type, ->(type) { where(type: Array.wrap(type).map(&:to_s)) }
17
17
  scope :shipping, -> { of_type(Spree::Config.environment.promotions.shipping_actions.to_a) }
18
18
 
19
+ def preload_relations
20
+ []
21
+ end
22
+
19
23
  # Updates the state of the order or performs some other action depending on
20
24
  # the subclass options will contain the payload from the event that
21
25
  # activated the promotion. This will include the key :user which allows
@@ -5,12 +5,12 @@ class Spree::PromotionCode < Spree::Base
5
5
  belongs_to :promotion_code_batch, class_name: "Spree::PromotionCodeBatch", optional: true
6
6
  has_many :adjustments
7
7
 
8
+ before_validation :normalize_code
9
+
8
10
  validates :value, presence: true, uniqueness: { allow_blank: true, case_sensitive: true }
9
11
  validates :promotion, presence: true
10
12
  validate :promotion_not_apply_automatically, on: :create
11
13
 
12
- before_save :normalize_code
13
-
14
14
  self.whitelisted_ransackable_attributes = ['value']
15
15
 
16
16
  # Whether the promotion code has exceeded its usage restrictions
@@ -28,10 +28,14 @@ class Spree::PromotionCode < Spree::Base
28
28
  # @param excluded_orders [Array<Spree::Order>] Orders to exclude from usage count
29
29
  # @return [Integer] usage count
30
30
  def usage_count(excluded_orders: [])
31
- adjustments.
32
- eligible.
33
- in_completed_orders(excluded_orders: excluded_orders, exclude_canceled: true).
34
- count(:order_id)
31
+ promotion.
32
+ discounted_orders.
33
+ complete.
34
+ where.not(spree_orders: { state: :canceled }).
35
+ joins(:order_promotions).
36
+ where(spree_orders_promotions: { promotion_code_id: self.id }).
37
+ where.not(id: excluded_orders.map(&:id)).
38
+ count
35
39
  end
36
40
 
37
41
  def usage_limit
@@ -33,23 +33,43 @@ module Spree
33
33
  private
34
34
 
35
35
  def promotions
36
- connected_order_promotions | sale_promotions
36
+ promos = connected_order_promotions | sale_promotions
37
+ promos.flat_map(&:promotion_actions).group_by(&:preload_relations).each do |preload_relations, actions|
38
+ preload(records: actions, associations: preload_relations)
39
+ end
40
+ promos.flat_map(&:promotion_rules).group_by(&:preload_relations).each do |preload_relations, rules|
41
+ preload(records: rules, associations: preload_relations)
42
+ end
43
+ promos
44
+ end
45
+
46
+ def preload(records:, associations:)
47
+ if Rails::VERSION::MAJOR >= 7
48
+ ActiveRecord::Associations::Preloader.new(records: records, associations: associations).call
49
+ else
50
+ ActiveRecord::Associations::Preloader.new.preload(records, associations)
51
+ end
37
52
  end
38
53
 
39
54
  def connected_order_promotions
40
- Spree::Promotion.active.includes(:promotion_rules).
41
- joins(:order_promotions).
42
- where(spree_orders_promotions: { order_id: order.id }).readonly(false).to_a
55
+ order.promotions.active.includes(promotion_includes)
43
56
  end
44
57
 
45
58
  def sale_promotions
46
- Spree::Promotion.where(apply_automatically: true).active.includes(:promotion_rules)
59
+ Spree::Promotion.where(apply_automatically: true).active.includes(promotion_includes)
47
60
  end
48
61
 
49
62
  def promotion_code(promotion)
50
- order_promotion = Spree::OrderPromotion.where(order: order, promotion: promotion).first
63
+ order_promotion = order.order_promotions.detect { |op| op.promotion_id == promotion.id }
51
64
  order_promotion.present? ? order_promotion.promotion_code : nil
52
65
  end
66
+
67
+ def promotion_includes
68
+ [
69
+ :promotion_rules,
70
+ :promotion_actions,
71
+ ]
72
+ end
53
73
  end
54
74
  end
55
75
  end
@@ -14,9 +14,14 @@ module Spree
14
14
  validates :promotion, presence: true
15
15
  validate :unique_per_promotion, on: :create
16
16
 
17
+ def preload_relations
18
+ []
19
+ end
20
+
17
21
  def self.for(promotable)
18
22
  all.select { |rule| rule.applicable?(promotable) }
19
23
  end
24
+ deprecate :for, "Please select promotion rules by their applicable status on the promotable instead."
20
25
 
21
26
  def applicable?(_promotable)
22
27
  raise NotImplementedError, "applicable? should be implemented in a sub-class of Spree::PromotionRule"
@@ -31,6 +31,14 @@ module Spree
31
31
  end
32
32
  end
33
33
 
34
+ # Sets this price's amount to a new value, parsing it if the new value is
35
+ # a string.
36
+ #
37
+ # @param price [String, #to_d] a new amount
38
+ def amount=(price)
39
+ self[:amount] = Spree::LocalizedNumber.parse(price)
40
+ end
41
+
34
42
  def description
35
43
  payment.payment_method.name
36
44
  end
@@ -92,10 +92,10 @@ module Spree
92
92
 
93
93
  if unpaid_amount_within_tolerance?
94
94
  reimbursed!
95
- Spree::Event.fire 'reimbursement_reimbursed', reimbursement: self
95
+ Spree::Bus.publish :reimbursement_reimbursed, reimbursement: self
96
96
  else
97
97
  errored!
98
- Spree::Event.fire 'reimbursement_errored', reimbursement: self
98
+ Spree::Bus.publish :reimbursement_errored, reimbursement: self
99
99
  end
100
100
 
101
101
  if errored?
@@ -30,7 +30,7 @@ module Spree
30
30
  self.refund_amount_calculator = Calculator::Returns::DefaultRefundAmount
31
31
 
32
32
  belongs_to :return_authorization, inverse_of: :return_items, optional: true
33
- belongs_to :inventory_unit, inverse_of: :return_items, optional: true
33
+ belongs_to :inventory_unit, inverse_of: :return_items
34
34
  belongs_to :exchange_variant, class_name: 'Spree::Variant', optional: true
35
35
  belongs_to :exchange_inventory_unit, class_name: 'Spree::InventoryUnit', inverse_of: :original_return_item, optional: true
36
36
  belongs_to :customer_return, inverse_of: :return_items, optional: true
@@ -42,7 +42,6 @@ module Spree
42
42
  validate :eligible_exchange_variant
43
43
  validate :belongs_to_same_customer_order
44
44
  validate :validate_acceptance_status_for_reimbursement
45
- validates :inventory_unit, presence: true
46
45
  validate :validate_no_other_completed_return_items
47
46
 
48
47
  after_create :cancel_others, unless: :cancelled?
@@ -7,11 +7,11 @@ module Spree
7
7
  def allocate_inventory(desired)
8
8
  # Allocate any available on hand inventory
9
9
  on_hand = allocate_on_hand(desired)
10
- desired -= on_hand.values.sum if on_hand.present?
10
+ desired -= on_hand.values.reduce(&:+) if on_hand.present?
11
11
 
12
12
  # Allocate remaining desired inventory from backorders
13
13
  backordered = allocate_backordered(desired)
14
- desired -= backordered.values.sum if backordered.present?
14
+ desired -= backordered.values.reduce(&:+) if backordered.present?
15
15
 
16
16
  # If all works at this point desired must be empty
17
17
  [on_hand, backordered, desired]
@@ -6,14 +6,18 @@ module Spree
6
6
  attr_reader :stock_items
7
7
 
8
8
  # @param [Variant] variant The variant to check inventory for.
9
- # @param [StockLocation, Integer] stock_location The stock_location to check inventory in. If unspecified it will check inventory in all available StockLocations
10
- def initialize(variant, stock_location = nil)
9
+ # @param [StockLocation, Integer] stock_location_or_id
10
+ # The stock_location or stock location ID to check inventory in.
11
+ # If unspecified it will check inventory in all available StockLocations
12
+ def initialize(variant, stock_location_or_id = nil)
11
13
  @variant = variant
12
- @stock_items = Spree::StockItem.where(variant_id: variant)
13
- if stock_location
14
- @stock_items.where!(stock_location: stock_location)
15
- else
16
- @stock_items.joins!(:stock_location).merge!(Spree::StockLocation.active)
14
+ @stock_items = variant.stock_items.select do |stock_item|
15
+ if stock_location_or_id
16
+ stock_item.stock_location == stock_location_or_id ||
17
+ stock_item.stock_location_id == stock_location_or_id
18
+ else
19
+ stock_item.stock_location.active?
20
+ end
17
21
  end
18
22
  end
19
23
 
@@ -23,7 +27,7 @@ module Spree
23
27
  # inventory is not tracked on the variant.
24
28
  def total_on_hand
25
29
  if @variant.should_track_inventory?
26
- stock_items.sum(:count_on_hand)
30
+ stock_items.sum(&:count_on_hand)
27
31
  else
28
32
  Float::INFINITY
29
33
  end
@@ -24,7 +24,8 @@ module Spree
24
24
 
25
25
  def initialize(order, inventory_units = nil)
26
26
  @order = order
27
- @inventory_units = inventory_units || InventoryUnitBuilder.new(order).units
27
+ @inventory_units =
28
+ inventory_units || Spree::Config.stock.inventory_unit_builder_class.new(order).units
28
29
  @splitters = Spree::Config.environment.stock_splitters
29
30
 
30
31
  filtered_stock_locations = Spree::Config.stock.location_filter_class.new(Spree::StockLocation.all, @order).filter
@@ -40,6 +40,14 @@ class Spree::StoreCredit < Spree::PaymentSource
40
40
  extend Spree::DisplayMoney
41
41
  money_methods :amount, :amount_used, :amount_authorized
42
42
 
43
+ # Sets this store credit's amount to a new value,
44
+ # parsing it as a localized number if the new value is a string.
45
+ #
46
+ # @param number [String, #to_d] a new amount
47
+ def amount=(number)
48
+ self[:amount] = Spree::LocalizedNumber.parse(number)
49
+ end
50
+
43
51
  def amount_remaining
44
52
  return 0.0.to_d if invalidated?
45
53
  amount - amount_used - amount_authorized
@@ -5,9 +5,10 @@ module Spree
5
5
  # Simple object used to hold tax data for an item.
6
6
  #
7
7
  # This generic object will hold the amount of tax that should be applied to
8
- # an item. (Either a {Spree::LineItem} or a {Spree::Shipment}.)
8
+ # an item. (Either a {Spree::Order}, a {Spree::LineItem} or a {Spree::Shipment}.)
9
9
  #
10
- # @attr_reader [Integer] item_id the {Spree::LineItem} or {Spree::Shipment} ID
10
+ # @attr_reader [Integer] item_id the {Spree::LineItem} or {Spree::Shipment} ID.
11
+ # Or blank if an order-level tax.
11
12
  # @attr_reader [String] label information about the taxes
12
13
  # @attr_reader [Spree::TaxRate] tax_rate will be used as the source for tax
13
14
  # adjustments
@@ -8,13 +8,15 @@ module Spree
8
8
  # adjustments on an order.
9
9
  #
10
10
  # @attr_reader [Integer] order_id the {Spree::Order} these taxes apply to
11
+ # @attr_reader [Array<Spree::Tax::ItemTax>] order_taxes an array of tax
12
+ # data for the order
11
13
  # @attr_reader [Array<Spree::Tax::ItemTax>] line_item_taxes an array of
12
14
  # tax data for order's line items
13
15
  # @attr_reader [Array<Spree::Tax::ItemTax>] shipment_taxes an array of
14
16
  # tax data for the order's shipments
15
17
  class OrderTax
16
18
  include ActiveModel::Model
17
- attr_accessor :order_id, :line_item_taxes, :shipment_taxes
19
+ attr_accessor :order_id, :order_taxes, :line_item_taxes, :shipment_taxes
18
20
  end
19
21
  end
20
22
  end
@@ -8,7 +8,7 @@ module Spree
8
8
  # @attr_reader [Integer] country_id the ID of a Spree::Country object
9
9
  # @attr_reader [Integer] state_id the ID of a Spree::State object
10
10
  class TaxLocation
11
- attr_reader :country_id, :state_id
11
+ attr_reader :country, :state
12
12
 
13
13
  # Create a new TaxLocation object
14
14
  #
@@ -19,18 +19,15 @@ module Spree
19
19
  #
20
20
  # @return [Spree::Tax::TaxLocation] a Spree::Tax::TaxLocation object
21
21
  def initialize(country: nil, state: nil)
22
- @country_id = country && country.id
23
- @state_id = state && state.id
22
+ @country, @state = country, state
24
23
  end
24
+ delegate :id, to: :state, prefix: true, allow_nil: true
25
+ delegate :id, to: :country, prefix: true, allow_nil: true
25
26
 
26
27
  def ==(other)
27
28
  state_id == other.state_id && country_id == other.country_id
28
29
  end
29
30
 
30
- def country
31
- Spree::Country.find_by(id: country_id)
32
- end
33
-
34
31
  def empty?
35
32
  country_id.nil? && state_id.nil?
36
33
  end
@@ -27,6 +27,8 @@ module Spree
27
27
 
28
28
  validates :amount, presence: true, numericality: true
29
29
 
30
+ self.whitelisted_ransackable_associations = %w[tax_categories zone]
31
+
30
32
  # Finds all tax rates whose zones match a given address
31
33
  scope :for_address, ->(address) { joins(:zone).merge(Spree::Zone.for_address(address)) }
32
34
  scope :for_country,
@@ -66,7 +66,7 @@ module Spree
66
66
 
67
67
  validates :cost_price, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
68
68
  validates :price, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
69
- validates_uniqueness_of :sku, allow_blank: true, case_sensitive: true, if: :enforce_unique_sku?
69
+ validates_uniqueness_of :sku, allow_blank: true, case_sensitive: true, conditions: -> { where(deleted_at: nil) }, if: :enforce_unique_sku?
70
70
 
71
71
  after_create :create_stock_items
72
72
  after_create :set_position
@@ -3,6 +3,10 @@
3
3
  require 'spree/event/subscriber'
4
4
 
5
5
  module Spree
6
+ # Legacy subscriber
7
+ #
8
+ # This subscriber module is used by the legacy pub/sub system (see
9
+ # {Spree::Event}).
6
10
  module MailerSubscriber
7
11
  include Spree::Event::Subscriber
8
12
 
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ # Mailing after events on a {Spree::Order}
5
+ class OrderMailerSubscriber
6
+ include Omnes::Subscriber
7
+
8
+ handle :order_finalized,
9
+ with: :send_confirmation_email,
10
+ id: :spree_order_mailer_send_confirmation_email
11
+
12
+ handle :reimbursement_reimbursed,
13
+ with: :send_reimbursement_email,
14
+ id: :spree_order_mailer_send_reimbursement_email
15
+
16
+ # Sends confirmation email to the user
17
+ #
18
+ # @param event [Omnes::UnstructuredEvent]
19
+ def send_confirmation_email(event)
20
+ order = event[:order]
21
+ unless order.confirmation_delivered?
22
+ Spree::Config.order_mailer_class.confirm_email(order).deliver_later
23
+ order.update_column(:confirmation_delivered, true)
24
+ end
25
+ end
26
+
27
+ # Sends reimbursement email to the user
28
+ #
29
+ # @param event [Omnes::UnstructuredEvent]
30
+ def send_reimbursement_email(event)
31
+ reimbursement = event[:reimbursement]
32
+ Spree::Config.reimbursement_mailer_class.reimbursement_email(reimbursement.id).deliver_later
33
+ end
34
+ end
35
+ end
@@ -365,7 +365,7 @@ en:
365
365
  amount_authorized: Amount Authorized
366
366
  amount_credited: Amount Credited
367
367
  amount_used: Amount Used
368
- category_id: Credit Type
368
+ category_id: Category
369
369
  created_at: Issued On
370
370
  created_by_id: Created By
371
371
  invalidated_at: Invalidated
@@ -866,6 +866,10 @@ en:
866
866
  new_price: New Price
867
867
  new:
868
868
  new_price: New Price
869
+ master_variant_table:
870
+ master_variant: Master Variant Prices
871
+ table:
872
+ variant_pricing: Variant Prices
869
873
  promotions:
870
874
  actions:
871
875
  calculator_label: Calculated by
@@ -998,12 +1002,16 @@ en:
998
1002
  edit_variant: Edit Variant
999
1003
  form:
1000
1004
  dimensions: Dimensions
1005
+ options: Options
1001
1006
  pricing: Pricing
1002
1007
  pricing_hint: These values are populated from the product details page and
1003
1008
  can be overridden below
1009
+ properties: Properties
1004
1010
  use_product_tax_category: Use Product Tax Category
1005
1011
  new:
1006
1012
  new_variant: New Variant
1013
+ table:
1014
+ no_variants_found: No variants found for '%{term}'
1007
1015
  table_filter:
1008
1016
  show_deleted: Show Deleted Variants
1009
1017
  administration: Administration
@@ -2248,7 +2256,6 @@ en:
2248
2256
  value: Value
2249
2257
  variant: Variant
2250
2258
  variant_placeholder: Choose a Variant
2251
- variant_pricing: Variant Pricing
2252
2259
  variant_properties: Variant Properties
2253
2260
  variant_search: Variant Search
2254
2261
  variant_search_placeholder: SKU or Option Value
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddTypeBeforeRemovalToSpreePaymentMethods < ActiveRecord::Migration[5.2]
4
+ def change
5
+ add_column :spree_payment_methods, :type_before_removal, :string
6
+ end
7
+ end