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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd88c98f5da37d9a89e981aad39979998349f0e2405a4a7095a8f75f4d3839f8
4
- data.tar.gz: 656beea8141300d09b62b4970dccd7691556112597a891a4f0d1821a67914ea3
3
+ metadata.gz: b1b866f15d9df0394383e7f634858ee99f21ed214e8fae6928af514a6421ddbc
4
+ data.tar.gz: d2f5c689bf6debe86f5b9be575eea7640e496606f8933b85a1d2e7a2cb90123c
5
5
  SHA512:
6
- metadata.gz: 44d04ac1151fd4f7f2ac20b7f117e500b76be1d39fd2ad5ef72654dca8749bd9dd42eb9acd402adf519d2769d7099b8d44977ccf0a906089c10315675b22cad1
7
- data.tar.gz: 93ff18fdf9856b60aec7f484bd468db70c67bd9e446e7fbedf383ff88f21f59fb261d0c53b7db1f46c0a8848f133fb032258ae7d57c968d485503abc248fb606
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,13 +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,
32
+ transformation = @transformations[style] || default_transformation(width, height)
33
+
34
+ @attachment.variant({
33
35
  saver: {
34
36
  strip: true
35
37
  }
36
- ).processed
38
+ }.merge(transformation)).processed
37
39
  end
38
40
 
39
41
  def height
@@ -59,12 +61,23 @@ module Spree
59
61
  @attachment.metadata
60
62
  end
61
63
 
62
- def normalize_styles(styles)
63
- styles.transform_values { |v| v.split('x').map(&:to_i) }
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
64
77
  end
65
78
 
66
- def style_to_size(style)
67
- @styles.fetch(style&.to_sym) { [width, height] }
79
+ def default_transformation(width, height)
80
+ { resize_to_limit: [width, height] }
68
81
  end
69
82
  end
70
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}
@@ -16,12 +16,10 @@ module Spree
16
16
 
17
17
  # Returns `#prices` prioritized for being considered as default price
18
18
  #
19
- # @deprecated
20
19
  # @return [ActiveRecord::Relation<Spree::Price>]
21
20
  def currently_valid_prices
22
21
  prices.currently_valid
23
22
  end
24
- deprecate :currently_valid_prices, deprecator: Spree::Deprecation
25
23
 
26
24
  # Returns {#default_price} or builds it from {Spree::Variant.default_price_attributes}
27
25
  #
@@ -35,7 +33,7 @@ module Spree
35
33
  # Select from {#prices} the one to be considered as the default
36
34
  #
37
35
  # This method works with the in-memory association, so non-persisted prices
38
- # are taken into account.
36
+ # are taken into account. Discarded prices are also considered.
39
37
  #
40
38
  # A price is a candidate to be considered as the default when it meets
41
39
  # {Spree::Variant.default_price_attributes} criteria. When more than one candidate is
@@ -46,11 +44,37 @@ module Spree
46
44
  # @return [Spree::Price, nil]
47
45
  # @see Spree::Variant.default_price_attributes
48
46
  def default_price
49
- price_selector.price_for_options(Spree::Config.default_pricing_options)
47
+ prioritized_default(
48
+ prices_meeting_criteria_to_be_default(
49
+ (prices + prices.with_discarded).uniq
50
+ )
51
+ )
50
52
  end
51
53
 
52
54
  def has_default_price?
53
55
  default_price.present? && !default_price.discarded?
54
56
  end
57
+
58
+ private
59
+
60
+ def prices_meeting_criteria_to_be_default(prices)
61
+ criteria = self.class.default_price_attributes.transform_keys(&:to_s)
62
+ prices.select do |price|
63
+ contender = price.attributes.slice(*criteria.keys)
64
+ criteria == contender
65
+ end
66
+ end
67
+
68
+ def prioritized_default(prices)
69
+ prices.min do |prev, succ|
70
+ contender_one, contender_two = [succ, prev].map do |item|
71
+ [
72
+ item.updated_at || Time.zone.now,
73
+ item.id || Float::INFINITY
74
+ ]
75
+ end
76
+ contender_one <=> contender_two
77
+ end
78
+ end
55
79
  end
56
80
  end
@@ -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?
@@ -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,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
 
@@ -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
@@ -55,7 +55,7 @@ module Spree
55
55
 
56
56
  def display_country
57
57
  if country_iso
58
- "#{country_iso} (#{I18n.t(country_iso, scope: [:spree, :country_names])})"
58
+ "#{country_iso} (#{country.name})"
59
59
  else
60
60
  I18n.t(:any_country, scope: [:spree, :admin, :prices])
61
61
  end
@@ -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
 
@@ -135,7 +135,7 @@ module Spree
135
135
  self.whitelisted_ransackable_attributes = %w[name slug]
136
136
 
137
137
  def self.ransackable_scopes(_auth_object = nil)
138
- %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)
139
139
  end
140
140
 
141
141
  # @return [Boolean] true if there are any variants
@@ -15,6 +15,10 @@ module Spree
15
15
  before_destroy :remove_adjustments_from_incomplete_orders
16
16
  before_discard :remove_adjustments_from_incomplete_orders
17
17
 
18
+ def preload_relations
19
+ [:calculator]
20
+ end
21
+
18
22
  # Creates the adjustment related to a promotion for the order passed
19
23
  # through options hash
20
24
  #
@@ -15,6 +15,10 @@ module Spree
15
15
  before_destroy :remove_adjustments_from_incomplete_orders
16
16
  before_discard :remove_adjustments_from_incomplete_orders
17
17
 
18
+ def preload_relations
19
+ [:calculator]
20
+ end
21
+
18
22
  def perform(payload = {})
19
23
  order = payload[:order]
20
24
  promotion = payload[:promotion]
@@ -83,13 +87,8 @@ module Spree
83
87
  end
84
88
 
85
89
  def line_items_to_adjust(promotion, order)
86
- excluded_ids = adjustments.
87
- where(adjustable_id: order.line_items.pluck(:id), adjustable_type: 'Spree::LineItem').
88
- pluck(:adjustable_id).
89
- to_set
90
-
91
90
  order.line_items.select do |line_item|
92
- !excluded_ids.include?(line_item.id) &&
91
+ line_item.adjustments.none? { |adjustment| adjustment.source == self } &&
93
92
  promotion.line_item_actionable?(order, line_item)
94
93
  end
95
94
  end
@@ -12,6 +12,10 @@ module Spree
12
12
  class_name: 'Spree::ProductPromotionRule'
13
13
  has_many :products, class_name: 'Spree::Product', through: :product_promotion_rules
14
14
 
15
+ def preload_relations
16
+ [:products]
17
+ end
18
+
15
19
  MATCH_POLICIES = %w(any all none)
16
20
 
17
21
  validates_inclusion_of :preferred_match_policy, in: MATCH_POLICIES
@@ -31,17 +35,19 @@ module Spree
31
35
  return true if eligible_products.empty?
32
36
 
33
37
  case preferred_match_policy
34
- when 'all'
35
- unless eligible_products.all? { |product| order.products.include?(product) }
38
+ when "all"
39
+ unless eligible_products.all? { |product| order_products(order).include?(product) }
36
40
  eligibility_errors.add(:base, eligibility_error_message(:missing_product), error_code: :missing_product)
37
41
  end
38
- when 'any'
39
- unless order.products.any? { |product| eligible_products.include?(product) }
40
- eligibility_errors.add(:base, eligibility_error_message(:no_applicable_products), error_code: :no_applicable_products)
42
+ when "any"
43
+ unless order_products(order).any? { |product| eligible_products.include?(product) }
44
+ eligibility_errors.add(:base, eligibility_error_message(:no_applicable_products),
45
+ error_code: :no_applicable_products)
41
46
  end
42
- when 'none'
43
- unless order.products.none? { |product| eligible_products.include?(product) }
44
- eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product), error_code: :has_excluded_product)
47
+ when "none"
48
+ unless order_products(order).none? { |product| eligible_products.include?(product) }
49
+ eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product),
50
+ error_code: :has_excluded_product)
45
51
  end
46
52
  else
47
53
  raise "unexpected match policy: #{preferred_match_policy.inspect}"
@@ -68,6 +74,12 @@ module Spree
68
74
  def product_ids_string=(product_ids)
69
75
  self.product_ids = product_ids.to_s.split(',').map(&:strip)
70
76
  end
77
+
78
+ private
79
+
80
+ def order_products(order)
81
+ order.line_items.map(&:variant).map(&:product)
82
+ end
71
83
  end
72
84
  end
73
85
  end