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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 924090a77efc768ede5475d819872ed30b02cb7990e87d3fdd5877b58b8e7fac
4
- data.tar.gz: 1eeee8182a6f5ade835a347711a9c74b3b29e201bfe93c7fef7c710e6b62b6a1
3
+ metadata.gz: b1b866f15d9df0394383e7f634858ee99f21ed214e8fae6928af514a6421ddbc
4
+ data.tar.gz: d2f5c689bf6debe86f5b9be575eea7640e496606f8933b85a1d2e7a2cb90123c
5
5
  SHA512:
6
- metadata.gz: 8b6f4995130898a6874b22b97d390b89fa51be305b0d2fcd223c2ee8db2263a30ff599e5109e6eb2e58da33ef2c9014a7f8c980764914d30325997bddc36759d
7
- data.tar.gz: eaba68996368d1569f7e76c348c2126eeedc0fb29cf0378056d6f9e340e0937004661f7a0994284db40e38e5234174d34552700b2a57806fd713fc436dae1938
6
+ metadata.gz: df2acde7ab4aa4b01e3d5b187b6c47a911714c868237a1ba494095a43bdd07bac5082e2080d4aabc563576d502fecda0d28a94b9cef53db8e6c1e1c89a0cbc97
7
+ data.tar.gz: c183f6200f52b585a4ae391bd8b412c6a4f58e987fcc6ceb3cd1cf6835b92b1019f6077e4a13c75dbe6a4c0dd64c81fe7c4d8d3deeed2b8fbadc67ed74148cb5
@@ -68,7 +68,7 @@ module Spree
68
68
  # @return [String] a cache invalidation key for products
69
69
  def cache_key_for_products
70
70
  count = @products.count
71
- max_updated_at = (@products.maximum(:updated_at) || Date.today).to_s(:number)
71
+ max_updated_at = Spree::RailsCompatibility.to_fs((@products.maximum(:updated_at) || Date.today), :number)
72
72
  "#{I18n.locale}/#{current_pricing_options.cache_key}/spree/products/all-#{params[:page]}-#{max_updated_at}-#{count}"
73
73
  end
74
74
  end
@@ -4,14 +4,16 @@ require 'mini_magick'
4
4
 
5
5
  module Spree
6
6
  module ActiveStorageAdapter
7
- # Decorares AtiveStorage attachment to add methods exptected by Solidus'
7
+ # Decorates ActiveStorage attachment to add methods expected by Solidus'
8
8
  # Paperclip-oriented attachment support.
9
9
  class Attachment
10
10
  delegate_missing_to :@attachment
11
11
 
12
+ attr_reader :attachment
13
+
12
14
  def initialize(attachment, styles: {})
13
15
  @attachment = attachment
14
- @styles = normalize_styles(styles)
16
+ @transformations = styles_to_transformations(styles)
15
17
  end
16
18
 
17
19
  def exists?
@@ -27,11 +29,13 @@ module Spree
27
29
  end
28
30
 
29
31
  def variant(style = nil)
30
- size = style_to_size(style)
31
- @attachment.variant(
32
- resize_to_limit: size,
33
- strip: true
34
- ).processed
32
+ transformation = @transformations[style] || default_transformation(width, height)
33
+
34
+ @attachment.variant({
35
+ saver: {
36
+ strip: true
37
+ }
38
+ }.merge(transformation)).processed
35
39
  end
36
40
 
37
41
  def height
@@ -57,12 +61,23 @@ module Spree
57
61
  @attachment.metadata
58
62
  end
59
63
 
60
- def normalize_styles(styles)
61
- styles.transform_values { |v| v.split('x') }
64
+ def styles_to_transformations(styles)
65
+ styles.transform_values(&method(:imagemagick_to_image_processing_definition))
66
+ end
67
+
68
+ def imagemagick_to_image_processing_definition(definition)
69
+ width_height = definition.split('x').map(&:to_i)
70
+
71
+ case definition[-1].to_sym
72
+ when :^
73
+ { resize_to_fill: width_height }
74
+ else
75
+ default_transformation(*width_height)
76
+ end
62
77
  end
63
78
 
64
- def style_to_size(style)
65
- @styles.fetch(style&.to_sym) { [width, height] }
79
+ def default_transformation(width, height)
80
+ { resize_to_limit: [width, height] }
66
81
  end
67
82
  end
68
83
  end
@@ -10,7 +10,7 @@ module Spree
10
10
  included do
11
11
  next if Rails.gem_version >= Gem::Version.new('6.1.0.alpha')
12
12
 
13
- abort <<~MESSAGE
13
+ raise <<~MESSAGE
14
14
  Configuration Error: Solidus ActiveStorage attachment adpater requires Rails >= 6.1.0.
15
15
 
16
16
  Spree::Config.image_attachment_module preference is set to #{Spree::Config.image_attachment_module}
@@ -37,6 +37,9 @@ module Spree
37
37
 
38
38
  has_one :default_user_ship_address, ->{ default_shipping }, class_name: 'Spree::UserAddress', foreign_key: 'user_id'
39
39
  has_one :ship_address, through: :default_user_ship_address, source: :address
40
+
41
+ accepts_nested_attributes_for :ship_address
42
+ accepts_nested_attributes_for :bill_address
40
43
  end
41
44
 
42
45
  # saves address in address book
@@ -104,12 +107,19 @@ module Spree
104
107
  return new_address unless new_address.valid?
105
108
 
106
109
  first_one = user_addresses.empty?
110
+ user_address = prepare_user_address(new_address)
107
111
 
108
112
  if address_attributes[:id].present? && new_address.id != address_attributes[:id]
113
+ if ship_address&.id == address_attributes[:id].to_i
114
+ user_addresses.mark_default(user_address, address_type: :shipping)
115
+ end
116
+
117
+ if bill_address&.id == address_attributes[:id].to_i
118
+ user_addresses.mark_default(user_address, address_type: :billing)
119
+ end
109
120
  remove_from_address_book(address_attributes[:id])
110
121
  end
111
122
 
112
- user_address = prepare_user_address(new_address)
113
123
  user_addresses.mark_default(user_address, address_type: address_type) if default || first_one
114
124
 
115
125
  if persisted?
@@ -18,7 +18,7 @@ module Spree
18
18
  has_many :stock_locations, through: :user_stock_locations
19
19
 
20
20
  has_many :spree_orders, foreign_key: "user_id", class_name: "Spree::Order"
21
- has_many :orders, foreign_key: "user_id", class_name: "Spree::Order", dependent: :restrict_with_exception
21
+ has_many :orders, foreign_key: "user_id", class_name: "Spree::Order"
22
22
 
23
23
  has_many :store_credits, -> { includes(:credit_type) }, foreign_key: "user_id", class_name: "Spree::StoreCredit"
24
24
  has_many :store_credit_events, through: :store_credits
@@ -27,6 +27,7 @@ module Spree
27
27
  has_many :wallet_payment_sources, foreign_key: 'user_id', class_name: 'Spree::WalletPaymentSource', inverse_of: :user
28
28
 
29
29
  after_create :auto_generate_spree_api_key
30
+ before_destroy :check_for_deletion
30
31
 
31
32
  include Spree::RansackableAttributes unless included_modules.include?(Spree::RansackableAttributes)
32
33
 
@@ -76,5 +77,23 @@ module Spree
76
77
  currency: currency,
77
78
  )
78
79
  end
80
+
81
+ # Restrict to delete users with existing orders
82
+ #
83
+ # Override this in your user model class to add another logic.
84
+ #
85
+ # Ie. to allow to delete users with incomplete orders add:
86
+ #
87
+ # orders.complete.none?
88
+ #
89
+ def can_be_deleted?
90
+ orders.none?
91
+ end
92
+
93
+ private
94
+
95
+ def check_for_deletion
96
+ raise ActiveRecord::DeleteRestrictionError unless can_be_deleted?
97
+ end
79
98
  end
80
99
  end
@@ -62,6 +62,7 @@ module Spree
62
62
  .distinct
63
63
  exclude_canceled ? result.merge(Spree::Order.not_canceled) : result
64
64
  end
65
+ deprecate :in_completed_orders, "Please don't use this and rather go through Spree::Promotion#discounted_orders"
65
66
 
66
67
  def finalize!
67
68
  update!(finalized: true)
@@ -42,7 +42,7 @@ class Spree::Carton < Spree::Base
42
42
  end
43
43
 
44
44
  def display_shipped_at
45
- shipped_at.to_s(:rfc822)
45
+ Spree::RailsCompatibility.to_fs(shipped_at, :rfc822)
46
46
  end
47
47
 
48
48
  def manifest
@@ -2,10 +2,83 @@
2
2
 
3
3
  module Spree
4
4
  class LogEntry < Spree::Base
5
+ # Classes used in core that can be present in serialized details
6
+ #
7
+ # Users can add their own classes in
8
+ # `Spree::Config#log_entry_permitted_classes`.
9
+ #
10
+ # @see Spree::AppConfiguration#log_entry_permitted_classes
11
+ CORE_PERMITTED_CLASSES = [
12
+ ActiveMerchant::Billing::Response,
13
+ ActiveSupport::TimeWithZone,
14
+ Time,
15
+ ActiveSupport::TimeZone
16
+ ].freeze
17
+
18
+ # Raised when a disallowed class is tried to be loaded
19
+ class DisallowedClass < RuntimeError
20
+ attr_reader :psych_exception
21
+
22
+ def initialize(psych_exception:)
23
+ @psych_exception = psych_exception
24
+ super(default_message)
25
+ end
26
+
27
+ private
28
+
29
+ def default_message
30
+ <<~MSG
31
+ #{psych_exception.message}
32
+
33
+ You can specify custom classes to be loaded in config/initializers/spree.rb. E.g:
34
+
35
+ Spree.config do |config|
36
+ config.log_entry_permitted_classes = ['MyClass']
37
+ end
38
+ MSG
39
+ end
40
+ end
41
+
42
+ # Raised when YAML contains aliases and they're not enabled
43
+ class BadAlias < RuntimeError
44
+ attr_reader :psych_exception
45
+
46
+ def initialize(psych_exception:)
47
+ @psych_exception = psych_exception
48
+ super(default_message)
49
+ end
50
+
51
+ private
52
+
53
+ def default_message
54
+ <<~MSG
55
+ #{psych_exception.message}
56
+
57
+ You can explicitly enable aliases in config/initializers/spree.rb. E.g:
58
+
59
+ Spree.config do |config|
60
+ config.log_entry_allow_aliases = true
61
+ end
62
+ MSG
63
+ end
64
+ end
65
+
66
+ def self.permitted_classes
67
+ CORE_PERMITTED_CLASSES + Spree::Config.log_entry_permitted_classes.map(&:constantize)
68
+ end
69
+
5
70
  belongs_to :source, polymorphic: true, optional: true
6
71
 
7
72
  def parsed_details
8
- @details ||= YAML.load(details)
73
+ @details ||= YAML.safe_load(
74
+ details,
75
+ permitted_classes: self.class.permitted_classes,
76
+ aliases: Spree::Config.log_entry_allow_aliases
77
+ )
78
+ rescue Psych::DisallowedClass => e
79
+ raise DisallowedClass.new(psych_exception: e)
80
+ rescue Psych::BadAlias => e
81
+ raise BadAlias.new(psych_exception: e)
9
82
  end
10
83
  end
11
84
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Spree
4
4
  class OptionValue < Spree::Base
5
+ # TODO: Remove optional on Solidus v4.0. Don't forget adding a migration to
6
+ # enforce at the database layer
5
7
  belongs_to :option_type, class_name: 'Spree::OptionType', inverse_of: :option_values, optional: true
6
8
  acts_as_list scope: :option_type
7
9
 
@@ -13,7 +15,14 @@ module Spree
13
15
 
14
16
  after_save :touch, if: :saved_changes?
15
17
  after_touch :touch_all_variants
18
+ after_save do
19
+ Spree::Deprecation.warn <<~MSG if option_type.nil?
20
+ Having an option_value with no associated option_type will be deprecated
21
+ on Solidus v4.0
22
+ MSG
23
+ end
16
24
 
25
+ # TODO: Remove allow_nil once option_type is required on Solidus v4.0
17
26
  delegate :name, :presentation, to: :option_type, prefix: :option_type, allow_nil: true
18
27
 
19
28
  self.whitelisted_ransackable_attributes = %w[name presentation]
@@ -38,9 +38,21 @@ module Spree
38
38
  class CannotRebuildShipments < StandardError; end
39
39
 
40
40
  extend Spree::DisplayMoney
41
- money_methods :outstanding_balance, :item_total, :adjustment_total,
42
- :included_tax_total, :additional_tax_total, :tax_total,
43
- :shipment_total, :total, :order_total_after_store_credit, :total_available_store_credit
41
+ money_methods(
42
+ :outstanding_balance,
43
+ :item_total,
44
+ :adjustment_total,
45
+ :included_tax_total,
46
+ :additional_tax_total,
47
+ :tax_total,
48
+ :shipment_total,
49
+ :total,
50
+ :order_total_after_store_credit,
51
+ :total_available_store_credit,
52
+ :item_total_before_tax,
53
+ :shipment_total_before_tax,
54
+ :item_total_excluding_vat
55
+ )
44
56
  alias :display_ship_total :display_shipment_total
45
57
 
46
58
  checkout_flow do
@@ -102,7 +114,7 @@ module Spree
102
114
  # Returns
103
115
  has_many :return_authorizations, dependent: :destroy, inverse_of: :order
104
116
  has_many :return_items, through: :inventory_units
105
- has_many :customer_returns, through: :return_items
117
+ has_many :customer_returns, -> { distinct }, through: :return_items
106
118
  has_many :reimbursements, inverse_of: :order
107
119
  has_many :refunds, through: :payments
108
120
 
@@ -195,6 +207,10 @@ module Spree
195
207
  line_items.to_a.sum(&:total_before_tax)
196
208
  end
197
209
 
210
+ def shipment_total_before_tax
211
+ shipments.to_a.sum(&:total_before_tax)
212
+ end
213
+
198
214
  # Sum of all line item amounts pre-tax
199
215
  def item_total_excluding_vat
200
216
  line_items.to_a.sum(&:total_excluding_vat)
@@ -239,7 +255,7 @@ module Spree
239
255
  ship_address
240
256
  else
241
257
  bill_address
242
- end || store.default_cart_tax_location
258
+ end || store&.default_cart_tax_location
243
259
  end
244
260
 
245
261
  def updater
@@ -379,25 +395,16 @@ module Spree
379
395
  Spree::CreditCard.where(id: credit_card_ids)
380
396
  end
381
397
 
382
- # Finalizes an in progress order after checkout is complete.
383
- # Called after transition to complete state when payments will have been processed
398
+ # TODO: Remove on Solidus 4.0
399
+ # @api private
384
400
  def finalize!
385
- # lock all adjustments (coupon promotions, etc.)
386
- all_adjustments.each(&:finalize!)
387
-
388
- # update payment and shipment(s) states, and save
389
- updater.update_payment_state
390
- shipments.each do |shipment|
391
- shipment.update_state
392
- shipment.finalize!
393
- end
394
-
395
- updater.update_shipment_state
396
- save!
397
-
398
- touch :completed_at
399
-
400
- Spree::Event.fire 'order_finalized', order: self
401
+ Spree::Deprecation.warn <<~MSG
402
+ Calling `Spree::Order#finalize!` is discouraged. Instead, use
403
+ `Spree::Order#complete!`, which goes through all the needed safety
404
+ checks before finalizing an order. This method will be removed
405
+ altogether on Solidus 4.0.
406
+ MSG
407
+ finalize
401
408
  end
402
409
 
403
410
  def fulfill!
@@ -495,7 +502,8 @@ module Spree
495
502
  end
496
503
 
497
504
  def create_shipments_for_line_item(line_item)
498
- units = Spree::Stock::InventoryUnitBuilder.new(self).missing_units_for_line_item(line_item)
505
+ units = Spree::Config.stock.inventory_unit_builder_class.new(self).missing_units_for_line_item(line_item)
506
+
499
507
  Spree::Config.stock.coordinator_class.new(self, units).shipments.each do |shipment|
500
508
  shipments << shipment
501
509
  end
@@ -526,7 +534,7 @@ module Spree
526
534
  state: 'cart',
527
535
  updated_at: Time.current
528
536
  )
529
- next! if !line_items.empty?
537
+ self.next
530
538
  end
531
539
 
532
540
  def refresh_shipment_rates
@@ -741,6 +749,27 @@ module Spree
741
749
  end
742
750
  end
743
751
 
752
+ # Finalizes an in progress order after checkout is complete.
753
+ # Called after transition to complete state when payments will have been processed
754
+ def finalize
755
+ # lock all adjustments (coupon promotions, etc.)
756
+ all_adjustments.each(&:finalize!)
757
+
758
+ # update payment and shipment(s) states, and save
759
+ updater.update_payment_state
760
+ shipments.each do |shipment|
761
+ shipment.update_state
762
+ shipment.finalize!
763
+ end
764
+
765
+ updater.update_shipment_state
766
+ save!
767
+
768
+ touch :completed_at
769
+
770
+ Spree::Bus.publish :order_finalized, order: self
771
+ end
772
+
744
773
  def associate_store
745
774
  self.store ||= Spree::Store.default
746
775
  end
@@ -761,9 +790,12 @@ module Spree
761
790
 
762
791
  def ensure_inventory_units
763
792
  if has_checkout_step?("delivery")
764
- inventory_validator = Spree::Stock::InventoryValidator.new
793
+ inventory_validator = Spree::Config.stock.inventory_validator_class.new
794
+
795
+ errors = line_items.map { |line_item|
796
+ inventory_validator.validate(line_item)
797
+ }.compact
765
798
 
766
- errors = line_items.map { |line_item| inventory_validator.validate(line_item) }.compact
767
799
  raise InsufficientStock if errors.any?
768
800
  end
769
801
  end
@@ -781,8 +813,15 @@ module Spree
781
813
  end
782
814
 
783
815
  def validate_line_item_availability
784
- availability_validator = Spree::Stock::AvailabilityValidator.new
785
- raise InsufficientStock unless line_items.all? { |line_item| availability_validator.validate(line_item) }
816
+ availability_validator = Spree::Config.stock.availability_validator_class.new
817
+
818
+ # NOTE: This code assumes that the availability validator will return
819
+ # true for success and false for failure. This is not normally the
820
+ # behaviour of validators, as the framework only cares about the
821
+ # population of the errors, not the return value of the validate method.
822
+ raise InsufficientStock unless line_items.all? { |line_item|
823
+ availability_validator.validate(line_item)
824
+ }
786
825
  end
787
826
 
788
827
  def ensure_line_items_present
@@ -42,8 +42,8 @@ module Spree
42
42
  # If we do not update first, then the item total will be wrong and ItemTotal
43
43
  # promotion rules would not be triggered.
44
44
  reload_totals
45
- PromotionHandler::Cart.new(order).activate
46
45
  order.ensure_updated_shipments
46
+ PromotionHandler::Cart.new(order).activate
47
47
  end
48
48
  reload_totals
49
49
  true
@@ -89,6 +89,7 @@ module Spree
89
89
  line_item ||= order.line_items.new(
90
90
  quantity: 0,
91
91
  variant: variant,
92
+ adjustments: [],
92
93
  )
93
94
 
94
95
  line_item.quantity += quantity.to_i
@@ -62,7 +62,7 @@ module Spree
62
62
  potential_shipments.detect do |shipment|
63
63
  shipment.include?(variant)
64
64
  end || potential_shipments.detect do |shipment|
65
- stock_item = shipment.stock_location.stock_item(variant.id)
65
+ stock_item = variant.stock_items.detect { |stock_item| stock_item.stock_location == shipment.stock_location }
66
66
  if stock_item
67
67
  stock_item.backorderable? || stock_item.count_on_hand >= quantity
68
68
  end
@@ -115,8 +115,8 @@ module Spree
115
115
  current_line_item.quantity += other_order_line_item.quantity
116
116
  handle_error(current_line_item) unless current_line_item.save
117
117
  else
118
- order.line_items << other_order_line_item
119
- handle_error(other_order_line_item) unless other_order_line_item.save
118
+ new_line_item = order.line_items.build(other_order_line_item.attributes.except("id"))
119
+ handle_error(new_line_item) unless new_line_item.save
120
120
  end
121
121
  end
122
122
 
@@ -62,18 +62,15 @@ class Spree::OrderShipping
62
62
  end
63
63
 
64
64
  inventory_units.map(&:shipment).uniq.each do |shipment|
65
- # Temporarily propagate the tracking number to the shipment as well
66
- # TODO: Remove tracking numbers from shipments.
67
- shipment.update!(tracking: tracking_number)
68
-
69
- next unless shipment.inventory_units.reload.all? { |iu| iu.shipped? || iu.canceled? }
70
- # TODO: make OrderShipping#ship_shipment call Shipment#ship! rather than
71
- # having Shipment#ship! call OrderShipping#ship_shipment. We only really
72
- # need this `update_columns` for the specs, until we make that change.
73
- shipment.update_columns(state: 'shipped', shipped_at: Time.current)
65
+ if shipment.inventory_units.reload.all? { |iu| iu.shipped? || iu.canceled? }
66
+ shipment.update!(state: "shipped", shipped_at: Time.current, tracking: tracking_number)
67
+ else
68
+ shipment.update!(tracking: tracking_number)
69
+ end
74
70
  end
75
71
 
76
72
  send_shipment_emails(carton) if stock_location.fulfillable? && !suppress_mailer # e.g. digital gift cards that aren't actually shipped
73
+ @order.shipments.reload
77
74
  @order.recalculate
78
75
 
79
76
  carton
@@ -17,13 +17,15 @@ module Spree
17
17
 
18
18
  # Apply taxes to the order.
19
19
  #
20
- # This method will create or update adjustments on all line items and
21
- # shipments in the order to reflect the appropriate taxes passed in. It
22
- # will also remove any now inapplicable tax adjustments.
20
+ # This method will create or update adjustments on the order and all line
21
+ # items and shipments in the order to reflect the appropriate taxes passed
22
+ # in. It will also remove any now inapplicable tax adjustments.
23
23
  #
24
24
  # @param [Spree::Tax::OrderTax] taxes the taxes to apply to the order
25
25
  # @return [void]
26
26
  def apply(taxes)
27
+ update_adjustments(@order, taxes.order_taxes) if taxes.order_taxes
28
+
27
29
  @order.line_items.each do |item|
28
30
  taxed_items = taxes.line_item_taxes.select { |element| element.item_id == item.id }
29
31
  update_adjustments(item, taxed_items)
@@ -70,7 +72,7 @@ module Spree
70
72
 
71
73
  tax_adjustment ||= item.adjustments.new(
72
74
  source: tax_item.tax_rate,
73
- order_id: item.order_id,
75
+ order_id: item.is_a?(Spree::Order) ? item.id : item.order_id,
74
76
  label: tax_item.label,
75
77
  included: tax_item.included_in_price
76
78
  )
@@ -26,7 +26,7 @@ module Spree
26
26
  update_shipments
27
27
  update_shipment_state
28
28
  end
29
- Spree::Event.fire 'order_recalculated', order: order
29
+ Spree::Bus.publish :order_recalculated, order: order
30
30
  persist_totals
31
31
  end
32
32
  end
@@ -157,10 +157,11 @@ module Spree
157
157
  recalculate_adjustments
158
158
 
159
159
  all_items = line_items + shipments
160
+ order_tax_adjustments = adjustments.select(&:eligible?).select(&:tax?)
160
161
 
161
162
  order.adjustment_total = all_items.sum(&:adjustment_total) + adjustments.select(&:eligible?).sum(&:amount)
162
- order.included_tax_total = all_items.sum(&:included_tax_total)
163
- order.additional_tax_total = all_items.sum(&:additional_tax_total)
163
+ order.included_tax_total = all_items.sum(&:included_tax_total) + order_tax_adjustments.select(&:included?).sum(&:amount)
164
+ order.additional_tax_total = all_items.sum(&:additional_tax_total) + order_tax_adjustments.reject(&:included?).sum(&:amount)
164
165
 
165
166
  order.promo_total = all_items.sum(&:promo_total) + adjustments.select(&:eligible?).select(&:promotion?).sum(&:amount)
166
167
 
@@ -13,6 +13,7 @@ module Spree
13
13
  #
14
14
  class PaymentMethod < Spree::Base
15
15
  include Spree::Preferences::Persistable
16
+ class UnsupportedPaymentMethod < StandardError; end
16
17
 
17
18
  preference :server, :string, default: 'test'
18
19
  preference :test_mode, :boolean, default: true
@@ -60,6 +61,16 @@ module Spree
60
61
  def model_name
61
62
  ModelName.new(self, Spree)
62
63
  end
64
+
65
+ def find_sti_class(type_name)
66
+ super(type_name)
67
+ rescue ActiveRecord::SubclassNotFound
68
+ raise UnsupportedPaymentMethod, "Found invalid payment type '#{type_name}'.\n"\
69
+ "This may happen after switching payment service provider, when payment methods "\
70
+ "reference old types that are not supported any more.\n"\
71
+ "If that is the case, consider running 'rake payment_method:deprecate_unsupported_payment_methods' "\
72
+ "to fix the issue."
73
+ end
63
74
  end
64
75
 
65
76
  # Represents the gateway of this payment method
@@ -194,13 +194,31 @@ module Spree
194
194
  group("spree_products.id").joins(:taxons).where(Spree::Taxon.arel_table[:name].eq(name))
195
195
  end
196
196
 
197
- def self.with_variant_sku_cont(sku)
198
- sku_match = "%#{sku}%"
197
+ def self.with_all_variant_sku_cont(sku)
198
+ variant_table = Spree::Variant.arel_table
199
+ subquery = Spree::Variant.with_discarded.where(
200
+ variant_table[:sku].matches("%#{sku}%").and(
201
+ variant_table[:product_id].eq(arel_table[:id])
202
+ )
203
+ )
204
+ where(subquery.arel.exists)
205
+ end
206
+
207
+ def self.with_kept_variant_sku_cont(sku)
199
208
  variant_table = Spree::Variant.arel_table
200
- subquery = Spree::Variant.where(variant_table[:sku].matches(sku_match).and(variant_table[:product_id].eq(arel_table[:id])))
209
+ subquery = Spree::Variant.where(
210
+ variant_table[:sku].matches("%#{sku}%").and(
211
+ variant_table[:product_id].eq(arel_table[:id])
212
+ )
213
+ )
201
214
  where(subquery.arel.exists)
202
215
  end
203
216
 
217
+ def self.with_variant_sku_cont(sku)
218
+ Spree::Deprecation.warn("use .with_kept_variant_sku_cont instead")
219
+ with_kept_variant_sku_cont(sku)
220
+ end
221
+
204
222
  class << self
205
223
  private
206
224
 
@@ -45,6 +45,7 @@ module Spree
45
45
 
46
46
  has_many :variants,
47
47
  -> { where(is_master: false).order(:position) },
48
+ inverse_of: :product,
48
49
  class_name: 'Spree::Variant'
49
50
 
50
51
  has_many :variants_including_master,
@@ -134,7 +135,7 @@ module Spree
134
135
  self.whitelisted_ransackable_attributes = %w[name slug]
135
136
 
136
137
  def self.ransackable_scopes(_auth_object = nil)
137
- %i(with_discarded with_variant_sku_cont)
138
+ %i(with_discarded with_variant_sku_cont with_all_variant_sku_cont with_kept_variant_sku_cont)
138
139
  end
139
140
 
140
141
  # @return [Boolean] true if there are any variants