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 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