solidus_core 3.1.6 → 3.2.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.
Files changed (96) 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 +23 -10
  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_taxation.rb +6 -4
  16. data/app/models/spree/order_updater.rb +4 -3
  17. data/app/models/spree/payment_method.rb +11 -0
  18. data/app/models/spree/price.rb +1 -1
  19. data/app/models/spree/product/scopes.rb +21 -3
  20. data/app/models/spree/product.rb +1 -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 +8 -4
  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/reimbursement.rb +2 -2
  33. data/app/models/spree/return_item.rb +1 -2
  34. data/app/models/spree/stock/allocator/on_hand_first.rb +2 -2
  35. data/app/models/spree/stock/quantifier.rb +12 -8
  36. data/app/models/spree/stock/simple_coordinator.rb +2 -1
  37. data/app/models/spree/tax/item_tax.rb +3 -2
  38. data/app/models/spree/tax/order_tax.rb +3 -1
  39. data/app/models/spree/tax/tax_location.rb +4 -7
  40. data/app/models/spree/tax_rate.rb +2 -0
  41. data/app/models/spree/variant.rb +1 -1
  42. data/app/subscribers/spree/mailer_subscriber.rb +4 -0
  43. data/app/subscribers/spree/order_mailer_subscriber.rb +35 -0
  44. data/config/i18n-tasks.yml +134 -0
  45. data/config/locales/en.yml +391 -257
  46. data/db/migrate/20201127212108_add_type_before_removal_to_spree_payment_methods.rb +7 -0
  47. data/db/migrate/20220317165036_set_promotions_with_any_policy_to_all_if_possible.rb +20 -0
  48. data/lib/generators/solidus/install/install_generator/bundler_context.rb +97 -0
  49. data/lib/generators/solidus/install/install_generator/install_frontend.rb +53 -0
  50. data/lib/generators/solidus/install/install_generator/support_solidus_frontend_extraction.rb +48 -0
  51. data/lib/generators/solidus/install/install_generator.rb +58 -50
  52. data/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt +6 -16
  53. data/lib/generators/solidus/install/templates/vendor/assets/javascripts/spree/backend/all.js +2 -2
  54. data/lib/spree/app_configuration.rb +43 -0
  55. data/lib/spree/bus.rb +20 -0
  56. data/lib/spree/core/controller_helpers/auth.rb +9 -1
  57. data/lib/spree/core/controller_helpers/current_host.rb +1 -3
  58. data/lib/spree/core/controller_helpers/order.rb +10 -10
  59. data/lib/spree/core/controller_helpers/search.rb +1 -1
  60. data/lib/spree/core/engine.rb +39 -8
  61. data/lib/spree/core/state_machines/order.rb +1 -1
  62. data/lib/spree/core/stock_configuration.rb +18 -0
  63. data/lib/spree/core/validators/email.rb +3 -1
  64. data/lib/spree/core/version.rb +2 -2
  65. data/lib/spree/core.rb +20 -0
  66. data/lib/spree/event/subscriber_registry.rb +4 -6
  67. data/lib/spree/event.rb +1 -1
  68. data/lib/spree/migrations.rb +1 -1
  69. data/lib/spree/permission_sets/default_customer.rb +8 -1
  70. data/lib/spree/permitted_attributes.rb +4 -4
  71. data/lib/spree/preferences/configuration.rb +34 -12
  72. data/lib/spree/preferences/preferable_class_methods.rb +1 -1
  73. data/lib/spree/preferences/preference_differentiator.rb +2 -1
  74. data/lib/spree/preferences/static_model_preferences.rb +0 -2
  75. data/lib/spree/rails_compatibility.rb +99 -0
  76. data/lib/spree/testing_support/bus_helpers.rb +101 -0
  77. data/lib/spree/testing_support/common_rake.rb +47 -19
  78. data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/backend/all.js +1 -1
  79. data/lib/spree/testing_support/dummy_app/assets/javascripts/spree/frontend/all.js +1 -1
  80. data/lib/spree/testing_support/dummy_app.rb +6 -2
  81. data/lib/spree/testing_support/factories/address_factory.rb +7 -2
  82. data/lib/spree/testing_support/factories/inventory_unit_factory.rb +1 -1
  83. data/lib/spree/testing_support/factories/order_factory.rb +8 -4
  84. data/lib/spree/testing_support/factories/product_factory.rb +4 -1
  85. data/lib/spree/testing_support/factories/store_credit_factory.rb +4 -4
  86. data/lib/spree/testing_support/factories/user_factory.rb +6 -0
  87. data/lib/spree/testing_support/factory_bot.rb +1 -1
  88. data/lib/spree/testing_support/order_walkthrough.rb +5 -4
  89. data/lib/spree/testing_support/silence_deprecations.rb +9 -0
  90. data/lib/tasks/payment_method.rake +29 -0
  91. data/lib/tasks/solidus/delete_prices_with_nil_amount.rake +2 -2
  92. data/lib/tasks/solidus/split_promotions_with_any_match_policy.rake +33 -0
  93. data/solidus_core.gemspec +7 -2
  94. metadata +90 -24
  95. data/lib/generators/solidus/install/templates/vendor/assets/javascripts/spree/frontend/all.js +0 -10
  96. 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
  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
@@ -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"
@@ -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
@@ -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