solidus_core 1.3.2 → 1.4.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of solidus_core might be problematic. Click here for more details.

Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +15 -0
  3. data/app/helpers/spree/base_helper.rb +6 -2
  4. data/app/mailers/spree/base_mailer.rb +1 -1
  5. data/app/mailers/spree/carton_mailer.rb +1 -1
  6. data/app/mailers/spree/order_mailer.rb +2 -2
  7. data/app/mailers/spree/reimbursement_mailer.rb +7 -7
  8. data/app/mailers/spree/test_mailer.rb +3 -2
  9. data/app/models/concerns/spree/user_payment_source.rb +1 -1
  10. data/app/models/spree/address.rb +14 -4
  11. data/app/models/spree/adjustment.rb +11 -19
  12. data/app/models/spree/app_configuration.rb +23 -1
  13. data/app/models/spree/base.rb +9 -0
  14. data/app/models/spree/country.rb +2 -2
  15. data/app/models/spree/exchange.rb +1 -1
  16. data/app/models/spree/gateway.rb +1 -1
  17. data/app/models/spree/item_adjustments.rb +7 -0
  18. data/app/models/spree/line_item.rb +1 -13
  19. data/app/models/spree/order/checkout.rb +19 -16
  20. data/app/models/spree/order/payments.rb +0 -2
  21. data/app/models/spree/order.rb +7 -21
  22. data/app/models/spree/order_contents.rb +60 -4
  23. data/app/models/spree/order_merger.rb +2 -4
  24. data/app/models/spree/order_shipping.rb +1 -1
  25. data/app/models/spree/order_update_attributes.rb +0 -2
  26. data/app/models/spree/order_updater.rb +14 -11
  27. data/app/models/spree/payment.rb +2 -3
  28. data/app/models/spree/payment_create.rb +5 -2
  29. data/app/models/spree/payment_method/store_credit.rb +6 -5
  30. data/app/models/spree/product/scopes.rb +2 -1
  31. data/app/models/spree/product.rb +2 -5
  32. data/app/models/spree/promotion/actions/create_item_adjustments.rb +5 -3
  33. data/app/models/spree/promotion/actions/free_shipping.rb +1 -1
  34. data/app/models/spree/promotion/rules/option_value.rb +2 -3
  35. data/app/models/spree/promotion/rules/product.rb +9 -3
  36. data/app/models/spree/promotion/rules/taxon.rb +33 -7
  37. data/app/models/spree/promotion/rules/user.rb +2 -3
  38. data/app/models/spree/promotion.rb +16 -6
  39. data/app/models/spree/promotion_handler/coupon.rb +1 -1
  40. data/app/models/spree/shipment.rb +12 -16
  41. data/app/models/spree/stock/estimator.rb +1 -1
  42. data/app/models/spree/stock/package.rb +0 -1
  43. data/app/models/spree/stock_item.rb +2 -6
  44. data/app/models/spree/store.rb +2 -1
  45. data/app/models/spree/tax_rate.rb +1 -1
  46. data/app/models/spree/wallet/add_payment_sources_to_wallet.rb +29 -0
  47. data/app/models/spree/wallet/default_payment_builder.rb +26 -0
  48. data/app/views/spree/reimbursement_mailer/reimbursement_email.html.erb +46 -0
  49. data/app/views/spree/reimbursement_mailer/reimbursement_email.text.erb +1 -1
  50. data/app/views/spree/shared/_base_mailer_footer.html.erb +1 -20
  51. data/app/views/spree/shared/_base_mailer_header.html.erb +1 -31
  52. data/config/initializers/spree_user.rb +1 -1
  53. data/config/locales/en.yml +58 -34
  54. data/db/migrate/20130414000512_update_name_fields_on_spree_credit_cards.rb +1 -1
  55. data/db/migrate/20140601011216_set_shipment_total_for_users_upgrading.rb +15 -3
  56. data/db/migrate/20160527070401_rename_shipment_address_field.rb +7 -0
  57. data/db/migrate/20160616232103_remove_user_id_from_promotion_rules.rb +11 -0
  58. data/db/migrate/20160718205341_add_payment_id_index_to_spree_refunds.rb +5 -0
  59. data/db/migrate/20160718205859_add_reimbursement_id_index_to_spree_refunds.rb +5 -0
  60. data/lib/generators/spree/dummy/dummy_generator.rb +1 -1
  61. data/lib/generators/spree/dummy/templates/rails/database.yml +3 -1
  62. data/lib/generators/spree/dummy/templates/rails/test.rb +4 -2
  63. data/lib/generators/spree/install/install_generator.rb +6 -16
  64. data/lib/spree/core/controller_helpers/auth.rb +1 -1
  65. data/lib/spree/core/controller_helpers/common.rb +1 -1
  66. data/lib/spree/core/controller_helpers/order.rb +2 -2
  67. data/lib/spree/core/current_store.rb +5 -1
  68. data/lib/spree/core/delegate_belongs_to.rb +1 -1
  69. data/lib/spree/core/routes.rb +6 -32
  70. data/lib/spree/core/search/base.rb +2 -2
  71. data/lib/spree/core/stock_configuration.rb +6 -0
  72. data/lib/spree/core/version.rb +2 -2
  73. data/lib/spree/core.rb +2 -1
  74. data/lib/spree/i18n.rb +7 -0
  75. data/lib/spree/mailer_previews/carton_preview.rb +2 -1
  76. data/lib/spree/mailer_previews/order_preview.rb +8 -3
  77. data/lib/spree/mailer_previews/reimbursement_preview.rb +11 -0
  78. data/lib/spree/migrations.rb +13 -7
  79. data/lib/spree/permitted_attributes.rb +1 -1
  80. data/lib/spree/testing_support/capybara_ext.rb +6 -1
  81. data/lib/spree/testing_support/common_rake.rb +3 -7
  82. data/lib/spree/testing_support/controller_requests.rb +8 -0
  83. data/lib/spree/testing_support/factories/order_factory.rb +30 -8
  84. data/lib/spree/testing_support/factories/payment_factory.rb +1 -1
  85. data/lib/spree/testing_support/factories/product_factory.rb +6 -0
  86. data/lib/spree/testing_support/factories/reimbursement_factory.rb +1 -0
  87. data/lib/tasks/migrations/copy_shipped_shipments_to_cartons.rake +3 -1
  88. data/solidus_core.gemspec +3 -3
  89. data/spec/helpers/base_helper_spec.rb +18 -2
  90. data/spec/lib/i18n_spec.rb +4 -0
  91. data/spec/lib/spree/core/controller_helpers/order_spec.rb +16 -4
  92. data/spec/lib/spree/core/controller_helpers/payment_parameters_spec.rb +75 -59
  93. data/spec/lib/spree/core/delegate_belongs_to_spec.rb +1 -1
  94. data/spec/lib/spree/core/importer/order_spec.rb +4 -2
  95. data/spec/lib/spree/core/stock_configuration_spec.rb +17 -0
  96. data/spec/lib/spree/core/testing_support/factories/order_factory_spec.rb +195 -0
  97. data/spec/lib/spree/core/testing_support/factories/payment_factory_spec.rb +6 -0
  98. data/spec/lib/spree/core/testing_support/factories/reimbursement_factory_spec.rb +6 -0
  99. data/spec/lib/spree/migrations_spec.rb +3 -9
  100. data/spec/lib/tasks/migrations/copy_shipped_shipments_to_cartons_spec.rb +1 -1
  101. data/spec/mailers/carton_mailer_spec.rb +1 -1
  102. data/spec/mailers/order_mailer_spec.rb +2 -2
  103. data/spec/mailers/reimbursement_mailer_spec.rb +1 -1
  104. data/spec/models/spree/address_spec.rb +22 -0
  105. data/spec/models/spree/adjustment_spec.rb +0 -10
  106. data/spec/models/spree/carton_spec.rb +1 -1
  107. data/spec/models/spree/country_spec.rb +9 -1
  108. data/spec/models/spree/gateway/bogus_spec.rb +1 -1
  109. data/spec/models/spree/item_adjustments_spec.rb +33 -0
  110. data/spec/models/spree/line_item_spec.rb +2 -21
  111. data/spec/models/spree/order/checkout_spec.rb +32 -45
  112. data/spec/models/spree/order/payment_spec.rb +1 -16
  113. data/spec/models/spree/order_cancellations_spec.rb +8 -3
  114. data/spec/models/spree/order_contents_spec.rb +48 -0
  115. data/spec/models/spree/order_merger_spec.rb +8 -5
  116. data/spec/models/spree/order_shipping_spec.rb +3 -4
  117. data/spec/models/spree/order_spec.rb +23 -59
  118. data/spec/models/spree/order_update_attributes_spec.rb +1 -5
  119. data/spec/models/spree/order_updater_spec.rb +39 -11
  120. data/spec/models/spree/payment_create_spec.rb +61 -0
  121. data/spec/models/spree/payment_method/store_credit_spec.rb +23 -10
  122. data/spec/models/spree/payment_spec.rb +17 -4
  123. data/spec/models/spree/product_spec.rb +1 -1
  124. data/spec/models/spree/promotion/actions/create_item_adjustments_spec.rb +8 -13
  125. data/spec/models/spree/promotion/rules/product_spec.rb +33 -0
  126. data/spec/models/spree/promotion/rules/taxon_spec.rb +52 -8
  127. data/spec/models/spree/promotion_spec.rb +18 -12
  128. data/spec/models/spree/return_authorization_spec.rb +0 -16
  129. data/spec/models/spree/shipment_spec.rb +57 -36
  130. data/spec/models/spree/stock/coordinator_spec.rb +0 -5
  131. data/spec/models/spree/stock/package_spec.rb +0 -1
  132. data/spec/models/spree/stock_item_spec.rb +35 -145
  133. data/spec/models/spree/store_spec.rb +24 -10
  134. data/spec/models/spree/tax_rate_spec.rb +2 -2
  135. data/spec/models/spree/user_spec.rb +1 -1
  136. data/spec/models/spree/validations/db_maximum_length_validator_spec.rb +20 -12
  137. data/spec/support/concerns/working_factories.rb +4 -0
  138. metadata +30 -10
  139. data/config/routes.rb +0 -1
@@ -6,6 +6,19 @@ module Spree
6
6
  @order = order
7
7
  end
8
8
 
9
+ # Add a line items to the order if there is inventory to do so
10
+ # and populate Promotions
11
+ #
12
+ # @params [Spree::Variant] :variant The variant the line_item should
13
+ # be associated with
14
+ # @params [Integer] :quantity The line_item quantity
15
+ # @param [Hash] :options Options for the adding proccess
16
+ # Valid options:
17
+ # shipment: [Spree::Shipment] LineItem target shipment
18
+ # stock_location_quantities:
19
+ # stock_location_id: The stock location to source from
20
+ #
21
+ # @return [Spree::LineItem]
9
22
  def add(variant, quantity = 1, options = {})
10
23
  line_item = add_to_line_item(variant, quantity, options)
11
24
  after_add_or_remove(line_item, options)
@@ -17,12 +30,26 @@ module Spree
17
30
  end
18
31
 
19
32
  def remove_line_item(line_item, options = {})
20
- line_item.destroy!
33
+ order.line_items.destroy(line_item)
21
34
  after_add_or_remove(line_item, options)
22
35
  end
23
36
 
24
37
  def update_cart(params)
38
+ # We need old_tax_address / new_tax_address because we can't rely on methods
39
+ # offered by ActiveRecord::Dirty to determine if tax_address was updated
40
+ # because if we update the address, a new record will be created
41
+ # by the Address.factory instead of the old record being updated
42
+
43
+ old_tax_address = order.tax_address
44
+
25
45
  if order.update_attributes(params)
46
+
47
+ new_tax_address = order.tax_address
48
+
49
+ if should_recalculate_taxes?(old_tax_address, new_tax_address)
50
+ order.create_tax_charge!
51
+ end
52
+
26
53
  unless order.completed?
27
54
  order.line_items = order.line_items.select { |li| li.quantity > 0 }
28
55
  # Update totals, then check if the order is eligible for any cart promotions.
@@ -57,6 +84,36 @@ module Spree
57
84
 
58
85
  private
59
86
 
87
+ def should_recalculate_taxes?(old_address, new_address)
88
+ # Related to Solidus issue #894
89
+ # This is needed because if you update the shipping_address
90
+ # from the backend on an order that completed checkout,
91
+ # Taxes were not being recalculated if the Order tax zone
92
+ # was updated
93
+ #
94
+ # Possible cases:
95
+ #
96
+ # Case 1:
97
+ #
98
+ # If old_address is a TaxLocation it means that the order has not passed
99
+ # the address checkout state so taxes will be computed by the Order
100
+ # state machine, so we do not calculate taxes here.
101
+ #
102
+ # Case 2 :
103
+ # If new_address is a TaxLocation, but old_address is not, it means that
104
+ # an order has somehow lost his TaxAddress. Since it's not supposed to happen,
105
+ # we do not compute taxes.
106
+ #
107
+ # Case 3
108
+ # Both old_address and new_address are Spree::Address so the order
109
+ # has completed the checkout or that a registered user has updated his
110
+ # default addresses. We need to recalculate the taxes.
111
+
112
+ return if old_address.is_a?(Spree::Tax::TaxLocation) || new_address.is_a?(Spree::Tax::TaxLocation)
113
+
114
+ old_address.try!(:taxation_attributes) != new_address.try!(:taxation_attributes)
115
+ end
116
+
60
117
  def after_add_or_remove(line_item, options = {})
61
118
  reload_totals
62
119
  shipment = options[:shipment]
@@ -73,7 +130,6 @@ module Spree
73
130
 
74
131
  def reload_totals
75
132
  order_updater.update
76
- order.reload
77
133
  end
78
134
 
79
135
  def add_to_line_item(variant, quantity, options = {})
@@ -86,7 +142,7 @@ module Spree
86
142
  )
87
143
 
88
144
  line_item.quantity += quantity.to_i
89
- line_item.options = ActionController::Parameters.new(options).permit(PermittedAttributes.line_item_attributes)
145
+ line_item.options = ActionController::Parameters.new(options).permit(PermittedAttributes.line_item_attributes).to_h
90
146
 
91
147
  if line_item.new_record?
92
148
  create_order_stock_locations(line_item, options[:stock_location_quantities])
@@ -103,7 +159,7 @@ module Spree
103
159
  line_item.target_shipment = options[:shipment]
104
160
 
105
161
  if line_item.quantity == 0
106
- line_item.destroy
162
+ order.line_items.destroy(line_item)
107
163
  else
108
164
  line_item.save!
109
165
  end
@@ -111,7 +111,7 @@ module Spree
111
111
  current_line_item.quantity += other_order_line_item.quantity
112
112
  handle_error(current_line_item) unless current_line_item.save
113
113
  else
114
- other_order_line_item.order_id = order.id
114
+ order.line_items << other_order_line_item
115
115
  handle_error(other_order_line_item) unless other_order_line_item.save
116
116
  end
117
117
  end
@@ -135,9 +135,7 @@ module Spree
135
135
  # @api private
136
136
  # @return [void]
137
137
  def persist_merge
138
- updater.update_item_count
139
- updater.update_item_total
140
- updater.persist_totals
138
+ updater.update
141
139
  end
142
140
  end
143
141
  end
@@ -17,7 +17,7 @@ class Spree::OrderShipping
17
17
  ship(
18
18
  inventory_units: shipment.inventory_units.shippable,
19
19
  stock_location: shipment.stock_location,
20
- address: shipment.address,
20
+ address: shipment.order.ship_address,
21
21
  shipping_method: shipment.shipping_method,
22
22
  shipped_at: Time.current,
23
23
  external_number: external_number,
@@ -14,8 +14,6 @@ module Spree
14
14
  # Assign the attributes to the order and save the order
15
15
  # @return true if saved, otherwise false and errors will be set on the order
16
16
  def apply
17
- order.validate_payments_attributes(@payments_attributes)
18
-
19
17
  assign_order_attributes
20
18
  assign_payments_attributes
21
19
 
@@ -31,7 +31,11 @@ module Spree
31
31
  end
32
32
 
33
33
  def recalculate_adjustments
34
- all_adjustments.includes(:adjustable).map(&:adjustable).uniq.each { |adjustable| Spree::ItemAdjustments.new(adjustable).update }
34
+ adjustables = [*line_items, *shipments, order]
35
+
36
+ adjustables.each do |adjustable|
37
+ Spree::ItemAdjustments.new(adjustable).update
38
+ end
35
39
  end
36
40
 
37
41
  # Updates the following Order total values:
@@ -63,7 +67,7 @@ module Spree
63
67
  end
64
68
 
65
69
  def update_shipment_total
66
- order.shipment_total = shipments.sum(:cost)
70
+ order.shipment_total = shipments.to_a.sum(&:cost)
67
71
  update_order_total
68
72
  end
69
73
 
@@ -73,15 +77,14 @@ module Spree
73
77
 
74
78
  def update_adjustment_total
75
79
  recalculate_adjustments
76
- order.adjustment_total = line_items.sum(:adjustment_total) +
77
- shipments.sum(:adjustment_total) +
78
- adjustments.eligible.sum(:amount)
79
- order.included_tax_total = line_items.sum(:included_tax_total) + shipments.sum(:included_tax_total)
80
- order.additional_tax_total = line_items.sum(:additional_tax_total) + shipments.sum(:additional_tax_total)
81
80
 
82
- order.promo_total = line_items.sum(:promo_total) +
83
- shipments.sum(:promo_total) +
84
- adjustments.promotion.eligible.sum(:amount)
81
+ all_items = line_items + shipments
82
+
83
+ order.adjustment_total = all_items.sum(&:adjustment_total) + adjustments.eligible.sum(:amount)
84
+ order.included_tax_total = all_items.sum(&:included_tax_total)
85
+ order.additional_tax_total = all_items.sum(&:additional_tax_total)
86
+
87
+ order.promo_total = all_items.sum(&:promo_total) + adjustments.promotion.eligible.sum(:amount)
85
88
 
86
89
  update_order_total
87
90
  end
@@ -91,7 +94,7 @@ module Spree
91
94
  end
92
95
 
93
96
  def update_item_total
94
- order.item_total = line_items.sum('price * quantity')
97
+ order.item_total = line_items.to_a.sum(&:amount)
95
98
  update_order_total
96
99
  end
97
100
 
@@ -11,7 +11,7 @@ module Spree
11
11
 
12
12
  belongs_to :order, class_name: 'Spree::Order', touch: true, inverse_of: :payments
13
13
  belongs_to :source, polymorphic: true
14
- belongs_to :payment_method, class_name: 'Spree::PaymentMethod', inverse_of: :payments
14
+ belongs_to :payment_method, -> { with_deleted }, class_name: 'Spree::PaymentMethod', inverse_of: :payments
15
15
 
16
16
  has_many :offsets, -> { offset_payment }, class_name: "Spree::Payment", foreign_key: :source_id
17
17
  has_many :log_entries, as: :source
@@ -39,7 +39,6 @@ module Spree
39
39
 
40
40
  validates :amount, numericality: true
41
41
  validates :source, presence: true, if: :source_required?
42
- validates :payment_method, presence: true
43
42
 
44
43
  default_scope -> { order(:created_at) }
45
44
 
@@ -156,7 +155,7 @@ module Spree
156
155
  return unless new_record?
157
156
  return if source_attributes.blank?
158
157
 
159
- ActiveSupport::Deprecation.warn(<<WARN.squish)
158
+ Spree::Deprecation.warn(<<WARN.squish)
160
159
  Building payment sources by assigning source_attributes on payments is
161
160
  deprecated. Instead use either the PaymentCreate class or the
162
161
  OrderUpdateAttributes class.
@@ -2,7 +2,7 @@ module Spree
2
2
  # Service object for creating new payments on an Order
3
3
  class PaymentCreate
4
4
  # @param order [Order] The order for the new payment
5
- # @param attributes [Hash] attributes which are assigned to the new payment
5
+ # @param attributes [Hash,ActionController::Parameters] attributes which are assigned to the new payment
6
6
  # * :payment_method_id Id of payment method used for this payment
7
7
  # * :source_attributes Attributes used to build the source of this payment. Usually a {CreditCard}
8
8
  # * :existing_card_id (Integer) The id of an existing {CreditCard} object to use
@@ -11,7 +11,10 @@ module Spree
11
11
  def initialize(order, attributes, payment: nil, request_env: {})
12
12
  @order = order
13
13
  @payment = payment
14
- @attributes = attributes.dup.with_indifferent_access
14
+
15
+ # If AC::Params are passed in, attributes.to_h gives us a hash of only
16
+ # the permitted attributes.
17
+ @attributes = attributes.to_h.with_indifferent_access
15
18
  @source_attributes = @attributes.delete(:source_attributes) || {}
16
19
  @request_env = request_env
17
20
  end
@@ -61,7 +61,7 @@ module Spree
61
61
  handle_action(action, :void, auth_code)
62
62
  end
63
63
 
64
- def credit(amount_in_cents, auth_code, gateway_options)
64
+ def credit(amount_in_cents, auth_code, gateway_options = {})
65
65
  action = -> (store_credit) do
66
66
  currency = gateway_options[:currency] || store_credit.currency
67
67
  originator = gateway_options[:originator]
@@ -77,13 +77,14 @@ module Spree
77
77
  store_credit = store_credit_event.try(:store_credit)
78
78
 
79
79
  if store_credit_event.nil? || store_credit.nil?
80
- return false
80
+ ActiveMerchant::Billing::Response.new(false, '', {}, {})
81
81
  elsif store_credit_event.capture_action?
82
- store_credit.credit(store_credit_event.amount, auth_code, store_credit.currency)
82
+ amount_in_cents = (store_credit_event.amount * 100).round
83
+ credit(amount_in_cents, auth_code)
83
84
  elsif store_credit_event.authorization_action?
84
- store_credit.void(auth_code)
85
+ void(auth_code)
85
86
  else
86
- return false
87
+ ActiveMerchant::Billing::Response.new(false, '', {}, {})
87
88
  end
88
89
  end
89
90
 
@@ -172,7 +172,8 @@ module Spree
172
172
  end
173
173
 
174
174
  # Can't use add_search_scope for this as it needs a default argument
175
- def self.available(available_on = nil, _currency = nil)
175
+ def self.available(available_on = nil, currency = nil)
176
+ Spree::Deprecation.warn("The second currency argument on Product.available has no effect, and is deprecated", caller) if currency
176
177
  joins(master: :prices).where("#{Product.quoted_table_name}.available_on <= ?", available_on || Time.current)
177
178
  end
178
179
  search_scopes << :available
@@ -132,10 +132,7 @@ module Spree
132
132
  def ensure_option_types_exist_for_values_hash
133
133
  return if option_values_hash.nil?
134
134
  required_option_type_ids = option_values_hash.keys.map(&:to_i)
135
- missing_option_type_ids = required_option_type_ids - option_type_ids
136
- missing_option_type_ids.each do |id|
137
- product_option_types.create(option_type_id: id)
138
- end
135
+ self.option_type_ids |= required_option_type_ids
139
136
  end
140
137
 
141
138
  # Creates a new product with the same attributes, variants, etc.
@@ -259,7 +256,7 @@ module Spree
259
256
  # @return [Array] all advertised and not-rejected promotions
260
257
  def possible_promotions
261
258
  promotion_ids = promotion_rules.map(&:promotion_id).uniq
262
- Spree::Promotion.advertised.where(id: promotion_ids).reject(&:expired?)
259
+ Spree::Promotion.advertised.where(id: promotion_ids).reject(&:inactive?)
263
260
  end
264
261
 
265
262
  # The number of on-hand stock items; Infinity if any variant does not track
@@ -68,10 +68,12 @@ module Spree
68
68
  def line_items_to_adjust(promotion, order)
69
69
  excluded_ids = adjustments.
70
70
  where(adjustable_id: order.line_items.pluck(:id), adjustable_type: 'Spree::LineItem').
71
- pluck(:adjustable_id)
71
+ pluck(:adjustable_id).
72
+ to_set
72
73
 
73
- order.line_items.where.not(id: excluded_ids).select do |line_item|
74
- promotion.line_item_actionable? order, line_item
74
+ order.line_items.select do |line_item|
75
+ !excluded_ids.include?(line_item.id) &&
76
+ promotion.line_item_actionable?(order, line_item)
75
77
  end
76
78
  end
77
79
  end
@@ -33,7 +33,7 @@ module Spree
33
33
  private
34
34
 
35
35
  def promotion_credit_exists?(shipment)
36
- shipment.adjustments.where(source_id: id).exists?
36
+ shipment.adjustments.where(source: self).exists?
37
37
  end
38
38
  end
39
39
  end
@@ -24,15 +24,14 @@ module Spree
24
24
  product_ids.include?(pid) && (value_ids(pid) & ovids).present?
25
25
  end
26
26
 
27
- def preferred_eligible_values_with_numerification
28
- values = preferred_eligible_values_without_numerification || {}
27
+ def preferred_eligible_values
28
+ values = preferences[:eligible_values] || {}
29
29
  Hash[values.keys.map(&:to_i).zip(
30
30
  values.values.map do |v|
31
31
  (v.is_a?(Array) ? v : v.split(",")).map(&:to_i)
32
32
  end
33
33
  )]
34
34
  end
35
- alias_method_chain :preferred_eligible_values, :numerification
36
35
 
37
36
  private
38
37
 
@@ -11,6 +11,9 @@ module Spree
11
11
  has_many :products, class_name: 'Spree::Product', through: :product_promotion_rules
12
12
 
13
13
  MATCH_POLICIES = %w(any all none)
14
+
15
+ validates_inclusion_of :preferred_match_policy, in: MATCH_POLICIES
16
+
14
17
  preference :match_policy, :string, default: MATCH_POLICIES.first
15
18
 
16
19
  # scope/association that is used to test eligibility
@@ -25,18 +28,21 @@ module Spree
25
28
  def eligible?(order, _options = {})
26
29
  return true if eligible_products.empty?
27
30
 
28
- if preferred_match_policy == 'all'
31
+ case preferred_match_policy
32
+ when 'all'
29
33
  unless eligible_products.all? { |p| order.products.include?(p) }
30
34
  eligibility_errors.add(:base, eligibility_error_message(:missing_product))
31
35
  end
32
- elsif preferred_match_policy == 'any'
36
+ when 'any'
33
37
  unless order.products.any? { |p| eligible_products.include?(p) }
34
38
  eligibility_errors.add(:base, eligibility_error_message(:no_applicable_products))
35
39
  end
36
- else
40
+ when 'none'
37
41
  unless order.products.none? { |p| eligible_products.include?(p) }
38
42
  eligibility_errors.add(:base, eligibility_error_message(:has_excluded_product))
39
43
  end
44
+ else
45
+ raise "unexpected match policy: #{preferred_match_policy.inspect}"
40
46
  end
41
47
 
42
48
  eligibility_errors.empty?
@@ -2,33 +2,52 @@ module Spree
2
2
  class Promotion
3
3
  module Rules
4
4
  class Taxon < PromotionRule
5
- has_many :promotion_rule_taxons, class_name: 'Spree::PromotionRuleTaxon', foreign_key: :promotion_rule_id
5
+ has_many :promotion_rule_taxons, class_name: 'Spree::PromotionRuleTaxon', foreign_key: :promotion_rule_id,
6
+ dependent: :destroy
6
7
  has_many :taxons, through: :promotion_rule_taxons, class_name: 'Spree::Taxon'
7
8
 
8
9
  MATCH_POLICIES = %w(any all)
9
- preference :match_policy, default: MATCH_POLICIES.first
10
+
11
+ validates_inclusion_of :preferred_match_policy, in: MATCH_POLICIES
12
+
13
+ preference :match_policy, :string, default: MATCH_POLICIES.first
10
14
 
11
15
  def applicable?(promotable)
12
16
  promotable.is_a?(Spree::Order)
13
17
  end
14
18
 
15
19
  def eligible?(order, _options = {})
16
- if preferred_match_policy == 'all'
17
- unless (taxons.to_a - taxons_in_order_including_parents(order)).empty?
20
+ order_taxons = taxons_in_order_including_parents(order)
21
+
22
+ case preferred_match_policy
23
+ when 'all'
24
+ unless (taxons.to_a - order_taxons).empty?
18
25
  eligibility_errors.add(:base, eligibility_error_message(:missing_taxon))
19
26
  end
20
- else
21
- order_taxons = taxons_in_order_including_parents(order)
27
+ when 'any'
22
28
  unless taxons.any?{ |taxon| order_taxons.include? taxon }
23
29
  eligibility_errors.add(:base, eligibility_error_message(:no_matching_taxons))
24
30
  end
31
+ else
32
+ # Change this to an exception in a future version of Solidus
33
+ warn_invalid_match_policy(assume: 'any')
34
+ unless taxons.any? { |taxon| order_taxons.include? taxon }
35
+ eligibility_errors.add(:base, eligibility_error_message(:no_matching_taxons))
36
+ end
25
37
  end
26
38
 
27
39
  eligibility_errors.empty?
28
40
  end
29
41
 
30
42
  def actionable?(line_item)
31
- taxon_product_ids.include? line_item.variant.product_id
43
+ case preferred_match_policy
44
+ when 'any', 'all'
45
+ taxon_product_ids.include?(line_item.variant.product_id)
46
+ else
47
+ # Change this to an exception in a future version of Solidus
48
+ warn_invalid_match_policy(assume: 'any')
49
+ taxon_product_ids.include?(line_item.variant.product_id)
50
+ end
32
51
  end
33
52
 
34
53
  def taxon_ids_string
@@ -42,6 +61,13 @@ module Spree
42
61
 
43
62
  private
44
63
 
64
+ def warn_invalid_match_policy(assume:)
65
+ Spree::Deprecation.warn(
66
+ "#{self.class.name} id=#{id} has unexpected match policy #{preferred_match_policy.inspect}. "\
67
+ "Interpreting it as '#{assume}'."
68
+ )
69
+ end
70
+
45
71
  # All taxons in an order
46
72
  def order_taxons(order)
47
73
  Spree::Taxon.joins(products: { variants_including_master: :line_items }).where(spree_line_items: { order_id: order.id }).distinct
@@ -2,10 +2,9 @@ module Spree
2
2
  class Promotion
3
3
  module Rules
4
4
  class User < PromotionRule
5
- belongs_to :user, class_name: Spree::UserClassHandle.new
6
-
7
5
  has_many :promotion_rule_users, class_name: 'Spree::PromotionRuleUser',
8
- foreign_key: :promotion_rule_id
6
+ foreign_key: :promotion_rule_id,
7
+ dependent: :destroy
9
8
  has_many :users, through: :promotion_rule_users, class_name: Spree::UserClassHandle.new
10
9
 
11
10
  def applicable?(promotable)
@@ -73,15 +73,24 @@ module Spree
73
73
  super
74
74
  end
75
75
 
76
- def expired?
77
- !active?
78
- end
79
-
80
76
  def active?
81
77
  (starts_at.nil? || starts_at < Time.current) &&
82
78
  (expires_at.nil? || expires_at > Time.current)
83
79
  end
84
80
 
81
+ def inactive?
82
+ !active?
83
+ end
84
+
85
+ def expired?
86
+ Spree::Deprecation.warn <<-WARN.squish, caller
87
+ #expired? is deprecated, and will be removed in Solidus 2.0.
88
+ Please use #inactive? instead.
89
+ WARN
90
+
91
+ inactive?
92
+ end
93
+
85
94
  def activate(order:, line_item: nil, user: nil, path: nil, promotion_code: nil)
86
95
  return unless self.class.order_activatable?(order)
87
96
 
@@ -116,7 +125,7 @@ module Spree
116
125
 
117
126
  # called anytime order.update! happens
118
127
  def eligible?(promotable, promotion_code: nil)
119
- return false if expired?
128
+ return false if inactive?
120
129
  return false if usage_limit_exceeded?
121
130
  return false if promotion_code && promotion_code.usage_limit_exceeded?
122
131
  return false if blacklisted?(promotable)
@@ -176,8 +185,9 @@ module Spree
176
185
  count(:order_id)
177
186
  end
178
187
 
179
- # TODO: specs
180
188
  def line_item_actionable?(order, line_item, promotion_code: nil)
189
+ return false if blacklisted?(line_item)
190
+
181
191
  if eligible?(order, promotion_code: promotion_code)
182
192
  rules = eligible_rules(order)
183
193
  if rules.blank?
@@ -12,7 +12,7 @@ module Spree
12
12
  if order.coupon_code.present?
13
13
  if promotion.present? && promotion.actions.exists?
14
14
  handle_present_promotion(promotion)
15
- elsif promotion_code && promotion_code.promotion.expired?
15
+ elsif promotion_code && promotion_code.promotion.inactive?
16
16
  set_error_code :coupon_code_expired
17
17
  else
18
18
  set_error_code :coupon_code_not_found
@@ -1,7 +1,6 @@
1
1
  module Spree
2
2
  class Shipment < Spree::Base
3
3
  belongs_to :order, class_name: 'Spree::Order', touch: true, inverse_of: :shipments
4
- belongs_to :address, class_name: 'Spree::Address'
5
4
  belongs_to :stock_location, class_name: 'Spree::StockLocation'
6
5
 
7
6
  has_many :adjustments, as: :adjustable, inverse_of: :adjustable, dependent: :delete_all
@@ -11,8 +10,6 @@ module Spree
11
10
  has_many :state_changes, as: :stateful
12
11
  has_many :cartons, -> { uniq }, through: :inventory_units
13
12
 
14
- after_save :update_adjustments
15
-
16
13
  before_validation :set_cost_zero_when_nil
17
14
 
18
15
  before_destroy :ensure_can_destroy
@@ -21,7 +18,6 @@ module Spree
21
18
  # from outside of the state machine and can actually pass variables through.
22
19
  attr_accessor :special_instructions, :suppress_mailer
23
20
 
24
- accepts_nested_attributes_for :address
25
21
  accepts_nested_attributes_for :inventory_units
26
22
 
27
23
  make_permalink field: :number, length: 11, prefix: 'H'
@@ -210,8 +206,13 @@ module Spree
210
206
  end
211
207
 
212
208
  def selected_shipping_rate_id=(id)
213
- shipping_rates.update_all(selected: false)
214
- shipping_rates.update(id, selected: true)
209
+ selected_shipping_rate.update(selected: false) if selected_shipping_rate
210
+ new_rate = shipping_rates.detect { |rate| rate.id == id.to_i }
211
+ fail(
212
+ ArgumentError,
213
+ "Could not find shipping rate id #{id} for shipment #{number}"
214
+ ) unless new_rate
215
+ new_rate.update(selected: true)
215
216
  save!
216
217
  end
217
218
 
@@ -372,6 +373,11 @@ module Spree
372
373
  !stock_location || stock_location.fulfillable?
373
374
  end
374
375
 
376
+ def address
377
+ Spree::Deprecation.warn("Calling Shipment#address is deprecated. Use Order#ship_address instead", caller)
378
+ order.ship_address if order
379
+ end
380
+
375
381
  private
376
382
 
377
383
  def after_ship
@@ -396,20 +402,10 @@ module Spree
396
402
  stock_location.unstock item.variant, item.quantity, self
397
403
  end
398
404
 
399
- def recalculate_adjustments
400
- Spree::ItemAdjustments.new(self).update
401
- end
402
-
403
405
  def set_cost_zero_when_nil
404
406
  self.cost = 0 unless cost
405
407
  end
406
408
 
407
- def update_adjustments
408
- if cost_changed? && state != 'shipped'
409
- recalculate_adjustments
410
- end
411
- end
412
-
413
409
  def ensure_can_destroy
414
410
  unless pending?
415
411
  errors.add(:state, :cannot_destroy, state: state)
@@ -45,7 +45,7 @@ module Spree
45
45
 
46
46
  def shipping_methods(package)
47
47
  package.shipping_methods
48
- .available_for_address(package.shipment.address)
48
+ .available_for_address(package.shipment.order.ship_address)
49
49
  .includes(:calculator, tax_category: :tax_rates)
50
50
  .to_a
51
51
  .select do |ship_method|
@@ -123,7 +123,6 @@ module Spree
123
123
 
124
124
  Spree::Shipment.new(
125
125
  order: order,
126
- address: order.ship_address,
127
126
  stock_location: stock_location,
128
127
  inventory_units: contents.map(&:inventory_unit)
129
128
  )
@@ -8,7 +8,7 @@ module Spree
8
8
 
9
9
  validates :stock_location, :variant, presence: true
10
10
  validates :variant_id, uniqueness: { scope: [:stock_location_id, :deleted_at] }, allow_blank: true, unless: :deleted_at
11
- validates :count_on_hand, numericality: { greater_than_or_equal_to: 0 }, if: :verify_count_on_hand?
11
+ validates :count_on_hand, numericality: { greater_than_or_equal_to: 0 }, unless: :backorderable?
12
12
 
13
13
  delegate :weight, :should_track_inventory?, to: :variant
14
14
 
@@ -83,10 +83,6 @@ module Spree
83
83
 
84
84
  private
85
85
 
86
- def verify_count_on_hand?
87
- count_on_hand_changed? && !backorderable? && (count_on_hand < count_on_hand_was) && (count_on_hand < 0)
88
- end
89
-
90
86
  def count_on_hand=(value)
91
87
  write_attribute(:count_on_hand, value)
92
88
  end
@@ -114,7 +110,7 @@ module Spree
114
110
  def inventory_cache_threshold
115
111
  # only warn if store is setting binary_inventory_cache (default = false)
116
112
  @cache_threshold ||= if Spree::Config.binary_inventory_cache
117
- ActiveSupport::Deprecation.warn "Spree::Config.binary_inventory_cache=true is DEPRECATED. Instead use Spree::Config.inventory_cache_threshold=1"
113
+ Spree::Deprecation.warn "Spree::Config.binary_inventory_cache=true is DEPRECATED. Instead use Spree::Config.inventory_cache_threshold=1"
118
114
  1
119
115
  else
120
116
  Spree::Config.inventory_cache_threshold