solidus_core 3.1.9 → 3.2.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) 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/default_price.rb +28 -4
  6. data/app/models/concerns/spree/user_address_book.rb +11 -1
  7. data/app/models/spree/adjustment.rb +1 -0
  8. data/app/models/spree/carton.rb +1 -1
  9. data/app/models/spree/option_value.rb +9 -0
  10. data/app/models/spree/order.rb +68 -29
  11. data/app/models/spree/order_contents.rb +2 -1
  12. data/app/models/spree/order_inventory.rb +1 -1
  13. data/app/models/spree/order_merger.rb +2 -2
  14. data/app/models/spree/order_taxation.rb +6 -4
  15. data/app/models/spree/order_updater.rb +4 -3
  16. data/app/models/spree/payment_method.rb +11 -0
  17. data/app/models/spree/price.rb +1 -1
  18. data/app/models/spree/product/scopes.rb +21 -3
  19. data/app/models/spree/product.rb +1 -1
  20. data/app/models/spree/promotion/actions/create_adjustment.rb +4 -0
  21. data/app/models/spree/promotion/actions/create_item_adjustments.rb +5 -6
  22. data/app/models/spree/promotion/rules/product.rb +20 -8
  23. data/app/models/spree/promotion/rules/store.rb +4 -0
  24. data/app/models/spree/promotion/rules/taxon.rb +4 -0
  25. data/app/models/spree/promotion/rules/user.rb +4 -0
  26. data/app/models/spree/promotion.rb +34 -23
  27. data/app/models/spree/promotion_action.rb +4 -0
  28. data/app/models/spree/promotion_code.rb +8 -4
  29. data/app/models/spree/promotion_handler/cart.rb +26 -6
  30. data/app/models/spree/promotion_rule.rb +5 -0
  31. data/app/models/spree/reimbursement.rb +2 -2
  32. data/app/models/spree/return_item.rb +1 -2
  33. data/app/models/spree/stock/allocator/on_hand_first.rb +2 -2
  34. data/app/models/spree/stock/quantifier.rb +12 -8
  35. data/app/models/spree/stock/simple_coordinator.rb +2 -1
  36. data/app/models/spree/tax/item_tax.rb +3 -2
  37. data/app/models/spree/tax/order_tax.rb +3 -1
  38. data/app/models/spree/tax/tax_location.rb +4 -7
  39. data/app/models/spree/tax_rate.rb +2 -0
  40. data/app/models/spree/variant/price_selector.rb +1 -18
  41. data/app/models/spree/variant.rb +2 -2
  42. data/app/subscribers/spree/mailer_subscriber.rb +4 -0
  43. data/app/subscribers/spree/order_mailer_subscriber.rb +35 -0
  44. data/config/locales/en.yml +7 -253
  45. data/db/migrate/20201127212108_add_type_before_removal_to_spree_payment_methods.rb +7 -0
  46. data/db/migrate/20220317165036_set_promotions_with_any_policy_to_all_if_possible.rb +20 -0
  47. data/lib/generators/solidus/install/install_generator/bundler_context.rb +97 -0
  48. data/lib/generators/solidus/install/install_generator/install_frontend.rb +50 -0
  49. data/lib/generators/solidus/install/install_generator/support_solidus_frontend_extraction.rb +48 -0
  50. data/lib/generators/solidus/install/install_generator.rb +56 -49
  51. data/lib/generators/solidus/install/templates/config/initializers/spree.rb.tt +6 -16
  52. data/lib/generators/solidus/install/templates/vendor/assets/javascripts/spree/backend/all.js +2 -2
  53. data/lib/spree/app_configuration.rb +29 -3
  54. data/lib/spree/bus.rb +20 -0
  55. data/lib/spree/core/controller_helpers/auth.rb +9 -1
  56. data/lib/spree/core/controller_helpers/current_host.rb +1 -3
  57. data/lib/spree/core/controller_helpers/order.rb +10 -10
  58. data/lib/spree/core/controller_helpers/search.rb +1 -1
  59. data/lib/spree/core/engine.rb +33 -8
  60. data/lib/spree/core/state_machines/order.rb +1 -1
  61. data/lib/spree/core/stock_configuration.rb +18 -0
  62. data/lib/spree/core/validators/email.rb +3 -1
  63. data/lib/spree/core/version.rb +1 -1
  64. data/lib/spree/core.rb +20 -0
  65. data/lib/spree/event/subscriber_registry.rb +4 -6
  66. data/lib/spree/event.rb +1 -1
  67. data/lib/spree/migrations.rb +1 -1
  68. data/lib/spree/permission_sets/default_customer.rb +8 -1
  69. data/lib/spree/permitted_attributes.rb +4 -4
  70. data/lib/spree/preferences/configuration.rb +34 -12
  71. data/lib/spree/preferences/preferable.rb +0 -5
  72. data/lib/spree/preferences/preferable_class_methods.rb +3 -3
  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/factory_bot.rb +1 -1
  87. data/lib/spree/testing_support/order_walkthrough.rb +5 -4
  88. data/lib/spree/testing_support/silence_deprecations.rb +9 -0
  89. data/lib/tasks/payment_method.rake +29 -0
  90. data/lib/tasks/solidus/delete_prices_with_nil_amount.rake +2 -2
  91. data/lib/tasks/solidus/split_promotions_with_any_match_policy.rake +33 -0
  92. data/solidus_core.gemspec +6 -2
  93. metadata +71 -26
  94. data/lib/generators/solidus/install/templates/vendor/assets/javascripts/spree/frontend/all.js +0 -10
  95. data/lib/generators/solidus/install/templates/vendor/assets/stylesheets/spree/frontend/all.css +0 -9
@@ -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,
@@ -39,29 +39,12 @@ module Spree
39
39
  # @param [Spree::Variant::PricingOptions] price_options Pricing Options to abide by
40
40
  # @return [Spree::Price, nil] The most specific price for this set of pricing options.
41
41
  def price_for_options(price_options)
42
- sorted_prices_for(variant).detect do |price|
42
+ variant.currently_valid_prices.detect do |price|
43
43
  (price.country_iso == price_options.desired_attributes[:country_iso] ||
44
44
  price.country_iso.nil?
45
45
  ) && price.currency == price_options.desired_attributes[:currency]
46
46
  end
47
47
  end
48
-
49
- private
50
-
51
- # Returns `#prices` prioritized for being considered as default price
52
- #
53
- # @return [Array<Spree::Price>]
54
- def sorted_prices_for(variant)
55
- variant.prices.select do |price|
56
- variant.discarded? || price.kept?
57
- end.sort_by do |price|
58
- [
59
- price.country_iso.nil? ? 0 : 1,
60
- price.updated_at || Time.zone.now,
61
- price.id || Float::INFINITY,
62
- ]
63
- end.reverse
64
- end
65
48
  end
66
49
  end
67
50
  end
@@ -23,6 +23,7 @@ module Spree
23
23
  after_discard do
24
24
  stock_items.discard_all
25
25
  images.destroy_all
26
+ prices.discard_all
26
27
  end
27
28
 
28
29
  attr_writer :rebuild_vat_prices
@@ -51,7 +52,6 @@ module Spree
51
52
  has_many :images, -> { order(:position) }, as: :viewable, dependent: :destroy, class_name: "Spree::Image"
52
53
 
53
54
  has_many :prices,
54
- -> { with_discarded },
55
55
  class_name: 'Spree::Price',
56
56
  dependent: :destroy,
57
57
  inverse_of: :variant,
@@ -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