spree_core 2.3.1 → 2.3.2

Sign up to get free protection for your applications and to get access to all the features.
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