active_fulfillment 0.10.0 → 1.0.0
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.
- data/CHANGELOG +8 -0
- data/Rakefile +2 -0
- data/VERSION +1 -1
- data/active_fulfillment.gemspec +12 -2
- data/lib/active_fulfillment/fulfillment/services/amazon.rb +127 -28
- data/lib/active_fulfillment/fulfillment/services/webgistix.rb +65 -17
- data/test/fixtures/xml/amazon/inventory_get_response.xml +17 -0
- data/test/fixtures/xml/amazon/inventory_list_response.xml +29 -0
- data/test/fixtures/xml/amazon/inventory_list_response_with_next_1.xml +30 -0
- data/test/fixtures/xml/amazon/inventory_list_response_with_next_2.xml +29 -0
- data/test/remote/amazon_test.rb +12 -0
- data/test/remote/webgistix_test.rb +8 -0
- data/test/test_helper.rb +4 -0
- data/test/unit/services/amazon_test.rb +31 -0
- data/test/unit/services/webgistix_test.rb +17 -0
- metadata +41 -6
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
= ActiveFulfillment CHANGELOG
|
2
|
+
|
3
|
+
== Version 1.0.0 (July 12, 2010)
|
4
|
+
|
5
|
+
* Add inventory support to Amazon and Webgistix [Adrian Irving-Beer]
|
6
|
+
|
7
|
+
== Version 0.10.0 (July 6, 2010)
|
8
|
+
|
1
9
|
* Remove DHL from Webgistix shipping methods [Dennis Thiesen]
|
2
10
|
* Update Amazon FBA to use AWS credentials [John Tajima]
|
3
11
|
* Use new connection code from ActiveMerchant [cody]
|
data/Rakefile
CHANGED
@@ -47,6 +47,8 @@ begin
|
|
47
47
|
gemspec.email = "cody@shopify.com"
|
48
48
|
gemspec.homepage = "http://github.com/shopify/active_fulfillment"
|
49
49
|
gemspec.authors = ["Cody Fauser", "James MacAulay"]
|
50
|
+
gemspec.add_dependency('activesupport', '~> 2.3.2')
|
51
|
+
gemspec.add_dependency('builder', '>= 2.0.0')
|
50
52
|
end
|
51
53
|
rescue LoadError
|
52
54
|
puts "Jeweler not available. Install it with: gem install jeweler"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/active_fulfillment.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{active_fulfillment}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "1.0.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Cody Fauser", "James MacAulay"]
|
12
|
-
s.date = %q{2010-07-
|
12
|
+
s.date = %q{2010-07-12}
|
13
13
|
s.email = %q{cody@shopify.com}
|
14
14
|
s.files = [
|
15
15
|
"CHANGELOG",
|
@@ -36,6 +36,10 @@ Gem::Specification.new do |s|
|
|
36
36
|
"lib/active_merchant/common/validateable.rb",
|
37
37
|
"lib/certs/cacert.pem",
|
38
38
|
"test/fixtures.yml",
|
39
|
+
"test/fixtures/xml/amazon/inventory_get_response.xml",
|
40
|
+
"test/fixtures/xml/amazon/inventory_list_response.xml",
|
41
|
+
"test/fixtures/xml/amazon/inventory_list_response_with_next_1.xml",
|
42
|
+
"test/fixtures/xml/amazon/inventory_list_response_with_next_2.xml",
|
39
43
|
"test/remote/amazon_test.rb",
|
40
44
|
"test/remote/shipwire_test.rb",
|
41
45
|
"test/remote/webgistix_test.rb",
|
@@ -66,9 +70,15 @@ Gem::Specification.new do |s|
|
|
66
70
|
s.specification_version = 3
|
67
71
|
|
68
72
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
73
|
+
s.add_runtime_dependency(%q<activesupport>, ["~> 2.3.2"])
|
74
|
+
s.add_runtime_dependency(%q<builder>, [">= 2.0.0"])
|
69
75
|
else
|
76
|
+
s.add_dependency(%q<activesupport>, ["~> 2.3.2"])
|
77
|
+
s.add_dependency(%q<builder>, [">= 2.0.0"])
|
70
78
|
end
|
71
79
|
else
|
80
|
+
s.add_dependency(%q<activesupport>, ["~> 2.3.2"])
|
81
|
+
s.add_dependency(%q<builder>, [">= 2.0.0"])
|
72
82
|
end
|
73
83
|
end
|
74
84
|
|
@@ -4,9 +4,18 @@ require 'openssl'
|
|
4
4
|
module ActiveMerchant
|
5
5
|
module Fulfillment
|
6
6
|
class AmazonService < Service
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
SERVICES = {
|
8
|
+
:outbound => {
|
9
|
+
:url => 'https://fba-outbound.amazonaws.com',
|
10
|
+
:xmlns => 'http://fba-outbound.amazonaws.com/doc/2007-08-02/',
|
11
|
+
:version => '2007-08-02'
|
12
|
+
},
|
13
|
+
:inventory => {
|
14
|
+
:url => 'https://fba-inventory.amazonaws.com',
|
15
|
+
:xmlns => 'http://fba-inventory.amazonaws.com/doc/2009-07-31/',
|
16
|
+
:version => '2009-07-31'
|
17
|
+
}
|
18
|
+
}
|
10
19
|
|
11
20
|
SUCCESS, FAILURE, ERROR = 'Accepted', 'Failure', 'Error'
|
12
21
|
MESSAGES = {
|
@@ -42,9 +51,16 @@ module ActiveMerchant
|
|
42
51
|
@@digest = OpenSSL::Digest::Digest.new("sha1")
|
43
52
|
|
44
53
|
OPERATIONS = {
|
45
|
-
:
|
46
|
-
|
47
|
-
|
54
|
+
:outbound => {
|
55
|
+
:status => 'GetServiceStatus',
|
56
|
+
:create => 'CreateFulfillmentOrder',
|
57
|
+
:list => 'ListAllFulfillmentOrders'
|
58
|
+
},
|
59
|
+
:inventory => {
|
60
|
+
:get => 'GetInventorySupply',
|
61
|
+
:list => 'ListUpdatedInventorySupply',
|
62
|
+
:list_next => 'ListUpdatedInventorySupplyByNextToken'
|
63
|
+
}
|
48
64
|
}
|
49
65
|
|
50
66
|
# The first is the label, and the last is the code
|
@@ -69,16 +85,33 @@ module ActiveMerchant
|
|
69
85
|
end
|
70
86
|
|
71
87
|
def status
|
72
|
-
commit :status, build_status_request
|
88
|
+
commit :outbound, :status, build_status_request
|
73
89
|
end
|
74
90
|
|
75
91
|
def fulfill(order_id, shipping_address, line_items, options = {})
|
76
92
|
requires!(options, :order_date, :comment, :shipping_method)
|
77
|
-
commit :create, build_fulfillment_request(order_id, shipping_address, line_items, options)
|
93
|
+
commit :outbound, :create, build_fulfillment_request(order_id, shipping_address, line_items, options)
|
78
94
|
end
|
79
95
|
|
80
96
|
def fetch_current_orders
|
81
|
-
commit :list, build_get_current_fulfillment_orders_request
|
97
|
+
commit :outbound, :list, build_get_current_fulfillment_orders_request
|
98
|
+
end
|
99
|
+
|
100
|
+
def fetch_stock_levels(options = {})
|
101
|
+
if options[:sku]
|
102
|
+
commit :inventory, :get, build_inventory_get_request(options)
|
103
|
+
else
|
104
|
+
response = commit :inventory, :list, build_inventory_list_request(options)
|
105
|
+
|
106
|
+
while token = response.params['next_token'] do
|
107
|
+
next_page = commit :inventory, :list_next, build_next_inventory_list_request(token)
|
108
|
+
|
109
|
+
next_page.stock_levels.merge!(response.stock_levels)
|
110
|
+
response = next_page
|
111
|
+
end
|
112
|
+
|
113
|
+
response
|
114
|
+
end
|
82
115
|
end
|
83
116
|
|
84
117
|
def valid_credentials?
|
@@ -105,16 +138,16 @@ module ActiveMerchant
|
|
105
138
|
end
|
106
139
|
|
107
140
|
def build_status_request
|
108
|
-
request = OPERATIONS[:status]
|
141
|
+
request = OPERATIONS[:outbound][:status]
|
109
142
|
soap_request(request) do |xml|
|
110
|
-
xml.tag! request, { 'xmlns' =>
|
143
|
+
xml.tag! request, { 'xmlns' => SERVICES[:outbound][:xmlns] }
|
111
144
|
end
|
112
145
|
end
|
113
146
|
|
114
147
|
def build_get_current_fulfillment_orders_request
|
115
|
-
request = OPERATIONS[:list]
|
148
|
+
request = OPERATIONS[:outbound][:list]
|
116
149
|
soap_request(request) do |xml|
|
117
|
-
xml.tag! request, { 'xmlns' =>
|
150
|
+
xml.tag! request, { 'xmlns' => SERVICES[:outbound][:xmlns] } do
|
118
151
|
xml.tag! "NumberOfResultsRequested", 5
|
119
152
|
xml.tag! "QueryStartDateTime", Time.now.utc.yesterday.strftime("%Y-%m-%dT%H:%M:%SZ")
|
120
153
|
end
|
@@ -122,17 +155,49 @@ module ActiveMerchant
|
|
122
155
|
end
|
123
156
|
|
124
157
|
def build_fulfillment_request(order_id, shipping_address, line_items, options)
|
125
|
-
request = OPERATIONS[:create]
|
158
|
+
request = OPERATIONS[:outbound][:create]
|
126
159
|
soap_request(request) do |xml|
|
127
|
-
xml.tag! request, { 'xmlns' =>
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
160
|
+
xml.tag! request, { 'xmlns' => SERVICES[:outbound][:xmlns] } do
|
161
|
+
xml.tag! "MerchantFulfillmentOrderId", order_id
|
162
|
+
xml.tag! "DisplayableOrderId", order_id
|
163
|
+
xml.tag! "DisplayableOrderDateTime", options[:order_date].strftime("%Y-%m-%dT%H:%M:%SZ")
|
164
|
+
xml.tag! "DisplayableOrderComment", options[:comment]
|
165
|
+
xml.tag! "ShippingSpeedCategory", options[:shipping_method]
|
133
166
|
|
134
|
-
|
135
|
-
|
167
|
+
add_address(xml, shipping_address)
|
168
|
+
add_items(xml, line_items)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
def build_inventory_get_request(options)
|
174
|
+
request = OPERATIONS[:inventory][:get]
|
175
|
+
soap_request(request) do |xml|
|
176
|
+
xml.tag! request, { 'xmlns' => SERVICES[:inventory][:xmlns] } do
|
177
|
+
xml.tag! "MerchantSKU", options[:sku]
|
178
|
+
xml.tag! "ResponseGroup", "Basic"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def build_inventory_list_request(options)
|
184
|
+
start_time = options[:start_time] || 1.day.ago
|
185
|
+
|
186
|
+
request = OPERATIONS[:inventory][:list]
|
187
|
+
soap_request(request) do |xml|
|
188
|
+
xml.tag! request, { 'xmlns' => SERVICES[:inventory][:xmlns] } do
|
189
|
+
xml.tag! "NumberOfResultsRequested", 50
|
190
|
+
xml.tag! "QueryStartDateTime", start_time.utc.strftime("%Y-%m-%dT%H:%M:%SZ")
|
191
|
+
xml.tag! "ResponseGroup", "Basic"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def build_next_inventory_list_request(token)
|
197
|
+
request = OPERATIONS[:inventory][:list_next]
|
198
|
+
soap_request(request) do |xml|
|
199
|
+
xml.tag! request, { 'xmlns' => SERVICES[:inventory][:xmlns] } do
|
200
|
+
xml.tag! "NextToken", token
|
136
201
|
end
|
137
202
|
end
|
138
203
|
end
|
@@ -173,9 +238,9 @@ module ActiveMerchant
|
|
173
238
|
end
|
174
239
|
end
|
175
240
|
|
176
|
-
def commit(op, body)
|
177
|
-
data = ssl_post(
|
178
|
-
response =
|
241
|
+
def commit(service, op, body)
|
242
|
+
data = ssl_post(SERVICES[service][:url], body, 'Content-Type' => 'application/soap+xml; charset=utf-8')
|
243
|
+
response = parse_response(service, op, data)
|
179
244
|
Response.new(success?(response), message_from(response), response)
|
180
245
|
rescue ActiveMerchant::ResponseError => e
|
181
246
|
response = parse_error(e.response)
|
@@ -190,16 +255,50 @@ module ActiveMerchant
|
|
190
255
|
response[:response_comment]
|
191
256
|
end
|
192
257
|
|
193
|
-
def
|
258
|
+
def parse_response(service, op, xml)
|
259
|
+
begin
|
260
|
+
document = REXML::Document.new(xml)
|
261
|
+
rescue REXML::ParseException
|
262
|
+
return {:success => FAILURE}
|
263
|
+
end
|
264
|
+
|
265
|
+
case service
|
266
|
+
when :outbound
|
267
|
+
parse_fulfillment_response(op, document)
|
268
|
+
when :inventory
|
269
|
+
parse_inventory_response(document)
|
270
|
+
else
|
271
|
+
raise ArgumentError, "Unknown service #{service}"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
def parse_fulfillment_response(op, document)
|
194
276
|
response = {}
|
195
|
-
action = OPERATIONS[op]
|
196
|
-
document = REXML::Document.new(xml)
|
277
|
+
action = OPERATIONS[:outbound][op]
|
197
278
|
node = REXML::XPath.first(document, "//ns1:#{action}Response")
|
198
279
|
|
199
280
|
response[:response_status] = SUCCESS
|
200
281
|
response[:response_comment] = MESSAGES[op][SUCCESS]
|
201
282
|
response
|
202
283
|
end
|
284
|
+
|
285
|
+
def parse_inventory_response(document)
|
286
|
+
response = {}
|
287
|
+
response[:stock_levels] = {}
|
288
|
+
|
289
|
+
document.each_element('//ns1:MerchantSKUSupply') do |node|
|
290
|
+
# {MerchantSKU => 'SOME-ID', InStockSupplyQuantity => '101', ...}
|
291
|
+
params = node.elements.to_a.inject(Hash.new) {|h, k| h[k.name] = k.text; h}
|
292
|
+
|
293
|
+
response[:stock_levels][params['MerchantSKU']] = params['InStockSupplyQuantity'].to_i
|
294
|
+
end
|
295
|
+
|
296
|
+
next_token = REXML::XPath.first(document, '//ns1:NextToken')
|
297
|
+
response[:next_token] = (next_token ? next_token.text : nil)
|
298
|
+
|
299
|
+
response[:response_status] = SUCCESS
|
300
|
+
response
|
301
|
+
end
|
203
302
|
|
204
303
|
def parse_error(http_response)
|
205
304
|
response = {}
|
@@ -1,9 +1,14 @@
|
|
1
1
|
module ActiveMerchant
|
2
2
|
module Fulfillment
|
3
3
|
class WebgistixService < Service
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
SERVICE_URLS = {
|
5
|
+
:fulfillment => 'https://www.webgistix.com/XML/CreateOrder.asp',
|
6
|
+
:inventory => 'https://www.webgistix.com/XML/GetInventory.asp'
|
7
|
+
}
|
8
|
+
TEST_URLS = SERVICE_URLS.merge({
|
9
|
+
:fulfillment => 'https://www.webgistix.com/XML/CreateOrderTest.asp'
|
10
|
+
})
|
11
|
+
|
7
12
|
SUCCESS, FAILURE = 'True', 'False'
|
8
13
|
SUCCESS_MESSAGE = 'Successfully submitted the order'
|
9
14
|
FAILURE_MESSAGE = 'Failed to submit the order'
|
@@ -50,12 +55,15 @@ module ActiveMerchant
|
|
50
55
|
def initialize(options = {})
|
51
56
|
requires!(options, :login, :password)
|
52
57
|
super
|
53
|
-
@url = test? ? TEST_URL : LIVE_URL
|
54
58
|
end
|
55
59
|
|
56
60
|
def fulfill(order_id, shipping_address, line_items, options = {})
|
57
61
|
requires!(options, :shipping_method)
|
58
|
-
commit build_fulfillment_request(order_id, shipping_address, line_items, options)
|
62
|
+
commit :fulfillment, build_fulfillment_request(order_id, shipping_address, line_items, options)
|
63
|
+
end
|
64
|
+
|
65
|
+
def fetch_stock_levels(options = {})
|
66
|
+
commit :inventory, build_inventory_request(options)
|
59
67
|
end
|
60
68
|
|
61
69
|
def valid_credentials?
|
@@ -104,6 +112,19 @@ module ActiveMerchant
|
|
104
112
|
xml.target!
|
105
113
|
end
|
106
114
|
|
115
|
+
#<?xml version="1.0"?>
|
116
|
+
# <InventoryXML>
|
117
|
+
# <Password>Webgistix</Password>
|
118
|
+
# <CustomerID>3</CustomerID>
|
119
|
+
# </InventoryXML>
|
120
|
+
def build_inventory_request(options)
|
121
|
+
xml = Builder::XmlMarkup.new :indent => 2
|
122
|
+
xml.instruct!
|
123
|
+
xml.tag! 'InventoryXML' do
|
124
|
+
add_credentials(xml)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
107
128
|
def add_credentials(xml)
|
108
129
|
xml.tag! 'CustomerID', @options[:login]
|
109
130
|
xml.tag! 'Password', @options[:password]
|
@@ -150,15 +171,16 @@ module ActiveMerchant
|
|
150
171
|
end
|
151
172
|
end
|
152
173
|
|
153
|
-
def commit(request)
|
154
|
-
|
155
|
-
'EndPointURL' => @url,
|
156
|
-
'Content-Type' => 'text/xml; charset="utf-8"')
|
157
|
-
)
|
174
|
+
def commit(action, request)
|
175
|
+
url = test? ? TEST_URLS[action] : SERVICE_URLS[action]
|
158
176
|
|
159
|
-
|
160
|
-
|
177
|
+
data = ssl_post(url, request,
|
178
|
+
'EndPointURL' => url,
|
179
|
+
'Content-Type' => 'text/xml; charset="utf-8"'
|
161
180
|
)
|
181
|
+
|
182
|
+
response = parse_response(action, data)
|
183
|
+
Response.new(success?(response), message_from(response), response, :test => test?)
|
162
184
|
end
|
163
185
|
|
164
186
|
def success?(response)
|
@@ -175,15 +197,26 @@ module ActiveMerchant
|
|
175
197
|
end
|
176
198
|
end
|
177
199
|
|
178
|
-
def
|
179
|
-
response = {}
|
180
|
-
|
200
|
+
def parse_response(action, xml)
|
181
201
|
begin
|
182
202
|
document = REXML::Document.new("<response>#{xml}</response>")
|
183
203
|
rescue REXML::ParseException
|
184
|
-
|
185
|
-
return response
|
204
|
+
return {:success => FAILURE}
|
186
205
|
end
|
206
|
+
|
207
|
+
case action
|
208
|
+
when :fulfillment
|
209
|
+
parse_fulfillment_response(document)
|
210
|
+
when :inventory
|
211
|
+
parse_inventory_response(document)
|
212
|
+
else
|
213
|
+
raise ArgumentError, "Unknown action #{action}"
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def parse_fulfillment_response(document)
|
218
|
+
response = {}
|
219
|
+
|
187
220
|
# Fetch the errors
|
188
221
|
document.root.elements.to_a("Error").each_with_index do |e, i|
|
189
222
|
response["error_#{i}".to_sym] = e.text
|
@@ -200,6 +233,21 @@ module ActiveMerchant
|
|
200
233
|
|
201
234
|
response
|
202
235
|
end
|
236
|
+
|
237
|
+
def parse_inventory_response(document)
|
238
|
+
response = {}
|
239
|
+
response[:stock_levels] = {}
|
240
|
+
|
241
|
+
document.root.each_element('//Item') do |node|
|
242
|
+
# {ItemID => 'SOME-ID', ItemQty => '101'}
|
243
|
+
params = node.elements.to_a.inject(Hash.new) {|h, k| h[k.name] = k.text; h}
|
244
|
+
|
245
|
+
response[:stock_levels][params['ItemID']] = params['ItemQty'].to_i
|
246
|
+
end
|
247
|
+
|
248
|
+
response[:success] = SUCCESS
|
249
|
+
response
|
250
|
+
end
|
203
251
|
end
|
204
252
|
end
|
205
253
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
|
3
|
+
<env:Body>
|
4
|
+
<ns1:GetInventorySupplyResponse xmlns:ns1="http://fba-inventory.amazonaws.com/doc/2009-07-31/">
|
5
|
+
<ns1:GetInventorySupplyResult>
|
6
|
+
<ns1:MerchantSKUSupply>
|
7
|
+
<ns1:MerchantSKU>2R-JAXZ-P0IB</ns1:MerchantSKU>
|
8
|
+
<ns1:TotalSupplyQuantity>155</ns1:TotalSupplyQuantity>
|
9
|
+
<ns1:InStockSupplyQuantity>142</ns1:InStockSupplyQuantity>
|
10
|
+
</ns1:MerchantSKUSupply>
|
11
|
+
</ns1:GetInventorySupplyResult>
|
12
|
+
<ns1:ResponseMetadata>
|
13
|
+
<ns1:RequestId>463bcebc-ebd4-4122-abb2-dd05a79b35b6</ns1:RequestId>
|
14
|
+
</ns1:ResponseMetadata>
|
15
|
+
</ns1:GetInventorySupplyResponse>
|
16
|
+
</env:Body>
|
17
|
+
</env:Envelope>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
|
3
|
+
<env:Body>
|
4
|
+
<ns1:ListUpdatedInventorySupplyResponse xmlns:ns1="http://fba-inventory.amazonaws.com/doc/2009-07-31/">
|
5
|
+
<ns1:ListUpdatedInventorySupplyResult>
|
6
|
+
<ns1:HasNext>false</ns1:HasNext>
|
7
|
+
<ns1:MerchantSKUSupply>
|
8
|
+
<ns1:MerchantSKU>GN-00-01A</ns1:MerchantSKU>
|
9
|
+
<ns1:FulfillmentNetworkSKU>X00002YKJ1</ns1:FulfillmentNetworkSKU>
|
10
|
+
<ns1:ASIN>B000021Y68</ns1:ASIN>
|
11
|
+
<ns1:Condition>NewItem</ns1:Condition>
|
12
|
+
<ns1:TotalSupplyQuantity>202</ns1:TotalSupplyQuantity>
|
13
|
+
<ns1:InStockSupplyQuantity>202</ns1:InStockSupplyQuantity>
|
14
|
+
</ns1:MerchantSKUSupply>
|
15
|
+
<ns1:MerchantSKUSupply>
|
16
|
+
<ns1:MerchantSKU>GN-00-02A</ns1:MerchantSKU>
|
17
|
+
<ns1:FulfillmentNetworkSKU>X11112YKJ2</ns1:FulfillmentNetworkSKU>
|
18
|
+
<ns1:ASIN>B111121Y68</ns1:ASIN>
|
19
|
+
<ns1:Condition>NewItem</ns1:Condition>
|
20
|
+
<ns1:TotalSupplyQuantity>240</ns1:TotalSupplyQuantity>
|
21
|
+
<ns1:InStockSupplyQuantity>199</ns1:InStockSupplyQuantity>
|
22
|
+
</ns1:MerchantSKUSupply>
|
23
|
+
</ns1:ListUpdatedInventorySupplyResult>
|
24
|
+
<ns1:ResponseMetadata>
|
25
|
+
<ns1:RequestId>727a00b4-9909-4277-a341-b3ce069da95f</ns1:RequestId>
|
26
|
+
</ns1:ResponseMetadata>
|
27
|
+
</ns1:ListUpdatedInventorySupplyResponse>
|
28
|
+
</env:Body>
|
29
|
+
</env:Envelope>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
|
3
|
+
<env:Body>
|
4
|
+
<ns1:ListUpdatedInventorySupplyResponse xmlns:ns1="http://fba-inventory.amazonaws.com/doc/2009-07-31/">
|
5
|
+
<ns1:ListUpdatedInventorySupplyResult>
|
6
|
+
<ns1:NextToken>SOME-LONG-TOKEN</ns1:NextToken>
|
7
|
+
<ns1:HasNext>true</ns1:HasNext>
|
8
|
+
<ns1:MerchantSKUSupply>
|
9
|
+
<ns1:MerchantSKU>GN-01-01A</ns1:MerchantSKU>
|
10
|
+
<ns1:FulfillmentNetworkSKU>X00002YKJ1</ns1:FulfillmentNetworkSKU>
|
11
|
+
<ns1:ASIN>B000021Y68</ns1:ASIN>
|
12
|
+
<ns1:Condition>NewItem</ns1:Condition>
|
13
|
+
<ns1:TotalSupplyQuantity>42</ns1:TotalSupplyQuantity>
|
14
|
+
<ns1:InStockSupplyQuantity>42</ns1:InStockSupplyQuantity>
|
15
|
+
</ns1:MerchantSKUSupply>
|
16
|
+
<ns1:MerchantSKUSupply>
|
17
|
+
<ns1:MerchantSKU>GN-01-02A</ns1:MerchantSKU>
|
18
|
+
<ns1:FulfillmentNetworkSKU>X11112YKJ2</ns1:FulfillmentNetworkSKU>
|
19
|
+
<ns1:ASIN>B111121Y68</ns1:ASIN>
|
20
|
+
<ns1:Condition>NewItem</ns1:Condition>
|
21
|
+
<ns1:TotalSupplyQuantity>120</ns1:TotalSupplyQuantity>
|
22
|
+
<ns1:InStockSupplyQuantity>80</ns1:InStockSupplyQuantity>
|
23
|
+
</ns1:MerchantSKUSupply>
|
24
|
+
</ns1:ListUpdatedInventorySupplyResult>
|
25
|
+
<ns1:ResponseMetadata>
|
26
|
+
<ns1:RequestId>727a00b4-9909-4277-a341-b3ce069da95f</ns1:RequestId>
|
27
|
+
</ns1:ResponseMetadata>
|
28
|
+
</ns1:ListUpdatedInventorySupplyResponse>
|
29
|
+
</env:Body>
|
30
|
+
</env:Envelope>
|
@@ -0,0 +1,29 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
|
3
|
+
<env:Body>
|
4
|
+
<ns1:ListUpdatedInventorySupplyByNextTokenResponse xmlns:ns1="http://fba-inventory.amazonaws.com/doc/2009-07-31/">
|
5
|
+
<ns1:ListUpdatedInventorySupplyByNextTokenResult>
|
6
|
+
<ns1:HasNext>false</ns1:HasNext>
|
7
|
+
<ns1:MerchantSKUSupply>
|
8
|
+
<ns1:MerchantSKU>GN-02-01A</ns1:MerchantSKU>
|
9
|
+
<ns1:FulfillmentNetworkSKU>X22222YKJ1</ns1:FulfillmentNetworkSKU>
|
10
|
+
<ns1:ASIN>B222221Y68</ns1:ASIN>
|
11
|
+
<ns1:Condition>NewItem</ns1:Condition>
|
12
|
+
<ns1:TotalSupplyQuantity>124</ns1:TotalSupplyQuantity>
|
13
|
+
<ns1:InStockSupplyQuantity>123</ns1:InStockSupplyQuantity>
|
14
|
+
</ns1:MerchantSKUSupply>
|
15
|
+
<ns1:MerchantSKUSupply>
|
16
|
+
<ns1:MerchantSKU>GN-02-02A</ns1:MerchantSKU>
|
17
|
+
<ns1:FulfillmentNetworkSKU>X33332YKJ2</ns1:FulfillmentNetworkSKU>
|
18
|
+
<ns1:ASIN>B333321Y68</ns1:ASIN>
|
19
|
+
<ns1:Condition>NewItem</ns1:Condition>
|
20
|
+
<ns1:TotalSupplyQuantity>9999</ns1:TotalSupplyQuantity>
|
21
|
+
<ns1:InStockSupplyQuantity>555</ns1:InStockSupplyQuantity>
|
22
|
+
</ns1:MerchantSKUSupply>
|
23
|
+
</ns1:ListUpdatedInventorySupplyByNextTokenResult>
|
24
|
+
<ns1:ResponseMetadata>
|
25
|
+
<ns1:RequestId>727a00b4-9909-4277-a341-b3ce069da95f</ns1:RequestId>
|
26
|
+
</ns1:ResponseMetadata>
|
27
|
+
</ns1:ListUpdatedInventorySupplyByNextTokenResponse>
|
28
|
+
</env:Body>
|
29
|
+
</env:Envelope>
|
data/test/remote/amazon_test.rb
CHANGED
@@ -63,6 +63,18 @@ class RemoteAmazonTest < Test::Unit::TestCase
|
|
63
63
|
response = @service.fetch_current_orders
|
64
64
|
assert response.success?
|
65
65
|
end
|
66
|
+
|
67
|
+
def test_get_inventory
|
68
|
+
response = @service.fetch_stock_levels(:sku => '2R-JAXZ-P0IB')
|
69
|
+
assert response.success?
|
70
|
+
assert_equal 0, response.stock_levels['2R-JAXZ-P0IB']
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_list_inventory
|
74
|
+
response = @service.fetch_stock_levels(:start_time => 1.year.ago)
|
75
|
+
assert response.success?
|
76
|
+
assert_equal 0, response.stock_levels['SETTLERS']
|
77
|
+
end
|
66
78
|
|
67
79
|
def test_valid_credentials
|
68
80
|
assert @service.valid_credentials?
|
@@ -66,6 +66,14 @@ class RemoteWebgistixTest < Test::Unit::TestCase
|
|
66
66
|
assert_equal "Access Denied", response.message
|
67
67
|
end
|
68
68
|
|
69
|
+
def test_get_inventory
|
70
|
+
response = @service.fetch_stock_levels
|
71
|
+
assert response.success?
|
72
|
+
assert response.test?
|
73
|
+
assert_equal 95, response.stock_levels['GN-600-46']
|
74
|
+
assert_equal 97, response.stock_levels['GN-800-09']
|
75
|
+
end
|
76
|
+
|
69
77
|
def test_valid_credentials
|
70
78
|
assert @service.valid_credentials?
|
71
79
|
end
|
data/test/test_helper.rb
CHANGED
@@ -48,6 +48,10 @@ module Test
|
|
48
48
|
|
49
49
|
yaml_data
|
50
50
|
end
|
51
|
+
|
52
|
+
def xml_fixture(path) # where path is like 'usps/beverly_hills_to_ottawa_response'
|
53
|
+
open(File.join(File.dirname(__FILE__),'fixtures','xml',"#{path}.xml")) {|f| f.read}
|
54
|
+
end
|
51
55
|
|
52
56
|
def symbolize_keys(hash)
|
53
57
|
return unless hash.is_a?(Hash)
|
@@ -58,6 +58,37 @@ class AmazonTest < Test::Unit::TestCase
|
|
58
58
|
@options.delete(:shipping_method)
|
59
59
|
assert_raise(ArgumentError) { @service.fulfill('12345678', @address, @line_items, @options) }
|
60
60
|
end
|
61
|
+
|
62
|
+
def test_get_inventory
|
63
|
+
@service.expects(:ssl_post).returns(xml_fixture('amazon/inventory_get_response'))
|
64
|
+
|
65
|
+
response = @service.fetch_stock_levels(:sku => '2R-JAXZ-P0IB')
|
66
|
+
assert response.success?
|
67
|
+
assert_equal 142, response.stock_levels['2R-JAXZ-P0IB']
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_list_inventory
|
71
|
+
@service.expects(:ssl_post).returns(xml_fixture('amazon/inventory_list_response'))
|
72
|
+
|
73
|
+
response = @service.fetch_stock_levels
|
74
|
+
assert response.success?
|
75
|
+
assert_equal 202, response.stock_levels['GN-00-01A']
|
76
|
+
assert_equal 199, response.stock_levels['GN-00-02A']
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_list_inventory_multipage
|
80
|
+
@service.expects(:ssl_post).twice.returns(
|
81
|
+
xml_fixture('amazon/inventory_list_response_with_next_1'),
|
82
|
+
xml_fixture('amazon/inventory_list_response_with_next_2')
|
83
|
+
)
|
84
|
+
|
85
|
+
response = @service.fetch_stock_levels
|
86
|
+
assert response.success?
|
87
|
+
assert_equal 42, response.stock_levels['GN-01-01A']
|
88
|
+
assert_equal 80, response.stock_levels['GN-01-02A']
|
89
|
+
assert_equal 123, response.stock_levels['GN-02-01A']
|
90
|
+
assert_equal 555, response.stock_levels['GN-02-02A']
|
91
|
+
end
|
61
92
|
|
62
93
|
def test_404_error
|
63
94
|
http_response = build_mock_response(response_from_404, "Not Found", "404")
|
@@ -89,6 +89,16 @@ class WebgistixTest < Test::Unit::TestCase
|
|
89
89
|
assert_equal 'Unknown ItemID: testitem', response.params['error_1']
|
90
90
|
assert_equal 'Unknown ItemID: WX-01-1000', response.params['error_2']
|
91
91
|
end
|
92
|
+
|
93
|
+
def test_stock_levels
|
94
|
+
@service.expects(:ssl_post).returns(inventory_response)
|
95
|
+
|
96
|
+
response = @service.fetch_stock_levels
|
97
|
+
assert response.success?
|
98
|
+
assert_equal WebgistixService::SUCCESS_MESSAGE, response.message
|
99
|
+
assert_equal 202, response.stock_levels['GN-00-01A']
|
100
|
+
assert_equal 199, response.stock_levels['GN-00-02A']
|
101
|
+
end
|
92
102
|
|
93
103
|
def test_failed_login
|
94
104
|
@service.expects(:ssl_post).returns(invalid_login_response)
|
@@ -142,4 +152,11 @@ class WebgistixTest < Test::Unit::TestCase
|
|
142
152
|
def garbage_response
|
143
153
|
'<font face="Arial" size=2>/XML/shippingTest.asp</font><font face="Arial" size=2>, line 39</font>'
|
144
154
|
end
|
155
|
+
|
156
|
+
def inventory_response
|
157
|
+
'<InventoryXML>' +
|
158
|
+
'<Item><ItemID>GN-00-01A</ItemID><ItemQty>202</ItemQty></Item>' +
|
159
|
+
'<Item><ItemID>GN-00-02A</ItemID><ItemQty>199</ItemQty></Item>' +
|
160
|
+
'</InventoryXML>'
|
161
|
+
end
|
145
162
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_fulfillment
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
- 10
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 1.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Cody Fauser
|
@@ -16,10 +16,41 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date: 2010-07-
|
19
|
+
date: 2010-07-12 00:00:00 -04:00
|
20
20
|
default_executable:
|
21
|
-
dependencies:
|
22
|
-
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: activesupport
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ~>
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 7
|
31
|
+
segments:
|
32
|
+
- 2
|
33
|
+
- 3
|
34
|
+
- 2
|
35
|
+
version: 2.3.2
|
36
|
+
type: :runtime
|
37
|
+
version_requirements: *id001
|
38
|
+
- !ruby/object:Gem::Dependency
|
39
|
+
name: builder
|
40
|
+
prerelease: false
|
41
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 15
|
47
|
+
segments:
|
48
|
+
- 2
|
49
|
+
- 0
|
50
|
+
- 0
|
51
|
+
version: 2.0.0
|
52
|
+
type: :runtime
|
53
|
+
version_requirements: *id002
|
23
54
|
description:
|
24
55
|
email: cody@shopify.com
|
25
56
|
executables: []
|
@@ -53,6 +84,10 @@ files:
|
|
53
84
|
- lib/active_merchant/common/validateable.rb
|
54
85
|
- lib/certs/cacert.pem
|
55
86
|
- test/fixtures.yml
|
87
|
+
- test/fixtures/xml/amazon/inventory_get_response.xml
|
88
|
+
- test/fixtures/xml/amazon/inventory_list_response.xml
|
89
|
+
- test/fixtures/xml/amazon/inventory_list_response_with_next_1.xml
|
90
|
+
- test/fixtures/xml/amazon/inventory_list_response_with_next_2.xml
|
56
91
|
- test/remote/amazon_test.rb
|
57
92
|
- test/remote/shipwire_test.rb
|
58
93
|
- test/remote/webgistix_test.rb
|