spree_api 1.3.3 → 1.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/spree/api/checkouts_controller.rb +7 -0
- data/app/controllers/spree/api/orders_controller.rb +19 -1
- data/app/controllers/spree/api/products_controller.rb +8 -4
- data/app/controllers/spree/api/variants_controller.rb +1 -1
- data/app/helpers/spree/api/api_helpers.rb +1 -1
- data/app/models/spree/order_decorator.rb +156 -6
- data/app/overrides/api_key_spree_layout.rb +2 -1
- data/app/views/spree/api/variants/variant.v1.rabl +4 -0
- data/lib/spree/api/testing_support/setup.rb +0 -13
- data/spec/controllers/spree/api/checkouts_controller_spec.rb +17 -10
- data/spec/controllers/spree/api/orders_controller_spec.rb +51 -20
- data/spec/controllers/spree/api/products_controller_spec.rb +1 -2
- data/spec/controllers/spree/api/variants_controller_spec.rb +19 -9
- data/spec/models/spree/order_spec.rb +301 -7
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 288706a5a4c81fc84a82d1f1aa91dcd8ce546fb6
|
4
|
+
data.tar.gz: 75057c9776f7ecbe0374da3d4c155aa3bf61130d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83df37343d301ff95eb8f78144c36d6a0b88703f704400510dedf050b511482339e731b1396d3312012cbbcaeb843451bed73f3a8e257b6305d61859c9665c83
|
7
|
+
data.tar.gz: e88252e0d65aeee9ffcefab0b9c18bc088068881f7f42787615e1094c26fa076c3377cf4bb4c4f591edaf987b325b5b1321d3993ce82d2a4e5d69593c01077df
|
@@ -4,8 +4,14 @@ module Spree
|
|
4
4
|
before_filter :load_order, :only => :update
|
5
5
|
before_filter :associate_user, :only => :update
|
6
6
|
|
7
|
+
# Spree::Core::ControllerHelpers::Auth overrides
|
8
|
+
# Spree::Api::BaseController's unauthorized method...
|
9
|
+
# Which is not a good thing.
|
10
|
+
# Here's a small hack to shuffle around the method.
|
11
|
+
alias_method :real_unauthorized, :unauthorized
|
7
12
|
include Spree::Core::ControllerHelpers::Auth
|
8
13
|
include Spree::Core::ControllerHelpers::Order
|
14
|
+
alias_method :unauthorized, :real_unauthorized
|
9
15
|
|
10
16
|
respond_to :json
|
11
17
|
|
@@ -15,6 +21,7 @@ module Spree
|
|
15
21
|
end
|
16
22
|
|
17
23
|
def update
|
24
|
+
authorize! :update, @order, params[:order_token]
|
18
25
|
respond_with(@order, :default_template => 'spree/api/orders/show') and return if @order.state == "complete"
|
19
26
|
|
20
27
|
if object_params && object_params[:user_id].present?
|
@@ -17,12 +17,17 @@ module Spree
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def create
|
20
|
+
nested_params[:line_items_attributes] = sanitize_line_items(nested_params[:line_items_attributes])
|
20
21
|
@order = Order.build_from_api(current_api_user, nested_params)
|
21
22
|
respond_with(order, :default_template => :show, :status => 201)
|
22
23
|
end
|
23
24
|
|
24
25
|
def update
|
25
26
|
authorize! :update, Order
|
27
|
+
# Parsing line items through as an update_attributes call in the API will result in
|
28
|
+
# many line items for the same variant_id being created. We must be smarter about this,
|
29
|
+
# hence the use of the update_line_items method, defined within order_decorator.rb.
|
30
|
+
nested_params[:line_items_attributes] = sanitize_line_items(nested_params[:line_items_attributes])
|
26
31
|
if order.update_attributes(nested_params)
|
27
32
|
order.update!
|
28
33
|
respond_with(order, :default_template => :show)
|
@@ -45,7 +50,20 @@ module Spree
|
|
45
50
|
private
|
46
51
|
|
47
52
|
def nested_params
|
48
|
-
map_nested_attributes_keys
|
53
|
+
@nested_params ||= map_nested_attributes_keys(Order, params[:order] || {})
|
54
|
+
end
|
55
|
+
|
56
|
+
def sanitize_line_items(line_item_attributes)
|
57
|
+
return {} if line_item_attributes.blank?
|
58
|
+
line_item_attributes = line_item_attributes.map do |id, attributes|
|
59
|
+
# Faux Strong-Parameters code to strip price if user isn't an admin
|
60
|
+
if current_api_user.has_spree_role?("admin")
|
61
|
+
[id, attributes.slice(*Spree::LineItem.attr_accessible[:api])]
|
62
|
+
else
|
63
|
+
[id, attributes.slice(*Spree::LineItem.attr_accessible[:default])]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
line_item_attributes = Hash[line_item_attributes].delete_if { |k,v| v.empty? }
|
49
67
|
end
|
50
68
|
|
51
69
|
def order
|
@@ -20,10 +20,14 @@ module Spree
|
|
20
20
|
authorize! :create, Product
|
21
21
|
params[:product][:available_on] ||= Time.now
|
22
22
|
@product = Product.new(params[:product])
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
23
|
+
begin
|
24
|
+
if @product.save
|
25
|
+
respond_with(@product, :status => 201, :default_template => :show)
|
26
|
+
else
|
27
|
+
invalid_resource!(@product)
|
28
|
+
end
|
29
|
+
rescue ActiveRecord::RecordNotUnique
|
30
|
+
retry
|
27
31
|
end
|
28
32
|
end
|
29
33
|
|
@@ -21,7 +21,7 @@ module Spree
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def variant_attributes
|
24
|
-
[:id, :name, :count_on_hand, :sku, :price, :weight, :height, :width, :depth, :is_master, :cost_price, :permalink]
|
24
|
+
[:id, :name, :count_on_hand, :sku, :price, :weight, :height, :width, :depth, :is_master, :cost_price, :permalink, :product_id, :lock_version]
|
25
25
|
end
|
26
26
|
|
27
27
|
def image_attributes
|
@@ -1,13 +1,163 @@
|
|
1
1
|
Spree::Order.class_eval do
|
2
2
|
def self.build_from_api(user, params)
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
params[:
|
7
|
-
|
3
|
+
begin
|
4
|
+
ensure_country_id_from_api params[:ship_address_attributes]
|
5
|
+
ensure_state_id_from_api params[:ship_address_attributes]
|
6
|
+
ensure_country_id_from_api params[:bill_address_attributes]
|
7
|
+
ensure_state_id_from_api params[:bill_address_attributes]
|
8
|
+
|
9
|
+
order = create!
|
10
|
+
|
11
|
+
order.create_shipments_from_api params.delete(:shipments_attributes) || []
|
12
|
+
order.create_line_items_from_api params.delete(:line_items_attributes) || {}
|
13
|
+
order.create_adjustments_from_api params.delete(:adjustments_attributes) || []
|
14
|
+
order.create_payments_from_api params.delete(:payments_attributes) || []
|
15
|
+
order.complete_from_api params.delete(:completed_at)
|
16
|
+
|
17
|
+
destroy_automatic_taxes_on_import(order, params)
|
18
|
+
order.update_attributes!(params)
|
19
|
+
|
20
|
+
order.reload
|
21
|
+
rescue Exception => e
|
22
|
+
order.destroy if order && order.persisted?
|
23
|
+
raise e.message
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.destroy_automatic_taxes_on_import(order, params)
|
28
|
+
if params.delete :import
|
29
|
+
order.adjustments.tax.destroy_all
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def complete_from_api(completed_at)
|
34
|
+
if completed_at
|
35
|
+
self.completed_at = completed_at
|
36
|
+
self.state = 'complete'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def create_shipments_from_api(shipments_hash)
|
41
|
+
shipments_hash.each do |s|
|
42
|
+
begin
|
43
|
+
shipment = shipments.build
|
44
|
+
shipment.tracking = s[:tracking]
|
45
|
+
|
46
|
+
inventory_units = s[:inventory_units] || []
|
47
|
+
inventory_units.each do |iu|
|
48
|
+
self.class.ensure_variant_id_from_api(iu)
|
49
|
+
|
50
|
+
unit = shipment.inventory_units.build
|
51
|
+
unit.order = self
|
52
|
+
unit.variant_id = iu[:variant_id]
|
53
|
+
end
|
54
|
+
|
55
|
+
shipment.shipping_method = Spree::ShippingMethod.find_by_name!(s[:shipping_method])
|
56
|
+
shipment.save!
|
57
|
+
|
58
|
+
shipment.adjustment.locked = true
|
59
|
+
shipment.adjustment.amount = s[:cost].to_f
|
60
|
+
shipment.adjustment.save
|
61
|
+
rescue Exception => e
|
62
|
+
raise "#{e.message} #{s}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def create_payments_from_api(payments_hash)
|
68
|
+
payments_hash.each do |p|
|
69
|
+
begin
|
70
|
+
payment = payments.build
|
71
|
+
payment.amount = p[:amount].to_f
|
72
|
+
payment.state = p.fetch(:state, 'completed')
|
73
|
+
payment.payment_method = Spree::PaymentMethod.find_by_name!(p[:payment_method])
|
74
|
+
payment.save!
|
75
|
+
rescue Exception => e
|
76
|
+
raise "#{e.message} #{p}"
|
8
77
|
end
|
9
78
|
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def create_line_items_from_api(line_items_hash)
|
82
|
+
line_items_hash.each_key do |k|
|
83
|
+
begin
|
84
|
+
line_item = line_items_hash[k]
|
85
|
+
self.class.ensure_variant_id_from_api(line_item)
|
86
|
+
|
87
|
+
item = self.add_variant(Spree::Variant.find(line_item[:variant_id]), line_item[:quantity])
|
10
88
|
|
11
|
-
|
89
|
+
if line_item.key? :price
|
90
|
+
item.price = line_item[:price]
|
91
|
+
item.save!
|
92
|
+
end
|
93
|
+
rescue Exception => e
|
94
|
+
raise "#{e.message} #{line_item}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def create_adjustments_from_api(adjustments)
|
100
|
+
adjustments.each do |a|
|
101
|
+
begin
|
102
|
+
adjustment = self.adjustments.build(:amount => a['amount'].to_f,
|
103
|
+
:label => a['label'])
|
104
|
+
adjustment.locked = true
|
105
|
+
adjustment.save!
|
106
|
+
rescue Exception => e
|
107
|
+
raise "#{e.message} #{a}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.ensure_variant_id_from_api(hash)
|
113
|
+
begin
|
114
|
+
unless hash[:variant_id].present?
|
115
|
+
hash[:variant_id] = Spree::Variant.active.find_by_sku!(hash[:sku]).id
|
116
|
+
hash.delete(:sku)
|
117
|
+
end
|
118
|
+
rescue Exception => e
|
119
|
+
raise "#{e.message} #{hash}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def self.ensure_country_id_from_api(address)
|
124
|
+
return if address.nil? or address[:country_id].present? or address[:country].nil?
|
125
|
+
|
126
|
+
begin
|
127
|
+
search = {}
|
128
|
+
if name = address[:country]['name']
|
129
|
+
search[:name] = name
|
130
|
+
elsif iso_name = address[:country]['iso_name']
|
131
|
+
search[:iso_name] = iso_name.upcase
|
132
|
+
elsif iso = address[:country]['iso']
|
133
|
+
search[:iso] = iso.upcase
|
134
|
+
elsif iso3 = address[:country]['iso3']
|
135
|
+
search[:iso3] = iso3.upcase
|
136
|
+
end
|
137
|
+
|
138
|
+
address.delete(:country)
|
139
|
+
address[:country_id] = Spree::Country.where(search).first!.id
|
140
|
+
|
141
|
+
rescue Exception => e
|
142
|
+
raise "#{e.message} #{search}"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.ensure_state_id_from_api(address)
|
147
|
+
return if address.nil? or address[:state_id].present? or address[:state].nil?
|
148
|
+
|
149
|
+
begin
|
150
|
+
search = {}
|
151
|
+
if name = address[:state]['name']
|
152
|
+
search[:name] = name
|
153
|
+
elsif abbr = address[:state]['abbr']
|
154
|
+
search[:abbr] = abbr.upcase
|
155
|
+
end
|
156
|
+
|
157
|
+
address.delete(:state)
|
158
|
+
address[:state_id] = Spree::State.where(search).first!.id
|
159
|
+
rescue Exception => e
|
160
|
+
raise "#{e.message} #{search}"
|
161
|
+
end
|
12
162
|
end
|
13
163
|
end
|
@@ -2,5 +2,6 @@ Deface::Override.new(:virtual_path => "spree/layouts/spree_application",
|
|
2
2
|
:name => "api_key_spree_layout",
|
3
3
|
:insert_bottom => "body",
|
4
4
|
:partial => "spree/api/key",
|
5
|
-
:disabled => false
|
5
|
+
:disabled => false,
|
6
|
+
:original => "eb4b04993e8e4d1c20a7c3d974dfed20b59aec4c")
|
6
7
|
|
@@ -9,19 +9,6 @@ module Spree
|
|
9
9
|
user
|
10
10
|
end
|
11
11
|
end
|
12
|
-
|
13
|
-
# Default kaminari's pagination to a certain range
|
14
|
-
# Means that you don't need to create 25 objects to test pagination
|
15
|
-
def default_per_page(count)
|
16
|
-
before do
|
17
|
-
@current_default_per_page = Kaminari.config.default_per_page
|
18
|
-
Kaminari.config.default_per_page = 1
|
19
|
-
end
|
20
|
-
|
21
|
-
after do
|
22
|
-
Kaminari.config.default_per_page = @current_default_per_page
|
23
|
-
end
|
24
|
-
end
|
25
12
|
end
|
26
13
|
end
|
27
14
|
end
|
@@ -56,7 +56,7 @@ module Spree
|
|
56
56
|
order.email = nil # email is necessary to transition from cart to address
|
57
57
|
order.save!
|
58
58
|
|
59
|
-
api_put :update, :id => order.to_param
|
59
|
+
api_put :update, :id => order.to_param, :order_token => order.token
|
60
60
|
|
61
61
|
json_response['error'].should =~ /could not be transitioned/
|
62
62
|
response.status.should == 422
|
@@ -65,13 +65,13 @@ module Spree
|
|
65
65
|
it "should transition a recently created order from cart do address" do
|
66
66
|
order.state.should eq "cart"
|
67
67
|
order.email.should_not be_nil
|
68
|
-
api_put :update, :id => order.to_param
|
68
|
+
api_put :update, :id => order.to_param, :order_token => order.token
|
69
69
|
order.reload.state.should eq "address"
|
70
70
|
end
|
71
71
|
|
72
72
|
it "will return an error if the order cannot transition" do
|
73
73
|
order.update_column(:state, "address")
|
74
|
-
api_put :update, :id => order.to_param
|
74
|
+
api_put :update, :id => order.to_param, :order_token => order.token
|
75
75
|
json_response['error'].should =~ /could not be transitioned/
|
76
76
|
response.status.should == 422
|
77
77
|
end
|
@@ -89,7 +89,7 @@ module Spree
|
|
89
89
|
:country_id => @country.id
|
90
90
|
}
|
91
91
|
api_put :update,
|
92
|
-
:id => order.to_param,
|
92
|
+
:id => order.to_param, :order_token => order.token,
|
93
93
|
:order => { :bill_address_attributes => billing_address, :ship_address_attributes => shipping_address }
|
94
94
|
|
95
95
|
json_response['state'].should == 'delivery'
|
@@ -100,7 +100,8 @@ module Spree
|
|
100
100
|
|
101
101
|
it "can update shipping method and transition from delivery to payment" do
|
102
102
|
order.update_column(:state, "delivery")
|
103
|
-
api_put :update, :id => order.to_param, :
|
103
|
+
api_put :update, :id => order.to_param, :order_token => order.token,
|
104
|
+
:order => { :shipping_method_id => @shipping_method.id }
|
104
105
|
|
105
106
|
json_response['shipments'][0]['shipping_method']['name'].should == @shipping_method.name
|
106
107
|
json_response['state'].should == 'payment'
|
@@ -109,7 +110,8 @@ module Spree
|
|
109
110
|
|
110
111
|
it "can update payment method and transition from payment to confirm" do
|
111
112
|
order.update_column(:state, "payment")
|
112
|
-
api_put :update, :id => order.to_param, :
|
113
|
+
api_put :update, :id => order.to_param, :order_token => order.token,
|
114
|
+
:order => { :payments_attributes => [{ :payment_method_id => @payment_method.id }] }
|
113
115
|
json_response['state'].should == 'confirm'
|
114
116
|
json_response['payments'][0]['payment_method']['name'].should == @payment_method.name
|
115
117
|
response.status.should == 200
|
@@ -118,30 +120,35 @@ module Spree
|
|
118
120
|
it "can transition from confirm to complete" do
|
119
121
|
order.update_column(:state, "confirm")
|
120
122
|
Spree::Order.any_instance.stub(:payment_required? => false)
|
121
|
-
api_put :update, :id => order.to_param
|
123
|
+
api_put :update, :id => order.to_param, :order_token => order.token
|
122
124
|
json_response['state'].should == 'complete'
|
123
125
|
response.status.should == 200
|
124
126
|
end
|
125
127
|
|
126
128
|
it "returns the order if the order is already complete" do
|
127
129
|
order.update_column(:state, "complete")
|
128
|
-
api_put :update, :id => order.to_param
|
130
|
+
api_put :update, :id => order.to_param, :order_token => order.token
|
129
131
|
json_response['number'].should == order.number
|
130
132
|
response.status.should == 200
|
131
133
|
end
|
132
134
|
|
133
135
|
it "can assign a user to the order" do
|
134
136
|
user = create(:user)
|
135
|
-
api_put :update, :id => order.to_param, :order => { :user_id => user.id }
|
137
|
+
api_put :update, :id => order.to_param, :order_token => order.token, :order => { :user_id => user.id }
|
136
138
|
json_response['user_id'].should == user.id
|
137
139
|
response.status.should == 200
|
138
140
|
end
|
139
141
|
|
140
142
|
it "can assign an email to the order" do
|
141
|
-
api_put :update, :id => order.to_param, :order => { :email => "guest@spreecommerce.com" }
|
143
|
+
api_put :update, :id => order.to_param, :order_token => order.token, :order => { :email => "guest@spreecommerce.com" }
|
142
144
|
json_response['email'].should == "guest@spreecommerce.com"
|
143
145
|
response.status.should == 200
|
144
146
|
end
|
147
|
+
|
148
|
+
it "cannot update an order without authorization" do
|
149
|
+
api_put :update, :id => order.to_param
|
150
|
+
assert_unauthorized!
|
151
|
+
end
|
145
152
|
end
|
146
153
|
end
|
147
154
|
end
|
@@ -71,6 +71,25 @@ module Spree
|
|
71
71
|
json_response["state"].should == "cart"
|
72
72
|
end
|
73
73
|
|
74
|
+
it "cannot create an order with an abitrary price for the line item" do
|
75
|
+
variant = create(:variant)
|
76
|
+
api_post :create, :order => {
|
77
|
+
:line_items => {
|
78
|
+
"0" => {
|
79
|
+
:variant_id => variant.to_param,
|
80
|
+
:quantity => 5,
|
81
|
+
:price => 0.44
|
82
|
+
}
|
83
|
+
}
|
84
|
+
}
|
85
|
+
response.status.should == 201
|
86
|
+
order = Order.last
|
87
|
+
order.line_items.count.should == 1
|
88
|
+
order.line_items.first.variant.should == variant
|
89
|
+
order.line_items.first.quantity.should == 5
|
90
|
+
order.line_items.first.price.should == order.line_items.first.variant.price
|
91
|
+
end
|
92
|
+
|
74
93
|
context "working with an order" do
|
75
94
|
before do
|
76
95
|
Order.any_instance.stub :user => current_api_user
|
@@ -87,55 +106,67 @@ module Spree
|
|
87
106
|
end
|
88
107
|
|
89
108
|
let(:address_params) { { :country_id => Country.first.id, :state_id => State.first.id } }
|
90
|
-
let(:billing_address) { { :firstname => "Tiago", :lastname => "Motta", :address1 => "Av Paulista",
|
109
|
+
let(:billing_address) { { :firstname => "Tiago", :lastname => "Motta", :address1 => "Av Paulista",
|
91
110
|
:city => "Sao Paulo", :zipcode => "1234567", :phone => "12345678",
|
92
111
|
:country_id => Country.first.id, :state_id => State.first.id} }
|
93
|
-
let(:shipping_address) { { :firstname => "Tiago", :lastname => "Motta", :address1 => "Av Paulista",
|
112
|
+
let(:shipping_address) { { :firstname => "Tiago", :lastname => "Motta", :address1 => "Av Paulista",
|
94
113
|
:city => "Sao Paulo", :zipcode => "1234567", :phone => "12345678",
|
95
114
|
:country_id => Country.first.id, :state_id => State.first.id} }
|
96
115
|
let!(:shipping_method) { create(:shipping_method) }
|
97
116
|
let!(:payment_method) { create(:payment_method) }
|
98
117
|
|
99
|
-
it "can
|
100
|
-
|
118
|
+
it "can not update line item prices" do
|
119
|
+
order.line_items << create(:line_item)
|
120
|
+
api_put :update,
|
121
|
+
:id => order.to_param,
|
122
|
+
:order => {
|
123
|
+
:line_items => {
|
124
|
+
order.line_items.first.id =>
|
125
|
+
{
|
126
|
+
:variant_id => create(:variant).id,
|
127
|
+
:quantity => 2,
|
128
|
+
:price => 0.44
|
129
|
+
}
|
130
|
+
}
|
131
|
+
}
|
101
132
|
|
102
133
|
response.status.should == 200
|
103
134
|
json_response['item_total'].to_f.should_not == order.item_total.to_f
|
104
135
|
end
|
105
|
-
|
136
|
+
|
106
137
|
it "can add billing address" do
|
107
138
|
order.bill_address.should be_nil
|
108
|
-
|
139
|
+
|
109
140
|
api_put :update, :id => order.to_param, :order => { :bill_address_attributes => billing_address }
|
110
|
-
|
141
|
+
|
111
142
|
order.reload.bill_address.should_not be_nil
|
112
143
|
end
|
113
|
-
|
144
|
+
|
114
145
|
it "receives error message if trying to add billing address with errors" do
|
115
146
|
order.bill_address.should be_nil
|
116
147
|
billing_address[:firstname] = ""
|
117
|
-
|
148
|
+
|
118
149
|
api_put :update, :id => order.to_param, :order => { :bill_address_attributes => billing_address }
|
119
|
-
|
150
|
+
|
120
151
|
json_response['error'].should_not be_nil
|
121
152
|
json_response['errors'].should_not be_nil
|
122
153
|
json_response['errors']['bill_address.firstname'].first.should eq "can't be blank"
|
123
154
|
end
|
124
|
-
|
155
|
+
|
125
156
|
it "can add shipping address" do
|
126
157
|
order.ship_address.should be_nil
|
127
|
-
|
158
|
+
|
128
159
|
api_put :update, :id => order.to_param, :order => { :ship_address_attributes => shipping_address }
|
129
|
-
|
160
|
+
|
130
161
|
order.reload.ship_address.should_not be_nil
|
131
162
|
end
|
132
|
-
|
163
|
+
|
133
164
|
it "receives error message if trying to add shipping address with errors" do
|
134
165
|
order.ship_address.should be_nil
|
135
166
|
shipping_address[:firstname] = ""
|
136
|
-
|
167
|
+
|
137
168
|
api_put :update, :id => order.to_param, :order => { :ship_address_attributes => shipping_address }
|
138
|
-
|
169
|
+
|
139
170
|
json_response['error'].should_not be_nil
|
140
171
|
json_response['errors'].should_not be_nil
|
141
172
|
json_response['errors']['ship_address.firstname'].first.should eq "can't be blank"
|
@@ -151,15 +182,15 @@ module Spree
|
|
151
182
|
response.status.should == 200
|
152
183
|
order.reload.line_items.should be_empty
|
153
184
|
end
|
154
|
-
|
185
|
+
|
155
186
|
it "can list its line items with images" do
|
156
187
|
order.line_items.first.variant.images.create!(:attachment => image("thinking-cat.jpg"))
|
157
|
-
|
188
|
+
|
158
189
|
api_get :show, :id => order.to_param
|
159
|
-
|
190
|
+
|
160
191
|
json_response['line_items'].first['variant'].should have_attributes([:images])
|
161
192
|
end
|
162
|
-
|
193
|
+
|
163
194
|
it "lists variants product id" do
|
164
195
|
api_get :show, :id => order.to_param
|
165
196
|
|
@@ -28,11 +28,10 @@ module Spree
|
|
28
28
|
end
|
29
29
|
|
30
30
|
context "pagination" do
|
31
|
-
default_per_page(1)
|
32
31
|
|
33
32
|
it "can select the next page of products" do
|
34
33
|
second_product = create(:product)
|
35
|
-
api_get :index, :page => 2
|
34
|
+
api_get :index, :page => 2, :per_page => 1
|
36
35
|
json_response["products"].first.should have_attributes(attributes)
|
37
36
|
json_response["total_count"].should == 2
|
38
37
|
json_response["current_page"].should == 2
|
@@ -14,7 +14,7 @@ module Spree
|
|
14
14
|
let!(:attributes) { [:id, :name, :count_on_hand,
|
15
15
|
:sku, :price, :weight, :height,
|
16
16
|
:width, :depth, :is_master, :cost_price,
|
17
|
-
:permalink] }
|
17
|
+
:permalink, :product_id, :lock_version] }
|
18
18
|
|
19
19
|
before do
|
20
20
|
stub_authentication!
|
@@ -51,7 +51,7 @@ module Spree
|
|
51
51
|
:option_type_name,
|
52
52
|
:option_type_id])
|
53
53
|
end
|
54
|
-
|
54
|
+
|
55
55
|
it "variants returned contain images data" do
|
56
56
|
variant.images.create!(:attachment => image("thinking-cat.jpg"))
|
57
57
|
|
@@ -78,11 +78,9 @@ module Spree
|
|
78
78
|
end
|
79
79
|
|
80
80
|
context "pagination" do
|
81
|
-
default_per_page(1)
|
82
|
-
|
83
81
|
it "can select the next page of variants" do
|
84
82
|
second_variant = create(:variant)
|
85
|
-
api_get :index, :page => 2
|
83
|
+
api_get :index, :page => 2, :per_page => 1
|
86
84
|
json_response["variants"].first.should have_attributes(attributes)
|
87
85
|
json_response["total_count"].should == 3
|
88
86
|
json_response["current_page"].should == 2
|
@@ -99,13 +97,13 @@ module Spree
|
|
99
97
|
:option_type_name,
|
100
98
|
:option_type_id])
|
101
99
|
end
|
102
|
-
|
100
|
+
|
103
101
|
it "can see a single variant with images" do
|
104
102
|
variant.images.create!(:attachment => image("thinking-cat.jpg"))
|
105
|
-
|
103
|
+
|
106
104
|
api_get :show, :id => variant.to_param
|
107
|
-
|
108
|
-
json_response.should have_attributes(attributes + [:images])
|
105
|
+
|
106
|
+
json_response.should have_attributes(attributes + [:images])
|
109
107
|
option_values = json_response["option_values"]
|
110
108
|
option_values.first.should have_attributes([:name,
|
111
109
|
:presentation,
|
@@ -170,6 +168,18 @@ module Spree
|
|
170
168
|
response.status.should == 204
|
171
169
|
lambda { variant.reload }.should raise_error(ActiveRecord::RecordNotFound)
|
172
170
|
end
|
171
|
+
|
172
|
+
context "without product" do
|
173
|
+
sign_in_as_admin!
|
174
|
+
let(:resource_scoping) { { :product_id => variant.product.to_param } }
|
175
|
+
|
176
|
+
it "will not raise an ActiveRecord::ReadOnlyRecord exception" do
|
177
|
+
api_put :update, :id => variant.to_param, :variant => { :sku => "12345", :on_hand => 5 }
|
178
|
+
response.status.should_not eql 422
|
179
|
+
json_response["exception"].should_not eql "ActiveRecord::ReadOnlyRecord"
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
173
183
|
end
|
174
184
|
|
175
185
|
|
@@ -2,17 +2,311 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Spree
|
4
4
|
describe Order do
|
5
|
-
let(:
|
5
|
+
let!(:country) { FactoryGirl.create(:country) }
|
6
|
+
let!(:state) { country.states.first || FactoryGirl.create(:state, :country => country) }
|
7
|
+
let(:user) { create(:user) }
|
8
|
+
let(:product) { Spree::Product.create!(:name => 'Test', :sku => 'TEST-1', :price => 33.22) }
|
9
|
+
let(:sku) { product.master.sku }
|
10
|
+
let(:variant) { product.master }
|
11
|
+
let(:variant_id) { product.master.id }
|
12
|
+
let(:line_items) {{ "0" => { :variant_id => variant.id, :quantity => 5 }}}
|
13
|
+
let(:shipping_method) { create(:shipping_method) }
|
14
|
+
let(:payment_method) { create(:payment_method) }
|
15
|
+
let(:ship_address) {{
|
16
|
+
:address1 => '123 Testable Way',
|
17
|
+
:firstname => 'Fox',
|
18
|
+
:lastname => 'Mulder',
|
19
|
+
:city => 'Washington',
|
20
|
+
:country_id => country.id,
|
21
|
+
:state_id => state.id,
|
22
|
+
:zipcode => '666',
|
23
|
+
:phone => '666-666-6666'
|
24
|
+
}}
|
6
25
|
|
7
|
-
it 'can
|
8
|
-
|
9
|
-
|
10
|
-
order
|
26
|
+
it 'can import an order number' do
|
27
|
+
params = { number: '123-456-789' }
|
28
|
+
order = Order.build_from_api(user, params)
|
29
|
+
order.number.should eq '123-456-789'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'optionally add completed at' do
|
33
|
+
params = {:email => 'test@test.com',
|
34
|
+
:completed_at => Time.now,
|
35
|
+
:line_items_attributes => line_items }
|
36
|
+
|
37
|
+
order = Order.build_from_api(user, params)
|
38
|
+
order.should be_completed
|
39
|
+
order.state.should eq 'complete'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "assigns order[email] over user email to order" do
|
43
|
+
params = { email: 'wooowww@test.com' }
|
44
|
+
order = Order.build_from_api(user, params)
|
45
|
+
expect(order.email).to eq params[:email]
|
46
|
+
end
|
47
|
+
|
48
|
+
context "build order with line items" do
|
49
|
+
let(:attributes) do
|
50
|
+
{ :variant_id => variant.id, :quantity => 5, :price => 33.77 }
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'can build an order from API with just line items' do
|
54
|
+
params = { :line_items_attributes => { "0" => attributes } }
|
55
|
+
Order.should_receive(:ensure_variant_id_from_api)
|
56
|
+
order = Order.build_from_api(user, params)
|
57
|
+
|
58
|
+
order.user.should == nil
|
59
|
+
line_item = order.line_items.first
|
60
|
+
|
61
|
+
expect(line_item.quantity).to eq attributes[:quantity]
|
62
|
+
expect(line_item.variant_id).to eq attributes[:variant_id]
|
63
|
+
expect(line_item.price).to eq attributes[:price]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'uses line item price if present' do
|
68
|
+
line_items['0'][:price] = 12.00
|
69
|
+
params = { :line_items_attributes => line_items }
|
70
|
+
|
71
|
+
order = Order.build_from_api(user, params)
|
72
|
+
|
73
|
+
line_item = order.line_items.first
|
74
|
+
line_item.price.to_f.should == 12.00
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'handles line_item building exceptions' do
|
78
|
+
line_items['0'][:variant_id] = 'XXX'
|
79
|
+
params = { :line_items_attributes => line_items }
|
80
|
+
|
81
|
+
expect {
|
82
|
+
order = Order.build_from_api(user, params)
|
83
|
+
}.to raise_error /XXX/
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'can build an order from API with variant sku' do
|
87
|
+
params = { :line_items_attributes => {
|
88
|
+
"0" => { :sku => sku, :quantity => 5 } }}
|
89
|
+
|
90
|
+
order = Order.build_from_api(user, params)
|
11
91
|
|
12
|
-
order.user.should == nil
|
13
92
|
line_item = order.line_items.first
|
14
|
-
line_item.quantity.should == 5
|
15
93
|
line_item.variant_id.should == variant_id
|
94
|
+
line_item.quantity.should == 5
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'handles exceptions when sku is not found' do
|
98
|
+
params = { :line_items_attributes => {
|
99
|
+
"0" => { :sku => 'XXX', :quantity => 5 } }}
|
100
|
+
expect {
|
101
|
+
order = Order.build_from_api(user, params)
|
102
|
+
}.to raise_error /XXX/
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'can build an order from API shipping address' do
|
106
|
+
params = { :ship_address_attributes => ship_address,
|
107
|
+
:line_items_attributes => line_items }
|
108
|
+
|
109
|
+
order = Order.build_from_api(user, params)
|
110
|
+
order.ship_address.address1.should eq '123 Testable Way'
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'can build an order from API with country attributes' do
|
114
|
+
ship_address.delete(:country_id)
|
115
|
+
ship_address[:country] = { 'iso' => 'US' }
|
116
|
+
params = { :ship_address_attributes => ship_address,
|
117
|
+
:line_items_attributes => line_items }
|
118
|
+
|
119
|
+
order = Order.build_from_api(user, params)
|
120
|
+
order.ship_address.country.iso.should eq 'US'
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'handles country lookup exceptions' do
|
124
|
+
ship_address.delete(:country_id)
|
125
|
+
ship_address[:country] = { 'iso' => 'XXX' }
|
126
|
+
params = { :ship_address_attributes => ship_address,
|
127
|
+
:line_items_attributes => line_items }
|
128
|
+
|
129
|
+
expect {
|
130
|
+
order = Order.build_from_api(user, params)
|
131
|
+
}.to raise_error /XXX/
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'can build an order from API with state attributes' do
|
135
|
+
ship_address.delete(:state_id)
|
136
|
+
ship_address[:state] = { 'name' => 'Alabama' }
|
137
|
+
params = { :ship_address_attributes => ship_address,
|
138
|
+
:line_items_attributes => line_items }
|
139
|
+
|
140
|
+
order = Order.build_from_api(user, params)
|
141
|
+
order.ship_address.state.name.should eq 'Alabama'
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'handles state lookup exceptions' do
|
145
|
+
ship_address.delete(:state_id)
|
146
|
+
ship_address[:state] = { 'name' => 'XXX' }
|
147
|
+
params = { :ship_address_attributes => ship_address,
|
148
|
+
:line_items_attributes => line_items }
|
149
|
+
|
150
|
+
expect {
|
151
|
+
order = Order.build_from_api(user, params)
|
152
|
+
}.to raise_error /XXX/
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'variant not deleted' do
|
156
|
+
it 'ensures variant id from api' do
|
157
|
+
hash = { sku: variant.sku }
|
158
|
+
Order.ensure_variant_id_from_api(hash)
|
159
|
+
expect(hash[:variant_id]).to eq variant.id
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'variant was deleted' do
|
164
|
+
it 'raise error as variant shouldnt be found' do
|
165
|
+
variant.product.delete
|
166
|
+
hash = { sku: variant.sku }
|
167
|
+
expect {
|
168
|
+
Order.ensure_variant_id_from_api(hash)
|
169
|
+
}.to raise_error
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'ensures_country_id for country fields' do
|
174
|
+
[:name, :iso, :iso_name, :iso3].each do |field|
|
175
|
+
address = { :country => { field => country.send(field) }}
|
176
|
+
Order.ensure_country_id_from_api(address)
|
177
|
+
address[:country_id].should eq country.id
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
it "raises with proper message when cant find country" do
|
182
|
+
address = { :country => { "name" => "NoNoCountry" } }
|
183
|
+
expect {
|
184
|
+
Order.ensure_country_id_from_api(address)
|
185
|
+
}.to raise_error /NoNoCountry/
|
186
|
+
end
|
187
|
+
|
188
|
+
it 'ensures_state_id for state fields' do
|
189
|
+
[:name, :abbr].each do |field|
|
190
|
+
address = { :state => { field => state.send(field) }}
|
191
|
+
Order.ensure_state_id_from_api(address)
|
192
|
+
address[:state_id].should eq state.id
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
it "raises with proper message when cant find state" do
|
197
|
+
address = { :state => { "name" => "NoNoState" } }
|
198
|
+
expect {
|
199
|
+
Order.ensure_state_id_from_api(address)
|
200
|
+
}.to raise_error /NoNoState/
|
201
|
+
end
|
202
|
+
|
203
|
+
context "shippments" do
|
204
|
+
let(:params) do
|
205
|
+
{ :shipments_attributes => [
|
206
|
+
{ :tracking => '123456789',
|
207
|
+
:cost => '4.99',
|
208
|
+
:shipping_method => shipping_method.name,
|
209
|
+
:inventory_units => [{ :sku => sku }]
|
210
|
+
}
|
211
|
+
] }
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'ensures variant exists and is not deleted' do
|
215
|
+
Order.should_receive(:ensure_variant_id_from_api)
|
216
|
+
order = Order.build_from_api(user, params)
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'builds them properly' do
|
220
|
+
order = Order.build_from_api(user, params)
|
221
|
+
|
222
|
+
shipment = order.shipments.first
|
223
|
+
shipment.inventory_units.first.variant_id.should eq product.master.id
|
224
|
+
shipment.tracking.should eq '123456789'
|
225
|
+
shipment.adjustment.amount.should eq 4.99
|
226
|
+
shipment.adjustment.should be_locked
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'handles shipment building exceptions' do
|
231
|
+
params = { :shipments_attributes => [{ :tracking => '123456789',
|
232
|
+
:cost => '4.99',
|
233
|
+
:shipping_method => 'XXX',
|
234
|
+
:inventory_units => [{ :sku => sku }]
|
235
|
+
}] }
|
236
|
+
expect {
|
237
|
+
order = Order.build_from_api(user, params)
|
238
|
+
}.to raise_error /XXX/
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'adds adjustments' do
|
242
|
+
params = { :adjustments_attributes => [
|
243
|
+
{ "label" => "Shipping Discount", "amount" => "-4.99" },
|
244
|
+
{ "label" => "Promotion Discount", "amount" => "-3.00" }] }
|
245
|
+
|
246
|
+
order = Order.build_from_api(user, params)
|
247
|
+
order.adjustments.all?(&:locked).should be_true
|
248
|
+
order.adjustments.first.label.should eq 'Shipping Discount'
|
249
|
+
order.adjustments.first.amount.should eq -4.99
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'handles adjustment building exceptions' do
|
253
|
+
params = { :adjustments_attributes => [
|
254
|
+
{ "amount" => "XXX" },
|
255
|
+
{ "label" => "Promotion Discount", "amount" => "-3.00" }] }
|
256
|
+
|
257
|
+
expect {
|
258
|
+
order = Order.build_from_api(user, params)
|
259
|
+
}.to raise_error /XXX/
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'builds a payment' do
|
263
|
+
params = { :payments_attributes => [{ :amount => '4.99',
|
264
|
+
:payment_method => payment_method.name }] }
|
265
|
+
order = Order.build_from_api(user, params)
|
266
|
+
order.payments.first.amount.should eq 4.99
|
267
|
+
end
|
268
|
+
|
269
|
+
it 'handles payment building exceptions' do
|
270
|
+
params = { :payments_attributes => [{ :amount => '4.99',
|
271
|
+
:payment_method => 'XXX' }] }
|
272
|
+
expect {
|
273
|
+
order = Order.build_from_api(user, params)
|
274
|
+
}.to raise_error /XXX/
|
275
|
+
end
|
276
|
+
|
277
|
+
context "raises error" do
|
278
|
+
it "clears out order from db" do
|
279
|
+
params = { :payments_attributes => [{ payment_method: "XXX" }] }
|
280
|
+
count = Order.count
|
281
|
+
|
282
|
+
expect { order = Order.build_from_api(user, params) }.to raise_error
|
283
|
+
expect(Order.count).to eq count
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
context "import param and tax adjustments" do
|
288
|
+
let!(:tax_rate) { create(:tax_rate, amount: 0.05, calculator: Calculator::DefaultTax.create) }
|
289
|
+
let(:other_variant) { create(:variant) }
|
290
|
+
|
291
|
+
let(:line_item_attributes) do
|
292
|
+
line_items.merge({ "1" => { :variant_id => other_variant.id, :quantity => 5 }})
|
293
|
+
end
|
294
|
+
|
295
|
+
before { Zone.stub default_tax: tax_rate.zone }
|
296
|
+
|
297
|
+
it "doesnt create any tax ajustments when importing order" do
|
298
|
+
params = { import: true, line_items_attributes: line_item_attributes }
|
299
|
+
expect {
|
300
|
+
Order.build_from_api(user, params)
|
301
|
+
}.not_to change { Adjustment.count }
|
302
|
+
end
|
303
|
+
|
304
|
+
it "does create tax adjustments if not importing order" do
|
305
|
+
params = { import: false, line_items_attributes: line_item_attributes }
|
306
|
+
expect {
|
307
|
+
Order.build_from_api(user, params)
|
308
|
+
}.to change { Adjustment.count }
|
309
|
+
end
|
16
310
|
end
|
17
311
|
end
|
18
312
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spree_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.3.
|
4
|
+
version: 1.3.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Bigg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: spree_core
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.3.
|
19
|
+
version: 1.3.4
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.3.
|
26
|
+
version: 1.3.4
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: versioncake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -198,7 +198,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
198
198
|
version: '0'
|
199
199
|
requirements: []
|
200
200
|
rubyforge_project:
|
201
|
-
rubygems_version: 2.
|
201
|
+
rubygems_version: 2.1.0
|
202
202
|
signing_key:
|
203
203
|
specification_version: 4
|
204
204
|
summary: Spree's API
|