spree_core 1.1.0.rc1 → 1.1.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- data/app/assets/javascripts/admin/shipping_methods.js +3 -2
- data/app/assets/javascripts/admin/spree_core.js +0 -2
- data/app/assets/javascripts/store/spree_core.js +0 -2
- data/app/controllers/spree/admin/option_types_controller.rb +6 -8
- data/app/controllers/spree/admin/payments_controller.rb +1 -2
- data/app/controllers/spree/admin/products_controller.rb +13 -4
- data/app/controllers/spree/products_controller.rb +1 -1
- data/app/controllers/spree/states_controller.rb +1 -1
- data/app/helpers/spree/base_helper.rb +9 -7
- data/app/helpers/spree/products_helper.rb +0 -6
- data/app/mailers/spree/order_mailer.rb +4 -4
- data/app/mailers/spree/shipment_mailer.rb +2 -2
- data/app/models/spree/address.rb +25 -15
- data/app/models/spree/calculator/per_item.rb +4 -1
- data/app/models/spree/calculator.rb +1 -1
- data/app/models/spree/creditcard.rb +8 -184
- data/app/models/spree/gateway/bogus.rb +1 -1
- data/app/models/spree/gateway.rb +3 -1
- data/app/models/spree/inventory_unit.rb +3 -3
- data/app/models/spree/line_item.rb +14 -14
- data/app/models/spree/mail_method.rb +6 -1
- data/app/models/spree/payment/processing.rb +179 -0
- data/app/models/spree/payment.rb +3 -22
- data/app/models/spree/payment_method.rb +2 -2
- data/app/models/spree/product.rb +11 -10
- data/app/models/spree/product_property.rb +2 -0
- data/app/models/spree/return_authorization.rb +9 -7
- data/app/models/spree/shipment.rb +7 -6
- data/app/models/spree/shipping_method.rb +6 -5
- data/app/models/spree/shipping_rate.rb +2 -2
- data/app/models/spree/state.rb +1 -1
- data/app/models/spree/tax_category.rb +2 -2
- data/app/models/spree/tax_rate.rb +7 -7
- data/app/models/spree/taxon.rb +4 -4
- data/app/models/spree/taxonomy.rb +3 -3
- data/app/models/spree/variant.rb +19 -8
- data/app/models/spree/zone.rb +10 -10
- data/app/views/spree/admin/products/index.html.erb +1 -1
- data/app/views/spree/admin/shared/_additional_field.html.erb +1 -1
- data/app/views/spree/admin/shared/_head.html.erb +0 -1
- data/app/views/spree/admin/tax_rates/_form.html.erb +5 -5
- data/app/views/spree/admin/variants/index.html.erb +1 -1
- data/app/views/spree/layouts/spree_application.html.erb +1 -1
- data/app/views/spree/products/_cart_form.html.erb +1 -1
- data/app/views/spree/shared/_head.html.erb +1 -1
- data/config/locales/en.yml +2 -2
- data/db/migrate/20120315064358_migrate_images_from_products_to_variants.rb +1 -1
- data/db/migrate/20120416233427_rename_attachment_size_to_attachment_file_size.rb +5 -0
- data/lib/generators/spree/install/templates/app/assets/javascripts/admin/all.js +2 -0
- data/lib/generators/spree/install/templates/app/assets/javascripts/store/all.js +2 -0
- data/lib/spree/core/controller_helpers.rb +0 -1
- data/lib/spree/core/engine.rb +5 -1
- data/lib/spree/core/search/base.rb +1 -1
- data/lib/spree/core/testing_support/controller_requests.rb +60 -0
- data/lib/spree/core/testing_support/factories.rb +1 -2
- data/lib/spree/core/version.rb +1 -1
- metadata +35 -33
- data/app/helpers/spree/hook_helper.rb +0 -11
@@ -3,9 +3,10 @@ $(document).ready(function() {
|
|
3
3
|
$('input[type=checkbox]:not(:checked)').attr('disabled', true);
|
4
4
|
}
|
5
5
|
|
6
|
-
|
6
|
+
categoryCheckboxes = '.categories input[type=checkbox]';
|
7
|
+
$(categoryCheckboxes).change(function(){
|
7
8
|
if($(this).is(':checked')) {
|
8
|
-
$('
|
9
|
+
$(categoryCheckboxes + ':not(:checked)').attr('disabled', true);
|
9
10
|
$(this).removeAttr('disabled');
|
10
11
|
} else {
|
11
12
|
$('input[type=checkbox]').removeAttr('disabled');
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Spree
|
2
2
|
module Admin
|
3
3
|
class OptionTypesController < ResourceController
|
4
|
-
before_filter :load_product, :only => [:selected, :available, :remove]
|
4
|
+
before_filter :load_product, :only => [:select, :selected, :available, :remove]
|
5
5
|
|
6
6
|
def available
|
7
7
|
set_available_option_types
|
@@ -32,7 +32,6 @@ module Spree
|
|
32
32
|
|
33
33
|
# AJAX method for selecting an existing option type and associating with the current product
|
34
34
|
def select
|
35
|
-
@product = Product.find_by_param!(params[:product_id])
|
36
35
|
@product.option_types << OptionType.find(params[:id])
|
37
36
|
@product.reload
|
38
37
|
@option_types = @product.option_types
|
@@ -55,13 +54,12 @@ module Spree
|
|
55
54
|
@product = Product.find_by_param!(params[:product_id])
|
56
55
|
end
|
57
56
|
|
58
|
-
def set_available_option_types
|
59
|
-
@available_option_types =
|
60
|
-
|
61
|
-
|
62
|
-
|
57
|
+
def set_available_option_types
|
58
|
+
@available_option_types = if @product.option_type_ids.any?
|
59
|
+
OptionType.where('id NOT IN (?)', @product.option_type_ids)
|
60
|
+
else
|
61
|
+
OptionType.all
|
63
62
|
end
|
64
|
-
@available_option_types.delete_if {|ot| selected_option_types.include? ot}
|
65
63
|
end
|
66
64
|
end
|
67
65
|
end
|
@@ -51,9 +51,8 @@ module Spree
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def fire
|
54
|
-
# TODO: consider finer-grained control for this type of action (right now anyone in admin role can perform)
|
55
54
|
return unless event = params[:e] and @payment.payment_source
|
56
|
-
if @payment.
|
55
|
+
if @payment.send("#{event}!")
|
57
56
|
flash.notice = t(:payment_updated)
|
58
57
|
else
|
59
58
|
flash[:error] = t(:cannot_perform_operation)
|
@@ -85,7 +85,18 @@ module Spree
|
|
85
85
|
params[:q][:s] ||= "name asc"
|
86
86
|
|
87
87
|
@search = super.search(params[:q])
|
88
|
-
@collection = @search.result.
|
88
|
+
@collection = @search.result.
|
89
|
+
group_by_products_id.
|
90
|
+
includes([:master, {:variants => [:images, :option_values]}]).
|
91
|
+
page(params[:page]).
|
92
|
+
per(Spree::Config[:admin_products_per_page])
|
93
|
+
|
94
|
+
if params[:q][:s].include?("master_price")
|
95
|
+
# By applying the group in the main query we get an undefined method gsub for Arel::Nodes::Descending
|
96
|
+
# It seems to only work when the price is actually being sorted in the query
|
97
|
+
# To be investigated later.
|
98
|
+
@collection = @collection.group("spree_variants.price")
|
99
|
+
end
|
89
100
|
else
|
90
101
|
includes = [{:variants => [:images, {:option_values => :option_type}]}, {:master => :images}]
|
91
102
|
|
@@ -95,10 +106,8 @@ module Spree
|
|
95
106
|
tmp = super.where(["#{Variant.table_name}.sku #{LIKE} ?", "%#{params[:q]}%"])
|
96
107
|
tmp = tmp.includes(:variants_including_master).limit(params[:limit] || 10)
|
97
108
|
@collection.concat(tmp)
|
98
|
-
|
99
|
-
@collection
|
100
109
|
end
|
101
|
-
|
110
|
+
@collection
|
102
111
|
end
|
103
112
|
|
104
113
|
def create_before
|
@@ -13,7 +13,7 @@ module Spree
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def show
|
16
|
-
@product = Product.find_by_permalink!(params[:id])
|
16
|
+
@product = Product.active.find_by_permalink!(params[:id])
|
17
17
|
return unless @product
|
18
18
|
|
19
19
|
@variants = Variant.active.includes([:option_values, :images]).where(:product_id => @product.id)
|
@@ -7,7 +7,7 @@ module Spree
|
|
7
7
|
def index
|
8
8
|
# we return ALL known information, since billing country isn't restricted
|
9
9
|
# by shipping country
|
10
|
-
respond_with @state_info = Spree::State.states_group_by_country_id.to_json
|
10
|
+
respond_with @state_info = Spree::State.states_group_by_country_id.to_json, :layout => nil
|
11
11
|
end
|
12
12
|
end
|
13
13
|
end
|
@@ -64,6 +64,11 @@ module Spree
|
|
64
64
|
end
|
65
65
|
|
66
66
|
Spree::Image.attachment_definitions[:attachment][:styles].each do |style, v|
|
67
|
+
# Defines these methods by default:
|
68
|
+
# def mini_image
|
69
|
+
# def small_image
|
70
|
+
# def product_image
|
71
|
+
# def large_image
|
67
72
|
define_method "#{style}_image" do |product, *options|
|
68
73
|
options = options.first || {}
|
69
74
|
if product.images.empty?
|
@@ -100,13 +105,10 @@ module Spree
|
|
100
105
|
end
|
101
106
|
|
102
107
|
def flash_messages
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
''
|
108
|
-
end
|
109
|
-
end.join("\n").html_safe
|
108
|
+
flash.each do |msg_type, text|
|
109
|
+
concat(content_tag :div, text, :class => "flash #{msg_type}")
|
110
|
+
end
|
111
|
+
nil
|
110
112
|
end
|
111
113
|
|
112
114
|
def breadcrumbs(taxon, separator=" » ")
|
@@ -12,12 +12,6 @@ module Spree
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
-
# returns the price of the product to show for display purposes
|
16
|
-
def product_price(product_or_variant, options={})
|
17
|
-
ActiveSupport::Deprecation.warn('product_price is deprecated and no longer calculates
|
18
|
-
tax. Use number_to_currency instead.', caller)
|
19
|
-
end
|
20
|
-
|
21
15
|
# converts line breaks in product description into <p> tags (for html display purposes)
|
22
16
|
def product_description(product)
|
23
17
|
raw(product.description.gsub(/^(.*)$/, '<p>\1</p>'))
|
@@ -2,17 +2,17 @@ module Spree
|
|
2
2
|
class OrderMailer < ActionMailer::Base
|
3
3
|
helper 'spree/base'
|
4
4
|
|
5
|
-
def confirm_email(order, resend=false)
|
5
|
+
def confirm_email(order, resend = false)
|
6
6
|
@order = order
|
7
|
-
subject = (resend ? "[#{t(:resend).upcase}] " :
|
7
|
+
subject = (resend ? "[#{t(:resend).upcase}] " : '')
|
8
8
|
subject += "#{Spree::Config[:site_name]} #{t('order_mailer.confirm_email.subject')} ##{order.number}"
|
9
9
|
mail(:to => order.email,
|
10
10
|
:subject => subject)
|
11
11
|
end
|
12
12
|
|
13
|
-
def cancel_email(order, resend=false)
|
13
|
+
def cancel_email(order, resend = false)
|
14
14
|
@order = order
|
15
|
-
subject = (resend ? "[#{t(:resend).upcase}] " :
|
15
|
+
subject = (resend ? "[#{t(:resend).upcase}] " : '')
|
16
16
|
subject += "#{Spree::Config[:site_name]} #{t('order_mailer.cancel_email.subject')} ##{order.number}"
|
17
17
|
mail(:to => order.email,
|
18
18
|
:subject => subject)
|
@@ -2,9 +2,9 @@ module Spree
|
|
2
2
|
class ShipmentMailer < ActionMailer::Base
|
3
3
|
helper 'spree/base'
|
4
4
|
|
5
|
-
def shipped_email(shipment, resend=false)
|
5
|
+
def shipped_email(shipment, resend = false)
|
6
6
|
@shipment = shipment
|
7
|
-
subject = (resend ? "[#{t(:resend).upcase}] " :
|
7
|
+
subject = (resend ? "[#{t(:resend).upcase}] " : '')
|
8
8
|
subject += "#{Spree::Config[:site_name]} #{t('shipment_mailer.shipped_email.subject')} ##{shipment.order.number}"
|
9
9
|
mail(:to => shipment.order.email,
|
10
10
|
:subject => subject)
|
data/app/models/spree/address.rb
CHANGED
@@ -13,14 +13,12 @@ module Spree
|
|
13
13
|
:country, :state, :phone, :state_name,
|
14
14
|
:company, :alternative_phone
|
15
15
|
|
16
|
-
#
|
16
|
+
# Disconnected since there's no code to display error messages yet OR matching client-side validation
|
17
17
|
def phone_validate
|
18
18
|
return if phone.blank?
|
19
19
|
n_digits = phone.scan(/[0-9]/).size
|
20
20
|
valid_chars = (phone =~ /^[-+()\/\s\d]+$/)
|
21
|
-
|
22
|
-
errors.add(:phone, :invalid)
|
23
|
-
end
|
21
|
+
errors.add :phone, :invalid unless (n_digits > 5 && valid_chars)
|
24
22
|
end
|
25
23
|
|
26
24
|
def self.default
|
@@ -28,7 +26,7 @@ module Spree
|
|
28
26
|
new({:country => country}, :without_protection => true)
|
29
27
|
end
|
30
28
|
|
31
|
-
#
|
29
|
+
# Can modify an address if it's not been used in an order (but checkouts controller has finer control)
|
32
30
|
# def editable?
|
33
31
|
# new_record? || (shipments.empty? && checkouts.empty?)
|
34
32
|
# end
|
@@ -51,7 +49,7 @@ module Spree
|
|
51
49
|
|
52
50
|
def same_as?(other)
|
53
51
|
return false if other.nil?
|
54
|
-
attributes.except('id', 'updated_at', 'created_at') ==
|
52
|
+
attributes.except('id', 'updated_at', 'created_at') == other.attributes.except('id', 'updated_at', 'created_at')
|
55
53
|
end
|
56
54
|
|
57
55
|
alias same_as same_as?
|
@@ -77,14 +75,28 @@ module Spree
|
|
77
75
|
attributes.except('id', 'created_at', 'updated_at', 'order_id', 'country_id').all? { |_, v| v.nil? }
|
78
76
|
end
|
79
77
|
|
78
|
+
# Generates an ActiveMerchant compatible address hash
|
79
|
+
def active_merchant_hash
|
80
|
+
{
|
81
|
+
:name => full_name,
|
82
|
+
:address1 => address1,
|
83
|
+
:address2 => address2,
|
84
|
+
:city => city,
|
85
|
+
:state => state_text,
|
86
|
+
:zip => zipcode,
|
87
|
+
:country => country.try(:iso),
|
88
|
+
:phone => phone
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
80
92
|
private
|
81
93
|
|
82
94
|
def state_validate
|
83
|
-
#
|
84
|
-
#or when disabled by
|
85
|
-
return if
|
95
|
+
# Skip state validation without country (also required)
|
96
|
+
# or when disabled by preference
|
97
|
+
return if country.blank? || !Spree::Config[:address_requires_state]
|
86
98
|
|
87
|
-
#ensure associated state belongs to country
|
99
|
+
# ensure associated state belongs to country
|
88
100
|
if state.present?
|
89
101
|
if state.country == country
|
90
102
|
self.state_name = nil #not required as we have a valid state and country combo
|
@@ -97,7 +109,7 @@ module Spree
|
|
97
109
|
end
|
98
110
|
end
|
99
111
|
|
100
|
-
#ensure state_name belongs to country without states, or that it matches a predefined state name/abbr
|
112
|
+
# ensure state_name belongs to country without states, or that it matches a predefined state name/abbr
|
101
113
|
if state_name.present?
|
102
114
|
if country.states.present?
|
103
115
|
states = country.states.find_all_by_name_or_abbr(state_name)
|
@@ -111,10 +123,8 @@ module Spree
|
|
111
123
|
end
|
112
124
|
end
|
113
125
|
|
114
|
-
#ensure at least one state field is populated
|
115
|
-
if
|
116
|
-
errors.add(:state, :blank)
|
117
|
-
end
|
126
|
+
# ensure at least one state field is populated
|
127
|
+
errors.add :state, :blank if state.blank? && state_name.blank?
|
118
128
|
end
|
119
129
|
|
120
130
|
end
|
@@ -11,19 +11,14 @@ module Spree
|
|
11
11
|
validates :number, :presence => true, :unless => :has_payment_profile?, :on => :create
|
12
12
|
validates :verification_value, :presence => true, :unless => :has_payment_profile?, :on => :create
|
13
13
|
|
14
|
-
attr_accessible :first_name, :last_name, :number, :verification_value, :year,
|
14
|
+
attr_accessible :first_name, :last_name, :number, :verification_value, :year,
|
15
|
+
:month, :gateway_customer_profile_id
|
15
16
|
|
16
|
-
|
17
|
-
if Spree::Config[:auto_capture]
|
18
|
-
purchase(payment.amount.to_f, payment)
|
19
|
-
else
|
20
|
-
authorize(payment.amount.to_f, payment)
|
21
|
-
end
|
22
|
-
end
|
17
|
+
scope :with_payment_profile, where('gateway_customer_profile_id IS NOT NULL')
|
23
18
|
|
24
19
|
def set_last_digits
|
25
|
-
number.to_s.gsub!(/\s/,'')
|
26
|
-
verification_value.to_s.gsub!(/\s/,'')
|
20
|
+
number.to_s.gsub!(/\s/,'')
|
21
|
+
verification_value.to_s.gsub!(/\s/,'')
|
27
22
|
self.last_digits ||= number.to_s.length <= 4 ? number : number.to_s.slice(-4..-1)
|
28
23
|
end
|
29
24
|
|
@@ -37,7 +32,7 @@ module Spree
|
|
37
32
|
|
38
33
|
# sets self.cc_type while we still have the card number
|
39
34
|
def set_card_type
|
40
|
-
self.cc_type ||= CardDetector.type?(
|
35
|
+
self.cc_type ||= CardDetector.type?(number)
|
41
36
|
end
|
42
37
|
|
43
38
|
def name?
|
@@ -65,127 +60,11 @@ module Spree
|
|
65
60
|
"XXXX-XXXX-XXXX-#{last_digits}"
|
66
61
|
end
|
67
62
|
|
68
|
-
#RAILS 3 TODO
|
69
|
-
#alias :attributes_with_quotes_default :attributes_with_quotes
|
70
|
-
|
71
63
|
# needed for some of the ActiveMerchant gateways (eg. SagePay)
|
72
64
|
def brand
|
73
65
|
cc_type
|
74
66
|
end
|
75
67
|
|
76
|
-
scope :with_payment_profile, where('gateway_customer_profile_id IS NOT NULL')
|
77
|
-
|
78
|
-
def authorize(amount, payment)
|
79
|
-
# ActiveMerchant is configured to use cents so we need to multiply order total by 100
|
80
|
-
payment_gateway = payment.payment_method
|
81
|
-
check_environment(payment_gateway)
|
82
|
-
|
83
|
-
response = payment_gateway.authorize((amount * 100).round, self, gateway_options(payment))
|
84
|
-
record_log payment, response
|
85
|
-
|
86
|
-
if response.success?
|
87
|
-
payment.response_code = response.authorization
|
88
|
-
payment.avs_response = response.avs_result['code']
|
89
|
-
payment.pend
|
90
|
-
else
|
91
|
-
payment.failure
|
92
|
-
gateway_error(response)
|
93
|
-
end
|
94
|
-
rescue ActiveMerchant::ConnectionError => e
|
95
|
-
gateway_error e
|
96
|
-
end
|
97
|
-
|
98
|
-
def purchase(amount, payment)
|
99
|
-
#combined Authorize and Capture that gets processed by the ActiveMerchant gateway as one single transaction.
|
100
|
-
payment_gateway = payment.payment_method
|
101
|
-
check_environment(payment_gateway)
|
102
|
-
|
103
|
-
response = payment_gateway.purchase((amount * 100).round, self, gateway_options(payment))
|
104
|
-
record_log payment, response
|
105
|
-
|
106
|
-
if response.success?
|
107
|
-
payment.response_code = response.authorization
|
108
|
-
payment.avs_response = response.avs_result['code']
|
109
|
-
payment.complete
|
110
|
-
else
|
111
|
-
payment.failure
|
112
|
-
gateway_error(response) unless response.success?
|
113
|
-
end
|
114
|
-
rescue ActiveMerchant::ConnectionError => e
|
115
|
-
gateway_error e
|
116
|
-
end
|
117
|
-
|
118
|
-
def capture(payment)
|
119
|
-
return unless payment.pending?
|
120
|
-
payment_gateway = payment.payment_method
|
121
|
-
check_environment(payment_gateway)
|
122
|
-
|
123
|
-
if payment_gateway.payment_profiles_supported?
|
124
|
-
# Gateways supporting payment profiles will need access to creditcard object because this stores the payment profile information
|
125
|
-
# so supply the authorization itself as well as the creditcard, rather than just the authorization code
|
126
|
-
response = payment_gateway.capture(payment, self, minimal_gateway_options(payment, false))
|
127
|
-
else
|
128
|
-
# Standard ActiveMerchant capture usage
|
129
|
-
response = payment_gateway.capture((payment.amount * 100).round, payment.response_code, minimal_gateway_options(payment, false))
|
130
|
-
end
|
131
|
-
|
132
|
-
record_log payment, response
|
133
|
-
|
134
|
-
if response.success?
|
135
|
-
payment.response_code = response.authorization
|
136
|
-
payment.complete
|
137
|
-
else
|
138
|
-
payment.failure
|
139
|
-
gateway_error(response)
|
140
|
-
end
|
141
|
-
rescue ActiveMerchant::ConnectionError => e
|
142
|
-
gateway_error e
|
143
|
-
end
|
144
|
-
|
145
|
-
def void(payment)
|
146
|
-
payment_gateway = payment.payment_method
|
147
|
-
check_environment(payment_gateway)
|
148
|
-
|
149
|
-
response = payment_gateway.void(payment.response_code, minimal_gateway_options(payment, false))
|
150
|
-
record_log payment, response
|
151
|
-
|
152
|
-
if response.success?
|
153
|
-
payment.response_code = response.authorization
|
154
|
-
payment.void
|
155
|
-
else
|
156
|
-
gateway_error(response)
|
157
|
-
end
|
158
|
-
rescue ActiveMerchant::ConnectionError => e
|
159
|
-
gateway_error e
|
160
|
-
end
|
161
|
-
|
162
|
-
def credit(payment)
|
163
|
-
payment_gateway = payment.payment_method
|
164
|
-
check_environment(payment_gateway)
|
165
|
-
|
166
|
-
amount = payment.credit_allowed >= payment.order.outstanding_balance.abs ? payment.order.outstanding_balance.abs : payment.credit_allowed.abs
|
167
|
-
if payment_gateway.payment_profiles_supported?
|
168
|
-
response = payment_gateway.credit((amount * 100).round, self, payment.response_code, minimal_gateway_options(payment, false))
|
169
|
-
else
|
170
|
-
response = payment_gateway.credit((amount * 100).round, payment.response_code, minimal_gateway_options(payment, false))
|
171
|
-
end
|
172
|
-
|
173
|
-
record_log payment, response
|
174
|
-
|
175
|
-
if response.success?
|
176
|
-
Payment.create({:order => payment.order,
|
177
|
-
:source => payment,
|
178
|
-
:payment_method => payment.payment_method,
|
179
|
-
:amount => amount.abs * -1,
|
180
|
-
:response_code => response.authorization,
|
181
|
-
:state => 'completed'}, :without_protection => true)
|
182
|
-
else
|
183
|
-
gateway_error(response)
|
184
|
-
end
|
185
|
-
rescue ActiveMerchant::ConnectionError => e
|
186
|
-
gateway_error e
|
187
|
-
end
|
188
|
-
|
189
68
|
def actions
|
190
69
|
%w{capture void credit}
|
191
70
|
end
|
@@ -197,7 +76,7 @@ module Spree
|
|
197
76
|
|
198
77
|
# Indicates whether its possible to void the payment.
|
199
78
|
def can_void?(payment)
|
200
|
-
payment.state
|
79
|
+
payment.state != 'void'
|
201
80
|
end
|
202
81
|
|
203
82
|
# Indicates whether its possible to credit the payment. Note that most gateways require that the
|
@@ -212,64 +91,9 @@ module Spree
|
|
212
91
|
gateway_customer_profile_id.present?
|
213
92
|
end
|
214
93
|
|
215
|
-
def record_log(payment, response)
|
216
|
-
payment.log_entries.create({:details => response.to_yaml}, :without_protection => true)
|
217
|
-
end
|
218
|
-
|
219
|
-
def gateway_error(error)
|
220
|
-
if error.is_a? ActiveMerchant::Billing::Response
|
221
|
-
text = error.params['message'] || error.params['response_reason_text'] || error.message
|
222
|
-
elsif error.is_a? ActiveMerchant::ConnectionError
|
223
|
-
text = I18n.t(:unable_to_connect_to_gateway)
|
224
|
-
else
|
225
|
-
text = error.to_s
|
226
|
-
end
|
227
|
-
logger.error(I18n.t(:gateway_error))
|
228
|
-
logger.error(" #{error.to_yaml}")
|
229
|
-
raise Core::GatewayError.new(text)
|
230
|
-
end
|
231
|
-
|
232
|
-
def gateway_options(payment)
|
233
|
-
options = { :billing_address => generate_address_hash(payment.order.bill_address),
|
234
|
-
:shipping_address => generate_address_hash(payment.order.ship_address) }
|
235
|
-
options.merge minimal_gateway_options(payment)
|
236
|
-
end
|
237
|
-
|
238
|
-
# Generates an ActiveMerchant compatible address hash from one of Spree's address objects
|
239
|
-
def generate_address_hash(address)
|
240
|
-
return {} if address.nil?
|
241
|
-
{ :name => address.full_name, :address1 => address.address1, :address2 => address.address2, :city => address.city,
|
242
|
-
:state => address.state_text, :zip => address.zipcode, :country => address.country.iso, :phone => address.phone }
|
243
|
-
end
|
244
|
-
|
245
|
-
# Generates a minimal set of gateway options. There appears to be some issues with passing in
|
246
|
-
# a billing address when authorizing/voiding a previously captured transaction. So omits these
|
247
|
-
# options in this case since they aren't necessary.
|
248
|
-
def minimal_gateway_options(payment, totals=true)
|
249
|
-
|
250
|
-
options = { :email => payment.order.email,
|
251
|
-
:customer => payment.order.email,
|
252
|
-
:ip => payment.order.ip_address,
|
253
|
-
:order_id => payment.order.number }
|
254
|
-
if totals
|
255
|
-
options.merge!({ :shipping => payment.order.ship_total * 100,
|
256
|
-
:tax => payment.order.tax_total * 100,
|
257
|
-
:subtotal => payment.order.item_total * 100 })
|
258
|
-
end
|
259
|
-
options
|
260
|
-
end
|
261
|
-
|
262
94
|
def spree_cc_type
|
263
95
|
return 'visa' if Rails.env.development?
|
264
|
-
|
265
|
-
end
|
266
|
-
|
267
|
-
# Saftey check to make sure we're not accidentally performing operations on a live gateway.
|
268
|
-
# Ex. When testing in staging environment with a copy of production data.
|
269
|
-
def check_environment(gateway)
|
270
|
-
return if gateway.environment == Rails.env
|
271
|
-
message = I18n.t(:gateway_config_unavailable) + " - #{Rails.env}"
|
272
|
-
raise Core::GatewayError.new(message)
|
96
|
+
cc_type
|
273
97
|
end
|
274
98
|
end
|
275
99
|
end
|
data/app/models/spree/gateway.rb
CHANGED
@@ -6,6 +6,8 @@ module Spree
|
|
6
6
|
|
7
7
|
preference :server, :string, :default => 'test'
|
8
8
|
preference :test_mode, :boolean, :default => true
|
9
|
+
|
10
|
+
attr_accessible :preferred_server, :preferred_test_mode
|
9
11
|
|
10
12
|
def payment_source_class
|
11
13
|
Creditcard
|
@@ -25,7 +27,7 @@ module Spree
|
|
25
27
|
|
26
28
|
def options
|
27
29
|
options_hash = {}
|
28
|
-
self.preferences.each do |key,value|
|
30
|
+
self.preferences.each do |key, value|
|
29
31
|
options_hash[key.to_sym] = value
|
30
32
|
end
|
31
33
|
options_hash
|
@@ -106,12 +106,12 @@ module Spree
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def update_order
|
109
|
-
|
109
|
+
order.update!
|
110
110
|
end
|
111
111
|
|
112
112
|
def restock_variant
|
113
|
-
|
114
|
-
|
113
|
+
variant.on_hand = (variant.on_hand + 1)
|
114
|
+
variant.save
|
115
115
|
end
|
116
116
|
end
|
117
117
|
end
|
@@ -24,7 +24,7 @@ module Spree
|
|
24
24
|
after_destroy :update_order
|
25
25
|
|
26
26
|
def copy_price
|
27
|
-
self.price = variant.price if variant &&
|
27
|
+
self.price = variant.price if variant && price.nil?
|
28
28
|
end
|
29
29
|
|
30
30
|
def increment_quantity
|
@@ -36,16 +36,16 @@ module Spree
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def amount
|
39
|
-
|
39
|
+
price * quantity
|
40
40
|
end
|
41
41
|
alias total amount
|
42
42
|
|
43
43
|
def adjust_quantity
|
44
|
-
self.quantity = 0 if
|
44
|
+
self.quantity = 0 if quantity.nil? || quantity < 0
|
45
45
|
end
|
46
46
|
|
47
47
|
def sufficient_stock?
|
48
|
-
Spree::Config[:allow_backorders] ? true : (
|
48
|
+
Spree::Config[:allow_backorders] ? true : (variant.on_hand >= quantity)
|
49
49
|
end
|
50
50
|
|
51
51
|
def insufficient_stock?
|
@@ -54,23 +54,23 @@ module Spree
|
|
54
54
|
|
55
55
|
private
|
56
56
|
def update_inventory
|
57
|
-
return true unless
|
57
|
+
return true unless order.completed?
|
58
58
|
|
59
|
-
if
|
60
|
-
InventoryUnit.increase(
|
59
|
+
if new_record?
|
60
|
+
InventoryUnit.increase(order, variant, quantity)
|
61
61
|
elsif old_quantity = self.changed_attributes['quantity']
|
62
|
-
if old_quantity <
|
63
|
-
InventoryUnit.increase(
|
64
|
-
elsif old_quantity >
|
65
|
-
InventoryUnit.decrease(
|
62
|
+
if old_quantity < quantity
|
63
|
+
InventoryUnit.increase(order, variant, (quantity - old_quantity))
|
64
|
+
elsif old_quantity > quantity
|
65
|
+
InventoryUnit.decrease(order, variant, (old_quantity - quantity))
|
66
66
|
end
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
70
70
|
def remove_inventory
|
71
|
-
return true unless
|
71
|
+
return true unless order.completed?
|
72
72
|
|
73
|
-
InventoryUnit.decrease(
|
73
|
+
InventoryUnit.decrease(order, variant, quantity)
|
74
74
|
end
|
75
75
|
|
76
76
|
def update_order
|
@@ -79,7 +79,7 @@ module Spree
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def ensure_not_shipped
|
82
|
-
if order.try(:inventory_units).to_a.any?{|unit| unit.variant_id == variant_id && unit.shipped?}
|
82
|
+
if order.try(:inventory_units).to_a.any?{ |unit| unit.variant_id == variant_id && unit.shipped? }
|
83
83
|
errors.add :base, I18n.t('validation.cannot_destory_line_item_as_inventory_units_have_shipped')
|
84
84
|
return false
|
85
85
|
end
|