active_fulfillment 2.1.9 → 3.0.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +68 -0
- data/lib/active_fulfillment.rb +5 -5
- data/lib/active_fulfillment/base.rb +10 -0
- data/lib/active_fulfillment/response.rb +26 -0
- data/lib/active_fulfillment/service.rb +56 -0
- data/lib/active_fulfillment/services.rb +5 -0
- data/lib/active_fulfillment/services/amazon_mws.rb +473 -0
- data/lib/active_fulfillment/services/james_and_james.rb +122 -0
- data/lib/active_fulfillment/services/shipwire.rb +266 -0
- data/lib/active_fulfillment/services/shopify_api.rb +125 -0
- data/lib/active_fulfillment/services/webgistix.rb +334 -0
- data/lib/active_fulfillment/version.rb +4 -0
- data/test/remote/amazon_mws_test.rb +20 -17
- data/test/remote/james_and_james_test.rb +77 -0
- data/test/remote/shipwire_test.rb +25 -25
- data/test/remote/webgistix_test.rb +21 -21
- data/test/test_helper.rb +27 -52
- data/test/unit/base_test.rb +4 -4
- data/test/unit/services/amazon_mws_test.rb +56 -26
- data/test/unit/services/james_and_james_test.rb +90 -0
- data/test/unit/services/shipwire_test.rb +18 -18
- data/test/unit/services/shopify_api_test.rb +7 -20
- data/test/unit/services/webgistix_test.rb +35 -35
- metadata +32 -114
- data/CHANGELOG +0 -62
- data/lib/active_fulfillment/fulfillment/base.rb +0 -12
- data/lib/active_fulfillment/fulfillment/response.rb +0 -28
- data/lib/active_fulfillment/fulfillment/service.rb +0 -58
- data/lib/active_fulfillment/fulfillment/services.rb +0 -5
- data/lib/active_fulfillment/fulfillment/services/amazon.rb +0 -389
- data/lib/active_fulfillment/fulfillment/services/amazon_mws.rb +0 -454
- data/lib/active_fulfillment/fulfillment/services/shipwire.rb +0 -268
- data/lib/active_fulfillment/fulfillment/services/shopify_api.rb +0 -125
- data/lib/active_fulfillment/fulfillment/services/webgistix.rb +0 -338
- data/lib/active_fulfillment/fulfillment/version.rb +0 -6
- data/test/fixtures.yml +0 -16
- data/test/fixtures/xml/amazon/inventory_get_response.xml +0 -17
- data/test/fixtures/xml/amazon/inventory_list_response.xml +0 -29
- data/test/fixtures/xml/amazon/inventory_list_response_with_next_1.xml +0 -30
- data/test/fixtures/xml/amazon/inventory_list_response_with_next_2.xml +0 -29
- data/test/fixtures/xml/amazon/tracking_response_1.xml +0 -56
- data/test/fixtures/xml/amazon/tracking_response_2.xml +0 -38
- data/test/fixtures/xml/amazon/tracking_response_error.xml +0 -13
- data/test/fixtures/xml/amazon/tracking_response_not_found.xml +0 -13
- data/test/fixtures/xml/amazon_mws/fulfillment_get_fulfillment_order.xml +0 -114
- data/test/fixtures/xml/amazon_mws/fulfillment_get_fulfillment_order_2.xml +0 -90
- data/test/fixtures/xml/amazon_mws/fulfillment_get_fullfillment_order_with_multiple_tracking_numbers.xml +0 -121
- data/test/fixtures/xml/amazon_mws/fulfillment_list_all_fulfillment_orders.xml +0 -70
- data/test/fixtures/xml/amazon_mws/inventory_list_inventory_item_supply.xml +0 -32
- data/test/fixtures/xml/amazon_mws/inventory_list_inventory_supply.xml +0 -75
- data/test/fixtures/xml/amazon_mws/inventory_list_inventory_supply_by_next_token.xml +0 -38
- data/test/fixtures/xml/amazon_mws/tracking_response_error.xml +0 -9
- data/test/fixtures/xml/amazon_mws/tracking_response_not_found.xml +0 -9
- data/test/fixtures/xml/shipwire/fulfillment_failure_response.xml +0 -7
- data/test/fixtures/xml/shipwire/invalid_login_response.xml +0 -7
- data/test/fixtures/xml/shipwire/inventory_get_response.xml +0 -44
- data/test/fixtures/xml/shipwire/successful_empty_tracking_response.xml +0 -8
- data/test/fixtures/xml/shipwire/successful_live_tracking_response.xml +0 -53
- data/test/fixtures/xml/shipwire/successful_tracking_response.xml +0 -16
- data/test/fixtures/xml/shipwire/successful_tracking_response_with_tracking_urls.xml +0 -31
- data/test/fixtures/xml/webgistix/multiple_tracking_response.xml +0 -21
- data/test/fixtures/xml/webgistix/tracking_response.xml +0 -14
- data/test/remote/amazon_test.rb +0 -124
- data/test/unit/services/amazon_test.rb +0 -271
@@ -1,454 +0,0 @@
|
|
1
|
-
require 'base64'
|
2
|
-
require 'time'
|
3
|
-
require 'cgi'
|
4
|
-
|
5
|
-
module ActiveMerchant
|
6
|
-
module Fulfillment
|
7
|
-
class AmazonMarketplaceWebService < Service
|
8
|
-
|
9
|
-
APPLICATION_IDENTIFIER = "active_merchant_mws/0.01 (Language=ruby)"
|
10
|
-
|
11
|
-
REGISTRATION_URI = URI.parse("https://sellercentral.amazon.com/gp/mws/registration/register.html")
|
12
|
-
|
13
|
-
SIGNATURE_VERSION = 2
|
14
|
-
SIGNATURE_METHOD = "SHA256"
|
15
|
-
VERSION = "2010-10-01"
|
16
|
-
|
17
|
-
SUCCESS, FAILURE, ERROR = 'Accepted', 'Failure', 'Error'
|
18
|
-
|
19
|
-
MESSAGES = {
|
20
|
-
:status => {
|
21
|
-
'Accepted' => 'Success',
|
22
|
-
'Failure' => 'Failed',
|
23
|
-
'Error' => 'An error occurred'
|
24
|
-
},
|
25
|
-
:create => {
|
26
|
-
'Accepted' => 'Successfully submitted the order',
|
27
|
-
'Failure' => 'Failed to submit the order',
|
28
|
-
'Error' => 'An error occurred while submitting the order'
|
29
|
-
},
|
30
|
-
:list => {
|
31
|
-
'Accepted' => 'Successfully submitted request',
|
32
|
-
'Failure' => 'Failed to submit request',
|
33
|
-
'Error' => 'An error occurred while submitting request'
|
34
|
-
|
35
|
-
}
|
36
|
-
}
|
37
|
-
|
38
|
-
ENDPOINTS = {
|
39
|
-
:ca => 'mws.amazonservices.ca',
|
40
|
-
:cn => 'mws.amazonservices.com.cn',
|
41
|
-
:de => 'mws-eu.amazonservices.ca',
|
42
|
-
:es => 'mws-eu.amazonservices.ca',
|
43
|
-
:fr => 'mws-eu.amazonservices.ca',
|
44
|
-
:it => 'mws-eu.amazonservices.ca',
|
45
|
-
:jp => 'mws.amazonservices.jp',
|
46
|
-
:uk => 'mws-eu.amazonservices.ca',
|
47
|
-
:us => 'mws.amazonservices.com'
|
48
|
-
}
|
49
|
-
|
50
|
-
LOOKUPS = {
|
51
|
-
:destination_address => {
|
52
|
-
:name => "DestinationAddress.Name",
|
53
|
-
:address1 => "DestinationAddress.Line1",
|
54
|
-
:address2 => "DestinationAddress.Line2",
|
55
|
-
:city => "DestinationAddress.City",
|
56
|
-
:state => "DestinationAddress.StateOrProvinceCode",
|
57
|
-
:country => "DestinationAddress.CountryCode",
|
58
|
-
:zip => "DestinationAddress.PostalCode",
|
59
|
-
:phone => "DestinationAddress.PhoneNumber"
|
60
|
-
},
|
61
|
-
:line_items => {
|
62
|
-
:comment => "Items.member.%d.DisplayableComment",
|
63
|
-
:gift_message => "Items.member.%d.GiftMessage",
|
64
|
-
:currency_code => "Items.member.%d.PerUnitDeclaredValue.CurrencyCode",
|
65
|
-
:value => "Items.member.%d.PerUnitDeclaredValue.Value",
|
66
|
-
:quantity => "Items.member.%d.Quantity",
|
67
|
-
:order_id => "Items.member.%d.SellerFulfillmentOrderItemId",
|
68
|
-
:sku => "Items.member.%d.SellerSKU",
|
69
|
-
:network_sku => "Items.member.%d.FulfillmentNetworkSKU",
|
70
|
-
:item_disposition => "Items.member.%d.OrderItemDisposition",
|
71
|
-
},
|
72
|
-
:list_inventory => {
|
73
|
-
:sku => "SellerSkus.member.%d"
|
74
|
-
}
|
75
|
-
}
|
76
|
-
|
77
|
-
ACTIONS = {
|
78
|
-
:outbound => "FulfillmentOutboundShipment",
|
79
|
-
:inventory => "FulfillmentInventory"
|
80
|
-
}
|
81
|
-
|
82
|
-
OPERATIONS = {
|
83
|
-
:outbound => {
|
84
|
-
:status => 'GetServiceStatus',
|
85
|
-
:create => 'CreateFulfillmentOrder',
|
86
|
-
:list => 'ListAllFulfillmentOrders',
|
87
|
-
:tracking => 'GetFulfillmentOrder'
|
88
|
-
},
|
89
|
-
:inventory => {
|
90
|
-
:get => 'ListInventorySupply',
|
91
|
-
:list => 'ListInventorySupply',
|
92
|
-
:list_next => 'ListInventorySupplyByNextToken'
|
93
|
-
}
|
94
|
-
}
|
95
|
-
|
96
|
-
# The first is the label, and the last is the code
|
97
|
-
# Standard: 3-5 business days
|
98
|
-
# Expedited: 2 business days
|
99
|
-
# Priority: 1 business day
|
100
|
-
def self.shipping_methods
|
101
|
-
[
|
102
|
-
[ 'Standard Shipping', 'Standard' ],
|
103
|
-
[ 'Expedited Shipping', 'Expedited' ],
|
104
|
-
[ 'Priority Shipping', 'Priority' ]
|
105
|
-
].inject(ActiveSupport::OrderedHash.new){|h, (k,v)| h[k] = v; h}
|
106
|
-
end
|
107
|
-
|
108
|
-
def initialize(options = {})
|
109
|
-
requires!(options, :login, :password)
|
110
|
-
@seller_id = options[:seller_id]
|
111
|
-
super
|
112
|
-
end
|
113
|
-
|
114
|
-
def seller_id=(seller_id)
|
115
|
-
@seller_id = seller_id
|
116
|
-
end
|
117
|
-
|
118
|
-
def endpoint
|
119
|
-
ENDPOINTS[@options[:endpoint] || :us]
|
120
|
-
end
|
121
|
-
|
122
|
-
def fulfill(order_id, shipping_address, line_items, options = {})
|
123
|
-
requires!(options, :order_date, :shipping_method)
|
124
|
-
commit :post, :outbound, :create, build_fulfillment_request(order_id, shipping_address, line_items, options)
|
125
|
-
end
|
126
|
-
|
127
|
-
def status
|
128
|
-
commit :post, :outbound, :status, build_status_request
|
129
|
-
end
|
130
|
-
|
131
|
-
def fetch_current_orders
|
132
|
-
commit :post, :outbound, :status, build_get_current_fulfillment_orders_request
|
133
|
-
end
|
134
|
-
|
135
|
-
def fetch_stock_levels(options = {})
|
136
|
-
options[:skus] = [options.delete(:sku)] if options.include?(:sku)
|
137
|
-
response = commit :post, :inventory, :list, build_inventory_list_request(options)
|
138
|
-
|
139
|
-
while token = response.params['next_token'] do
|
140
|
-
next_page = commit :post, :inventory, :list_next, build_next_inventory_list_request(token)
|
141
|
-
|
142
|
-
# if we fail during the stock-level-via-token gathering, fail the whole request
|
143
|
-
return next_page if next_page.params['response_status'] != SUCCESS
|
144
|
-
next_page.stock_levels.merge!(response.stock_levels)
|
145
|
-
response = next_page
|
146
|
-
end
|
147
|
-
|
148
|
-
response
|
149
|
-
end
|
150
|
-
|
151
|
-
def fetch_tracking_data(order_ids, options = {})
|
152
|
-
order_ids.reduce(nil) do |previous, order_id|
|
153
|
-
response = commit :post, :outbound, :tracking, build_tracking_request(order_id, options)
|
154
|
-
return response if !response.success?
|
155
|
-
|
156
|
-
if previous
|
157
|
-
response.tracking_numbers.merge!(previous.tracking_numbers)
|
158
|
-
response.tracking_companies.merge!(previous.tracking_companies)
|
159
|
-
response.tracking_urls.merge!(previous.tracking_urls)
|
160
|
-
end
|
161
|
-
|
162
|
-
response
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
def valid_credentials?
|
167
|
-
fetch_stock_levels.success?
|
168
|
-
end
|
169
|
-
|
170
|
-
def test_mode?
|
171
|
-
false
|
172
|
-
end
|
173
|
-
|
174
|
-
def build_full_query(verb, uri, params)
|
175
|
-
signature = sign(verb, uri, params)
|
176
|
-
build_query(params) + "&Signature=#{signature}"
|
177
|
-
end
|
178
|
-
|
179
|
-
def commit(verb, service, op, params)
|
180
|
-
uri = URI.parse("https://#{endpoint}/#{ACTIONS[service]}/#{VERSION}")
|
181
|
-
query = build_full_query(verb, uri, params)
|
182
|
-
headers = build_headers(query)
|
183
|
-
|
184
|
-
data = ssl_post(uri.to_s, query, headers)
|
185
|
-
response = parse_response(service, op, data)
|
186
|
-
Response.new(success?(response), message_from(response), response)
|
187
|
-
rescue ActiveMerchant::ResponseError => e
|
188
|
-
handle_error(e)
|
189
|
-
end
|
190
|
-
|
191
|
-
def handle_error(e)
|
192
|
-
response = parse_error(e.response)
|
193
|
-
if response.fetch(:faultstring, "").match(/^Requested order \'.+\' not found$/)
|
194
|
-
Response.new(true, nil, {:status => SUCCESS, :tracking_numbers => {}, :tracking_companies => {}, :tracking_urls => {}})
|
195
|
-
else
|
196
|
-
Response.new(false, message_from(response), response)
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
def success?(response)
|
201
|
-
response[:response_status] == SUCCESS
|
202
|
-
end
|
203
|
-
|
204
|
-
def message_from(response)
|
205
|
-
response[:response_message]
|
206
|
-
end
|
207
|
-
|
208
|
-
## PARSING
|
209
|
-
|
210
|
-
def parse_response(service, op, xml)
|
211
|
-
begin
|
212
|
-
document = REXML::Document.new(xml)
|
213
|
-
rescue REXML::ParseException
|
214
|
-
return { :success => FAILURE }
|
215
|
-
end
|
216
|
-
|
217
|
-
case service
|
218
|
-
when :outbound
|
219
|
-
case op
|
220
|
-
when :tracking
|
221
|
-
parse_tracking_response(document)
|
222
|
-
else
|
223
|
-
parse_fulfillment_response(op, document)
|
224
|
-
end
|
225
|
-
when :inventory
|
226
|
-
parse_inventory_response(document)
|
227
|
-
else
|
228
|
-
raise ArgumentError, "Unknown service #{service}"
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
def parse_tracking_response(document)
|
233
|
-
response = {}
|
234
|
-
response[:tracking_numbers] = {}
|
235
|
-
response[:tracking_companies] = {}
|
236
|
-
response[:tracking_urls] = {}
|
237
|
-
|
238
|
-
tracking_numbers = REXML::XPath.match(document, "//FulfillmentShipmentPackage/member/TrackingNumber")
|
239
|
-
if tracking_numbers.present?
|
240
|
-
order_id = REXML::XPath.first(document, "//FulfillmentOrder/SellerFulfillmentOrderId").text.strip
|
241
|
-
response[:tracking_numbers][order_id] = tracking_numbers.map{ |t| t.text.strip }
|
242
|
-
end
|
243
|
-
|
244
|
-
tracking_companies = REXML::XPath.match(document, "//FulfillmentShipmentPackage/member/CarrierCode")
|
245
|
-
if tracking_companies.present?
|
246
|
-
order_id = REXML::XPath.first(document, "//FulfillmentOrder/SellerFulfillmentOrderId").text.strip
|
247
|
-
response[:tracking_companies][order_id] = tracking_companies.map{ |t| t.text.strip }
|
248
|
-
end
|
249
|
-
|
250
|
-
response[:response_status] = SUCCESS
|
251
|
-
response
|
252
|
-
end
|
253
|
-
|
254
|
-
def parse_fulfillment_response(op, document)
|
255
|
-
{ :response_status => SUCCESS, :response_comment => MESSAGES[op][SUCCESS] }
|
256
|
-
end
|
257
|
-
|
258
|
-
def parse_inventory_response(document)
|
259
|
-
response = {}
|
260
|
-
response[:stock_levels] = {}
|
261
|
-
|
262
|
-
document.each_element('//InventorySupplyList/member') do |node|
|
263
|
-
params = node.elements.to_a.each_with_object({}) { |elem, hash| hash[elem.name] = elem.text }
|
264
|
-
|
265
|
-
response[:stock_levels][params['SellerSKU']] = params['InStockSupplyQuantity'].to_i
|
266
|
-
end
|
267
|
-
|
268
|
-
next_token = REXML::XPath.first(document, '//NextToken')
|
269
|
-
response[:next_token] = next_token ? next_token.text : nil
|
270
|
-
|
271
|
-
response[:response_status] = SUCCESS
|
272
|
-
response
|
273
|
-
end
|
274
|
-
|
275
|
-
def parse_error(http_response)
|
276
|
-
response = {}
|
277
|
-
response[:http_code] = http_response.code
|
278
|
-
response[:http_message] = http_response.message
|
279
|
-
|
280
|
-
document = REXML::Document.new(http_response.body)
|
281
|
-
|
282
|
-
node = REXML::XPath.first(document, '//Error')
|
283
|
-
error_code = REXML::XPath.first(node, '//Code')
|
284
|
-
error_message = REXML::XPath.first(node, '//Message')
|
285
|
-
|
286
|
-
response[:status] = FAILURE
|
287
|
-
response[:faultcode] = error_code ? error_code.text : ""
|
288
|
-
response[:faultstring] = error_message ? error_message.text : ""
|
289
|
-
response[:response_message] = error_message ? error_message.text : ""
|
290
|
-
response[:response_comment] = "#{response[:faultcode]}: #{response[:faultstring]}"
|
291
|
-
response
|
292
|
-
rescue REXML::ParseException => e
|
293
|
-
rescue NoMethodError => e
|
294
|
-
response[:http_body] = http_response.body
|
295
|
-
response[:response_status] = FAILURE
|
296
|
-
response[:response_comment] = "#{response[:http_code]}: #{response[:http_message]}"
|
297
|
-
response
|
298
|
-
end
|
299
|
-
|
300
|
-
def sign(http_verb, uri, options)
|
301
|
-
string_to_sign = "#{http_verb.to_s.upcase}\n"
|
302
|
-
string_to_sign += "#{uri.host}\n"
|
303
|
-
string_to_sign += uri.path.length <= 0 ? "/\n" : "#{uri.path}\n"
|
304
|
-
string_to_sign += build_query(options)
|
305
|
-
|
306
|
-
# remove trailing newline created by encode64
|
307
|
-
escape(Base64.encode64(OpenSSL::HMAC.digest(SIGNATURE_METHOD, @options[:password], string_to_sign)).chomp)
|
308
|
-
end
|
309
|
-
|
310
|
-
def amazon_request?(signed_string, expected_signature)
|
311
|
-
calculated_signature = escape(Base64.encode64(OpenSSL::HMAC.digest(SIGNATURE_METHOD, @options[:password], signed_string)).chomp)
|
312
|
-
calculated_signature == expected_signature
|
313
|
-
end
|
314
|
-
|
315
|
-
def registration_url(options)
|
316
|
-
opts = {
|
317
|
-
"returnPathAndParameters" => options["returnPathAndParameters"],
|
318
|
-
"id" => @options[:app_id],
|
319
|
-
"AWSAccessKeyId" => @options[:login],
|
320
|
-
"SignatureMethod" => "Hmac#{SIGNATURE_METHOD}",
|
321
|
-
"SignatureVersion" => SIGNATURE_VERSION
|
322
|
-
}
|
323
|
-
signature = sign(:get, REGISTRATION_URI, opts)
|
324
|
-
"#{REGISTRATION_URI.to_s}?#{build_query(opts)}&Signature=#{signature}"
|
325
|
-
end
|
326
|
-
|
327
|
-
def md5_content(content)
|
328
|
-
Base64.encode64(OpenSSL::Digest.new('md5', content).digest).chomp
|
329
|
-
end
|
330
|
-
|
331
|
-
def build_query(query_params)
|
332
|
-
query_params.sort.map{ |key, value| [escape(key.to_s), escape(value.to_s)].join('=') }.join('&')
|
333
|
-
end
|
334
|
-
|
335
|
-
def build_headers(querystr)
|
336
|
-
{
|
337
|
-
'User-Agent' => APPLICATION_IDENTIFIER,
|
338
|
-
'Content-MD5' => md5_content(querystr),
|
339
|
-
'Content-Type' => 'application/x-www-form-urlencoded'
|
340
|
-
}
|
341
|
-
end
|
342
|
-
|
343
|
-
def build_basic_api_query(options)
|
344
|
-
opts = Hash[options.map{ |k,v| [k.to_s, v.to_s] }]
|
345
|
-
opts["AWSAccessKeyId"] = @options[:login] unless opts["AWSAccessKey"]
|
346
|
-
opts["Timestamp"] = Time.now.utc.iso8601 unless opts["Timestamp"]
|
347
|
-
opts["Version"] = VERSION unless opts["Version"]
|
348
|
-
opts["SignatureMethod"] = "Hmac#{SIGNATURE_METHOD}" unless opts["SignatureMethod"]
|
349
|
-
opts["SignatureVersion"] = SIGNATURE_VERSION unless opts["SignatureVersion"]
|
350
|
-
opts["SellerId"] = @seller_id unless opts["SellerId"] || !@seller_id
|
351
|
-
opts
|
352
|
-
end
|
353
|
-
|
354
|
-
def build_fulfillment_request(order_id, shipping_address, line_items, options)
|
355
|
-
params = {
|
356
|
-
:Action => OPERATIONS[:outbound][:create],
|
357
|
-
:SellerFulfillmentOrderId => order_id.to_s,
|
358
|
-
:DisplayableOrderId => order_id.to_s,
|
359
|
-
:DisplayableOrderDateTime => options[:order_date].utc.iso8601,
|
360
|
-
:ShippingSpeedCategory => options[:shipping_method]
|
361
|
-
}
|
362
|
-
params[:DisplayableOrderComment] = options[:comment] if options[:comment]
|
363
|
-
|
364
|
-
request = build_basic_api_query(params.merge(options))
|
365
|
-
request = request.merge build_address(shipping_address)
|
366
|
-
request = request.merge build_items(line_items)
|
367
|
-
|
368
|
-
request
|
369
|
-
end
|
370
|
-
|
371
|
-
def build_get_current_fulfillment_orders_request(options = {})
|
372
|
-
start_time = options.delete(:start_time) || 1.day.ago.utc
|
373
|
-
params = {
|
374
|
-
:Action => OPERATIONS[:outbound][:list],
|
375
|
-
:QueryStartDateTime => start_time.strftime("%Y-%m-%dT%H:%M:%SZ")
|
376
|
-
}
|
377
|
-
|
378
|
-
build_basic_api_query(params.merge(options))
|
379
|
-
end
|
380
|
-
|
381
|
-
def build_inventory_list_request(options = {})
|
382
|
-
response_group = options.delete(:response_group) || "Basic"
|
383
|
-
params = {
|
384
|
-
:Action => OPERATIONS[:inventory][:list],
|
385
|
-
:ResponseGroup => response_group
|
386
|
-
}
|
387
|
-
if skus = options.delete(:skus)
|
388
|
-
skus.each_with_index do |sku, index|
|
389
|
-
params[LOOKUPS[:list_inventory][:sku] % (index + 1)] = sku
|
390
|
-
end
|
391
|
-
else
|
392
|
-
start_time = options.delete(:start_time) || 1.day.ago
|
393
|
-
params[:QueryStartDateTime] = start_time.utc.iso8601
|
394
|
-
end
|
395
|
-
|
396
|
-
build_basic_api_query(params.merge(options))
|
397
|
-
end
|
398
|
-
|
399
|
-
def build_next_inventory_list_request(token)
|
400
|
-
params = {
|
401
|
-
:NextToken => token,
|
402
|
-
:Action => OPERATIONS[:inventory][:list_next]
|
403
|
-
}
|
404
|
-
|
405
|
-
build_basic_api_query(params)
|
406
|
-
end
|
407
|
-
|
408
|
-
def build_tracking_request(order_id, options)
|
409
|
-
params = {:Action => OPERATIONS[:outbound][:tracking], :SellerFulfillmentOrderId => order_id}
|
410
|
-
|
411
|
-
build_basic_api_query(params.merge(options))
|
412
|
-
end
|
413
|
-
|
414
|
-
def build_address(address)
|
415
|
-
requires!(address, :name, :address1, :city, :country, :zip)
|
416
|
-
address[:state] ||= "N/A"
|
417
|
-
address[:zip].upcase!
|
418
|
-
address[:name] = "#{address[:company]} - #{address[:name]}" if address[:company].present?
|
419
|
-
ary = address.map{ |key, value| [LOOKUPS[:destination_address][key], value] if LOOKUPS[:destination_address].include?(key) && value.present? }
|
420
|
-
Hash[ary.compact]
|
421
|
-
end
|
422
|
-
|
423
|
-
def build_items(line_items)
|
424
|
-
lookup = LOOKUPS[:line_items]
|
425
|
-
counter = 0
|
426
|
-
line_items.reduce({}) do |items, line_item|
|
427
|
-
counter += 1
|
428
|
-
lookup.each do |key, value|
|
429
|
-
entry = value % counter
|
430
|
-
case key
|
431
|
-
when :sku
|
432
|
-
items[entry] = line_item[:sku] || "SKU-#{counter}"
|
433
|
-
when :order_id
|
434
|
-
items[entry] = line_item[:sku] || "FULFILLMENT-ITEM-ID-#{counter}"
|
435
|
-
when :quantity
|
436
|
-
items[entry] = line_item[:quantity] || 1
|
437
|
-
else
|
438
|
-
items[entry] = line_item[key] if line_item.include? key
|
439
|
-
end
|
440
|
-
end
|
441
|
-
items
|
442
|
-
end
|
443
|
-
end
|
444
|
-
|
445
|
-
def build_status_request
|
446
|
-
build_basic_api_query({ :Action => OPERATIONS[:outbound][:status] })
|
447
|
-
end
|
448
|
-
|
449
|
-
def escape(str)
|
450
|
-
CGI.escape(str.to_s).gsub('+', '%20')
|
451
|
-
end
|
452
|
-
end
|
453
|
-
end
|
454
|
-
end
|