active_fulfillment 0.10.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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.10.0
1
+ 1.0.0
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{active_fulfillment}
8
- s.version = "0.10.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-06}
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
- OUTBOUND_URL = "https://fba-outbound.amazonaws.com"
8
- OUTBOUND_XMLNS = 'http://fba-outbound.amazonaws.com/doc/2007-08-02/'
9
- VERSION = "2007-08-02"
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
- :status => 'GetServiceStatus',
46
- :create => 'CreateFulfillmentOrder',
47
- :list => 'ListAllFulfillmentOrders'
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' => OUTBOUND_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' => OUTBOUND_XMLNS } do
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' => OUTBOUND_XMLNS } do
128
- xml.tag! "MerchantFulfillmentOrderId", order_id
129
- xml.tag! "DisplayableOrderId", order_id
130
- xml.tag! "DisplayableOrderDateTime", options[:order_date].strftime("%Y-%m-%dT%H:%M:%SZ")
131
- xml.tag! "DisplayableOrderComment", options[:comment]
132
- xml.tag! "ShippingSpeedCategory", options[:shipping_method]
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
- add_address(xml, shipping_address)
135
- add_items(xml, line_items)
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(OUTBOUND_URL, body, 'Content-Type' => 'application/soap+xml; charset=utf-8')
178
- response = parse(op, data)
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 parse(op, xml)
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
- TEST_URL = 'https://www.webgistix.com/XML/shippingTest.asp'
5
- LIVE_URL = 'https://www.webgistix.com/XML/API.asp'
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
- @response = parse(ssl_post(@url, request,
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
- Response.new(success?(@response), message_from(@response), @response,
160
- :test => test?
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 parse(xml)
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
- response[:success] = FAILURE
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>
@@ -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: 55
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
+ - 1
7
8
  - 0
8
- - 10
9
9
  - 0
10
- version: 0.10.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-06 00:00:00 -04:00
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