radiant-shop-extension 0.90.4 → 0.91.2
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/app/models/form_address.rb +21 -67
- data/app/models/form_checkout.rb +37 -43
- data/app/models/shop_address.rb +2 -3
- data/app/models/shop_billing.rb +7 -0
- data/app/models/shop_line_item.rb +1 -1
- data/app/models/shop_order.rb +9 -8
- data/app/models/shop_shipping.rb +5 -0
- data/app/views/admin/shop/orders/index/_order.html.haml +1 -1
- data/config/locales/en.yml +117 -0
- data/db/migrate/20101208045754_address_changes.rb +32 -0
- data/lib/shop/controllers/application_controller.rb +2 -0
- data/lib/shop/models/user.rb +3 -3
- data/lib/shop/tags/address.rb +20 -21
- data/lib/shop/tags/card.rb +3 -3
- data/lib/shop/tags/helpers.rb +20 -14
- data/radiant-shop-extension.gemspec +9 -2
- data/spec/controllers/admin/shop/orders_controller_spec.rb +1 -1
- data/spec/datasets/forms.rb +25 -23
- data/spec/datasets/shop_addresses.rb +31 -19
- data/spec/datasets/shop_orders.rb +3 -5
- data/spec/lib/shop/tags/address_spec.rb +120 -60
- data/spec/lib/shop/tags/card_spec.rb +5 -5
- data/spec/lib/shop/tags/helpers_spec.rb +21 -8
- data/spec/models/form_address_spec.rb +11 -55
- data/spec/models/form_checkout_spec.rb +3 -3
- data/spec/models/shop_address_spec.rb +22 -36
- data/spec/models/shop_billing_spec.rb +30 -0
- data/spec/models/shop_customer_spec.rb +2 -0
- data/spec/models/shop_shipping_spec.rb +30 -0
- metadata +12 -5
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.91.2
|
data/app/models/form_address.rb
CHANGED
@@ -18,28 +18,18 @@ class FormAddress
|
|
18
18
|
|
19
19
|
def create_result_object
|
20
20
|
@result = {
|
21
|
-
:order => @order.id,
|
21
|
+
:order => @order.id,
|
22
22
|
:billing => nil,
|
23
23
|
:shipping => nil
|
24
24
|
}
|
25
25
|
end
|
26
26
|
|
27
|
-
# Assigns a shipping and billing address to the @order
|
28
27
|
def create_order_addresses
|
29
28
|
if billing?
|
30
|
-
# We're going to create a billing object
|
31
29
|
create_order_billing_address
|
32
|
-
|
33
|
-
# We're going to assign shipping to billing because they didn't send shipping
|
34
|
-
if !shipping.present? and @billing.id
|
35
|
-
@shipping = @billing
|
36
|
-
@order.update_attribute(:shipping, @shipping)
|
37
|
-
end
|
38
30
|
end
|
39
31
|
|
40
|
-
|
41
|
-
create_order_shipping_address
|
42
|
-
end
|
32
|
+
create_order_shipping_address
|
43
33
|
|
44
34
|
unless (@billing.present? and @billing.valid?) and (@shipping.present? and @shipping.valid?)
|
45
35
|
@form.redirect_to = :back
|
@@ -49,77 +39,41 @@ class FormAddress
|
|
49
39
|
@result[:shipping] = (@shipping.valid? ? @shipping.id : false) rescue false
|
50
40
|
end
|
51
41
|
|
52
|
-
# Attaches a billing address to the order (and current customer)
|
53
42
|
def create_order_billing_address
|
54
|
-
|
55
|
-
# Billing Address
|
56
|
-
if billing[:id] and current_customer.present?
|
57
|
-
begin
|
58
|
-
# Use an existing Address and update its values
|
59
|
-
@billing = current_customer.billings.find(billing[:id])
|
60
|
-
@billing.update_attributes(billing)
|
61
|
-
@order.update_attribute(:billing, @billing)
|
62
|
-
rescue
|
63
|
-
# We cant find that address for that user
|
64
|
-
end
|
65
|
-
|
66
|
-
elsif @order.billing.present?
|
67
|
-
|
68
|
-
# Use the current billing and update its values
|
43
|
+
if @order.billing.present?
|
69
44
|
@billing = @order.billing
|
70
45
|
@billing.update_attributes(billing)
|
71
46
|
|
72
47
|
else
|
73
|
-
|
74
|
-
@billing = ShopAddress.new(billing)
|
48
|
+
@billing = ShopBilling.new(billing)
|
75
49
|
if @billing.save
|
76
50
|
@order.update_attribute(:billing, @billing)
|
77
51
|
end
|
52
|
+
|
78
53
|
end
|
79
|
-
|
80
54
|
end
|
81
55
|
|
82
|
-
# Attaches a shipping address to the order (and current customer)
|
83
56
|
def create_order_shipping_address
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
@shipping = @billing
|
93
|
-
@order.update_attribute(:shipping, @billing)
|
94
|
-
|
95
|
-
elsif (shipping.reject!{|k,v| k == :id }).values.all?(&:blank?)
|
96
|
-
# We have just passed the id and not the data
|
57
|
+
if shipping?
|
58
|
+
if @order.shipping.present?
|
59
|
+
@shipping = @order.shipping
|
60
|
+
@shipping.update_attributes(shipping)
|
61
|
+
|
62
|
+
else
|
63
|
+
@shipping = ShopShipping.new(shipping)
|
64
|
+
if @shipping.save
|
97
65
|
@order.update_attribute(:shipping, @shipping)
|
98
|
-
|
99
|
-
elsif @shipping == @billing and shipping != billing
|
100
|
-
# We have conflicting data so create a new address
|
101
|
-
# the id is rejected so we'll get a new address
|
102
|
-
@order.update_attributes({ :shipping_attributes => shipping })
|
103
|
-
@shipping = @order.shipping
|
104
66
|
end
|
105
|
-
|
106
|
-
# We cant find that address for that customer
|
67
|
+
|
107
68
|
end
|
108
|
-
|
109
|
-
elsif @order.shipping.present?
|
110
|
-
# Use the current shipping and update its values
|
111
|
-
@shipping = @order.shipping
|
112
|
-
@shipping.update_attributes(shipping)
|
113
|
-
|
114
|
-
elsif shipping.values.all?(&:blank?) or shipping == billing
|
115
|
-
# We haven't set a shipping, or we have copied billing, so use billing
|
116
|
-
@shipping = @billing
|
117
|
-
@order.update_attribute(:shipping, @billing)
|
118
|
-
|
119
69
|
else
|
120
|
-
|
121
|
-
|
122
|
-
|
70
|
+
if @order.shipping.nil?
|
71
|
+
@shipping = ShopShipping.new(@billing.attributes)
|
72
|
+
if @shipping.save
|
73
|
+
@order.update_attribute(:shipping, @shipping)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
123
77
|
end
|
124
78
|
end
|
125
79
|
|
data/app/models/form_checkout.rb
CHANGED
@@ -4,7 +4,7 @@ class FormCheckout
|
|
4
4
|
|
5
5
|
attr_accessor :config, :data, :result, :gateway, :card
|
6
6
|
|
7
|
-
def create
|
7
|
+
def create
|
8
8
|
find_current_order # locate the @order object
|
9
9
|
|
10
10
|
create_result_object # A default response object
|
@@ -27,43 +27,39 @@ class FormCheckout
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def create_order_payments
|
30
|
-
|
31
|
-
|
30
|
+
success_redirect = @form.redirect_to
|
31
|
+
@form.redirect_to = :back
|
32
|
+
|
33
|
+
unless @order.billing.present?
|
34
|
+
@result[:message] = "Billing Address has not been set"
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
unless gateway.present?
|
39
|
+
@result[:message] = "Payment gateway has not been configured"
|
40
|
+
return false
|
41
|
+
end
|
32
42
|
|
33
|
-
|
43
|
+
unless prepare_gateway # Create the @gateway object
|
44
|
+
@result[:message] = "The Payment Gateway '#{gateway_name}' doesn't exist to ActiveMerchant"
|
45
|
+
return false
|
46
|
+
end
|
47
|
+
|
48
|
+
unless card.present?
|
49
|
+
@result[:message] = "Credit card details were not sent"
|
50
|
+
return false
|
51
|
+
end
|
34
52
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
prepare_credit_card # Create the @card object
|
40
|
-
|
41
|
-
if @result[:card]
|
42
|
-
|
43
|
-
if purchase.success?
|
44
|
-
finalize_checkout # We have a paid for order with a billing address
|
45
|
-
@result[:message] = "Order successfully processed"
|
46
|
-
end
|
47
|
-
|
48
|
-
else
|
49
|
-
@result[:message] = @card.errors.full_messages.to_sentence
|
50
|
-
end
|
51
|
-
|
52
|
-
else
|
53
|
-
@result[:message] = "Credit card details were not sent"
|
54
|
-
end
|
55
|
-
else
|
56
|
-
@result[:message] = "The Payment Gateway '#{gateway_name}' doesn't exist to ActiveMerchant"
|
57
|
-
end
|
58
|
-
else
|
59
|
-
@result[:message] = "Payment gateway has not been configured"
|
60
|
-
end
|
61
|
-
else
|
62
|
-
@result[:message] = "Billing Address has not been set"
|
53
|
+
unless prepare_credit_card # Create the @card object
|
54
|
+
@result[:message] = @card.errors.full_messages.to_sentence
|
55
|
+
return false
|
63
56
|
end
|
64
57
|
|
65
|
-
|
66
|
-
|
58
|
+
if purchase
|
59
|
+
create_payment
|
60
|
+
finalize_checkout
|
61
|
+
@form.redirect_to = success_redirect
|
62
|
+
end
|
67
63
|
end
|
68
64
|
|
69
65
|
# Creates a gateway instance variable based off the form configuration
|
@@ -73,6 +69,7 @@ class FormCheckout
|
|
73
69
|
@gateway = ActiveMerchant::Billing.const_get("#{gateway_name}Gateway").new(gateway_credentials)
|
74
70
|
@result[:gateway] = true
|
75
71
|
end
|
72
|
+
@result[:gateway]
|
76
73
|
end
|
77
74
|
|
78
75
|
# Creates a payment object attached to the order
|
@@ -107,17 +104,14 @@ class FormCheckout
|
|
107
104
|
else
|
108
105
|
@result[:card] = false
|
109
106
|
end
|
107
|
+
@result[:card]
|
110
108
|
end
|
111
109
|
|
112
110
|
# Uses the gateway and card objects to carry out an ActiveMerchant purchase
|
113
111
|
def purchase
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
create_payment
|
118
|
-
end
|
119
|
-
|
120
|
-
result
|
112
|
+
purchase = @gateway.purchase(amount, @card, options)
|
113
|
+
@result[:message] = purchase.message
|
114
|
+
purchase.success?
|
121
115
|
end
|
122
116
|
|
123
117
|
def finalize_checkout
|
@@ -169,7 +163,7 @@ class FormCheckout
|
|
169
163
|
|
170
164
|
# Returns the submitted card attributes
|
171
165
|
def card
|
172
|
-
@data[:
|
166
|
+
@data[:credit_card]
|
173
167
|
end
|
174
168
|
|
175
169
|
# Returns card number (1234123412341234)
|
@@ -205,7 +199,7 @@ class FormCheckout
|
|
205
199
|
# Splits the card names into an array we can inspect
|
206
200
|
def card_names
|
207
201
|
return @card_names if @card_names.present?
|
208
|
-
@card_names = @data[:
|
202
|
+
@card_names = @data[:credit_card][:name].split(' ')
|
209
203
|
end
|
210
204
|
|
211
205
|
# Return all the strings bar the last on the card
|
data/app/models/shop_address.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
class ShopAddress < ActiveRecord::Base
|
2
2
|
|
3
|
-
|
4
|
-
has_many :shippings, :class_name => 'ShopOrder', :foreign_key => :shipping_id
|
3
|
+
belongs_to :addressable, :polymorphic => true
|
5
4
|
|
6
5
|
validates_presence_of :name
|
7
|
-
validates_presence_of :
|
6
|
+
validates_presence_of :street_1
|
8
7
|
validates_presence_of :city
|
9
8
|
validates_presence_of :postcode
|
10
9
|
validates_presence_of :state
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class ShopLineItem < ActiveRecord::Base
|
2
2
|
|
3
3
|
belongs_to :order, :class_name => 'ShopOrder'
|
4
|
-
has_one :customer, :class_name => '
|
4
|
+
has_one :customer, :class_name => 'User', :through => :order, :source => :customer
|
5
5
|
belongs_to :item, :polymorphic => true
|
6
6
|
|
7
7
|
before_validation :adjust_quantity, :copy_price
|
data/app/models/shop_order.rb
CHANGED
@@ -2,14 +2,15 @@ class ShopOrder < ActiveRecord::Base
|
|
2
2
|
|
3
3
|
default_scope :order => 'shop_orders.updated_at DESC'
|
4
4
|
|
5
|
-
has_one :payment,
|
6
|
-
has_many :line_items,
|
7
|
-
|
8
|
-
belongs_to :
|
9
|
-
belongs_to :
|
10
|
-
belongs_to :
|
11
|
-
|
12
|
-
|
5
|
+
has_one :payment, :class_name => 'ShopPayment', :foreign_key => :order_id, :dependent => :destroy
|
6
|
+
has_many :line_items, :class_name => 'ShopLineItem', :foreign_key => :order_id, :dependent => :destroy
|
7
|
+
|
8
|
+
belongs_to :created_by, :class_name => 'User'
|
9
|
+
belongs_to :updated_by, :class_name => 'User'
|
10
|
+
belongs_to :customer, :class_name => 'User', :foreign_key => :customer_id
|
11
|
+
|
12
|
+
has_one :billing, :class_name => 'ShopBilling', :as => :addressable
|
13
|
+
has_one :shipping, :class_name => 'ShopShipping', :as => :addressable
|
13
14
|
|
14
15
|
accepts_nested_attributes_for :line_items, :reject_if => :all_blank
|
15
16
|
accepts_nested_attributes_for :billing, :reject_if => :all_blank
|
@@ -7,7 +7,7 @@
|
|
7
7
|
- body.status do
|
8
8
|
%span.status.attribute= order.status
|
9
9
|
- body.updated do
|
10
|
-
%span.updated.attribute=
|
10
|
+
%span.updated.attribute= distance_of_time_in_words(Time.now, order.updated_at)
|
11
11
|
- body.customer do
|
12
12
|
.modify
|
13
13
|
%span.customer.attribute= order.customer.name rescue nil
|
data/config/locales/en.yml
CHANGED
@@ -64,3 +64,120 @@ en:
|
|
64
64
|
warning: "Deleting a product is irreversible, make sure you're sure."
|
65
65
|
remove_category:
|
66
66
|
warning: "Deleting a category (and its products) is irreversible, make sure you're sure."
|
67
|
+
en:
|
68
|
+
number:
|
69
|
+
# Used in number_with_delimiter()
|
70
|
+
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
|
71
|
+
format:
|
72
|
+
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
|
73
|
+
separator: "."
|
74
|
+
# Delimets thousands (e.g. 1,000,000 is a million) (always in groups of three)
|
75
|
+
delimiter: ","
|
76
|
+
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
|
77
|
+
precision: 3
|
78
|
+
|
79
|
+
# Used in number_to_currency()
|
80
|
+
currency:
|
81
|
+
format:
|
82
|
+
# Where is the currency sign? %u is the currency unit, %n the number (default: $5.00)
|
83
|
+
format: "%u%n"
|
84
|
+
unit: "$"
|
85
|
+
# These three are to override number.format and are optional
|
86
|
+
separator: "."
|
87
|
+
delimiter: ","
|
88
|
+
precision: 2
|
89
|
+
|
90
|
+
# Used in number_to_percentage()
|
91
|
+
percentage:
|
92
|
+
format:
|
93
|
+
# These three are to override number.format and are optional
|
94
|
+
# separator:
|
95
|
+
delimiter: ""
|
96
|
+
# precision:
|
97
|
+
|
98
|
+
# Used in number_to_precision()
|
99
|
+
precision:
|
100
|
+
format:
|
101
|
+
# These three are to override number.format and are optional
|
102
|
+
# separator:
|
103
|
+
delimiter: ""
|
104
|
+
# precision:
|
105
|
+
|
106
|
+
# Used in number_to_human_size()
|
107
|
+
human:
|
108
|
+
format:
|
109
|
+
# These three are to override number.format and are optional
|
110
|
+
# separator:
|
111
|
+
delimiter: ""
|
112
|
+
precision: 1
|
113
|
+
storage_units:
|
114
|
+
# Storage units output formatting.
|
115
|
+
# %u is the storage unit, %n is the number (default: 2 MB)
|
116
|
+
format: "%n %u"
|
117
|
+
units:
|
118
|
+
byte:
|
119
|
+
one: "Byte"
|
120
|
+
other: "Bytes"
|
121
|
+
kb: "KB"
|
122
|
+
mb: "MB"
|
123
|
+
gb: "GB"
|
124
|
+
tb: "TB"
|
125
|
+
|
126
|
+
# Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
|
127
|
+
datetime:
|
128
|
+
distance_in_words:
|
129
|
+
half_a_minute: "half a minute"
|
130
|
+
less_than_x_seconds:
|
131
|
+
one: "less than 1 second"
|
132
|
+
other: "less than %{count} seconds"
|
133
|
+
x_seconds:
|
134
|
+
one: "1 second"
|
135
|
+
other: "%{count} seconds"
|
136
|
+
less_than_x_minutes:
|
137
|
+
one: "less than a minute"
|
138
|
+
other: "less than %{count} minutes"
|
139
|
+
x_minutes:
|
140
|
+
one: "1 minute"
|
141
|
+
other: "%{count} minutes"
|
142
|
+
about_x_hours:
|
143
|
+
one: "about 1 hour"
|
144
|
+
other: "about %{count} hours"
|
145
|
+
x_days:
|
146
|
+
one: "1 day"
|
147
|
+
other: "%{count} days"
|
148
|
+
about_x_months:
|
149
|
+
one: "about 1 month"
|
150
|
+
other: "about %{count} months"
|
151
|
+
x_months:
|
152
|
+
one: "1 month"
|
153
|
+
other: "%{count} months"
|
154
|
+
about_x_years:
|
155
|
+
one: "about 1 year"
|
156
|
+
other: "about %{count} years"
|
157
|
+
over_x_years:
|
158
|
+
one: "over 1 year"
|
159
|
+
other: "over %{count} years"
|
160
|
+
almost_x_years:
|
161
|
+
one: "almost 1 year"
|
162
|
+
other: "almost %{count} years"
|
163
|
+
prompts:
|
164
|
+
year: "Year"
|
165
|
+
month: "Month"
|
166
|
+
day: "Day"
|
167
|
+
hour: "Hour"
|
168
|
+
minute: "Minute"
|
169
|
+
second: "Seconds"
|
170
|
+
|
171
|
+
activerecord:
|
172
|
+
errors:
|
173
|
+
template:
|
174
|
+
header:
|
175
|
+
one: "1 error prohibited this %{model} from being saved"
|
176
|
+
other: "%{count} errors prohibited this %{model} from being saved"
|
177
|
+
# The variable :count is also available
|
178
|
+
body: "There were problems with the following fields:"
|
179
|
+
|
180
|
+
support:
|
181
|
+
select:
|
182
|
+
# default value for :prompt => true in FormOptionsHelper
|
183
|
+
prompt: "Please select"
|