ideaoforder-shipping 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +429 -0
- data/README +51 -0
- data/Rakefile +251 -0
- data/lib/shipping/base.rb +314 -0
- data/lib/shipping/fedex.rb +585 -0
- data/lib/shipping/ups.rb +809 -0
- metadata +69 -0
@@ -0,0 +1,585 @@
|
|
1
|
+
# Author:: Lucas Carlson (mailto:lucas@rufy.com)
|
2
|
+
# Copyright:: Copyright (c) 2005 Lucas Carlson
|
3
|
+
# License:: LGPL
|
4
|
+
#
|
5
|
+
#
|
6
|
+
# See http://www.fedex.com/us/solutions/wis/pdf/xml_transguide.pdf?link=4 for the full XML-based API
|
7
|
+
|
8
|
+
module Shipping
|
9
|
+
class FedEx < Base
|
10
|
+
# Gets the list price the regular consumer would have to pay. Discount price is what the
|
11
|
+
# person with this particular account number will pay
|
12
|
+
def price
|
13
|
+
get_price
|
14
|
+
return REXML::XPath.first(@response, "//FDXRateReply/EstimatedCharges/ListCharges/NetCharge").text.to_f
|
15
|
+
rescue ShippingError
|
16
|
+
raise ShippingError, get_error
|
17
|
+
end
|
18
|
+
|
19
|
+
# Gets the discount price of the shipping (with discounts taken into consideration).
|
20
|
+
# "base price" doesn't include surcharges like fuel cost, so I don't think it is the correct price to get
|
21
|
+
def discount_price
|
22
|
+
get_price
|
23
|
+
return REXML::XPath.first(@response, "//FDXRateReply/EstimatedCharges/DiscountedCharges/NetCharge").text.to_f
|
24
|
+
rescue ShippingError
|
25
|
+
raise ShippingError, get_error
|
26
|
+
end
|
27
|
+
|
28
|
+
# still not sure what the best way to handle this transaction's return data would be. Possibly a hash of the form {service => delivery_estimate}?
|
29
|
+
def express_service_availability
|
30
|
+
@data = String.new
|
31
|
+
b = Builder::XmlMarkup.new :target => @data
|
32
|
+
b.instruct!
|
33
|
+
b.FDXServiceAvailabilityRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXServiceAvailabilityRequest.xsd') { |b|
|
34
|
+
b.RequestHeader { |b|
|
35
|
+
b.AccountNumber @fedex_account
|
36
|
+
b.MeterNumber @fedex_meter
|
37
|
+
}
|
38
|
+
b.OriginAddress { |b|
|
39
|
+
b.PostalCode @sender_zip
|
40
|
+
b.CountryCode @sender_country_code || "US"
|
41
|
+
}
|
42
|
+
b.DestinationAddress { |b|
|
43
|
+
b.PostalCode @zip
|
44
|
+
b.CountryCode @country || "US"
|
45
|
+
}
|
46
|
+
b.ShipDate @ship_date unless @ship_date.blank?
|
47
|
+
b.PackageCount @package_total || '1'
|
48
|
+
}
|
49
|
+
|
50
|
+
get_response @fedex_url
|
51
|
+
end
|
52
|
+
|
53
|
+
def register
|
54
|
+
@required = [:name, :company, :phone, :email, :address, :city, :state, :zip]
|
55
|
+
@required += [:fedex_account, :fedex_url]
|
56
|
+
|
57
|
+
state = STATES.has_value?(@state.downcase) ? STATES.index(@state.downcase).upcase : @state.upcase rescue nil
|
58
|
+
|
59
|
+
@data = String.new
|
60
|
+
b = Builder::XmlMarkup.new :target => @data
|
61
|
+
b.instruct!
|
62
|
+
b.FDXSubscriptionRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXSubscriptionRequest.xsd') { |b|
|
63
|
+
b.RequestHeader { |b|
|
64
|
+
b.CustomerTransactionIdentifier @transaction_identifier if @transaction_identifier # optional
|
65
|
+
b.AccountNumber @fedex_account
|
66
|
+
}
|
67
|
+
b.Contact { |b|
|
68
|
+
b.PersonName @name
|
69
|
+
b.CompanyName @company
|
70
|
+
b.Department @department if @department
|
71
|
+
b.PhoneNumber @phone.to_s.gsub(/[^\d]/,"")
|
72
|
+
b.tag! :"E-MailAddress", @email
|
73
|
+
}
|
74
|
+
b.Address { |b|
|
75
|
+
b.Line1 @address
|
76
|
+
b.Line2 @address2 if @address2
|
77
|
+
b.City @city
|
78
|
+
b.StateOrProvinceCode state
|
79
|
+
b.PostalCode @zip
|
80
|
+
b.CountryCode @country || 'US'
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
get_response @fedex_url
|
85
|
+
|
86
|
+
return REXML::XPath.first(@response, "//FDXSubscriptionReply/MeterNumber").text
|
87
|
+
end
|
88
|
+
|
89
|
+
# require 'fileutils'
|
90
|
+
# fedex = Shipping::FedEx.new :name => 'John Doe', ... , :sender_zip => 97202
|
91
|
+
# label = fedex.label
|
92
|
+
# puts label.tracking_number
|
93
|
+
# FileUtils.cp label.image.path, '/path/to/my/images/directory/'
|
94
|
+
#
|
95
|
+
# There are several types of labels that can be returned by changing @image_type.
|
96
|
+
# PNG is selected by default.
|
97
|
+
#
|
98
|
+
def label
|
99
|
+
@required = [:phone, :email, :address, :city, :state, :zip ]
|
100
|
+
@required += [:sender_phone, :sender_email, :sender_address, :sender_city, :sender_state, :sender_zip ]
|
101
|
+
@required += [:fedex_account, :fedex_url, :fedex_meter]
|
102
|
+
|
103
|
+
@transaction_type ||= 'ship_ground'
|
104
|
+
@weight = (@weight.to_f*10).round/10.0
|
105
|
+
@declared_value = (@declared_value.to_f*100).round/100.0 unless @declared_value.blank?
|
106
|
+
state = STATES.has_value?(@state.downcase) ? STATES.index(@state.downcase).upcase : @state.upcase
|
107
|
+
sender_state = STATES.has_value?(@sender_state.downcase) ? STATES.index(@sender_state.downcase).upcase : @sender_state.upcase
|
108
|
+
|
109
|
+
@data = String.new
|
110
|
+
b = Builder::XmlMarkup.new :target => @data
|
111
|
+
b.instruct!
|
112
|
+
b.FDXShipRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXShipRequest.xsd') { |b|
|
113
|
+
b.RequestHeader { |b|
|
114
|
+
b.AccountNumber @fedex_account
|
115
|
+
b.MeterNumber @fedex_meter
|
116
|
+
b.CarrierCode TransactionTypes[@transaction_type][1]
|
117
|
+
}
|
118
|
+
b.ShipDate((Time.now).strftime("%Y-%m-%d"))
|
119
|
+
b.ShipTime((Time.now).strftime("%H:%M:%S"))
|
120
|
+
b.DropoffType @dropoff_type || 'REGULARPICKUP'
|
121
|
+
b.Service ServiceTypes[@service_type] || ServiceTypes['ground_service'] # default to ground service
|
122
|
+
b.Packaging PackageTypes[@packaging_type] || 'YOURPACKAGING'
|
123
|
+
b.WeightUnits @weight_units || 'LBS' # or KGS
|
124
|
+
b.Weight @weight
|
125
|
+
b.CurrencyCode @currency_code || 'USD'
|
126
|
+
b.Origin { |b|
|
127
|
+
b.Contact { |b|
|
128
|
+
if @sender_name.to_s.size > 2
|
129
|
+
b.PersonName @sender_name
|
130
|
+
b.CompanyName @sender_company unless @sender_company.blank?
|
131
|
+
elsif @sender_company.to_s.size > 2
|
132
|
+
b.PersonName @sender_name unless @sender_name.blank?
|
133
|
+
b.CompanyName @sender_company
|
134
|
+
else
|
135
|
+
raise ShippingError, "Either the sender_name or the sender_company value must be bigger than 2 characters."
|
136
|
+
end
|
137
|
+
b.Department @sender_department unless @sender_department.blank?
|
138
|
+
b.PhoneNumber @sender_phone.gsub(/[^\d]/,"")
|
139
|
+
b.PagerNumber @sender_pager.gsub(/[^\d]/,"") if @sender_pager.class == String
|
140
|
+
b.FaxNumber @sender_fax.gsub(/[^\d]/,"") if @sender_fax.class == String
|
141
|
+
b.tag! :"E-MailAddress", @sender_email
|
142
|
+
}
|
143
|
+
b.Address { |b|
|
144
|
+
b.Line1 @sender_address
|
145
|
+
b.Line2 @sender_address2 unless @sender_address2.blank?
|
146
|
+
b.City @sender_city
|
147
|
+
b.StateOrProvinceCode sender_state
|
148
|
+
b.PostalCode @sender_zip
|
149
|
+
b.CountryCode @sender_country || 'US'
|
150
|
+
}
|
151
|
+
}
|
152
|
+
b.Destination { |b|
|
153
|
+
b.Contact { |b|
|
154
|
+
if @name.to_s.size > 2
|
155
|
+
b.PersonName @name
|
156
|
+
b.CompanyName @company unless @company.blank?
|
157
|
+
elsif @company.to_s.size > 2
|
158
|
+
b.PersonName @name unless @name.blank?
|
159
|
+
b.CompanyName @company
|
160
|
+
else
|
161
|
+
raise ShippingError, "Either the name or the company value must be bigger than 2 characters."
|
162
|
+
end
|
163
|
+
b.Department @department unless @department.blank?
|
164
|
+
b.PhoneNumber @phone.gsub(/[^\d]/,"")
|
165
|
+
b.PagerNumber @pager.gsub(/[^\d]/,"") if @pager.class == String
|
166
|
+
b.FaxNumber @fax.gsub(/[^\d]/,"") if @fax.class == String
|
167
|
+
b.tag! :"E-MailAddress", @email
|
168
|
+
}
|
169
|
+
b.Address { |b|
|
170
|
+
b.Line1 @address
|
171
|
+
b.Line2 @address2 unless @address2.blank?
|
172
|
+
b.City @city
|
173
|
+
b.StateOrProvinceCode state
|
174
|
+
b.PostalCode @zip
|
175
|
+
b.CountryCode @country || 'US'
|
176
|
+
}
|
177
|
+
}
|
178
|
+
b.Payment { |b|
|
179
|
+
b.PayorType PaymentTypes[@pay_type] || 'SENDER'
|
180
|
+
b.Payor { |b|
|
181
|
+
b.AccountNumber @payor_account_number
|
182
|
+
b.CountryCode @payor_country_code unless @payor_country_code.blank?
|
183
|
+
} unless @payor_account_number.blank?
|
184
|
+
}
|
185
|
+
b.RMA { |b|
|
186
|
+
b.Number @rma_number
|
187
|
+
} unless @rma_number.blank?
|
188
|
+
b.SpecialServices { |b|
|
189
|
+
b.EMailNotification { |b|
|
190
|
+
b.ShipAlertOptionalMessage @message
|
191
|
+
b.Shipper { |b|
|
192
|
+
b.ShipAlert @shipper_ship_alert ? 'true' : 'false'
|
193
|
+
b.LanguageCode @shipper_language || 'EN' # FR also available
|
194
|
+
}
|
195
|
+
b.Recipient { |b|
|
196
|
+
b.ShipAlert @recipient_ship_alert ? 'true' : 'false'
|
197
|
+
b.LanguageCode @recipient_language || 'EN' # FR also available
|
198
|
+
}
|
199
|
+
b.Other { |b|
|
200
|
+
b.tag! :"E-MailAddress", @other_email
|
201
|
+
b.ShipAlert @other_ship_alert ? 'true' : 'false'
|
202
|
+
b.LanguageCode @other_language || 'EN' # FR also available
|
203
|
+
} unless @other_email.blank?
|
204
|
+
}
|
205
|
+
} unless @message.blank?
|
206
|
+
b.Label { |b|
|
207
|
+
b.Type @label_type || '2DCOMMON'
|
208
|
+
b.ImageType @image_type || 'PNG'
|
209
|
+
}
|
210
|
+
}
|
211
|
+
get_response @fedex_url
|
212
|
+
|
213
|
+
begin
|
214
|
+
response = Hash.new
|
215
|
+
response[:tracking_number] = REXML::XPath.first(@response, "//FDXShipReply/Tracking/TrackingNumber").text
|
216
|
+
response[:encoded_image] = REXML::XPath.first(@response, "//FDXShipReply/Labels/OutboundLabel").text
|
217
|
+
response[:image] = Tempfile.new("shipping_label")
|
218
|
+
response[:image].write Base64.decode64( response[:encoded_image] )
|
219
|
+
response[:image].rewind
|
220
|
+
rescue
|
221
|
+
raise ShippingError, get_error
|
222
|
+
end
|
223
|
+
|
224
|
+
# allows for things like fedex.label.url
|
225
|
+
def response.method_missing(name, *args)
|
226
|
+
has_key?(name) ? self[name] : super
|
227
|
+
end
|
228
|
+
|
229
|
+
# don't allow people to edit the response
|
230
|
+
response.freeze
|
231
|
+
end
|
232
|
+
|
233
|
+
# require 'fileutils'
|
234
|
+
# fedex = Shipping::FedEx.new :name => 'John Doe', ... , :sender_zip => 97202
|
235
|
+
# label = fedex.email_label
|
236
|
+
# puts label.url
|
237
|
+
# puts label.tracking_number
|
238
|
+
#
|
239
|
+
def return_label
|
240
|
+
@required = [:phone, :email, :address, :city, :state, :zip ]
|
241
|
+
@required += [:sender_phone, :sender_email, :sender_address, :sender_city, :sender_state, :sender_zip ]
|
242
|
+
@required += [:fedex_account, :fedex_url, :fedex_meter, :weight ]
|
243
|
+
|
244
|
+
@transaction_type ||= 'ship_ground'
|
245
|
+
@weight = (@weight.to_f*10).round/10.0
|
246
|
+
@declared_value = (@declared_value.to_f*100).round/100.0 unless @declared_value.blank?
|
247
|
+
|
248
|
+
state = STATES.has_value?(@state.downcase) ? STATES.index(@state.downcase).upcase : @state.upcase rescue nil
|
249
|
+
sender_state = STATES.has_value?(@sender_state.downcase) ? STATES.index(@sender_state.downcase).upcase : @sender_state.upcase rescue nil
|
250
|
+
|
251
|
+
@data = String.new
|
252
|
+
b = Builder::XmlMarkup.new :target => @data
|
253
|
+
b.instruct!
|
254
|
+
b.FDXEmailLabelRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXEmailLabelRequest.xsd') { |b|
|
255
|
+
b.RequestHeader { |b|
|
256
|
+
b.AccountNumber @fedex_account
|
257
|
+
b.MeterNumber @fedex_meter
|
258
|
+
b.CarrierCode TransactionTypes[@transaction_type][1]
|
259
|
+
}
|
260
|
+
b.URLExpirationDate((Time.now + 3600*24).strftime("%Y-%m-%d"))
|
261
|
+
b.tag! :"URLNotificationE-MailAddress", @sender_email
|
262
|
+
b.MerchantPhoneNumber @sender_phone.gsub(/[^\d]/,"")
|
263
|
+
b.Service ServiceTypes[@service_type] || ServiceTypes['ground_service'] # default to ground service
|
264
|
+
b.Packaging PackageTypes[@packaging_type] || 'YOURPACKAGING'
|
265
|
+
b.WeightUnits @weight_units || 'LBS' # or KGS
|
266
|
+
b.CurrencyCode @currency_code || 'USD'
|
267
|
+
b.Origin { |b|
|
268
|
+
b.Contact { |b|
|
269
|
+
if @sender_name.to_s.size > 2
|
270
|
+
b.PersonName @sender_name
|
271
|
+
b.CompanyName @sender_company unless @sender_company.blank?
|
272
|
+
elsif @sender_company.to_s.size > 2
|
273
|
+
b.PersonName @sender_name unless @sender_name.blank?
|
274
|
+
b.CompanyName @sender_company
|
275
|
+
else
|
276
|
+
raise ShippingError, "Either the sender_name or the sender_company value must be bigger than 2 characters."
|
277
|
+
end
|
278
|
+
b.Department @sender_department unless @sender_department.blank?
|
279
|
+
b.PhoneNumber @sender_phone.gsub(/[^\d]/,"")
|
280
|
+
b.PagerNumber @sender_pager.gsub(/[^\d]/,"") if @sender_pager.class == String
|
281
|
+
b.FaxNumber @sender_fax.gsub(/[^\d]/,"") if @sender_fax.class == String
|
282
|
+
b.tag! :"E-MailAddress", @sender_email
|
283
|
+
}
|
284
|
+
b.Address { |b|
|
285
|
+
b.Line1 @sender_address
|
286
|
+
b.Line2 @sender_address2 unless @sender_address2.blank?
|
287
|
+
b.City @sender_city
|
288
|
+
b.StateOrProvinceCode sender_state
|
289
|
+
b.PostalCode @sender_zip
|
290
|
+
b.CountryCode @sender_country || 'US'
|
291
|
+
}
|
292
|
+
}
|
293
|
+
b.Destination { |b|
|
294
|
+
b.Contact { |b|
|
295
|
+
if @name.to_s.size > 2
|
296
|
+
b.PersonName @name
|
297
|
+
b.CompanyName @company unless @company.blank?
|
298
|
+
elsif @company.to_s.size > 2
|
299
|
+
b.PersonName @name unless @name.blank?
|
300
|
+
b.CompanyName @company
|
301
|
+
else
|
302
|
+
raise ShippingError, "Either the name or the company value must be bigger than 2 characters."
|
303
|
+
end
|
304
|
+
b.Department @department unless @department.blank?
|
305
|
+
b.PhoneNumber @phone.gsub(/[^\d]/,"")
|
306
|
+
b.PagerNumber @pager.gsub(/[^\d]/,"") if @pager.class == String
|
307
|
+
b.FaxNumber @fax.gsub(/[^\d]/,"") if @fax.class == String
|
308
|
+
b.tag! :"E-MailAddress", @email
|
309
|
+
}
|
310
|
+
b.Address { |b|
|
311
|
+
b.Line1 @address
|
312
|
+
b.Line2 @address2 unless @address2.blank?
|
313
|
+
b.City @city
|
314
|
+
b.StateOrProvinceCode state
|
315
|
+
b.PostalCode @zip
|
316
|
+
b.CountryCode @country || 'US'
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
320
|
+
b.Payment { |b|
|
321
|
+
b.PayorType PaymentTypes[@pay_type] || 'SENDER'
|
322
|
+
b.Payor { |b|
|
323
|
+
b.AccountNumber @payor_account_number
|
324
|
+
b.CountryCode @payor_country_code unless @payor_country_code.blank?
|
325
|
+
} unless @payor_account_number.blank?
|
326
|
+
}
|
327
|
+
|
328
|
+
b.RMA { |b|
|
329
|
+
b.Number @rma_number
|
330
|
+
} unless @rma_number.blank?
|
331
|
+
|
332
|
+
b.Package { |b|
|
333
|
+
b.Weight @weight
|
334
|
+
b.DeclaredValue @declared_value || '99.00'
|
335
|
+
b.ReferenceInfo { |b|
|
336
|
+
b.CustomerReference @customer_reference
|
337
|
+
} unless @customer_reference.blank?
|
338
|
+
b.ItemDescription @description || "Shipment"
|
339
|
+
}
|
340
|
+
|
341
|
+
b.SpecialServices { |b|
|
342
|
+
b.EMailNotification { |b|
|
343
|
+
b.ShipAlertOptionalMessage @message
|
344
|
+
b.Shipper { |b|
|
345
|
+
b.ShipAlert @shipper_ship_alert ? 'true' : 'false'
|
346
|
+
b.LanguageCode @shipper_language || 'EN' # FR also available
|
347
|
+
}
|
348
|
+
b.Recipient { |b|
|
349
|
+
b.ShipAlert @recipient_ship_alert ? 'true' : 'false'
|
350
|
+
b.LanguageCode @recipient_language || 'EN' # FR also available
|
351
|
+
}
|
352
|
+
b.Other { |b|
|
353
|
+
b.tag! :"E-MailAddress", @other_email
|
354
|
+
b.ShipAlert @other_ship_alert ? 'true' : 'false'
|
355
|
+
b.LanguageCode @other_language || 'EN' # FR also available
|
356
|
+
} unless @other_email.blank?
|
357
|
+
}
|
358
|
+
} unless @message.blank?
|
359
|
+
}
|
360
|
+
|
361
|
+
get_response @fedex_url
|
362
|
+
|
363
|
+
begin
|
364
|
+
response = Hash.new
|
365
|
+
response[:url] = REXML::XPath.first(@response, "//FDXEmailLabelReply/URL").text
|
366
|
+
response[:userid] = REXML::XPath.first(@response, "//FDXEmailLabelReply/UserID").text
|
367
|
+
response[:password] = REXML::XPath.first(@response, "//FDXEmailLabelReply/Password").text
|
368
|
+
response[:tracking_number] = REXML::XPath.first(@response, "//FDXEmailLabelReply/Package/TrackingNumber").text
|
369
|
+
rescue
|
370
|
+
raise ShippingError, get_error
|
371
|
+
end
|
372
|
+
|
373
|
+
# allows for things like fedex.label.url
|
374
|
+
def response.method_missing(name, *args)
|
375
|
+
has_key?(name) ? self[name] : super
|
376
|
+
end
|
377
|
+
|
378
|
+
# don't allow people to edit the response
|
379
|
+
return response.freeze
|
380
|
+
end
|
381
|
+
|
382
|
+
def void(tracking_number)
|
383
|
+
@required = [:fedex_account, :fedex_url, :fedex_meter]
|
384
|
+
|
385
|
+
@transaction_type ||= 'ship_ground'
|
386
|
+
|
387
|
+
@data = String.new
|
388
|
+
b = Builder::XmlMarkup.new :target => @data
|
389
|
+
b.instruct!
|
390
|
+
b.FDXShipDeleteRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXShipDeleteRequest.xsd') { |b|
|
391
|
+
b.RequestHeader { |b|
|
392
|
+
b.AccountNumber @fedex_account
|
393
|
+
b.MeterNumber @fedex_meter
|
394
|
+
b.CarrierCode TransactionTypes[@transaction_type][1]
|
395
|
+
}
|
396
|
+
b.TrackingNumber tracking_number
|
397
|
+
}
|
398
|
+
|
399
|
+
get_response @fedex_url
|
400
|
+
raise ShippingError, get_error if get_error
|
401
|
+
return true
|
402
|
+
end
|
403
|
+
|
404
|
+
def available_services(force=nil)
|
405
|
+
if @services.empty? || force
|
406
|
+
get_available_services
|
407
|
+
else
|
408
|
+
@services
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
private
|
413
|
+
|
414
|
+
def get_price #:nodoc:
|
415
|
+
@required = [:zip, :sender_zip, :weight]
|
416
|
+
@required += [:transaction_type, :fedex_account, :fedex_meter, :fedex_url]
|
417
|
+
|
418
|
+
@transaction_type ||= 'rate_ground'
|
419
|
+
@weight = (@weight.to_f*10).round/10.0
|
420
|
+
|
421
|
+
@data = String.new
|
422
|
+
b = Builder::XmlMarkup.new(:target => @data)
|
423
|
+
b.instruct!
|
424
|
+
b.FDXRateRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXRateRequest.xsd') { |b|
|
425
|
+
b.RequestHeader { |b|
|
426
|
+
b.AccountNumber @fedex_account
|
427
|
+
b.MeterNumber @fedex_meter
|
428
|
+
b.CarrierCode TransactionTypes[@transaction_type][1]
|
429
|
+
}
|
430
|
+
b.ShipDate @ship_date unless @ship_date.blank?
|
431
|
+
b.DropoffType @dropoff_type || 'REGULARPICKUP'
|
432
|
+
b.Service ServiceTypes[@service_type] || ServiceTypes['ground_service'] # default to ground service
|
433
|
+
b.Packaging PackageTypes[@packaging_type] || 'YOURPACKAGING'
|
434
|
+
b.WeightUnits @weight_units || 'LBS'
|
435
|
+
b.Weight @weight
|
436
|
+
b.ListRate true #tells fedex to return list rates as well as discounted rates
|
437
|
+
b.OriginAddress { |b|
|
438
|
+
b.StateOrProvinceCode self.class.state_from_zip(@sender_zip)
|
439
|
+
b.PostalCode @sender_zip
|
440
|
+
b.CountryCode @sender_country_code || "US"
|
441
|
+
}
|
442
|
+
b.DestinationAddress { |b|
|
443
|
+
b.StateOrProvinceCode self.class.state_from_zip(@zip)
|
444
|
+
b.PostalCode @zip
|
445
|
+
b.CountryCode @country || "US"
|
446
|
+
}
|
447
|
+
b.Payment { |b|
|
448
|
+
b.PayorType PaymentTypes[@pay_type] || 'SENDER'
|
449
|
+
}
|
450
|
+
b.PackageCount @package_total || '1'
|
451
|
+
}
|
452
|
+
|
453
|
+
get_response @fedex_url
|
454
|
+
end
|
455
|
+
|
456
|
+
def get_error
|
457
|
+
return if @response.class != REXML::Document
|
458
|
+
error = REXML::XPath.first(@response, "//Error")
|
459
|
+
return if !error
|
460
|
+
|
461
|
+
code = REXML::XPath.first(error, "//Code").text
|
462
|
+
message = REXML::XPath.first(error, "//Message").text
|
463
|
+
|
464
|
+
return "Error #{code}: #{message}"
|
465
|
+
end
|
466
|
+
|
467
|
+
def get_available_services
|
468
|
+
@required = [:zip, :sender_zip, :weight]
|
469
|
+
@required += [:fedex_account, :fedex_meter, :fedex_url]
|
470
|
+
|
471
|
+
@transaction_type = 'rate_services'
|
472
|
+
@weight = (@weight.to_f*10).round/10.0
|
473
|
+
|
474
|
+
# Ground first
|
475
|
+
@services = []
|
476
|
+
rate_available_services_request('FDXG')
|
477
|
+
rate_available_services_request('FDXE')
|
478
|
+
|
479
|
+
return @services
|
480
|
+
end
|
481
|
+
|
482
|
+
def rate_available_services_request(carrier_code)
|
483
|
+
@data = String.new
|
484
|
+
b = Builder::XmlMarkup.new(:target => @data)
|
485
|
+
b.instruct!
|
486
|
+
b.FDXRateAvailableServicesRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXRateAvailableServicesRequest.xsd') { |b|
|
487
|
+
b.RequestHeader { |b|
|
488
|
+
b.AccountNumber @fedex_account
|
489
|
+
b.MeterNumber @fedex_meter
|
490
|
+
b.CarrierCode carrier_code
|
491
|
+
}
|
492
|
+
b.ShipDate @ship_date unless @ship_date.blank?
|
493
|
+
b.DropoffType DropoffTypes[@dropoff_type] || DropoffTypes['regular_pickup']
|
494
|
+
b.Packaging PackageTypes[@packaging_type] || PackageTypes['your_packaging']
|
495
|
+
b.WeightUnits @weight_units || 'LBS'
|
496
|
+
b.Weight @weight || '1.0'
|
497
|
+
b.ListRate 'false'
|
498
|
+
b.OriginAddress { |b|
|
499
|
+
b.StateOrProvince @sender_state
|
500
|
+
b.PostalCode @sender_zip
|
501
|
+
b.CountryCode @sender_country_code || 'US'
|
502
|
+
}
|
503
|
+
b.DestinationAddress { |b|
|
504
|
+
b.StateOrProvince @state
|
505
|
+
b.PostalCode @zip
|
506
|
+
b.CountryCode @country_code || 'US'
|
507
|
+
}
|
508
|
+
b.Payment { |b|
|
509
|
+
b.PayorType PaymentTypes[@pay_type] || PaymentTypes['sender']
|
510
|
+
}
|
511
|
+
b.PackageCount @package_total || 1
|
512
|
+
}
|
513
|
+
get_response @fedex_url
|
514
|
+
|
515
|
+
REXML::XPath.each(@response, "//Entry") { |el|
|
516
|
+
@services << Shipping::Base.initialize_for_fedex_service(el)
|
517
|
+
}
|
518
|
+
|
519
|
+
end
|
520
|
+
|
521
|
+
# The following type hashes are to allow cross-api data retrieval
|
522
|
+
|
523
|
+
ServiceTypes = {
|
524
|
+
"priority" => "PRIORITYOVERNIGHT",
|
525
|
+
"2day" => "FEDEX2DAY",
|
526
|
+
"standard_overnight" => "STANDARDOVERNIGHT",
|
527
|
+
"first_overnight" => "FIRSTOVERNIGHT",
|
528
|
+
"express_saver" => "FEDEXEXPRESSSAVER",
|
529
|
+
"1day_freight" => "FEDEX1DAYFREIGHT",
|
530
|
+
"2day_freight" => "FEDEX2DAYFREIGHT",
|
531
|
+
"3day_freight" => "FEDEX3DAYFREIGHT",
|
532
|
+
"international_priority" => "INTERNATIONALPRIORITY",
|
533
|
+
"international_economy" => "INTERNATIONALECONOMY",
|
534
|
+
"international_first" => "INTERNATIONALFIRST",
|
535
|
+
"international_priority_freight" => "INTERNATIONALPRIORITYFREIGHT",
|
536
|
+
"international_economy_freight" => "INTERNATIONALECONOMYFREIGHT",
|
537
|
+
"home_delivery" => "GROUNDHOMEDELIVERY",
|
538
|
+
"ground_service" => "FEDEXGROUND",
|
539
|
+
"international_ground_service" => "INTERNATIONALGROUND"
|
540
|
+
}
|
541
|
+
|
542
|
+
PackageTypes = {
|
543
|
+
"fedex_envelope" => "FEDEXENVELOPE",
|
544
|
+
"fedex_pak" => "FEDEXPAK",
|
545
|
+
"fedex_box" => "FEDEXBOX",
|
546
|
+
"fedex_tube" => "FEDEXTUBE",
|
547
|
+
"fedex_10_kg_box" => "FEDEX10KGBOX",
|
548
|
+
"fedex_25_kg_box" => "FEDEX25KGBOX",
|
549
|
+
"your_packaging" => "YOURPACKAGING"
|
550
|
+
}
|
551
|
+
|
552
|
+
DropoffTypes = {
|
553
|
+
'regular_pickup' => 'REGULARPICKUP',
|
554
|
+
'request_courier' => 'REQUESTCOURIER',
|
555
|
+
'dropbox' => 'DROPBOX',
|
556
|
+
'business_service_center' => 'BUSINESSSERVICECENTER',
|
557
|
+
'station' => 'STATION'
|
558
|
+
}
|
559
|
+
|
560
|
+
PaymentTypes = {
|
561
|
+
'sender' => 'SENDER',
|
562
|
+
'recipient' => 'RECIPIENT',
|
563
|
+
'third_party' => 'THIRDPARTY',
|
564
|
+
'collect' => 'COLLECT'
|
565
|
+
}
|
566
|
+
|
567
|
+
|
568
|
+
TransactionTypes = {
|
569
|
+
'rate_ground' => ['022','FDXG'],
|
570
|
+
'rate_express' => ['022','FDXE'],
|
571
|
+
'rate_services' => ['025',''],
|
572
|
+
'ship_ground' => ['021','FDXG'],
|
573
|
+
'ship_express' => ['021','FDXE'],
|
574
|
+
'cancel_express' => ['023','FDXE'],
|
575
|
+
'cancel_ground' => ['023','FDXG'],
|
576
|
+
'close_ground' => ['007','FDXG'],
|
577
|
+
'service_available' => ['019','FDXE'],
|
578
|
+
'fedex_locater' => ['410',''],
|
579
|
+
'subscribe' => ['211',''],
|
580
|
+
'sig_proof_delivery' => ['402',''],
|
581
|
+
'track' => ['405',''],
|
582
|
+
'ref_track' => ['403','']
|
583
|
+
}
|
584
|
+
end
|
585
|
+
end
|