spree_core 3.0.1 → 3.0.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.
- 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
|