spree_core 2.3.1 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/app/helpers/spree/base_helper.rb +3 -3
  3. data/app/models/spree/ability.rb +1 -0
  4. data/app/models/spree/app_configuration.rb +0 -1
  5. data/app/models/spree/base.rb +6 -0
  6. data/app/models/spree/calculator/flat_percent_item_total.rb +9 -3
  7. data/app/models/spree/calculator/flexi_rate.rb +1 -1
  8. data/app/models/spree/calculator/percent_on_line_item.rb +1 -1
  9. data/app/models/spree/calculator/tiered_flat_rate.rb +37 -0
  10. data/app/models/spree/calculator/tiered_percent.rb +44 -0
  11. data/app/models/spree/credit_card.rb +35 -14
  12. data/app/models/spree/inventory_unit.rb +1 -0
  13. data/app/models/spree/item_adjustments.rb +3 -2
  14. data/app/models/spree/line_item.rb +2 -2
  15. data/app/models/spree/order.rb +36 -20
  16. data/app/models/spree/order/checkout.rb +60 -24
  17. data/app/models/spree/order_contents.rb +3 -6
  18. data/app/models/spree/order_populator.rb +1 -1
  19. data/app/models/spree/order_updater.rb +19 -4
  20. data/app/models/spree/payment.rb +4 -0
  21. data/app/models/spree/payment/processing.rb +6 -2
  22. data/app/models/spree/price.rb +10 -0
  23. data/app/models/spree/product.rb +81 -54
  24. data/app/models/spree/promotion/actions/create_adjustment.rb +2 -11
  25. data/app/models/spree/promotion/actions/create_item_adjustments.rb +2 -19
  26. data/app/models/spree/promotion_handler/cart.rb +14 -2
  27. data/app/models/spree/promotion_handler/coupon.rb +8 -2
  28. data/app/models/spree/return_authorization.rb +2 -2
  29. data/app/models/spree/shipping_rate.rb +2 -2
  30. data/app/models/spree/stock/availability_validator.rb +3 -7
  31. data/app/models/spree/stock/estimator.rb +1 -1
  32. data/app/models/spree/stock/package.rb +1 -0
  33. data/app/models/spree/stock_item.rb +6 -1
  34. data/app/models/spree/stock_location.rb +4 -0
  35. data/app/models/spree/tax_rate.rb +15 -2
  36. data/app/models/spree/variant.rb +8 -3
  37. data/app/models/spree/zone.rb +2 -2
  38. data/config/locales/en.yml +33 -3
  39. data/db/default/spree/countries.rb +2 -1
  40. data/db/migrate/20130807024302_rename_adjustment_fields.rb +2 -5
  41. data/db/migrate/20140804185157_add_default_to_shipment_cost.rb +10 -0
  42. data/lib/generators/spree/custom_user/templates/authentication_helpers.rb.tt +12 -4
  43. data/lib/generators/spree/install/install_generator.rb +8 -0
  44. data/lib/spree/core.rb +1 -0
  45. data/lib/spree/core/adjustment_source.rb +26 -0
  46. data/lib/spree/core/controller_helpers.rb +10 -9
  47. data/lib/spree/core/controller_helpers/order.rb +18 -5
  48. data/lib/spree/core/engine.rb +6 -2
  49. data/lib/spree/core/importer/order.rb +52 -9
  50. data/lib/spree/core/version.rb +1 -1
  51. data/lib/spree/permitted_attributes.rb +4 -4
  52. data/lib/spree/testing_support/authorization_helpers.rb +1 -1
  53. data/lib/spree/testing_support/factories/product_factory.rb +1 -1
  54. metadata +27 -37
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 66920ae53fa26364425582e338582f7543cfde28
4
- data.tar.gz: fec0a8ddbf653e8a1a21e5fc8ae1954745b77a23
3
+ metadata.gz: b7bfe527c1516e5d8393c4cb6a23bf795bff2f8d
4
+ data.tar.gz: dd9c6e528157dd843740ff2cd5bc86d6fa906192
5
5
  SHA512:
6
- metadata.gz: 0223b441cc91485cb7d8c138ee3ca4f79120b03067b0374fd0f3b6ab5e4b1dfe88f0811719d7517c0a782ae2f1f421d0ad7e4257279b028d2f82071aeee604fc
7
- data.tar.gz: 8a79116e5fed7a827e1f01c3ddc3b5893a8ce007c1e67fb620040b7dd1975a93f097df4714ea527801ee02e9559fda64375fecb939f9e9cf9a5c6d7381a38ac7
6
+ metadata.gz: 0fb13c6c497d8ab9d920e6f950a532e16524f305e2591e82b12bd1ae1aa47d53f2a8ca0fd293d588c8b8ed32d3bac49c2a2cfef851a1ba9b237b7149994c9f60
7
+ data.tar.gz: 5ea8cacb10c49bf4afaa936a9ae348d13979a1f30855214e491aff0abf9ca9ba3f495f7d38c295a1266b4be9ab26e5adbf47c5dc464b72fc0555b2fc2740f1d8
@@ -68,10 +68,10 @@ module Spree
68
68
  end
69
69
 
70
70
  def flash_messages(opts = {})
71
- opts[:ignore_types] = [:order_completed].concat(Array(opts[:ignore_types]) || [])
71
+ ignore_types = ["order_completed"].concat(Array(opts[:ignore_types]).map(&:to_s) || [])
72
72
 
73
73
  flash.each do |msg_type, text|
74
- unless opts[:ignore_types].include?(msg_type)
74
+ unless ignore_types.include?(msg_type)
75
75
  concat(content_tag :div, text, class: "flash #{msg_type}")
76
76
  end
77
77
  end
@@ -118,7 +118,7 @@ module Spree
118
118
  countries.collect do |country|
119
119
  country.name = Spree.t(country.iso, scope: 'country_names', default: country.name)
120
120
  country
121
- end.sort { |a, b| a.name.parameterize <=> b.name.parameterize }
121
+ end.sort_by { |c| c.name.parameterize }
122
122
  end
123
123
 
124
124
  def seo_url(taxon)
@@ -50,6 +50,7 @@ module Spree
50
50
  can :update, Address do |address|
51
51
  user.bill_address == address || user.ship_address == address
52
52
  end
53
+ can :display, CreditCard, user_id: user.id
53
54
  can :display, Product
54
55
  can :display, ProductProperty
55
56
  can :display, Property
@@ -57,7 +57,6 @@ module Spree
57
57
  preference :promotions_per_page, :integer, default: 15
58
58
  preference :redirect_https_to_http, :boolean, :default => false
59
59
  preference :require_master_price, :boolean, default: true
60
- preference :shipment_inc_vat, :boolean, default: false
61
60
  preference :shipping_instructions, :boolean, default: false # Request instructions/info for shipping
62
61
  preference :show_only_complete_orders_by_default, :boolean, default: true
63
62
  preference :show_variant_full_price, :boolean, default: false #Displays variant full price or difference with product price. Default false to be compatible with older behavior
@@ -5,5 +5,11 @@ class Spree::Base < ActiveRecord::Base
5
5
  self.preferences = default_preferences.merge(preferences) if has_attribute?(:preferences)
6
6
  end
7
7
 
8
+ if Kaminari.config.page_method_name != :page
9
+ def self.page num
10
+ send Kaminari.config.page_method_name, num
11
+ end
12
+ end
13
+
8
14
  self.abstract_class = true
9
15
  end
@@ -8,9 +8,15 @@ module Spree
8
8
  Spree.t(:flat_percent)
9
9
  end
10
10
 
11
- def compute(line_item)
12
- value = line_item.amount * BigDecimal(self.preferred_flat_percent.to_s) / 100.0
13
- (value * 100).round.to_f / 100
11
+ def compute(object)
12
+ computed_amount = (object.amount * preferred_flat_percent / 100).round(2)
13
+
14
+ # We don't want to cause the promotion adjustments to push the order into a negative total.
15
+ if computed_amount > object.amount
16
+ object.amount
17
+ else
18
+ computed_amount
19
+ end
14
20
  end
15
21
  end
16
22
  end
@@ -18,7 +18,7 @@ module Spree
18
18
  def compute(object)
19
19
  sum = 0
20
20
  max = self.preferred_max_items.to_i
21
- items_count = object.line_items.map(&:quantity).sum
21
+ items_count = object.quantity
22
22
  items_count.times do |i|
23
23
  if i == 0
24
24
  sum += self.preferred_first_item.to_f
@@ -8,7 +8,7 @@ module Spree
8
8
  end
9
9
 
10
10
  def compute(object)
11
- ((object.price * object.quantity) * preferred_percent) / 100
11
+ (object.amount * preferred_percent) / 100
12
12
  end
13
13
  end
14
14
  end
@@ -0,0 +1,37 @@
1
+ require_dependency 'spree/calculator'
2
+
3
+ module Spree
4
+ class Calculator::TieredFlatRate < Calculator
5
+ preference :base_amount, :decimal, default: 0
6
+ preference :tiers, :hash, default: {}
7
+
8
+ before_validation do
9
+ # Convert tier values to decimals. Strings don't do us much good.
10
+ if preferred_tiers.is_a?(Hash)
11
+ self.preferred_tiers = Hash[*preferred_tiers.flatten.map(&:to_f)]
12
+ end
13
+ end
14
+
15
+ validate :preferred_tiers_content
16
+
17
+ def self.description
18
+ Spree.t(:tiered_flat_rate)
19
+ end
20
+
21
+ def compute(object)
22
+ base, amount = preferred_tiers.sort.reverse.detect{ |b,_| object.amount >= b }
23
+ amount || preferred_base_amount
24
+ end
25
+
26
+ private
27
+ def preferred_tiers_content
28
+ if preferred_tiers.is_a? Hash
29
+ unless preferred_tiers.keys.all?{ |k| k.is_a?(Numeric) && k > 0 }
30
+ errors.add(:base, :keys_should_be_positive_number)
31
+ end
32
+ else
33
+ errors.add(:preferred_tiers, :should_be_hash)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,44 @@
1
+ require_dependency 'spree/calculator'
2
+
3
+ module Spree
4
+ class Calculator::TieredPercent < Calculator
5
+ preference :base_percent, :decimal, default: 0
6
+ preference :tiers, :hash, default: {}
7
+
8
+ before_validation do
9
+ # Convert tier values to decimals. Strings don't do us much good.
10
+ if preferred_tiers.is_a?(Hash)
11
+ self.preferred_tiers = Hash[*preferred_tiers.flatten.map(&:to_f)]
12
+ end
13
+ end
14
+
15
+ validates :preferred_base_percent, numericality: {
16
+ greater_than_or_equal_to: 0,
17
+ less_than_or_equal_to: 100
18
+ }
19
+ validate :preferred_tiers_content
20
+
21
+ def self.description
22
+ Spree.t(:tiered_percent)
23
+ end
24
+
25
+ def compute(object)
26
+ base, percent = preferred_tiers.sort.reverse.detect{ |b,_| object.amount >= b }
27
+ (object.amount * (percent || preferred_base_percent) / 100).round(2)
28
+ end
29
+
30
+ private
31
+ def preferred_tiers_content
32
+ if preferred_tiers.is_a? Hash
33
+ unless preferred_tiers.keys.all?{ |k| k.is_a?(Numeric) && k > 0 }
34
+ errors.add(:base, :keys_should_be_positive_number)
35
+ end
36
+ unless preferred_tiers.values.all?{ |k| k.is_a?(Numeric) && k >= 0 && k <= 100 }
37
+ errors.add(:base, :values_should_be_percent)
38
+ end
39
+ else
40
+ errors.add(:preferred_tiers, :should_be_hash)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -3,14 +3,17 @@ module Spree
3
3
  belongs_to :payment_method
4
4
  has_many :payments, as: :source
5
5
 
6
- before_save :set_last_digits
6
+ before_create :set_missing_info
7
7
 
8
- attr_accessor :number, :verification_value, :encrypted_data
8
+ attr_accessor :encrypted_data,
9
+ :number,
10
+ :imported,
11
+ :verification_value
9
12
 
10
13
  validates :month, :year, numericality: { only_integer: true }, if: :require_card_numbers?, on: :create
11
- validates :number, presence: true, if: :require_card_numbers?, on: :create
14
+ validates :number, presence: true, if: :require_card_numbers?, on: :create, unless: :imported
12
15
  validates :name, presence: true, if: :require_card_numbers?, on: :create
13
- validates :verification_value, presence: true, if: :require_card_numbers?, on: :create
16
+ validates :verification_value, presence: true, if: :require_card_numbers?, on: :create, unless: :imported
14
17
 
15
18
  validate :expiry_not_in_the_past
16
19
 
@@ -60,12 +63,6 @@ module Spree
60
63
  end
61
64
  end
62
65
 
63
- def set_last_digits
64
- number.to_s.gsub!(/\s/,'')
65
- verification_value.to_s.gsub!(/\s/,'')
66
- self.last_digits ||= number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
67
- end
68
-
69
66
  def try_type_from_number
70
67
  numbers = number.delete(' ') if number
71
68
  CARD_TYPES.find{|type, pattern| return type.to_s if numbers =~ pattern}.to_s
@@ -91,7 +88,7 @@ module Spree
91
88
 
92
89
  # Indicates whether its possible to void the payment.
93
90
  def can_void?(payment)
94
- !payment.void?
91
+ !payment.failed? && !payment.void?
95
92
  end
96
93
 
97
94
  # Indicates whether its possible to credit the payment. Note that most gateways require that the
@@ -132,9 +129,13 @@ module Spree
132
129
 
133
130
  def expiry_not_in_the_past
134
131
  if year.present? && month.present?
135
- time = Time.zone.parse("#{year}-#{month}-1")
136
- if time < Time.zone.now.to_time.beginning_of_month
137
- errors.add(:base, :card_expired)
132
+ if month.to_i < 1 || month.to_i > 12
133
+ errors.add(:base, :expiry_invalid)
134
+ else
135
+ time = Time.zone.parse("#{year}-#{month}-1")
136
+ if time < Time.zone.now.to_time.beginning_of_month
137
+ errors.add(:base, :card_expired)
138
+ end
138
139
  end
139
140
  end
140
141
  end
@@ -142,5 +143,25 @@ module Spree
142
143
  def require_card_numbers?
143
144
  !self.encrypted_data.present? && !self.has_payment_profile?
144
145
  end
146
+
147
+ def set_last_digits
148
+ number.to_s.gsub!(/\s/,'')
149
+ verification_value.to_s.gsub!(/\s/,'')
150
+ self.last_digits ||= number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
151
+ end
152
+
153
+ def set_missing_info
154
+ set_last_digits
155
+ if has_payment_profile?
156
+ if matching_card = self.class.where(gateway_customer_profile_id: self.gateway_customer_profile_id, gateway_payment_profile_id: self.gateway_payment_profile_id).first
157
+ self.cc_type = matching_card.cc_type
158
+ self.last_digits = matching_card.last_digits
159
+ self.month = matching_card.month
160
+ self.name = matching_card.name
161
+ self.year = matching_card.year
162
+ end
163
+ end
164
+ end
165
+
145
166
  end
146
167
  end
@@ -70,6 +70,7 @@ module Spree
70
70
  end
71
71
 
72
72
  def update_order
73
+ self.reload
73
74
  order.update!
74
75
  end
75
76
  end
@@ -47,8 +47,9 @@ module Spree
47
47
  included_tax_total = 0
48
48
  additional_tax_total = 0
49
49
  run_callbacks :tax_adjustments do
50
- included_tax_total = adjustments.tax.included.reload.map(&:update!).compact.sum
51
- additional_tax_total = adjustments.tax.additional.reload.map(&:update!).compact.sum
50
+ tax = (item.respond_to?(:all_adjustments) ? item.all_adjustments : item.adjustments).tax
51
+ included_tax_total = tax.included.reload.map(&:update!).compact.sum
52
+ additional_tax_total = tax.additional.reload.map(&:update!).compact.sum
52
53
  end
53
54
 
54
55
  item.update_columns(
@@ -97,7 +97,7 @@ module Spree
97
97
 
98
98
  private
99
99
  def update_inventory
100
- if changed? || target_shipment.present?
100
+ if (changed? || target_shipment.present?) && self.order.has_checkout_step?("delivery")
101
101
  Spree::OrderInventory.new(self.order, self).verify(target_shipment)
102
102
  end
103
103
  end
@@ -119,7 +119,7 @@ module Spree
119
119
 
120
120
  def ensure_proper_currency
121
121
  unless currency == order.currency
122
- errors.add(:currency, t(:must_match_order_currency))
122
+ errors.add(:currency, :must_match_order_currency)
123
123
  end
124
124
  end
125
125
  end
@@ -3,25 +3,20 @@ require 'spree/order/checkout'
3
3
 
4
4
  module Spree
5
5
  class Order < Spree::Base
6
- include Checkout
7
- include CurrencyUpdater
6
+ include Spree::Order::Checkout
7
+ include Spree::Order::CurrencyUpdater
8
8
 
9
9
  checkout_flow do
10
10
  go_to_state :address
11
11
  go_to_state :delivery
12
- go_to_state :payment, if: ->(order) do
13
- # TODO there should be a better fix work around for the issues this is
14
- # resolving we shouldn't be setting shipments cost every time a order
15
- # object is loaded
16
- order.set_shipments_cost if order.shipments.any?
17
- order.payment_required?
18
- end
12
+ go_to_state :payment, if: ->(order) { order.payment_required? }
19
13
  go_to_state :confirm, if: ->(order) { order.confirmation_required? }
20
14
  go_to_state :complete
21
15
  remove_transition from: :delivery, to: :confirm
22
16
  end
23
17
 
24
18
  attr_reader :coupon_code
19
+ attr_accessor :temporary_address
25
20
 
26
21
  if Spree.user_class
27
22
  belongs_to :user, class_name: Spree.user_class.to_s
@@ -79,6 +74,7 @@ module Spree
79
74
 
80
75
  validates :email, presence: true, if: :require_email
81
76
  validates :email, email: true, if: :require_email, allow_blank: true
77
+ validates :number, uniqueness: true
82
78
  validate :has_available_shipment
83
79
 
84
80
  make_permalink field: :number
@@ -95,6 +91,8 @@ module Spree
95
91
  scope :created_between, ->(start_date, end_date) { where(created_at: start_date..end_date) }
96
92
  scope :completed_between, ->(start_date, end_date) { where(completed_at: start_date..end_date) }
97
93
 
94
+ scope :reverse_chronological, -> { order(created_at: :desc) }
95
+
98
96
  def self.by_customer(customer)
99
97
  joins(:user).where("#{Spree.user_class.table_name}.email" => customer)
100
98
  end
@@ -258,15 +256,18 @@ module Spree
258
256
  end
259
257
  end
260
258
 
261
- # FIXME refactor this method and implement validation using validates_* utilities
262
- def generate_order_number
263
- record = true
264
- while record
265
- random = "R#{Array.new(9){rand(9)}.join}"
266
- record = self.class.where(number: random).first
267
- end
268
- self.number = random if self.number.blank?
269
- self.number
259
+ def generate_order_number(digits = 9)
260
+ self.number ||= loop do
261
+ # Make a random number.
262
+ random = "R#{Array.new(digits){rand(10)}.join}"
263
+ # Use the random number if no other order exists with it.
264
+ if self.class.exists?(number: random)
265
+ # If over half of all possible options are taken add another digit.
266
+ digits += 1 if self.class.count > (10 ** digits / 2)
267
+ else
268
+ break random
269
+ end
270
+ end
270
271
  end
271
272
 
272
273
  def shipped_shipments
@@ -296,7 +297,11 @@ module Spree
296
297
  end
297
298
 
298
299
  def outstanding_balance
299
- total - payment_total
300
+ if self.state == 'canceled' && self.payments.present? && self.payments.completed.size > 0
301
+ -1 * payment_total
302
+ else
303
+ total - payment_total
304
+ end
300
305
  end
301
306
 
302
307
  def outstanding_balance?
@@ -403,6 +408,12 @@ module Spree
403
408
  line_items.select(&:insufficient_stock?)
404
409
  end
405
410
 
411
+ def ensure_line_items_are_in_stock
412
+ if insufficient_stock_lines.present?
413
+ errors.add(:base, Spree.t(:insufficient_stock_lines_present)) and return false
414
+ end
415
+ end
416
+
406
417
  def merge!(order, user = nil)
407
418
  order.line_items.each do |line_item|
408
419
  next unless line_item.currency == currency
@@ -572,7 +583,7 @@ module Spree
572
583
  self.ensure_updated_shipments
573
584
  end
574
585
 
575
- def reload
586
+ def reload(options=nil)
576
587
  remove_instance_variable(:@tax_zone) if defined?(@tax_zone)
577
588
  super
578
589
  end
@@ -581,6 +592,10 @@ module Spree
581
592
  included_tax_total + additional_tax_total
582
593
  end
583
594
 
595
+ def quantity
596
+ line_items.sum(:quantity)
597
+ end
598
+
584
599
  private
585
600
 
586
601
  def link_by_email
@@ -620,6 +635,7 @@ module Spree
620
635
 
621
636
  send_cancel_email
622
637
  self.update_column(:payment_state, 'credit_owed') unless shipped?
638
+ self.update!
623
639
  end
624
640
 
625
641
  def send_cancel_email
@@ -38,8 +38,8 @@ module Spree
38
38
  # To avoid multiple occurrences of the same transition being defined
39
39
  # On first definition, state_machines will not be defined
40
40
  state_machines.clear if respond_to?(:state_machines)
41
- state_machine :state, :initial => :cart, :use_transactions => false, :action => :save_state do
42
- klass.next_event_transitions.each { |t| transition(t.merge(:on => :next)) }
41
+ state_machine :state, initial: :cart, use_transactions: false, action: :save_state do
42
+ klass.next_event_transitions.each { |t| transition(t.merge(on: :next)) }
43
43
 
44
44
  # Persist the state on the order
45
45
  after_transition do |order, transition|
@@ -54,23 +54,23 @@ module Spree
54
54
  end
55
55
 
56
56
  event :cancel do
57
- transition :to => :canceled, :if => :allow_cancel?
57
+ transition to: :canceled, if: :allow_cancel?
58
58
  end
59
59
 
60
60
  event :return do
61
- transition :to => :returned, :from => :awaiting_return, :unless => :awaiting_returns?
61
+ transition to: :returned, from: :awaiting_return, unless: :awaiting_returns?
62
62
  end
63
63
 
64
64
  event :resume do
65
- transition :to => :resumed, :from => :canceled, :if => :canceled?
65
+ transition to: :resumed, from: :canceled, if: :canceled?
66
66
  end
67
67
 
68
68
  event :authorize_return do
69
- transition :to => :awaiting_return
69
+ transition to: :awaiting_return
70
70
  end
71
71
 
72
72
  if states[:payment]
73
- before_transition :to => :complete do |order|
73
+ before_transition to: :complete do |order|
74
74
  if order.payment_required? && order.payments.empty?
75
75
  order.errors.add(:base, Spree.t(:no_payment_found))
76
76
  false
@@ -80,28 +80,32 @@ module Spree
80
80
  end
81
81
  end
82
82
 
83
- before_transition :from => :cart, :do => :ensure_line_items_present
83
+ before_transition from: :cart, do: :ensure_line_items_present
84
84
 
85
85
  if states[:address]
86
- before_transition :from => :address, :do => :create_tax_charge!
86
+ before_transition from: :address, do: :create_tax_charge!
87
+ before_transition to: :address, do: :assign_default_addresses!
88
+ before_transition from: :address, do: :persist_user_address!
87
89
  end
88
90
 
89
91
  if states[:payment]
90
- before_transition :to => :payment, :do => :set_shipments_cost
91
- before_transition :to => :payment, :do => :create_tax_charge!
92
+ before_transition to: :payment, do: :set_shipments_cost
93
+ before_transition to: :payment, do: :create_tax_charge!
92
94
  end
93
95
 
94
96
  if states[:delivery]
95
- before_transition :to => :delivery, :do => :create_proposed_shipments
96
- before_transition :to => :delivery, :do => :ensure_available_shipping_rates
97
- before_transition :from => :delivery, :do => :apply_free_shipping_promotions
97
+ before_transition to: :delivery, do: :create_proposed_shipments
98
+ before_transition to: :delivery, do: :ensure_available_shipping_rates
99
+ before_transition from: :delivery, do: :apply_free_shipping_promotions
98
100
  end
99
101
 
100
- after_transition :to => :complete, :do => :finalize!
101
- after_transition :to => :resumed, :do => :after_resume
102
- after_transition :to => :canceled, :do => :after_cancel
102
+ before_transition to: :resumed, do: :ensure_line_items_are_in_stock
103
103
 
104
- after_transition :from => any - :cart, :to => any - [:confirm, :complete] do |order|
104
+ after_transition to: :complete, do: :finalize!
105
+ after_transition to: :resumed, do: :after_resume
106
+ after_transition to: :canceled, do: :after_cancel
107
+
108
+ after_transition from: any - :cart, to: any - [:confirm, :complete] do |order|
105
109
  order.update_totals
106
110
  order.persist_totals
107
111
  end
@@ -113,7 +117,7 @@ module Spree
113
117
  def self.go_to_state(name, options={})
114
118
  self.checkout_steps[name] = options
115
119
  previous_states.each do |state|
116
- add_transition({:from => state, :to => name}.merge(options))
120
+ add_transition({from: state, to: name}.merge(options))
117
121
  end
118
122
  if options[:if]
119
123
  self.previous_states << name
@@ -217,12 +221,17 @@ module Spree
217
221
  success = false
218
222
  @updating_params = params
219
223
  run_callbacks :updating_from_params do
220
- attributes = @updating_params[:order] ? @updating_params[:order].permit(permitted_params) : {}
224
+ attributes = @updating_params[:order] ? @updating_params[:order].permit(permitted_params).delete_if { |k,v| v.nil? } : {}
221
225
 
222
226
  # Set existing card after setting permitted parameters because
223
227
  # rails would slice parameters containg ruby objects, apparently
224
- if @updating_params[:existing_card].present?
225
- credit_card = CreditCard.find(@updating_params[:existing_card])
228
+ #
229
+ # Need to check both outside and inside :order beacuse frontend
230
+ # sends existing_card out of :order
231
+ existing_card_id = @updating_params[:existing_card] || (@updating_params[:order] ? @updating_params[:order][:existing_card] : nil)
232
+
233
+ if existing_card_id.present?
234
+ credit_card = CreditCard.find existing_card_id
226
235
  if credit_card.user_id != self.user_id || credit_card.user_id.blank?
227
236
  raise Core::GatewayError.new Spree.t(:invalid_credit_card)
228
237
  end
@@ -239,27 +248,54 @@ module Spree
239
248
  end
240
249
 
241
250
  success = self.update_attributes(attributes)
251
+ set_shipments_cost if self.shipments.any?
242
252
  end
243
253
 
244
254
  @updating_params = nil
245
255
  success
246
256
  end
247
257
 
258
+ def assign_default_addresses!
259
+ if self.user
260
+ self.bill_address = user.bill_address.try(:clone) if !self.bill_address_id && user.bill_address.try(:valid?)
261
+ # Skip setting ship address if order doesn't have a delivery checkout step
262
+ # to avoid triggering validations on shipping address
263
+ self.ship_address = user.ship_address.try(:clone) if !self.ship_address_id && user.ship_address.try(:valid?) && self.checkout_steps.include?("delivery")
264
+ end
265
+ end
266
+
267
+ def persist_user_address!
268
+ if !self.temporary_address && self.user && self.user.respond_to?(:persist_order_address) && self.bill_address_id
269
+ self.user.persist_order_address(self)
270
+ end
271
+ end
272
+
248
273
  private
249
274
  # For payment step, filter order parameters to produce the expected nested
250
275
  # attributes for a single payment and its source, discarding attributes
251
276
  # for payment methods other than the one selected
277
+ #
278
+ # In case a existing credit card is provided it needs to build the payment
279
+ # attributes from scratch so we can set the amount. example payload:
280
+ #
281
+ # {
282
+ # "order": {
283
+ # "existing_card": "2"
284
+ # }
285
+ # }
286
+ #
252
287
  def update_params_payment_source
253
288
  if has_checkout_step?("payment") && self.payment?
254
289
  if @updating_params[:payment_source].present?
255
- source_params = @updating_params.delete(:payment_source)[@updating_params[:order][:payments_attributes].first[:payment_method_id].underscore]
290
+ source_params = @updating_params.delete(:payment_source)[@updating_params[:order][:payments_attributes].first[:payment_method_id].to_s]
256
291
 
257
292
  if source_params
258
293
  @updating_params[:order][:payments_attributes].first[:source_attributes] = source_params
259
294
  end
260
295
  end
261
296
 
262
- if (@updating_params[:order][:payments_attributes])
297
+ if @updating_params[:order][:payments_attributes] || @updating_params[:order][:existing_card]
298
+ @updating_params[:order][:payments_attributes] ||= [{}]
263
299
  @updating_params[:order][:payments_attributes].first[:amount] = self.total
264
300
  end
265
301
  end