spree_core 2.0.4 → 2.0.5
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/mailers/spree/test_mailer.rb +2 -1
- data/app/models/spree/adjustment.rb +18 -7
- data/app/models/spree/alert.rb +1 -1
- data/app/models/spree/credit_card.rb +2 -2
- data/app/models/spree/inventory_unit.rb +5 -0
- data/app/models/spree/line_item.rb +18 -4
- data/app/models/spree/order/checkout.rb +10 -14
- data/app/models/spree/order.rb +39 -27
- data/app/models/spree/order_contents.rb +2 -2
- data/app/models/spree/order_inventory.rb +2 -9
- data/app/models/spree/order_updater.rb +18 -7
- data/app/models/spree/payment/processing.rb +102 -102
- data/app/models/spree/payment.rb +10 -9
- data/app/models/spree/preferences/store.rb +1 -1
- data/app/models/spree/product.rb +11 -2
- data/app/models/spree/promotion/rules/user.rb +7 -2
- data/app/models/spree/property.rb +1 -1
- data/app/models/spree/shipment.rb +12 -7
- data/app/models/spree/shipping_method.rb +1 -1
- data/app/models/spree/stock/availability_validator.rb +9 -1
- data/app/models/spree/stock/coordinator.rb +1 -3
- data/app/models/spree/stock/estimator.rb +1 -1
- data/app/models/spree/stock/packer.rb +13 -5
- data/app/models/spree/stock_item.rb +10 -1
- data/app/models/spree/stock_location.rb +1 -1
- data/app/models/spree/tax_rate.rb +3 -1
- data/app/models/spree/zone.rb +37 -1
- data/app/views/spree/test_mailer/test_email.text.erb +4 -0
- data/config/locales/en.yml +24 -1
- data/db/default/spree/roles.rb +2 -2
- data/db/migrate/20130213191427_create_default_stock.rb +2 -1
- data/db/migrate/20130802022321_migrate_tax_categories_to_line_items.rb +1 -1
- data/db/migrate/20130806022521_drop_spree_mail_methods.rb +12 -0
- data/db/migrate/20130806145853_set_default_stock_location_on_shipments.rb +8 -0
- data/db/migrate/20130809164245_add_admin_name_column_to_spree_shipping_methods.rb +5 -0
- data/db/migrate/20130809164330_add_admin_name_column_to_spree_stock_locations.rb +5 -0
- data/db/migrate/20130813140619_expand_order_number_size.rb +9 -0
- data/db/migrate/20130826062534_add_depth_to_spree_taxons.rb +16 -0
- data/db/migrate/20130915032339_add_deleted_at_to_spree_stock_items.rb +5 -0
- data/lib/generators/spree/dummy/templates/rails/database.yml +0 -3
- data/lib/spree/core/engine.rb +1 -5
- data/lib/spree/core/permalinks.rb +4 -2
- data/lib/spree/core/version.rb +1 -1
- data/lib/spree/promo/coupon_applicator.rb +2 -2
- data/lib/spree/testing_support/capybara_ext.rb +2 -2
- data/lib/spree/testing_support/factories/order_factory.rb +8 -4
- metadata +17 -12
- data/app/models/spree/new_adjustment.rb +0 -4
- data/app/models/spree/shipping_adjustment.rb +0 -4
- data/db/migrate/20130805043440_create_spree_new_adjustments.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4eb6afc84186dd0b46d37daebea561e582412dfb
|
4
|
+
data.tar.gz: 96ad34a945d823c7f0ce6d17797c183eee5f4261
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a990089d87e7ed851394892fe747d8146fdd7992a1f1d70f1cb7447a6471c937dd1faf5380cbec891844f048049b5f8b65312bae912ae69cf46690f235e2dab
|
7
|
+
data.tar.gz: c13e7f2071ddac220493da65f4407b25e00cf0704a7d205637c67513487f7646e60ab209d43ed3a2365d81d473420c6fb56760331a7598dc2e66c7966837e9e6
|
@@ -1,8 +1,9 @@
|
|
1
1
|
module Spree
|
2
2
|
class TestMailer < BaseMailer
|
3
3
|
def test_email(user)
|
4
|
+
recipient = user.respond_to?(:id) ? user : Spree.user_class.find(user)
|
4
5
|
subject = "#{Spree::Config[:site_name]} #{Spree.t('test_mailer.test_email.subject')}"
|
5
|
-
mail(to:
|
6
|
+
mail(to: recipient.email, from: from_address, subject: subject)
|
6
7
|
end
|
7
8
|
end
|
8
9
|
end
|
@@ -60,10 +60,14 @@ module Spree
|
|
60
60
|
scope :promotion, -> { where(originator_type: 'Spree::PromotionAction') }
|
61
61
|
scope :return_authorization, -> { where(source_type: "Spree::ReturnAuthorization") }
|
62
62
|
|
63
|
+
def promotion?
|
64
|
+
originator_type == 'Spree::PromotionAction'
|
65
|
+
end
|
66
|
+
|
63
67
|
# Update the boolean _eligible_ attribute which determines which adjustments
|
64
68
|
# count towards the order's adjustment_total.
|
65
69
|
def set_eligibility
|
66
|
-
result =
|
70
|
+
result = mandatory || ((amount != 0 || promotion?) && eligible_for_originator?)
|
67
71
|
update_attribute_without_callbacks(:eligible, result)
|
68
72
|
end
|
69
73
|
|
@@ -74,20 +78,27 @@ module Spree
|
|
74
78
|
!originator.respond_to?(:eligible?) || originator.eligible?(source)
|
75
79
|
end
|
76
80
|
|
77
|
-
# Update both the eligibility and amount of the adjustment. Adjustments
|
81
|
+
# Update both the eligibility and amount of the adjustment. Adjustments
|
78
82
|
# delegate updating of amount to their Originator when present, but only if
|
79
83
|
# +locked+ is false. Adjustments that are +locked+ will never change their amount.
|
80
84
|
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
|
85
|
+
# Adjustments delegate updating of amount to their Originator when present,
|
86
|
+
# but only if when they're in "open" state, closed or finalized adjustments
|
87
|
+
# are not recalculated.
|
88
|
+
#
|
89
|
+
# It receives +calculable+ as the updated source here so calculations can be
|
90
|
+
# performed on the current values of that source. If we used +source+ it
|
91
|
+
# could load the old record from db for the association. e.g. when updating
|
92
|
+
# more than on line items at once via accepted_nested_attributes the order
|
93
|
+
# object on the association would be in a old state and therefore the
|
94
|
+
# adjustment calculations would not performed on proper values
|
95
|
+
def update!(calculable = nil)
|
85
96
|
return if immutable?
|
86
97
|
# Fix for #3381
|
87
98
|
# If we attempt to call 'source' before the reload, then source is currently
|
88
99
|
# the order object. After calling a reload, the source is the Shipment.
|
89
100
|
reload
|
90
|
-
originator.update_adjustment(self, source) if originator.present?
|
101
|
+
originator.update_adjustment(self, calculable || source) if originator.present?
|
91
102
|
set_eligibility
|
92
103
|
end
|
93
104
|
|
data/app/models/spree/alert.rb
CHANGED
@@ -11,7 +11,7 @@ module Spree
|
|
11
11
|
rails_version: Rails.version
|
12
12
|
}
|
13
13
|
|
14
|
-
HTTParty.get('http://alerts.spreecommerce.com/alerts.json',
|
14
|
+
HTTParty.get('http://alerts.spreecommerce.com/alerts.json', query: params).parsed_response
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -105,9 +105,9 @@ module Spree
|
|
105
105
|
private
|
106
106
|
|
107
107
|
def expiry_not_in_the_past
|
108
|
-
if year && month
|
108
|
+
if year.present? && month.present?
|
109
109
|
time = "#{year}-#{month}-1".to_time
|
110
|
-
if time < Time.zone.now.beginning_of_month
|
110
|
+
if time < Time.zone.now.to_time.beginning_of_month
|
111
111
|
errors.add(:base, :card_expired)
|
112
112
|
end
|
113
113
|
end
|
@@ -85,15 +85,29 @@ module Spree
|
|
85
85
|
@preferred_shipment = shipment
|
86
86
|
end
|
87
87
|
|
88
|
+
# Remove product default_scope `deleted_at: nil`
|
89
|
+
def product
|
90
|
+
variant.product
|
91
|
+
end
|
92
|
+
|
93
|
+
# Remove variant default_scope `deleted_at: nil`
|
94
|
+
def variant
|
95
|
+
Spree::Variant.unscoped { super }
|
96
|
+
end
|
97
|
+
|
88
98
|
private
|
89
99
|
def update_inventory
|
90
|
-
|
100
|
+
if changed?
|
101
|
+
Spree::OrderInventory.new(self.order).verify(self, target_shipment)
|
102
|
+
end
|
91
103
|
end
|
92
104
|
|
93
105
|
def update_order
|
94
|
-
|
95
|
-
|
96
|
-
|
106
|
+
if changed? || destroyed?
|
107
|
+
# update the order totals, etc.
|
108
|
+
order.create_tax_charge!
|
109
|
+
order.update!
|
110
|
+
end
|
97
111
|
end
|
98
112
|
end
|
99
113
|
end
|
@@ -63,10 +63,12 @@ module Spree
|
|
63
63
|
transition :to => :awaiting_return
|
64
64
|
end
|
65
65
|
|
66
|
-
|
67
|
-
|
68
|
-
|
66
|
+
if states[:payment]
|
67
|
+
before_transition :to => :complete do |order|
|
68
|
+
order.process_payments! if order.payment_required?
|
69
|
+
end
|
69
70
|
end
|
71
|
+
|
70
72
|
before_transition :from => :cart, :do => :ensure_line_items_present
|
71
73
|
|
72
74
|
before_transition :to => :delivery, :do => :create_proposed_shipments
|
@@ -125,12 +127,11 @@ module Spree
|
|
125
127
|
|
126
128
|
def self.remove_transition(options={})
|
127
129
|
self.removed_transitions << options
|
128
|
-
|
129
|
-
self.next_event_transitions.delete(transition)
|
130
|
-
end
|
130
|
+
self.next_event_transitions.delete(find_transition(options))
|
131
131
|
end
|
132
132
|
|
133
133
|
def self.find_transition(options={})
|
134
|
+
return nil if options.nil? || !options.include?(:from) || !options.include?(:to)
|
134
135
|
self.next_event_transitions.detect do |transition|
|
135
136
|
transition[options[:from].to_sym] == options[:to].to_sym
|
136
137
|
end
|
@@ -149,15 +150,10 @@ module Spree
|
|
149
150
|
end
|
150
151
|
|
151
152
|
def checkout_steps
|
152
|
-
|
153
|
-
|
154
|
-
self.class.checkout_steps.each do |step, options|
|
155
|
-
if options[:if]
|
156
|
-
next unless options[:if].call(self)
|
157
|
-
end
|
153
|
+
steps = self.class.checkout_steps.each_with_object([]) { |(step, options), checkout_steps|
|
154
|
+
next if options.include?(:if) && !options[:if].call(self)
|
158
155
|
checkout_steps << step
|
159
|
-
|
160
|
-
steps = checkout_steps.map(&:to_s)
|
156
|
+
}.map(&:to_s)
|
161
157
|
# Ensure there is always a complete step
|
162
158
|
steps << "complete" unless steps.include?("complete")
|
163
159
|
steps
|
data/app/models/spree/order.rb
CHANGED
@@ -47,9 +47,12 @@ module Spree
|
|
47
47
|
belongs_to :ship_address, foreign_key: :ship_address_id, class_name: 'Spree::Address'
|
48
48
|
alias_attribute :shipping_address, :ship_address
|
49
49
|
|
50
|
+
has_many :adjustments, as: :adjustable, dependent: :destroy, order: 'created_at ASC'
|
51
|
+
has_many :line_item_adjustments, through: :line_items, source: :adjustments
|
52
|
+
has_many :line_items, dependent: :destroy, order: 'created_at ASC'
|
53
|
+
has_many :payments, dependent: :destroy
|
54
|
+
has_many :return_authorizations, dependent: :destroy
|
50
55
|
has_many :state_changes, as: :stateful
|
51
|
-
has_many :line_items, dependent: :destroy, order: "#{Spree::LineItem.table_name}.created_at ASC"
|
52
|
-
has_many :payments, dependent: :destroy, :class_name => "Spree::Payment"
|
53
56
|
|
54
57
|
has_many :shipments, dependent: :destroy, :class_name => "Shipment" do
|
55
58
|
def states
|
@@ -57,13 +60,6 @@ module Spree
|
|
57
60
|
end
|
58
61
|
end
|
59
62
|
|
60
|
-
has_many :return_authorizations, dependent: :destroy
|
61
|
-
has_many :adjustments,
|
62
|
-
as: :adjustable,
|
63
|
-
dependent: :destroy,
|
64
|
-
order: "#{Spree::Adjustment.table_name}.created_at ASC",
|
65
|
-
inverse_of: :source
|
66
|
-
|
67
63
|
accepts_nested_attributes_for :line_items
|
68
64
|
accepts_nested_attributes_for :bill_address
|
69
65
|
accepts_nested_attributes_for :ship_address
|
@@ -202,26 +198,26 @@ module Spree
|
|
202
198
|
return tax_zone != Zone.default_tax
|
203
199
|
end
|
204
200
|
|
205
|
-
# Array of adjustments that are inclusive in the variant price. Useful for when
|
206
|
-
# prices include tax (ex. VAT) and you need to record the tax amount separately.
|
207
201
|
def price_adjustments
|
208
|
-
|
209
|
-
|
210
|
-
line_items.each { |line_item| adjustments.concat line_item.adjustments }
|
211
|
-
|
212
|
-
adjustments
|
202
|
+
ActiveSupport::Deprecation.warn("Order#price_adjustments will be deprecated in Spree 2.1, please use Order#line_item_adjustments instead.")
|
203
|
+
self.line_item_adjustments
|
213
204
|
end
|
214
205
|
|
215
|
-
# Array of totals grouped by Adjustment#label. Useful for displaying
|
206
|
+
# Array of totals grouped by Adjustment#label. Useful for displaying line item
|
216
207
|
# adjustments on an invoice. For example, you can display tax breakout for
|
217
208
|
# cases where tax is included in price.
|
218
|
-
def
|
219
|
-
Hash[
|
209
|
+
def line_item_adjustment_totals
|
210
|
+
Hash[self.line_item_adjustments.eligible.group_by(&:label).map do |label, adjustments|
|
220
211
|
total = adjustments.sum(&:amount)
|
221
212
|
[label, Spree::Money.new(total, { currency: currency })]
|
222
213
|
end]
|
223
214
|
end
|
224
215
|
|
216
|
+
def price_adjustment_totals
|
217
|
+
ActiveSupport::Deprecation.warn("Order#price_adjustment_totals will be deprecated in Spree 2.1, please use Order#line_item_adjustment_totals instead.")
|
218
|
+
self.line_item_adjustment_totals
|
219
|
+
end
|
220
|
+
|
225
221
|
def updater
|
226
222
|
@updater ||= OrderUpdater.new(self)
|
227
223
|
end
|
@@ -279,10 +275,11 @@ module Spree
|
|
279
275
|
def associate_user!(user)
|
280
276
|
self.user = user
|
281
277
|
self.email = user.email
|
278
|
+
self.created_by = user if self.created_by.blank?
|
282
279
|
|
283
280
|
if persisted?
|
284
281
|
# immediately persist the changes we just made, but don't use save since we might have an invalid address associated
|
285
|
-
self.class.unscoped.where(id: id).update_all(email: user.email, user_id: user.id)
|
282
|
+
self.class.unscoped.where(id: id).update_all(email: user.email, user_id: user.id, created_by_id: self.created_by_id)
|
286
283
|
end
|
287
284
|
end
|
288
285
|
|
@@ -368,7 +365,7 @@ module Spree
|
|
368
365
|
touch :completed_at
|
369
366
|
|
370
367
|
# lock all adjustments (coupon promotions, etc.)
|
371
|
-
adjustments.
|
368
|
+
adjustments.update_all state: 'closed'
|
372
369
|
|
373
370
|
# update payment and shipment(s) states, and save
|
374
371
|
updater.update_payment_state
|
@@ -462,7 +459,7 @@ module Spree
|
|
462
459
|
end
|
463
460
|
|
464
461
|
def insufficient_stock_lines
|
465
|
-
|
462
|
+
@insufficient_stock_lines ||= line_items.select(&:insufficient_stock?)
|
466
463
|
end
|
467
464
|
|
468
465
|
def merge!(order)
|
@@ -487,11 +484,9 @@ module Spree
|
|
487
484
|
adjustments.destroy_all
|
488
485
|
end
|
489
486
|
|
490
|
-
# destroy any previous adjustments.
|
491
|
-
# Adjustments will be recalculated during order update.
|
492
487
|
def clear_adjustments!
|
493
|
-
adjustments.
|
494
|
-
|
488
|
+
self.adjustments.destroy_all
|
489
|
+
self.line_item_adjustments.destroy_all
|
495
490
|
end
|
496
491
|
|
497
492
|
def has_step?(step)
|
@@ -522,7 +517,7 @@ module Spree
|
|
522
517
|
# Receives an adjustment +originator+ (here a PromotionAction object) and tells
|
523
518
|
# if the order has adjustments from that already
|
524
519
|
def promotion_credit_exists?(originator)
|
525
|
-
!! adjustments.promotion.reload.detect { |credit| credit.originator.id == originator.id }
|
520
|
+
!! adjustments.includes(:originator).promotion.reload.detect { |credit| credit.originator.id == originator.id }
|
526
521
|
end
|
527
522
|
|
528
523
|
def promo_total
|
@@ -534,6 +529,7 @@ module Spree
|
|
534
529
|
end
|
535
530
|
|
536
531
|
def create_proposed_shipments
|
532
|
+
adjustments.shipping.delete_all
|
537
533
|
shipments.destroy_all
|
538
534
|
|
539
535
|
packages = Spree::Stock::Coordinator.new(self).packages
|
@@ -544,6 +540,22 @@ module Spree
|
|
544
540
|
shipments
|
545
541
|
end
|
546
542
|
|
543
|
+
# Clean shipments and make order back to address state
|
544
|
+
#
|
545
|
+
# At some point the might need to force the order to transition from address
|
546
|
+
# to delivery again so that proper updated shipments are created.
|
547
|
+
# e.g. customer goes back from payment step and changes order items
|
548
|
+
def ensure_updated_shipments
|
549
|
+
if shipments.any?
|
550
|
+
self.shipments.destroy_all
|
551
|
+
self.update_column(:state, "address")
|
552
|
+
end
|
553
|
+
end
|
554
|
+
|
555
|
+
def refresh_shipment_rates
|
556
|
+
shipments.map &:refresh_rates
|
557
|
+
end
|
558
|
+
|
547
559
|
private
|
548
560
|
|
549
561
|
def link_by_email
|
@@ -8,14 +8,14 @@ module Spree
|
|
8
8
|
|
9
9
|
# Get current line item for variant if exists
|
10
10
|
# Add variant qty to line_item
|
11
|
-
def add(variant, quantity, currency=nil, shipment=nil)
|
11
|
+
def add(variant, quantity=1, currency=nil, shipment=nil)
|
12
12
|
line_item = order.find_line_item_by_variant(variant)
|
13
13
|
add_to_line_item(line_item, variant, quantity, currency, shipment)
|
14
14
|
end
|
15
15
|
|
16
16
|
# Get current line item for variant
|
17
17
|
# Remove variant qty from line_item
|
18
|
-
def remove(variant, quantity, shipment=nil)
|
18
|
+
def remove(variant, quantity=1, shipment=nil)
|
19
19
|
line_item = order.find_line_item_by_variant(variant)
|
20
20
|
|
21
21
|
unless line_item
|
@@ -67,15 +67,8 @@ module Spree
|
|
67
67
|
def add_to_shipment(shipment, variant, quantity)
|
68
68
|
on_hand, back_order = shipment.stock_location.fill_status(variant, quantity)
|
69
69
|
|
70
|
-
on_hand.times
|
71
|
-
|
72
|
-
state: 'on_hand'}, without_protection: true)
|
73
|
-
end
|
74
|
-
|
75
|
-
back_order.times do
|
76
|
-
shipment.inventory_units.create({variant_id: variant.id,
|
77
|
-
state: 'backordered'}, without_protection: true)
|
78
|
-
end
|
70
|
+
on_hand.times { shipment.set_up_inventory('on_hand', variant, order) }
|
71
|
+
back_order.times { shipment.set_up_inventory('backordered', variant, order) }
|
79
72
|
|
80
73
|
# adding to this shipment, and removing from stock_location
|
81
74
|
if order.completed?
|
@@ -16,12 +16,17 @@ module Spree
|
|
16
16
|
# associations try to save and then in turn try to call +update!+ again.)
|
17
17
|
def update
|
18
18
|
update_totals
|
19
|
-
update_payment_state
|
20
19
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
20
|
+
if order.completed?
|
21
|
+
update_payment_state
|
22
|
+
|
23
|
+
# give each of the shipments a chance to update themselves
|
24
|
+
shipments.each { |shipment| shipment.update!(order) }
|
25
|
+
update_shipment_state
|
26
|
+
end
|
27
|
+
|
28
|
+
update_promotion_adjustments
|
29
|
+
update_shipping_adjustments
|
25
30
|
# update totals a second time in case updated adjustments have an effect on the total
|
26
31
|
update_totals
|
27
32
|
|
@@ -120,11 +125,17 @@ module Spree
|
|
120
125
|
#
|
121
126
|
# Adjustments will check if they are still eligible. Ineligible adjustments
|
122
127
|
# are preserved but not counted towards adjustment_total.
|
123
|
-
def
|
124
|
-
order.adjustments.reload.each { |adjustment| adjustment.update! }
|
128
|
+
def update_promotion_adjustments
|
129
|
+
order.adjustments.reload.promotion.each { |adjustment| adjustment.update!(order) }
|
125
130
|
choose_best_promotion_adjustment
|
126
131
|
end
|
127
132
|
|
133
|
+
# Shipping adjustments don't receive order on update! because they calculated
|
134
|
+
# over a shipping / package object rather than an order object
|
135
|
+
def update_shipping_adjustments
|
136
|
+
order.adjustments.reload.shipping.each { |adjustment| adjustment.update! }
|
137
|
+
end
|
138
|
+
|
128
139
|
private
|
129
140
|
|
130
141
|
# Picks one (and only one) promotion to be eligible for this order
|
@@ -76,133 +76,133 @@ module Spree
|
|
76
76
|
end
|
77
77
|
end
|
78
78
|
end
|
79
|
-
end
|
80
79
|
|
81
|
-
|
82
|
-
|
83
|
-
|
80
|
+
def credit!(credit_amount=nil)
|
81
|
+
protect_from_connection_error do
|
82
|
+
check_environment
|
84
83
|
|
85
|
-
|
86
|
-
|
84
|
+
credit_amount ||= credit_allowed >= order.outstanding_balance.abs ? order.outstanding_balance.abs : credit_allowed.abs
|
85
|
+
credit_amount = credit_amount.to_f
|
87
86
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
87
|
+
if payment_method.payment_profiles_supported?
|
88
|
+
response = payment_method.credit((credit_amount * 100).round, source, response_code, gateway_options)
|
89
|
+
else
|
90
|
+
response = payment_method.credit((credit_amount * 100).round, response_code, gateway_options)
|
91
|
+
end
|
93
92
|
|
94
|
-
|
93
|
+
record_response(response)
|
95
94
|
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
95
|
+
if response.success?
|
96
|
+
self.class.create({ :order => order,
|
97
|
+
:source => self,
|
98
|
+
:payment_method => payment_method,
|
99
|
+
:amount => credit_amount.abs * -1,
|
100
|
+
:response_code => response.authorization,
|
101
|
+
:state => 'completed' }, :without_protection => true)
|
102
|
+
else
|
103
|
+
gateway_error(response)
|
104
|
+
end
|
105
105
|
end
|
106
106
|
end
|
107
|
-
end
|
108
107
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
108
|
+
def partial_credit(amount)
|
109
|
+
return if amount > credit_allowed
|
110
|
+
started_processing!
|
111
|
+
credit!(amount)
|
112
|
+
end
|
114
113
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
114
|
+
def gateway_options
|
115
|
+
options = { :email => order.email,
|
116
|
+
:customer => order.email,
|
117
|
+
:ip => order.last_ip_address,
|
118
|
+
# Need to pass in a unique identifier here to make some
|
119
|
+
# payment gateways happy.
|
120
|
+
#
|
121
|
+
# For more information, please see Spree::Payment#set_unique_identifier
|
122
|
+
:order_id => gateway_order_id }
|
123
|
+
|
124
|
+
options.merge!({ :shipping => order.ship_total * 100,
|
125
|
+
:tax => order.tax_total * 100,
|
126
|
+
:subtotal => order.item_total * 100,
|
127
|
+
:discount => order.promo_total * 100,
|
128
|
+
:currency => currency })
|
129
|
+
|
130
|
+
options.merge!({ :billing_address => order.bill_address.try(:active_merchant_hash),
|
131
|
+
:shipping_address => order.ship_address.try(:active_merchant_hash) })
|
132
|
+
|
133
|
+
options
|
134
|
+
end
|
136
135
|
|
137
|
-
|
136
|
+
private
|
138
137
|
|
139
|
-
|
140
|
-
|
141
|
-
|
138
|
+
def gateway_action(source, action, success_state)
|
139
|
+
protect_from_connection_error do
|
140
|
+
check_environment
|
142
141
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
142
|
+
response = payment_method.send(action, (amount * 100).round,
|
143
|
+
source,
|
144
|
+
gateway_options)
|
145
|
+
handle_response(response, success_state, :failure)
|
146
|
+
end
|
147
147
|
end
|
148
|
-
end
|
149
148
|
|
150
|
-
|
151
|
-
|
149
|
+
def handle_response(response, success_state, failure_state)
|
150
|
+
record_response(response)
|
152
151
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
152
|
+
if response.success?
|
153
|
+
unless response.authorization.nil?
|
154
|
+
self.response_code = response.authorization
|
155
|
+
self.avs_response = response.avs_result['code']
|
157
156
|
|
158
|
-
|
159
|
-
|
160
|
-
|
157
|
+
if response.cvv_result
|
158
|
+
self.cvv_response_code = response.cvv_result['code']
|
159
|
+
self.cvv_response_message = response.cvv_result['message']
|
160
|
+
end
|
161
161
|
end
|
162
|
+
self.send("#{success_state}!")
|
163
|
+
else
|
164
|
+
self.send(failure_state)
|
165
|
+
gateway_error(response)
|
162
166
|
end
|
163
|
-
self.send("#{success_state}!")
|
164
|
-
else
|
165
|
-
self.send(failure_state)
|
166
|
-
gateway_error(response)
|
167
167
|
end
|
168
|
-
end
|
169
168
|
|
170
|
-
|
171
|
-
|
172
|
-
|
169
|
+
def record_response(response)
|
170
|
+
log_entries.create({:details => response.to_yaml}, :without_protection => true)
|
171
|
+
end
|
173
172
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
173
|
+
def protect_from_connection_error
|
174
|
+
begin
|
175
|
+
yield
|
176
|
+
rescue ActiveMerchant::ConnectionError => e
|
177
|
+
gateway_error(e)
|
178
|
+
end
|
179
179
|
end
|
180
|
-
end
|
181
180
|
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
181
|
+
def gateway_error(error)
|
182
|
+
if error.is_a? ActiveMerchant::Billing::Response
|
183
|
+
text = error.params['message'] || error.params['response_reason_text'] || error.message
|
184
|
+
elsif error.is_a? ActiveMerchant::ConnectionError
|
185
|
+
text = Spree.t(:unable_to_connect_to_gateway)
|
186
|
+
else
|
187
|
+
text = error.to_s
|
188
|
+
end
|
189
|
+
logger.error(Spree.t(:gateway_error))
|
190
|
+
logger.error(" #{error.to_yaml}")
|
191
|
+
raise Core::GatewayError.new(text)
|
189
192
|
end
|
190
|
-
logger.error(Spree.t(:gateway_error))
|
191
|
-
logger.error(" #{error.to_yaml}")
|
192
|
-
raise Core::GatewayError.new(text)
|
193
|
-
end
|
194
193
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
194
|
+
# Saftey check to make sure we're not accidentally performing operations on a live gateway.
|
195
|
+
# Ex. When testing in staging environment with a copy of production data.
|
196
|
+
def check_environment
|
197
|
+
return if payment_method.environment == Rails.env
|
198
|
+
message = Spree.t(:gateway_config_unavailable) + " - #{Rails.env}"
|
199
|
+
raise Core::GatewayError.new(message)
|
200
|
+
end
|
202
201
|
|
203
|
-
|
204
|
-
|
205
|
-
|
202
|
+
# The unique identifier to be passed in to the payment gateway
|
203
|
+
def gateway_order_id
|
204
|
+
"#{order.number}-#{self.identifier}"
|
205
|
+
end
|
206
206
|
end
|
207
207
|
end
|
208
|
-
end
|
208
|
+
end
|