spree_core 3.0.1 → 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/models/friendly_id/slug_decorator.rb +3 -0
- data/app/models/spree/adjustment.rb +1 -1
- data/app/models/spree/calculator.rb +5 -0
- data/app/models/spree/customer_return.rb +22 -16
- data/app/models/spree/gateway/bogus.rb +4 -0
- data/app/models/spree/line_item.rb +1 -1
- data/app/models/spree/order.rb +22 -48
- data/app/models/spree/order/checkout.rb +270 -255
- data/app/models/spree/order_contents.rb +64 -61
- data/app/models/spree/order_merger.rb +65 -0
- data/app/models/spree/payment.rb +5 -0
- data/app/models/spree/payment/processing.rb +2 -1
- data/app/models/spree/payment_method/check.rb +12 -2
- data/app/models/spree/product.rb +5 -0
- data/app/models/spree/promotion_handler/cart.rb +18 -14
- data/app/models/spree/shipment.rb +1 -1
- data/app/models/spree/stock/availability_validator.rb +10 -9
- data/app/models/spree/stock/content_item.rb +8 -0
- data/app/models/spree/stock/package.rb +8 -0
- data/app/models/spree/stock_item.rb +5 -1
- data/app/models/spree/stock_movement.rb +6 -1
- data/app/models/spree/variant.rb +18 -15
- data/app/models/spree/zone.rb +39 -29
- data/app/views/spree/order_mailer/cancel_email.html.erb +1 -1
- data/app/views/spree/order_mailer/cancel_email.text.erb +1 -1
- data/app/views/spree/order_mailer/confirm_email.html.erb +1 -1
- data/app/views/spree/order_mailer/confirm_email.text.erb +1 -1
- data/config/initializers/user_class_extensions.rb +4 -0
- data/config/locales/en.yml +4 -3
- data/db/default/spree/default_reimbursement_type.rb +1 -0
- data/db/migrate/20150515211137_fix_adjustment_order_id.rb +70 -0
- data/db/migrate/20150522181728_add_deleted_at_to_friendly_id_slugs.rb +6 -0
- data/db/migrate/20150609093816_increase_scale_on_pre_tax_amounts.rb +16 -0
- data/db/migrate/20150707204155_enable_acts_as_paranoid_on_calculators.rb +6 -0
- data/lib/spree/core/controller_helpers/auth.rb +1 -1
- data/lib/spree/core/engine.rb +7 -0
- data/lib/spree/core/validators/email.rb +7 -3
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/permitted_attributes.rb +2 -2
- data/lib/spree/testing_support/common_rake.rb +3 -8
- data/lib/spree/testing_support/factories.rb +1 -1
- data/lib/spree/testing_support/factories/order_factory.rb +11 -0
- data/lib/spree/testing_support/order_walkthrough.rb +1 -1
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7aeecb32436b2f2e63f695ae7f8e72e0fc5d3dff
|
4
|
+
data.tar.gz: d3d9c62816751345378389914b51d876ac8f2910
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36a03013620da8c4081e87c51c29722106df038560a5cc0da2a6c3d39e5441d073e7deb643c349aacf276d96fb0c53f6fa3bb880ba6082dcb5ef86add172c2ce
|
7
|
+
data.tar.gz: e7e6816d26ae750cc2c9b5faee24fe5814d280679758e77e271d9a35952dcb52eb85e4bd54fb9598a1ea0240426f4870a51eb29a3725457b4573381bbce1fcdb
|
@@ -24,7 +24,7 @@ module Spree
|
|
24
24
|
class Adjustment < Spree::Base
|
25
25
|
belongs_to :adjustable, polymorphic: true, touch: true
|
26
26
|
belongs_to :source, polymorphic: true
|
27
|
-
belongs_to :order, class_name:
|
27
|
+
belongs_to :order, class_name: 'Spree::Order', inverse_of: :all_adjustments
|
28
28
|
|
29
29
|
validates :adjustable, presence: true
|
30
30
|
validates :order, presence: true
|
@@ -1,5 +1,10 @@
|
|
1
1
|
module Spree
|
2
2
|
class Calculator < Spree::Base
|
3
|
+
# Conditional check for backwards compatibilty since acts as paranoid was added late https://github.com/spree/spree/issues/5858
|
4
|
+
if connection.table_exists?(:spree_calculators) && connection.column_exists?(:spree_calculators, :deleted_at)
|
5
|
+
acts_as_paranoid
|
6
|
+
end
|
7
|
+
|
3
8
|
belongs_to :calculable, polymorphic: true
|
4
9
|
|
5
10
|
# This method calls a compute_<computable> method. must be overriden in concrete calculator.
|
@@ -2,15 +2,17 @@ module Spree
|
|
2
2
|
class CustomerReturn < Spree::Base
|
3
3
|
belongs_to :stock_location
|
4
4
|
|
5
|
-
has_many :return_items, inverse_of: :customer_return
|
6
|
-
has_many :return_authorizations, through: :return_items
|
7
5
|
has_many :reimbursements, inverse_of: :customer_return
|
6
|
+
has_many :return_authorizations, through: :return_items
|
7
|
+
has_many :return_items, inverse_of: :customer_return
|
8
8
|
|
9
9
|
after_create :process_return!
|
10
10
|
before_create :generate_number
|
11
11
|
|
12
12
|
validates :return_items, presence: true
|
13
13
|
validates :stock_location, presence: true
|
14
|
+
|
15
|
+
validate :must_have_return_authorization, on: :create
|
14
16
|
validate :return_items_belong_to_same_order
|
15
17
|
|
16
18
|
accepts_nested_attributes_for :return_items
|
@@ -18,8 +20,12 @@ module Spree
|
|
18
20
|
extend DisplayMoney
|
19
21
|
money_methods pre_tax_total: { currency: Spree::Config[:currency] }
|
20
22
|
|
21
|
-
def
|
22
|
-
return_items.
|
23
|
+
def completely_decided?
|
24
|
+
!return_items.undecided.exists?
|
25
|
+
end
|
26
|
+
|
27
|
+
def fully_reimbursed?
|
28
|
+
completely_decided? && return_items.accepted.includes(:reimbursement).all? { |return_item| return_item.reimbursement.try(:reimbursed?) }
|
23
29
|
end
|
24
30
|
|
25
31
|
# Temporarily tie a customer_return to one order
|
@@ -32,15 +38,21 @@ module Spree
|
|
32
38
|
order.try(:id)
|
33
39
|
end
|
34
40
|
|
35
|
-
def
|
36
|
-
|
41
|
+
def pre_tax_total
|
42
|
+
return_items.sum(:pre_tax_amount)
|
37
43
|
end
|
38
44
|
|
39
|
-
|
40
|
-
|
45
|
+
private
|
46
|
+
|
47
|
+
def inventory_units
|
48
|
+
return_items.flat_map(&:inventory_unit)
|
41
49
|
end
|
42
50
|
|
43
|
-
|
51
|
+
def must_have_return_authorization
|
52
|
+
if item = return_items.find { |ri| ri.return_authorization.blank? }
|
53
|
+
errors.add(:base, Spree.t(:missing_return_authorization, item_name: item.inventory_unit.variant.name))
|
54
|
+
end
|
55
|
+
end
|
44
56
|
|
45
57
|
def generate_number
|
46
58
|
self.number ||= loop do
|
@@ -55,15 +67,9 @@ module Spree
|
|
55
67
|
end
|
56
68
|
|
57
69
|
def return_items_belong_to_same_order
|
58
|
-
if return_items.select{ |return_item| return_item.inventory_unit.order_id != order_id }.any?
|
70
|
+
if return_items.select { |return_item| return_item.inventory_unit.order_id != order_id }.any?
|
59
71
|
errors.add(:base, Spree.t(:return_items_cannot_be_associated_with_multiple_orders))
|
60
72
|
end
|
61
73
|
end
|
62
|
-
|
63
|
-
def inventory_units
|
64
|
-
return_items.flat_map(&:inventory_unit)
|
65
|
-
end
|
66
|
-
|
67
74
|
end
|
68
75
|
end
|
69
|
-
|
@@ -60,6 +60,10 @@ module Spree
|
|
60
60
|
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, :test => true, :authorization => '12345')
|
61
61
|
end
|
62
62
|
|
63
|
+
def cancel(_response_code)
|
64
|
+
ActiveMerchant::Billing::Response.new(true, 'Bogus Gateway: Forced success', {}, test: true, authorization: '12345')
|
65
|
+
end
|
66
|
+
|
63
67
|
def test?
|
64
68
|
# Test mode is not really relevant with bogus gateway (no such thing as live server)
|
65
69
|
true
|
@@ -132,8 +132,8 @@ module Spree
|
|
132
132
|
|
133
133
|
def update_adjustments
|
134
134
|
if quantity_changed?
|
135
|
-
update_tax_charge # Called to ensure pre_tax_amount is updated.
|
136
135
|
recalculate_adjustments
|
136
|
+
update_tax_charge # Called to ensure pre_tax_amount is updated.
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
data/app/models/spree/order.rb
CHANGED
@@ -58,7 +58,7 @@ module Spree
|
|
58
58
|
alias_attribute :shipping_address, :ship_address
|
59
59
|
|
60
60
|
belongs_to :store, class_name: 'Spree::Store'
|
61
|
-
has_many :state_changes, as: :stateful
|
61
|
+
has_many :state_changes, as: :stateful, dependent: :destroy
|
62
62
|
has_many :line_items, -> { order("#{LineItem.table_name}.created_at ASC") }, dependent: :destroy, inverse_of: :order
|
63
63
|
has_many :payments, dependent: :destroy
|
64
64
|
has_many :return_authorizations, dependent: :destroy, inverse_of: :order
|
@@ -70,6 +70,11 @@ module Spree
|
|
70
70
|
has_many :products, through: :variants
|
71
71
|
has_many :variants, through: :line_items
|
72
72
|
has_many :refunds, through: :payments
|
73
|
+
has_many :all_adjustments,
|
74
|
+
class_name: 'Spree::Adjustment',
|
75
|
+
foreign_key: :order_id,
|
76
|
+
dependent: :destroy,
|
77
|
+
inverse_of: :order
|
73
78
|
|
74
79
|
has_and_belongs_to_many :promotions, join_table: 'spree_orders_promotions'
|
75
80
|
|
@@ -99,6 +104,7 @@ module Spree
|
|
99
104
|
validate :has_available_shipment
|
100
105
|
|
101
106
|
delegate :update_totals, :persist_totals, :to => :updater
|
107
|
+
delegate :merge!, to: :merger
|
102
108
|
|
103
109
|
class_attribute :update_hooks
|
104
110
|
self.update_hooks = Set.new
|
@@ -132,11 +138,6 @@ module Spree
|
|
132
138
|
self.line_item_comparison_hooks.add(hook)
|
133
139
|
end
|
134
140
|
|
135
|
-
def all_adjustments
|
136
|
-
Adjustment.where("order_id = :order_id OR (adjustable_id = :order_id AND adjustable_type = 'Spree::Order')",
|
137
|
-
order_id: self.id)
|
138
|
-
end
|
139
|
-
|
140
141
|
# For compatiblity with Calculator::PriceSack
|
141
142
|
def amount
|
142
143
|
line_items.inject(0.0) { |sum, li| sum + li.amount }
|
@@ -205,6 +206,10 @@ module Spree
|
|
205
206
|
updater.update
|
206
207
|
end
|
207
208
|
|
209
|
+
def merger
|
210
|
+
@merger ||= Spree::OrderMerger.new(self)
|
211
|
+
end
|
212
|
+
|
208
213
|
def clone_billing_address
|
209
214
|
if bill_address and self.ship_address.nil?
|
210
215
|
self.ship_address = bill_address.clone
|
@@ -229,16 +234,17 @@ module Spree
|
|
229
234
|
|
230
235
|
# Associates the specified user with the order.
|
231
236
|
def associate_user!(user, override_email = true)
|
232
|
-
self.user
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
+
self.user = user
|
238
|
+
self.email = user.email if override_email
|
239
|
+
self.created_by ||= user
|
240
|
+
self.bill_address ||= user.bill_address
|
241
|
+
self.ship_address ||= user.ship_address
|
237
242
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
243
|
+
changes = slice(:user_id, :email, :created_by_id, :bill_address_id, :ship_address_id)
|
244
|
+
|
245
|
+
# immediately persist the changes we just made, but don't use save
|
246
|
+
# since we might have an invalid address associated
|
247
|
+
self.class.unscoped.where(id: self).update_all(changes)
|
242
248
|
end
|
243
249
|
|
244
250
|
def quantity_of(variant, options = {})
|
@@ -395,44 +401,12 @@ module Spree
|
|
395
401
|
end
|
396
402
|
end
|
397
403
|
|
398
|
-
def merge!(order, user = nil)
|
399
|
-
order.line_items.each do |other_order_line_item|
|
400
|
-
next unless other_order_line_item.currency == currency
|
401
|
-
|
402
|
-
# Compare the line items of the other order with mine.
|
403
|
-
# Make sure you allow any extensions to chime in on whether or
|
404
|
-
# not the extension-specific parts of the line item match
|
405
|
-
current_line_item = self.line_items.detect { |my_li|
|
406
|
-
my_li.variant == other_order_line_item.variant &&
|
407
|
-
self.line_item_comparison_hooks.all? { |hook|
|
408
|
-
self.send(hook, my_li, other_order_line_item.serializable_hash)
|
409
|
-
}
|
410
|
-
}
|
411
|
-
if current_line_item
|
412
|
-
current_line_item.quantity += other_order_line_item.quantity
|
413
|
-
current_line_item.save!
|
414
|
-
else
|
415
|
-
other_order_line_item.order_id = self.id
|
416
|
-
other_order_line_item.save!
|
417
|
-
end
|
418
|
-
end
|
419
|
-
|
420
|
-
self.associate_user!(user) if !self.user && !user.blank?
|
421
|
-
|
422
|
-
updater.update_item_count
|
423
|
-
updater.update_item_total
|
424
|
-
updater.persist_totals
|
425
|
-
|
426
|
-
# So that the destroy doesn't take out line items which may have been re-assigned
|
427
|
-
order.line_items.reload
|
428
|
-
order.destroy
|
429
|
-
end
|
430
|
-
|
431
404
|
def empty!
|
432
405
|
line_items.destroy_all
|
433
406
|
updater.update_item_count
|
434
407
|
adjustments.destroy_all
|
435
408
|
shipments.destroy_all
|
409
|
+
state_changes.destroy_all
|
436
410
|
|
437
411
|
update_totals
|
438
412
|
persist_totals
|
@@ -1,323 +1,338 @@
|
|
1
1
|
module Spree
|
2
2
|
class Order < Spree::Base
|
3
3
|
module Checkout
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
class_attribute :next_event_transitions
|
8
|
+
class_attribute :previous_states
|
9
|
+
class_attribute :checkout_flow
|
10
|
+
class_attribute :checkout_steps
|
11
|
+
class_attribute :removed_transitions
|
12
|
+
|
13
|
+
self.checkout_steps ||= {}
|
14
|
+
self.next_event_transitions ||= []
|
15
|
+
self.previous_states ||= [:cart]
|
16
|
+
self.removed_transitions ||= []
|
17
|
+
|
18
|
+
def self.checkout_flow(&block)
|
19
|
+
if block_given?
|
20
|
+
@checkout_flow = block
|
21
|
+
define_state_machine!
|
22
|
+
else
|
23
|
+
@checkout_flow
|
19
24
|
end
|
25
|
+
end
|
20
26
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
# Build the checkout flow using the checkout_flow defined either
|
28
|
-
# within the Order class, or a decorator for that class.
|
29
|
-
#
|
30
|
-
# This method may be called multiple times depending on if the
|
31
|
-
# checkout_flow is re-defined in a decorator or not.
|
32
|
-
instance_eval(&checkout_flow)
|
33
|
-
|
34
|
-
klass = self
|
35
|
-
|
36
|
-
# To avoid a ton of warnings when the state machine is re-defined
|
37
|
-
StateMachines::Machine.ignore_method_conflicts = true
|
38
|
-
# To avoid multiple occurrences of the same transition being defined
|
39
|
-
# On first definition, state_machines will not be defined
|
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)) }
|
43
|
-
|
44
|
-
# Persist the state on the order
|
45
|
-
after_transition do |order, transition|
|
46
|
-
order.state = order.state
|
47
|
-
order.state_changes.create(
|
48
|
-
previous_state: transition.from,
|
49
|
-
next_state: transition.to,
|
50
|
-
name: 'order',
|
51
|
-
user_id: order.user_id
|
52
|
-
)
|
53
|
-
order.save
|
54
|
-
end
|
27
|
+
def self.define_state_machine!
|
28
|
+
self.checkout_steps = {}
|
29
|
+
self.next_event_transitions = []
|
30
|
+
self.previous_states = [:cart]
|
31
|
+
self.removed_transitions = []
|
55
32
|
|
56
|
-
|
57
|
-
|
58
|
-
|
33
|
+
# Build the checkout flow using the checkout_flow defined either
|
34
|
+
# within the Order class, or a decorator for that class.
|
35
|
+
#
|
36
|
+
# This method may be called multiple times depending on if the
|
37
|
+
# checkout_flow is re-defined in a decorator or not.
|
38
|
+
instance_eval(&checkout_flow)
|
39
|
+
|
40
|
+
klass = self
|
41
|
+
|
42
|
+
# To avoid a ton of warnings when the state machine is re-defined
|
43
|
+
StateMachines::Machine.ignore_method_conflicts = true
|
44
|
+
# To avoid multiple occurrences of the same transition being defined
|
45
|
+
# On first definition, state_machines will not be defined
|
46
|
+
state_machines.clear if respond_to?(:state_machines)
|
47
|
+
state_machine :state, initial: :cart, use_transactions: false, action: :save_state do
|
48
|
+
klass.next_event_transitions.each { |t| transition(t.merge(on: :next)) }
|
49
|
+
|
50
|
+
# Persist the state on the order
|
51
|
+
after_transition do |order, transition|
|
52
|
+
order.state = order.state
|
53
|
+
order.state_changes.create(
|
54
|
+
previous_state: transition.from,
|
55
|
+
next_state: transition.to,
|
56
|
+
name: 'order',
|
57
|
+
user_id: order.user_id
|
58
|
+
)
|
59
|
+
order.save
|
60
|
+
end
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
-
|
62
|
+
event :cancel do
|
63
|
+
transition to: :canceled, if: :allow_cancel?
|
64
|
+
end
|
63
65
|
|
64
|
-
|
65
|
-
|
66
|
-
|
66
|
+
event :return do
|
67
|
+
transition to: :returned,
|
68
|
+
from: [:complete, :awaiting_return, :canceled],
|
69
|
+
if: :all_inventory_units_returned?
|
70
|
+
end
|
67
71
|
|
68
|
-
|
69
|
-
|
70
|
-
|
72
|
+
event :resume do
|
73
|
+
transition to: :resumed, from: :canceled, if: :canceled?
|
74
|
+
end
|
75
|
+
|
76
|
+
event :authorize_return do
|
77
|
+
transition to: :awaiting_return
|
78
|
+
end
|
71
79
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
end
|
80
|
+
if states[:payment]
|
81
|
+
before_transition to: :complete do |order|
|
82
|
+
if order.payment_required? && order.payments.valid.empty?
|
83
|
+
order.errors.add(:base, Spree.t(:no_payment_found))
|
84
|
+
false
|
85
|
+
elsif order.payment_required?
|
86
|
+
order.process_payments!
|
80
87
|
end
|
81
|
-
after_transition to: :complete, do: :persist_user_credit_card
|
82
|
-
before_transition to: :payment, do: :set_shipments_cost
|
83
|
-
before_transition to: :payment, do: :create_tax_charge!
|
84
|
-
before_transition to: :payment, do: :assign_default_credit_card
|
85
88
|
end
|
89
|
+
after_transition to: :complete, do: :persist_user_credit_card
|
90
|
+
before_transition to: :payment, do: :set_shipments_cost
|
91
|
+
before_transition to: :payment, do: :create_tax_charge!
|
92
|
+
before_transition to: :payment, do: :assign_default_credit_card
|
93
|
+
end
|
86
94
|
|
87
|
-
|
95
|
+
before_transition from: :cart, do: :ensure_line_items_present
|
88
96
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
97
|
+
if states[:address]
|
98
|
+
before_transition from: :address, do: :create_tax_charge!
|
99
|
+
before_transition to: :address, do: :assign_default_addresses!
|
100
|
+
before_transition from: :address, do: :persist_user_address!
|
101
|
+
end
|
94
102
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
103
|
+
if states[:delivery]
|
104
|
+
before_transition to: :delivery, do: :create_proposed_shipments
|
105
|
+
before_transition to: :delivery, do: :ensure_available_shipping_rates
|
106
|
+
before_transition to: :delivery, do: :set_shipments_cost
|
107
|
+
before_transition from: :delivery, do: :apply_free_shipping_promotions
|
108
|
+
end
|
101
109
|
|
102
|
-
|
103
|
-
|
110
|
+
before_transition to: :resumed, do: :ensure_line_item_variants_are_not_deleted
|
111
|
+
before_transition to: :resumed, do: :ensure_line_items_are_in_stock
|
104
112
|
|
105
|
-
|
106
|
-
|
113
|
+
before_transition to: :complete, do: :ensure_line_item_variants_are_not_deleted
|
114
|
+
before_transition to: :complete, do: :ensure_line_items_are_in_stock
|
107
115
|
|
108
|
-
|
109
|
-
|
110
|
-
|
116
|
+
after_transition to: :complete, do: :finalize!
|
117
|
+
after_transition to: :resumed, do: :after_resume
|
118
|
+
after_transition to: :canceled, do: :after_cancel
|
111
119
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
120
|
+
after_transition from: any - :cart, to: any - [:confirm, :complete] do |order|
|
121
|
+
order.update_totals
|
122
|
+
order.persist_totals
|
116
123
|
end
|
124
|
+
end
|
117
125
|
|
118
|
-
|
126
|
+
alias_method :save_state, :save
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.go_to_state(name, options = {})
|
130
|
+
self.checkout_steps[name] = options
|
131
|
+
previous_states.each do |state|
|
132
|
+
add_transition({ from: state, to: name }.merge(options))
|
133
|
+
end
|
134
|
+
if options[:if]
|
135
|
+
previous_states << name
|
136
|
+
else
|
137
|
+
self.previous_states = [name]
|
119
138
|
end
|
139
|
+
end
|
120
140
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
141
|
+
def self.insert_checkout_step(name, options = {})
|
142
|
+
before = options.delete(:before)
|
143
|
+
after = options.delete(:after) unless before
|
144
|
+
after = self.checkout_steps.keys.last unless before || after
|
145
|
+
|
146
|
+
cloned_steps = self.checkout_steps.clone
|
147
|
+
cloned_removed_transitions = self.removed_transitions.clone
|
148
|
+
checkout_flow do
|
149
|
+
cloned_steps.each_pair do |key, value|
|
150
|
+
go_to_state(name, options) if key == before
|
151
|
+
go_to_state(key, value)
|
152
|
+
go_to_state(name, options) if key == after
|
125
153
|
end
|
126
|
-
|
127
|
-
|
128
|
-
else
|
129
|
-
self.previous_states = [name]
|
154
|
+
cloned_removed_transitions.each do |transition|
|
155
|
+
remove_transition(transition)
|
130
156
|
end
|
131
157
|
end
|
158
|
+
end
|
132
159
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
cloned_removed_transitions = self.removed_transitions.clone
|
140
|
-
self.checkout_flow do
|
141
|
-
cloned_steps.each_pair do |key, value|
|
142
|
-
self.go_to_state(name, options) if key == before
|
143
|
-
self.go_to_state(key, value)
|
144
|
-
self.go_to_state(name, options) if key == after
|
145
|
-
end
|
146
|
-
cloned_removed_transitions.each do |transition|
|
147
|
-
self.remove_transition(transition)
|
148
|
-
end
|
160
|
+
def self.remove_checkout_step(name)
|
161
|
+
cloned_steps = self.checkout_steps.clone
|
162
|
+
cloned_removed_transitions = self.removed_transitions.clone
|
163
|
+
checkout_flow do
|
164
|
+
cloned_steps.each_pair do |key, value|
|
165
|
+
go_to_state(key, value) unless key == name
|
149
166
|
end
|
150
|
-
|
151
|
-
|
152
|
-
def self.remove_checkout_step(name)
|
153
|
-
cloned_steps = self.checkout_steps.clone
|
154
|
-
cloned_removed_transitions = self.removed_transitions.clone
|
155
|
-
self.checkout_flow do
|
156
|
-
cloned_steps.each_pair do |key, value|
|
157
|
-
self.go_to_state(key, value) unless key == name
|
158
|
-
end
|
159
|
-
cloned_removed_transitions.each do |transition|
|
160
|
-
self.remove_transition(transition)
|
161
|
-
end
|
167
|
+
cloned_removed_transitions.each do |transition|
|
168
|
+
remove_transition(transition)
|
162
169
|
end
|
163
170
|
end
|
171
|
+
end
|
164
172
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
def self.find_transition(options={})
|
171
|
-
return nil if options.nil? || !options.include?(:from) || !options.include?(:to)
|
172
|
-
self.next_event_transitions.detect do |transition|
|
173
|
-
transition[options[:from].to_sym] == options[:to].to_sym
|
174
|
-
end
|
175
|
-
end
|
173
|
+
def self.remove_transition(options = {})
|
174
|
+
self.removed_transitions << options
|
175
|
+
self.next_event_transitions.delete(find_transition(options))
|
176
|
+
end
|
176
177
|
|
177
|
-
|
178
|
-
|
178
|
+
def self.find_transition(options = {})
|
179
|
+
return nil if options.nil? || !options.include?(:from) || !options.include?(:to)
|
180
|
+
self.next_event_transitions.detect do |transition|
|
181
|
+
transition[options[:from].to_sym] == options[:to].to_sym
|
179
182
|
end
|
183
|
+
end
|
180
184
|
|
181
|
-
|
182
|
-
|
183
|
-
|
185
|
+
def self.checkout_step_names
|
186
|
+
self.checkout_steps.keys
|
187
|
+
end
|
184
188
|
|
185
|
-
|
186
|
-
|
187
|
-
|
189
|
+
def self.add_transition(options)
|
190
|
+
self.next_event_transitions << { options.delete(:from) => options.delete(:to) }.merge(options)
|
191
|
+
end
|
188
192
|
|
189
|
-
|
190
|
-
|
191
|
-
|
193
|
+
def checkout_steps
|
194
|
+
steps = (self.class.checkout_steps.each_with_object([]) do |(step, options), checkout_steps|
|
195
|
+
next if options.include?(:if) && !options[:if].call(self)
|
196
|
+
checkout_steps << step
|
197
|
+
end).map(&:to_s)
|
198
|
+
# Ensure there is always a complete step
|
199
|
+
steps << "complete" unless steps.include?("complete")
|
200
|
+
steps
|
201
|
+
end
|
192
202
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
checkout_steps << step
|
197
|
-
}.map(&:to_s)
|
198
|
-
# Ensure there is always a complete step
|
199
|
-
steps << "complete" unless steps.include?("complete")
|
200
|
-
steps
|
201
|
-
end
|
203
|
+
def has_checkout_step?(step)
|
204
|
+
step.present? && self.checkout_steps.include?(step)
|
205
|
+
end
|
202
206
|
|
203
|
-
|
204
|
-
|
205
|
-
|
207
|
+
def passed_checkout_step?(step)
|
208
|
+
has_checkout_step?(step) && checkout_step_index(step) < checkout_step_index(state)
|
209
|
+
end
|
206
210
|
|
207
|
-
|
208
|
-
|
209
|
-
|
211
|
+
def checkout_step_index(step)
|
212
|
+
self.checkout_steps.index(step).to_i
|
213
|
+
end
|
210
214
|
|
211
|
-
|
212
|
-
|
213
|
-
|
215
|
+
def can_go_to_state?(state)
|
216
|
+
return false unless has_checkout_step?(self.state) && has_checkout_step?(state)
|
217
|
+
checkout_step_index(state) > checkout_step_index(self.state)
|
218
|
+
end
|
214
219
|
|
215
|
-
|
216
|
-
@removed_transitions ||= []
|
217
|
-
end
|
220
|
+
define_callbacks :updating_from_params, terminator: ->(_target, result) { result == false }
|
218
221
|
|
219
|
-
|
220
|
-
return false unless has_checkout_step?(self.state) && has_checkout_step?(state)
|
221
|
-
checkout_step_index(state) > checkout_step_index(self.state)
|
222
|
-
end
|
222
|
+
set_callback :updating_from_params, :before, :update_params_payment_source
|
223
223
|
|
224
|
-
|
224
|
+
def update_from_params(params, permitted_params, request_env = {})
|
225
|
+
success = false
|
226
|
+
@updating_params = params
|
227
|
+
run_callbacks :updating_from_params do
|
228
|
+
# Set existing card after setting permitted parameters because
|
229
|
+
# rails would slice parameters containg ruby objects, apparently
|
230
|
+
existing_card_id = @updating_params[:order] ? @updating_params[:order].delete(:existing_card) : nil
|
225
231
|
|
226
|
-
|
232
|
+
attributes = @updating_params[:order] ? @updating_params[:order].permit(permitted_params).delete_if { |_k, v| v.nil? } : {}
|
227
233
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
# rails would slice parameters containg ruby objects, apparently
|
234
|
-
existing_card_id = @updating_params[:order] ? @updating_params[:order].delete(:existing_card) : nil
|
234
|
+
if existing_card_id.present?
|
235
|
+
credit_card = CreditCard.find existing_card_id
|
236
|
+
if credit_card.user_id != user_id || credit_card.user_id.blank?
|
237
|
+
raise Core::GatewayError.new Spree.t(:invalid_credit_card)
|
238
|
+
end
|
235
239
|
|
236
|
-
|
240
|
+
credit_card.verification_value = params[:cvc_confirm] if params[:cvc_confirm].present?
|
237
241
|
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
end
|
242
|
+
attributes[:payments_attributes].first[:source] = credit_card
|
243
|
+
attributes[:payments_attributes].first[:payment_method_id] = credit_card.payment_method_id
|
244
|
+
attributes[:payments_attributes].first.delete :source_attributes
|
245
|
+
end
|
243
246
|
|
244
|
-
|
247
|
+
if attributes[:payments_attributes]
|
248
|
+
attributes[:payments_attributes].first[:request_env] = request_env
|
249
|
+
end
|
245
250
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
end
|
251
|
+
success = update_attributes(attributes)
|
252
|
+
set_shipments_cost if shipments.any?
|
253
|
+
end
|
250
254
|
|
251
|
-
|
252
|
-
|
253
|
-
|
255
|
+
@updating_params = nil
|
256
|
+
success
|
257
|
+
end
|
254
258
|
|
255
|
-
|
256
|
-
|
257
|
-
|
259
|
+
def assign_default_addresses!
|
260
|
+
if user
|
261
|
+
clone_billing
|
262
|
+
# Skip setting ship address if order doesn't have a delivery checkout step
|
263
|
+
# to avoid triggering validations on shipping address
|
264
|
+
clone_shipping if checkout_steps.include?("delivery")
|
265
|
+
end
|
266
|
+
end
|
258
267
|
|
259
|
-
|
260
|
-
|
268
|
+
def clone_billing
|
269
|
+
if !bill_address_id && user.bill_address.try(:valid?)
|
270
|
+
self.bill_address = user.bill_address.try(:clone)
|
261
271
|
end
|
272
|
+
end
|
262
273
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
# Skip setting ship address if order doesn't have a delivery checkout step
|
267
|
-
# to avoid triggering validations on shipping address
|
268
|
-
self.ship_address = user.ship_address.try(:clone) if !self.ship_address_id && user.ship_address.try(:valid?) && self.checkout_steps.include?("delivery")
|
269
|
-
end
|
274
|
+
def clone_shipping
|
275
|
+
if !ship_address_id && user.ship_address.try(:valid?)
|
276
|
+
self.ship_address = user.ship_address.try(:clone)
|
270
277
|
end
|
278
|
+
end
|
271
279
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
end
|
280
|
+
def persist_user_address!
|
281
|
+
if !temporary_address && user && user.respond_to?(:persist_order_address) && bill_address_id
|
282
|
+
user.persist_order_address(self)
|
276
283
|
end
|
284
|
+
end
|
277
285
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
end
|
286
|
+
def persist_user_credit_card
|
287
|
+
if !temporary_credit_card && user_id && valid_credit_cards.present?
|
288
|
+
default_cc = valid_credit_cards.first
|
289
|
+
default_cc.user_id = user_id
|
290
|
+
default_cc.default = true
|
291
|
+
default_cc.save
|
285
292
|
end
|
293
|
+
end
|
286
294
|
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
end
|
295
|
+
def assign_default_credit_card
|
296
|
+
if payments.from_credit_card.count == 0 && user_has_valid_default_card? && payment_required?
|
297
|
+
cc = user.default_credit_card
|
298
|
+
payments.create!(payment_method_id: cc.payment_method_id, source: cc, amount: total)
|
292
299
|
end
|
300
|
+
end
|
293
301
|
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
# for payment methods other than the one selected
|
298
|
-
#
|
299
|
-
# In case a existing credit card is provided it needs to build the payment
|
300
|
-
# attributes from scratch so we can set the amount. example payload:
|
301
|
-
#
|
302
|
-
# {
|
303
|
-
# "order": {
|
304
|
-
# "existing_card": "2"
|
305
|
-
# }
|
306
|
-
# }
|
307
|
-
#
|
308
|
-
def update_params_payment_source
|
309
|
-
if @updating_params[:payment_source].present?
|
310
|
-
source_params = @updating_params.delete(:payment_source)[@updating_params[:order][:payments_attributes].first[:payment_method_id].to_s]
|
302
|
+
def user_has_valid_default_card?
|
303
|
+
user && user.default_credit_card.try(:valid?)
|
304
|
+
end
|
311
305
|
|
312
|
-
|
313
|
-
|
314
|
-
|
306
|
+
private
|
307
|
+
|
308
|
+
# For payment step, filter order parameters to produce the expected nested
|
309
|
+
# attributes for a single payment and its source, discarding attributes
|
310
|
+
# for payment methods other than the one selected
|
311
|
+
#
|
312
|
+
# In case a existing credit card is provided it needs to build the payment
|
313
|
+
# attributes from scratch so we can set the amount. example payload:
|
314
|
+
#
|
315
|
+
# {
|
316
|
+
# "order": {
|
317
|
+
# "existing_card": "2"
|
318
|
+
# }
|
319
|
+
# }
|
320
|
+
#
|
321
|
+
def update_params_payment_source
|
322
|
+
if @updating_params[:payment_source].present?
|
323
|
+
source_params = @updating_params.
|
324
|
+
delete(:payment_source)[@updating_params[:order][:payments_attributes].
|
325
|
+
first[:payment_method_id].to_s]
|
326
|
+
|
327
|
+
if source_params
|
328
|
+
@updating_params[:order][:payments_attributes].first[:source_attributes] = source_params
|
315
329
|
end
|
330
|
+
end
|
316
331
|
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
332
|
+
if @updating_params[:order] && (@updating_params[:order][:payments_attributes] ||
|
333
|
+
@updating_params[:order][:existing_card])
|
334
|
+
@updating_params[:order][:payments_attributes] ||= [{}]
|
335
|
+
@updating_params[:order][:payments_attributes].first[:amount] = total
|
321
336
|
end
|
322
337
|
end
|
323
338
|
end
|