solidus_core 4.5.1 → 4.6.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 (97) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/app/jobs/spree/base_job.rb +12 -0
  4. data/app/jobs/spree/state_change_tracking_job.rb +32 -0
  5. data/app/models/concerns/spree/state_change_tracking.rb +26 -0
  6. data/app/models/concerns/spree/user_address_book.rb +24 -6
  7. data/app/models/concerns/spree/user_methods.rb +52 -14
  8. data/app/models/concerns/spree/user_reporting.rb +1 -1
  9. data/app/models/spree/address.rb +8 -2
  10. data/app/models/spree/adjustment_reason.rb +1 -1
  11. data/app/models/spree/calculator.rb +0 -2
  12. data/app/models/spree/carton.rb +7 -0
  13. data/app/models/spree/classification.rb +3 -3
  14. data/app/models/spree/core/state_machines/order/class_methods.rb +0 -16
  15. data/app/models/spree/core/state_machines/order.rb +1 -0
  16. data/app/models/spree/core/state_machines/payment.rb +1 -8
  17. data/app/models/spree/core/state_machines/shipment.rb +1 -8
  18. data/app/models/spree/country.rb +16 -5
  19. data/app/models/spree/customer_return.rb +1 -2
  20. data/app/models/spree/fulfilment_changer.rb +38 -11
  21. data/app/models/spree/inventory_unit.rb +1 -1
  22. data/app/models/spree/line_item.rb +3 -2
  23. data/app/models/spree/order.rb +5 -7
  24. data/app/models/spree/order_cancellations.rb +17 -20
  25. data/app/models/spree/order_shipping.rb +2 -9
  26. data/app/models/spree/order_taxation.rb +3 -2
  27. data/app/models/spree/order_updater.rb +11 -7
  28. data/app/models/spree/payment_method.rb +0 -1
  29. data/app/models/spree/product.rb +6 -5
  30. data/app/models/spree/product_option_type.rb +2 -2
  31. data/app/models/spree/product_property.rb +2 -2
  32. data/app/models/spree/role_user.rb +3 -3
  33. data/app/models/spree/shipment.rb +26 -12
  34. data/app/models/spree/shipping_category.rb +2 -2
  35. data/app/models/spree/shipping_method_category.rb +2 -2
  36. data/app/models/spree/state.rb +7 -4
  37. data/app/models/spree/stock/quantifier.rb +33 -9
  38. data/app/models/spree/stock_item.rb +1 -1
  39. data/app/models/spree/stock_location.rb +3 -2
  40. data/app/models/spree/store.rb +6 -0
  41. data/app/models/spree/store_credit.rb +3 -3
  42. data/app/models/spree/store_credit_event.rb +1 -1
  43. data/app/models/spree/tax_category.rb +1 -1
  44. data/app/models/spree/taxon.rb +0 -3
  45. data/app/models/spree/taxonomy.rb +2 -2
  46. data/app/models/spree/user_address.rb +4 -4
  47. data/app/models/spree/variant.rb +1 -1
  48. data/app/models/spree/variant_property_rule.rb +1 -1
  49. data/app/models/spree/variant_property_rule_condition.rb +1 -1
  50. data/app/models/spree/wallet_payment_source.rb +2 -2
  51. data/app/subscribers/spree/carton_shipped_mailer_subscriber.rb +30 -0
  52. data/app/subscribers/spree/order_cancel_mailer_subscriber.rb +21 -0
  53. data/app/subscribers/spree/order_confirmation_mailer_subscriber.rb +23 -0
  54. data/app/subscribers/spree/order_inventory_cancellation_mailer_subscriber.rb +27 -0
  55. data/app/subscribers/spree/order_mailer_subscriber.rb +8 -26
  56. data/app/subscribers/spree/reimbursement_mailer_subscriber.rb +20 -0
  57. data/config/locales/en.yml +9 -0
  58. data/db/migrate/20160101010000_solidus_one_four.rb +92 -0
  59. data/db/migrate/20180416083007_add_apply_to_all_to_variant_property_rule.rb +1 -1
  60. data/db/migrate/20250129061658_add_metadata_to_spree_resources.rb +1 -1
  61. data/db/migrate/20250214094207_add_reverse_charge_status_to_store.rb +8 -0
  62. data/db/migrate/20250225051308_add_vat_id_email_and_reverse_charge_status_to_addresses.rb +10 -0
  63. data/db/migrate/20250508145917_add_email_to_stock_locations.rb +5 -0
  64. data/db/migrate/20250530102541_add_addressbook_foreign_key.rb +29 -0
  65. data/db/migrate/20250604072105_add_fk_products_variant_property_rules.rb +30 -0
  66. data/db/migrate/20250604072555_add_fk_to_product_properties.rb +52 -0
  67. data/db/migrate/20250604072948_add_fk_to_product_option_types.rb +52 -0
  68. data/db/migrate/20250604073219_add_fk_to_classifications.rb +52 -0
  69. data/db/migrate/20250605105424_add_shipping_category_foreign_keys.rb +73 -0
  70. data/db/migrate/20250626112117_add_foreign_key_to_spree_role_users.rb +30 -0
  71. data/db/migrate/20250628094037_change_countries_iso_to_unique.rb +9 -0
  72. data/db/migrate/20250708120317_add_adjustment_reason_foreign_keys.rb +30 -0
  73. data/db/migrate/20250709073151_add_country_foreign_keys.rb +75 -0
  74. data/db/migrate/20250709084513_add_state_foreign_keys.rb +30 -0
  75. data/db/migrate/20250726220709_add_fk_to_customer_return.rb +30 -0
  76. data/lib/generators/solidus/install/app_templates/frontend/starter.rb +1 -1
  77. data/lib/generators/spree/dummy/dummy_generator.rb +2 -1
  78. data/lib/generators/spree/dummy/templates/rails/{manifest.js → manifest.js.tt} +2 -0
  79. data/lib/spree/app_configuration.rb +8 -0
  80. data/lib/spree/core/engine.rb +7 -2
  81. data/lib/spree/core/environment.rb +1 -0
  82. data/lib/spree/core/version.rb +6 -10
  83. data/lib/spree/core.rb +2 -0
  84. data/lib/spree/permitted_attributes.rb +2 -1
  85. data/lib/spree/preferences/preferable.rb +1 -5
  86. data/lib/spree/testing_support/capybara_driver.rb +9 -0
  87. data/lib/spree/testing_support/common_rake.rb +6 -3
  88. data/lib/spree/testing_support/dummy_app/migrations.rb +2 -2
  89. data/lib/spree/testing_support/dummy_app/rake_tasks.rb +0 -6
  90. data/lib/spree/testing_support/dummy_app.rb +7 -0
  91. data/lib/spree/testing_support/factories/zone_factory.rb +4 -0
  92. data/lib/spree/testing_support/job_helpers.rb +6 -15
  93. data/lib/spree/testing_support/translations.rb +2 -2
  94. data/lib/spree/zero.rb +3 -0
  95. data/solidus_core.gemspec +6 -2
  96. metadata +56 -5
  97. /data/{lib → app/models/concerns}/spree/preferences/persistable.rb +0 -0
@@ -56,6 +56,7 @@ class Spree::OrderShipping
56
56
  inventory_units:,
57
57
  shipped_at:,
58
58
  external_number:,
59
+ suppress_email: suppress_mailer,
59
60
  tracking: tracking_number
60
61
  )
61
62
  end
@@ -68,18 +69,10 @@ class Spree::OrderShipping
68
69
  end
69
70
  end
70
71
 
71
- send_shipment_emails(carton) if stock_location.fulfillable? && !suppress_mailer # e.g. digital gift cards that aren't actually shipped
72
72
  @order.shipments.reload
73
73
  @order.recalculate
74
74
 
75
+ Spree::Bus.publish(:carton_shipped, carton:)
75
76
  carton
76
77
  end
77
-
78
- private
79
-
80
- def send_shipment_emails(carton)
81
- carton.orders.each do |order|
82
- Spree::Config.carton_shipped_email_class.shipped_email(order:, carton:).deliver_later
83
- end
84
- end
85
78
  end
@@ -57,7 +57,7 @@ module Spree
57
57
 
58
58
  # Remove any tax adjustments tied to rates which no longer match.
59
59
  unmatched_adjustments = tax_adjustments - active_adjustments
60
- item.adjustments.destroy(unmatched_adjustments)
60
+ unmatched_adjustments.each(&:mark_for_destruction)
61
61
  end
62
62
 
63
63
  # Update or create a new tax adjustment on an item.
@@ -77,7 +77,8 @@ module Spree
77
77
  label: tax_item.label,
78
78
  included: tax_item.included_in_price
79
79
  )
80
- tax_adjustment.update!(amount: tax_item.amount)
80
+
81
+ tax_adjustment.amount = tax_item.amount
81
82
  tax_adjustment
82
83
  end
83
84
  end
@@ -157,9 +157,12 @@ module Spree
157
157
  recalculate_adjustments
158
158
 
159
159
  all_items = line_items + shipments
160
- order_tax_adjustments = adjustments.select(&:tax?)
160
+ # Ignore any adjustments that have been marked for destruction in our
161
+ # calculations. They'll get removed when/if we persist the order.
162
+ valid_adjustments = adjustments.reject(&:marked_for_destruction?)
163
+ order_tax_adjustments = valid_adjustments.select(&:tax?)
161
164
 
162
- order.adjustment_total = all_items.sum(&:adjustment_total) + adjustments.sum(&:amount)
165
+ order.adjustment_total = all_items.sum(&:adjustment_total) + valid_adjustments.sum(&:amount)
163
166
  order.included_tax_total = all_items.sum(&:included_tax_total) + order_tax_adjustments.select(&:included?).sum(&:amount)
164
167
  order.additional_tax_total = all_items.sum(&:additional_tax_total) + order_tax_adjustments.reject(&:included?).sum(&:amount)
165
168
 
@@ -185,11 +188,12 @@ module Spree
185
188
  yield
186
189
  new_state = order.public_send(state)
187
190
  if old_state != new_state
188
- order.state_changes.new(
189
- previous_state: old_state,
190
- next_state: new_state,
191
- name:,
192
- user_id: order.user_id
191
+ StateChangeTrackingJob.perform_later(
192
+ order,
193
+ old_state,
194
+ new_state,
195
+ Time.current,
196
+ name
193
197
  )
194
198
  end
195
199
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'spree/preferences/persistable'
4
3
  require 'spree/preferences/statically_configurable'
5
4
 
6
5
  module Spree
@@ -23,7 +23,7 @@ module Spree
23
23
 
24
24
  has_many :product_properties, dependent: :destroy, inverse_of: :product
25
25
  has_many :properties, through: :product_properties
26
- has_many :variant_property_rules
26
+ has_many :variant_property_rules, dependent: :destroy
27
27
  has_many :variant_property_rule_values, through: :variant_property_rules, source: :values
28
28
  has_many :variant_property_rule_conditions, through: :variant_property_rules, source: :conditions
29
29
 
@@ -31,19 +31,21 @@ module Spree
31
31
  has_many :taxons, through: :classifications, before_remove: :remove_taxon
32
32
 
33
33
  belongs_to :tax_category, class_name: 'Spree::TaxCategory', optional: true
34
- belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :products, optional: true
34
+ belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :products
35
35
  belongs_to :primary_taxon, class_name: 'Spree::Taxon', optional: true
36
36
 
37
37
  has_one :master,
38
38
  -> { where(is_master: true).with_discarded },
39
39
  inverse_of: :product,
40
40
  class_name: 'Spree::Variant',
41
- autosave: true
41
+ autosave: true,
42
+ dependent: false
42
43
 
43
44
  has_many :variants,
44
45
  -> { where(is_master: false).order(:position) },
45
46
  inverse_of: :product,
46
- class_name: 'Spree::Variant'
47
+ class_name: 'Spree::Variant',
48
+ dependent: false
47
49
 
48
50
  has_many :variants_including_master,
49
51
  -> { order(:position) },
@@ -122,7 +124,6 @@ module Spree
122
124
  validates :meta_title, length: { maximum: 255 }
123
125
  validates :name, presence: true
124
126
  validates :price, presence: true, if: proc { Spree::Config[:require_master_price] }
125
- validates :shipping_category_id, presence: true
126
127
  validates :slug, presence: true, uniqueness: { allow_blank: true, case_sensitive: true }
127
128
 
128
129
  attr_accessor :option_values_hash
@@ -2,8 +2,8 @@
2
2
 
3
3
  module Spree
4
4
  class ProductOptionType < Spree::Base
5
- belongs_to :product, class_name: 'Spree::Product', inverse_of: :product_option_types, touch: true, optional: true
6
- belongs_to :option_type, class_name: 'Spree::OptionType', inverse_of: :product_option_types, optional: true
5
+ belongs_to :product, class_name: 'Spree::Product', inverse_of: :product_option_types, touch: true
6
+ belongs_to :option_type, class_name: 'Spree::OptionType', inverse_of: :product_option_types
7
7
  acts_as_list scope: :product
8
8
  end
9
9
  end
@@ -6,8 +6,8 @@ module Spree
6
6
 
7
7
  acts_as_list scope: :product
8
8
 
9
- belongs_to :product, touch: true, class_name: 'Spree::Product', inverse_of: :product_properties, optional: true
10
- belongs_to :property, class_name: 'Spree::Property', inverse_of: :product_properties, optional: true
9
+ belongs_to :product, touch: true, class_name: 'Spree::Product', inverse_of: :product_properties
10
+ belongs_to :property, class_name: 'Spree::Property', inverse_of: :product_properties
11
11
 
12
12
  self.allowed_ransackable_attributes = ['value']
13
13
  end
@@ -3,12 +3,12 @@
3
3
  module Spree
4
4
  class RoleUser < Spree::Base
5
5
  self.table_name = "spree_roles_users"
6
- belongs_to :role, class_name: "Spree::Role", optional: true
7
- belongs_to :user, class_name: Spree::UserClassHandle.new, optional: true
6
+ belongs_to :role, class_name: "Spree::Role"
7
+ belongs_to :user, class_name: Spree::UserClassHandle.new
8
8
 
9
9
  after_create :auto_generate_spree_api_key
10
10
 
11
- validates_uniqueness_of :role_id, scope: :user_id
11
+ validates :role_id, uniqueness: { scope: :user_id }
12
12
 
13
13
  private
14
14
 
@@ -200,15 +200,11 @@ module Spree
200
200
  end
201
201
  end
202
202
 
203
- # Determines the appropriate +state+ according to the following logic:
204
- #
205
- # canceled if order is canceled
206
- # pending unless order is complete and +order.payment_state+ is +paid+
207
- # shipped if already shipped (ie. does not change the state)
208
- # ready all other cases
209
203
  def determine_state(order)
210
- return 'canceled' if order.canceled?
204
+ Spree.deprecator.warn "Use Spree::Shipment#recalculate_state instead"
205
+
211
206
  return 'shipped' if shipped?
207
+ return 'canceled' if order.canceled? || inventory_units.all?(&:canceled?)
212
208
  return 'pending' unless order.can_ship?
213
209
  if can_transition_from_pending_to_ready?
214
210
  'ready'
@@ -217,6 +213,27 @@ module Spree
217
213
  end
218
214
  end
219
215
 
216
+ # Assigns the appropriate +state+ according to the following logic:
217
+ #
218
+ # canceled if order is canceled
219
+ # pending unless order is complete and +order.payment_state+ is +paid+
220
+ # shipped if already shipped (ie. does not change the state)
221
+ # ready all other cases
222
+ def recalculate_state
223
+ self.state =
224
+ if shipped?
225
+ "shipped"
226
+ elsif order.canceled? || inventory_units.all?(&:canceled?)
227
+ "canceled"
228
+ elsif !order.can_ship?
229
+ "pending"
230
+ elsif can_transition_from_pending_to_ready?
231
+ "ready"
232
+ else
233
+ "pending"
234
+ end
235
+ end
236
+
220
237
  def set_up_inventory(state, variant, _order, line_item)
221
238
  inventory_units.create(
222
239
  state:,
@@ -292,12 +309,9 @@ module Spree
292
309
  # called.
293
310
  def update_state
294
311
  old_state = state
295
- new_state = determine_state(order)
312
+ new_state = recalculate_state
296
313
  if new_state != old_state
297
- update_columns(
298
- state: new_state,
299
- updated_at: Time.current
300
- )
314
+ update_columns state: new_state, updated_at: Time.current
301
315
  after_ship if new_state == 'shipped'
302
316
  end
303
317
  end
@@ -5,8 +5,8 @@ module Spree
5
5
  self.allowed_ransackable_attributes = %w[name]
6
6
 
7
7
  validates :name, presence: true
8
- has_many :products, inverse_of: :shipping_category
9
- has_many :shipping_method_categories, inverse_of: :shipping_category
8
+ has_many :products, inverse_of: :shipping_category, dependent: :restrict_with_error
9
+ has_many :shipping_method_categories, inverse_of: :shipping_category, dependent: :destroy
10
10
  has_many :shipping_methods, through: :shipping_method_categories
11
11
  end
12
12
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Spree
4
4
  class ShippingMethodCategory < Spree::Base
5
- belongs_to :shipping_method, class_name: 'Spree::ShippingMethod', optional: true
6
- belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :shipping_method_categories, optional: true
5
+ belongs_to :shipping_method, class_name: 'Spree::ShippingMethod'
6
+ belongs_to :shipping_category, class_name: 'Spree::ShippingCategory', inverse_of: :shipping_method_categories
7
7
  end
8
8
  end
@@ -2,10 +2,10 @@
2
2
 
3
3
  module Spree
4
4
  class State < Spree::Base
5
- belongs_to :country, class_name: 'Spree::Country', optional: true
6
- has_many :addresses, dependent: :nullify
5
+ belongs_to :country, class_name: 'Spree::Country'
6
+ has_many :addresses, dependent: :nullify, inverse_of: :state
7
7
 
8
- validates :country, :name, presence: true
8
+ validates :name, presence: true
9
9
 
10
10
  scope :with_name_or_abbr, ->(name_or_abbr) do
11
11
  where(
@@ -15,6 +15,7 @@ module Spree
15
15
  )
16
16
  end
17
17
 
18
+ self.allowed_ransackable_associations = %w[country]
18
19
  self.allowed_ransackable_attributes = %w[name]
19
20
 
20
21
  # table of { country.id => [ state.id , state.name ] }, arrays sorted by name
@@ -36,7 +37,9 @@ module Spree
36
37
  end
37
38
 
38
39
  def state_with_country
39
- "#{name} (#{country})"
40
+ Rails.cache.fetch("#{cache_key_with_version}/state_with_country") do
41
+ "#{name} (#{country})"
42
+ end
40
43
  end
41
44
  end
42
45
  end
@@ -11,14 +11,8 @@ module Spree
11
11
  # If unspecified it will check inventory in all available StockLocations
12
12
  def initialize(variant, stock_location_or_id = nil)
13
13
  @variant = variant
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
21
- end
14
+ @stock_location_or_id = stock_location_or_id
15
+ @stock_items = variant_stock_items
22
16
  end
23
17
 
24
18
  # Returns the total number of inventory units on hand for the variant.
@@ -26,7 +20,7 @@ module Spree
26
20
  # @return [Fixnum] number of inventory units on hand, or infinity if
27
21
  # inventory is not tracked on the variant.
28
22
  def total_on_hand
29
- if @variant.should_track_inventory?
23
+ if variant.should_track_inventory?
30
24
  stock_items.sum(&:count_on_hand)
31
25
  else
32
26
  Float::INFINITY
@@ -48,6 +42,36 @@ module Spree
48
42
  def can_supply?(required)
49
43
  total_on_hand >= required || backorderable?
50
44
  end
45
+
46
+ def positive_stock
47
+ return unless stock_location
48
+
49
+ on_hand = stock_location.count_on_hand(variant)
50
+ on_hand.positive? ? on_hand : 0
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :variant, :stock_location_or_id
56
+
57
+ def stock_location
58
+ @stock_location ||= if stock_location_or_id.is_a?(Spree::StockLocation)
59
+ stock_location_or_id
60
+ else
61
+ Spree::StockLocation.find_by(id: stock_location_or_id)
62
+ end
63
+ end
64
+
65
+ def variant_stock_items
66
+ variant.stock_items.select do |stock_item|
67
+ if stock_location_or_id
68
+ stock_item.stock_location == stock_location_or_id ||
69
+ stock_item.stock_location_id == stock_location_or_id
70
+ else
71
+ stock_item.stock_location.active?
72
+ end
73
+ end
74
+ end
51
75
  end
52
76
  end
53
77
  end
@@ -106,7 +106,7 @@ module Spree
106
106
  def should_touch_variant?
107
107
  # the variant_id changes from nil when a new stock location is added
108
108
  inventory_cache_threshold &&
109
- (saved_change_to_count_on_hand&.any? { |cache| cache < inventory_cache_threshold }) ||
109
+ saved_change_to_count_on_hand&.any? { |cache| cache < inventory_cache_threshold } ||
110
110
  saved_change_to_variant_id?
111
111
  end
112
112
 
@@ -15,6 +15,7 @@ module Spree
15
15
  has_many :stock_movements, through: :stock_items
16
16
  has_many :user_stock_locations, dependent: :delete_all
17
17
  has_many :users, through: :user_stock_locations
18
+ has_many :customer_returns, inverse_of: :stock_location, dependent: :restrict_with_error
18
19
 
19
20
  belongs_to :state, class_name: 'Spree::State', optional: true
20
21
  belongs_to :country, class_name: 'Spree::Country', optional: true
@@ -22,8 +23,8 @@ module Spree
22
23
  has_many :shipping_method_stock_locations, dependent: :destroy
23
24
  has_many :shipping_methods, through: :shipping_method_stock_locations
24
25
 
25
- validates_presence_of :name
26
- validates_uniqueness_of :code, allow_blank: true, case_sensitive: false
26
+ validates :name, presence: true
27
+ validates :code, uniqueness: { allow_blank: true, case_sensitive: false }
27
28
 
28
29
  scope :active, -> { where(active: true) }
29
30
  scope :order_default, -> { order(default: :desc, position: :asc) }
@@ -27,6 +27,12 @@ module Spree
27
27
  before_save :ensure_default_exists_and_is_unique
28
28
  before_destroy :validate_not_default
29
29
 
30
+ enum :reverse_charge_status, {
31
+ disabled: 0,
32
+ enabled: 1,
33
+ not_validated: 2
34
+ }, prefix: true
35
+
30
36
  def available_locales
31
37
  locales = super()
32
38
  if locales
@@ -18,9 +18,9 @@ class Spree::StoreCredit < Spree::PaymentSource
18
18
  belongs_to :credit_type, class_name: 'Spree::StoreCreditType', foreign_key: 'type_id', optional: true
19
19
  has_many :store_credit_events
20
20
 
21
- validates_presence_of :user_id, :category_id, :type_id, :created_by_id, :currency
22
- validates_numericality_of :amount, { greater_than: 0 }
23
- validates_numericality_of :amount_used, { greater_than_or_equal_to: 0 }
21
+ validates :user_id, :category_id, :type_id, :created_by_id, :currency, presence: true
22
+ validates :amount, numericality: { greater_than: 0 }
23
+ validates :amount_used, numericality: { greater_than_or_equal_to: 0 }
24
24
  validate :amount_used_less_than_or_equal_to_amount
25
25
  validate :amount_authorized_less_than_or_equal_to_amount
26
26
 
@@ -9,7 +9,7 @@ module Spree
9
9
  belongs_to :originator, polymorphic: true, optional: true
10
10
  belongs_to :store_credit_reason, class_name: 'Spree::StoreCreditReason', inverse_of: :store_credit_events, optional: true
11
11
 
12
- validates_presence_of :store_credit_reason, if: :action_requires_reason?
12
+ validates :store_credit_reason, presence: { if: :action_requires_reason? }
13
13
 
14
14
  NON_EXPOSED_ACTIONS = [Spree::StoreCredit::ELIGIBLE_ACTION, Spree::StoreCredit::AUTHORIZE_ACTION]
15
15
 
@@ -11,7 +11,7 @@ module Spree
11
11
  end
12
12
 
13
13
  validates :name, presence: true
14
- validates_uniqueness_of :name, case_sensitive: true, unless: :deleted_at
14
+ validates :name, uniqueness: { case_sensitive: true, unless: :deleted_at }
15
15
 
16
16
  has_many :tax_rate_tax_categories,
17
17
  class_name: 'Spree::TaxRateTaxCategory',
@@ -13,9 +13,6 @@ module Spree
13
13
  has_many :classifications, -> { order(:position) }, dependent: :delete_all, inverse_of: :taxon
14
14
  has_many :products, through: :classifications
15
15
 
16
- has_many :promotion_rule_taxons
17
- has_many :promotion_rules, through: :promotion_rule_taxons
18
-
19
16
  before_save :set_permalink
20
17
  after_update :update_child_permalinks, if: :saved_change_to_permalink?
21
18
 
@@ -7,8 +7,8 @@ module Spree
7
7
  validates :name, presence: true
8
8
  validates :name, uniqueness: true
9
9
 
10
- has_many :taxons, inverse_of: :taxonomy
11
- has_one :root, -> { where parent_id: nil }, class_name: "Spree::Taxon", dependent: :destroy
10
+ has_many :taxons, inverse_of: :taxonomy, dependent: false
11
+ has_one :root, -> { where parent_id: nil }, class_name: "Spree::Taxon", dependent: :destroy, inverse_of: false
12
12
 
13
13
  after_save :set_name
14
14
 
@@ -2,11 +2,11 @@
2
2
 
3
3
  module Spree
4
4
  class UserAddress < Spree::Base
5
- belongs_to :user, class_name: UserClassHandle.new, foreign_key: "user_id", optional: true
6
- belongs_to :address, class_name: "Spree::Address", optional: true
5
+ belongs_to :user, class_name: UserClassHandle.new, foreign_key: "user_id", inverse_of: :user_addresses
6
+ belongs_to :address, class_name: "Spree::Address"
7
7
 
8
- validates_uniqueness_of :address_id, scope: :user_id
9
- validates_uniqueness_of :user_id, conditions: -> { default_shipping }, message: :default_address_exists, if: :default?
8
+ validates :address_id, uniqueness: { scope: :user_id }
9
+ validates :user_id, uniqueness: { conditions: -> { default_shipping }, message: :default_address_exists, if: :default? }
10
10
 
11
11
  scope :with_address_values, ->(address_attributes) do
12
12
  joins(:address).merge(
@@ -74,7 +74,7 @@ module Spree
74
74
 
75
75
  validates :cost_price, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
76
76
  validates :price, numericality: { greater_than_or_equal_to: 0, allow_nil: true }
77
- validates_uniqueness_of :sku, allow_blank: true, case_sensitive: true, conditions: -> { where(deleted_at: nil) }, if: :enforce_unique_sku?
77
+ validates :sku, uniqueness: { allow_blank: true, case_sensitive: true, conditions: -> { where(deleted_at: nil) }, if: :enforce_unique_sku? }
78
78
 
79
79
  after_create :create_stock_items
80
80
  after_create :set_master_out_of_stock, unless: :is_master?
@@ -13,7 +13,7 @@
13
13
  # targeted by the rule, the properties will automatically apply to the variant.
14
14
  module Spree
15
15
  class VariantPropertyRule < Spree::Base
16
- belongs_to :product, touch: true, optional: true
16
+ belongs_to :product, touch: true
17
17
 
18
18
  has_many :values, class_name: 'Spree::VariantPropertyRuleValue', dependent: :destroy
19
19
  has_many :properties, through: :values
@@ -5,6 +5,6 @@ module Spree
5
5
  belongs_to :option_value, optional: true
6
6
  belongs_to :variant_property_rule, touch: true, optional: true
7
7
 
8
- validates_uniqueness_of :option_value_id, scope: :variant_property_rule_id
8
+ validates :option_value_id, uniqueness: { scope: :variant_property_rule_id }
9
9
  end
10
10
  end
@@ -5,8 +5,8 @@ module Spree
5
5
  belongs_to :user, class_name: Spree::UserClassHandle.new, foreign_key: 'user_id', inverse_of: :wallet_payment_sources, optional: true
6
6
  belongs_to :payment_source, polymorphic: true, inverse_of: :wallet_payment_sources, optional: true
7
7
 
8
- validates_presence_of :user
9
- validates_presence_of :payment_source
8
+ validates :user, presence: true
9
+ validates :payment_source, presence: true
10
10
  validates :user_id, uniqueness: {
11
11
  scope: [:payment_source_type, :payment_source_id],
12
12
  message: :payment_source_already_exists
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ # Mailing after {Spree::Carton} is created.
5
+ class CartonShippedMailerSubscriber
6
+ include Omnes::Subscriber
7
+
8
+ handle :carton_shipped,
9
+ with: :send_carton_shipped_emails,
10
+ id: :spree_carton_mailer_send_carton_shipped_email
11
+
12
+ # Sends carton shipped emails to users.
13
+ #
14
+ # @param event [Omnes::UnstructuredEvent]
15
+ def send_carton_shipped_emails(event)
16
+ carton = event[:carton]
17
+
18
+ return if carton.suppress_email
19
+
20
+ # Do not send emails for unfulfillable cartons (i.e. for digital goods).
21
+ return unless carton.stock_location.fulfillable?
22
+
23
+ carton.orders.each do |order|
24
+ Spree::Config.carton_shipped_email_class
25
+ .shipped_email(order:, carton:)
26
+ .deliver_later
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ # Mailing after {Spree::Order} is cancelled.
5
+ class OrderCancelMailerSubscriber
6
+ include Omnes::Subscriber
7
+
8
+ handle :order_canceled,
9
+ with: :send_cancel_email,
10
+ id: :spree_order_mailer_send_cancel_email
11
+
12
+ # Sends cancellation email to the user.
13
+ #
14
+ # @param event [Omnes::UnstructuredEvent]
15
+ def send_cancel_email(event)
16
+ order = event[:order]
17
+
18
+ Spree::Config.order_mailer_class.cancel_email(order).deliver_later
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ # Mailing after {Spree::Order} is confirmed.
5
+ class OrderConfirmationMailerSubscriber
6
+ include Omnes::Subscriber
7
+
8
+ handle :order_finalized,
9
+ with: :send_confirmation_email,
10
+ id: :spree_order_mailer_send_confirmation_email
11
+
12
+ # Sends confirmation email to the user.
13
+ #
14
+ # @param event [Omnes::UnstructuredEvent]
15
+ def send_confirmation_email(event)
16
+ order = event[:order]
17
+ unless order.confirmation_delivered?
18
+ Spree::Config.order_mailer_class.confirm_email(order).deliver_later
19
+ order.update_column(:confirmation_delivered, true)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Spree
4
+ # Mailing after inventory units have been cancelled from a {Spree::Order}
5
+ class OrderInventoryCancellationMailerSubscriber
6
+ include Omnes::Subscriber
7
+
8
+ handle :order_short_shipped,
9
+ with: :send_inventory_cancellation_email,
10
+ id: :spree_order_mailer_send_inventory_cancellation_email
11
+
12
+ # Sends inventory cancellation email to the user.
13
+ #
14
+ # @param event [Omnes::UnstructuredEvent]
15
+ def send_inventory_cancellation_email(event)
16
+ return unless Spree::OrderCancellations.send_cancellation_mailer
17
+
18
+ order = event[:order]
19
+ inventory_units = event[:inventory_units]
20
+
21
+ Spree::Config
22
+ .order_mailer_class
23
+ .inventory_cancellation_email(order, inventory_units.to_a)
24
+ .deliver_later
25
+ end
26
+ end
27
+ end