fedex_ship 0.1.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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.idea/.rakeTasks +7 -0
- data/.idea/fedex_ship-0.1.0.iml +22 -0
- data/.idea/misc.xml +7 -0
- data/.idea/modules.xml +8 -0
- data/.idea/vcs.xml +6 -0
- data/.idea/workspace.xml +56 -0
- data/.rspec +2 -0
- data/Gemfile +5 -0
- data/Rakefile +7 -0
- data/Readme.md +496 -0
- data/fedex_ship.gemspec +28 -0
- data/lib/fedex_ship.rb +55 -0
- data/lib/fedex_ship/address.rb +31 -0
- data/lib/fedex_ship/credentials.rb +26 -0
- data/lib/fedex_ship/document.rb +51 -0
- data/lib/fedex_ship/ground_manifest.rb +25 -0
- data/lib/fedex_ship/helpers.rb +20 -0
- data/lib/fedex_ship/label.rb +71 -0
- data/lib/fedex_ship/rate.rb +38 -0
- data/lib/fedex_ship/request/address.rb +97 -0
- data/lib/fedex_ship/request/base.rb +443 -0
- data/lib/fedex_ship/request/delete.rb +76 -0
- data/lib/fedex_ship/request/document.rb +45 -0
- data/lib/fedex_ship/request/ground_close.rb +73 -0
- data/lib/fedex_ship/request/label.rb +29 -0
- data/lib/fedex_ship/request/logs_fedex.rb +74 -0
- data/lib/fedex_ship/request/pickup.rb +135 -0
- data/lib/fedex_ship/request/pickup_availability.rb +102 -0
- data/lib/fedex_ship/request/rate.rb +94 -0
- data/lib/fedex_ship/request/service_availability.rb +86 -0
- data/lib/fedex_ship/request/shipment.rb +249 -0
- data/lib/fedex_ship/request/tracking_information.rb +119 -0
- data/lib/fedex_ship/shipment.rb +115 -0
- data/lib/fedex_ship/tracking_information.rb +54 -0
- data/lib/fedex_ship/tracking_information/event.rb +24 -0
- data/lib/fedex_ship/version.rb +6 -0
- data/spec/config/fedex_credentials.example.yml +13 -0
- data/spec/lib/fedex_ship/address_spec.rb +59 -0
- data/spec/lib/fedex_ship/delete_spec.rb +26 -0
- data/spec/lib/fedex_ship/document_spec.rb +177 -0
- data/spec/lib/fedex_ship/ground_close_spec.rb +42 -0
- data/spec/lib/fedex_ship/label_spec.rb +73 -0
- data/spec/lib/fedex_ship/pickup_availability_spec.rb +19 -0
- data/spec/lib/fedex_ship/pickup_spec.rb +32 -0
- data/spec/lib/fedex_ship/rate_spec.rb +216 -0
- data/spec/lib/fedex_ship/service_availability_spec.rb +20 -0
- data/spec/lib/fedex_ship/shipment_spec.rb +86 -0
- data/spec/lib/fedex_ship/track_spec.rb +67 -0
- data/spec/spec_helper.rb +12 -0
- data/spec/support/credentials.rb +15 -0
- data/spec/support/vcr.rb +14 -0
- metadata +193 -0
@@ -0,0 +1,443 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'fedex_ship/helpers'
|
4
|
+
require 'fedex_ship/rate'
|
5
|
+
|
6
|
+
module FedexShip
|
7
|
+
module Request
|
8
|
+
class Base
|
9
|
+
include Helpers
|
10
|
+
include HTTParty
|
11
|
+
format :xml
|
12
|
+
# If true the rate method will return the complete response from the Fedex Web Service
|
13
|
+
attr_accessor :debug
|
14
|
+
# Fedex Text URL
|
15
|
+
# TEST_URL = "https://wsbeta.fedex.com:443/xml/"
|
16
|
+
TEST_URL = "https://wsbeta.fedex.com:443/web-services"
|
17
|
+
|
18
|
+
|
19
|
+
# Fedex Production URL
|
20
|
+
PRODUCTION_URL = "https://ws.fedex.com:443/web-services"
|
21
|
+
|
22
|
+
# List of available Service Types
|
23
|
+
SERVICE_TYPES = %w(EUROPE_FIRST_INTERNATIONAL_PRIORITY FEDEX_1_DAY_FREIGHT FEDEX_2_DAY FEDEX_2_DAY_AM FEDEX_2_DAY_FREIGHT FEDEX_3_DAY_FREIGHT FEDEX_EXPRESS_SAVER FEDEX_FIRST_FREIGHT FEDEX_FREIGHT_ECONOMY FEDEX_FREIGHT_PRIORITY FEDEX_GROUND FIRST_OVERNIGHT GROUND_HOME_DELIVERY INTERNATIONAL_ECONOMY INTERNATIONAL_ECONOMY_FREIGHT INTERNATIONAL_FIRST INTERNATIONAL_PRIORITY INTERNATIONAL_PRIORITY_FREIGHT PRIORITY_OVERNIGHT SMART_POST STANDARD_OVERNIGHT)
|
24
|
+
|
25
|
+
# List of available Packaging Type
|
26
|
+
PACKAGING_TYPES = %w(FEDEX_10KG_BOX FEDEX_25KG_BOX FEDEX_BOX FEDEX_ENVELOPE FEDEX_PAK FEDEX_TUBE YOUR_PACKAGING)
|
27
|
+
|
28
|
+
# List of available DropOffTypes
|
29
|
+
DROP_OFF_TYPES = %w(BUSINESS_SERVICE_CENTER DROP_BOX REGULAR_PICKUP REQUEST_COURIER STATION)
|
30
|
+
|
31
|
+
# Clearance Brokerage Type
|
32
|
+
CLEARANCE_BROKERAGE_TYPE = %w(BROKER_INCLUSIVE BROKER_INCLUSIVE_NON_RESIDENT_IMPORTER BROKER_SELECT BROKER_SELECT_NON_RESIDENT_IMPORTER BROKER_UNASSIGNED)
|
33
|
+
|
34
|
+
# Recipient Custom ID Type
|
35
|
+
RECIPIENT_CUSTOM_ID_TYPE = %w(COMPANY INDIVIDUAL PASSPORT)
|
36
|
+
|
37
|
+
# List of available Payment Types
|
38
|
+
PAYMENT_TYPE = %w(RECIPIENT SENDER THIRD_PARTY)
|
39
|
+
|
40
|
+
# List of available Carrier Codes
|
41
|
+
CARRIER_CODES = %w(FDXC FDXE FDXG FDCC FXFR FXSP)
|
42
|
+
|
43
|
+
# In order to use Fedex rates API you must first apply for a developer(and later production keys),
|
44
|
+
# Visit {http://www.fedex.com/us/developer/ Fedex Developer Center} for more information about how to obtain your keys.
|
45
|
+
# @param [String] key - Fedex web service key
|
46
|
+
# @param [String] password - Fedex password
|
47
|
+
# @param [String] account_number - Fedex account_number
|
48
|
+
# @param [String] meter - Fedex meter number
|
49
|
+
# @param [String] mode - [development/production]
|
50
|
+
#
|
51
|
+
# return a FedexShip::Request::Base object
|
52
|
+
def initialize(credentials, options = {})
|
53
|
+
requires!(options, :shipper, :recipient, :packages)
|
54
|
+
@credentials = credentials
|
55
|
+
@shipper, @recipient, @packages, @service_type, @customs_clearance_detail, @debug = options[:shipper], options[:recipient], options[:packages], options[:service_type], options[:customs_clearance_detail], options[:debug]
|
56
|
+
@origin = options[:origin]
|
57
|
+
@debug = ENV['DEBUG'] == 'true'
|
58
|
+
@preferred_currency = options[:preferred_currency]
|
59
|
+
@shipping_options = options[:shipping_options] ||= {}
|
60
|
+
@commercial_invoice_options = options[:commercial_invoice_options] ||= {}
|
61
|
+
@payment_options = options[:payment_options] ||= {}
|
62
|
+
requires!(@payment_options, :type, :account_number, :name, :company, :phone_number, :country_code) if @payment_options.length > 0
|
63
|
+
if options.has_key?(:mps)
|
64
|
+
@mps = options[:mps]
|
65
|
+
requires!(@mps, :package_count, :total_weight, :sequence_number)
|
66
|
+
requires!(@mps, :master_tracking_id) if @mps.has_key?(:sequence_number) && @mps[:sequence_number].to_i >= 2
|
67
|
+
else
|
68
|
+
@mps = {}
|
69
|
+
end
|
70
|
+
if options.has_key?(:total_insured_value)
|
71
|
+
@total_insured_value = options[:total_insured_value]
|
72
|
+
requires!(@total_insured_value, :currency, :amount)
|
73
|
+
else
|
74
|
+
@total_insured_value = {}
|
75
|
+
end
|
76
|
+
# Expects hash with addr and port
|
77
|
+
if options[:http_proxy]
|
78
|
+
self.class.http_proxy options[:http_proxy][:host], options[:http_proxy][:port]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Sends post request to Fedex web service and parse the response.
|
83
|
+
# Implemented by each subclass
|
84
|
+
def process_request
|
85
|
+
raise NotImplementedError, "Override process_request in subclass"
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# Add web authentication detail information(key and password) to xml request
|
91
|
+
def add_web_authentication_detail(xml)
|
92
|
+
xml.WebAuthenticationDetail {
|
93
|
+
xml.UserCredential {
|
94
|
+
xml.Key @credentials.key
|
95
|
+
xml.Password @credentials.password
|
96
|
+
}
|
97
|
+
}
|
98
|
+
end
|
99
|
+
|
100
|
+
# Add Client Detail information(account_number and meter_number) to xml request
|
101
|
+
def add_client_detail(xml)
|
102
|
+
xml.ClientDetail {
|
103
|
+
xml.AccountNumber @credentials.account_number
|
104
|
+
xml.MeterNumber @credentials.meter
|
105
|
+
=begin
|
106
|
+
xml.Localization{
|
107
|
+
xml.LanguageCode 'en' # English
|
108
|
+
xml.LocaleCode 'us' # United States
|
109
|
+
}
|
110
|
+
=end
|
111
|
+
}
|
112
|
+
end
|
113
|
+
|
114
|
+
# Add Version to xml request, using the version identified in the subclass
|
115
|
+
def add_version(xml)
|
116
|
+
xml.Version {
|
117
|
+
xml.ServiceId service[:id]
|
118
|
+
xml.Major service[:version]
|
119
|
+
xml.Intermediate 0
|
120
|
+
xml.Minor 0
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
# Add information for shipments
|
125
|
+
def add_requested_shipment(xml)
|
126
|
+
xml.RequestedShipment {
|
127
|
+
xml.DropoffType @shipping_options[:drop_off_type] ||= "REGULAR_PICKUP"
|
128
|
+
xml.ServiceType service_type
|
129
|
+
xml.PackagingType @shipping_options[:packaging_type] ||= "YOUR_PACKAGING"
|
130
|
+
xml.PreferredCurrency @preferred_currency
|
131
|
+
add_shipper(xml)
|
132
|
+
add_recipient(xml)
|
133
|
+
add_shipping_charges_payment(xml)
|
134
|
+
add_customs_clearance(xml) if @customs_clearance_detail
|
135
|
+
xml.RateRequestTypes "ACCOUNT"
|
136
|
+
add_packages(xml)
|
137
|
+
}
|
138
|
+
end
|
139
|
+
|
140
|
+
# Add shipper to xml request
|
141
|
+
def add_shipper(xml)
|
142
|
+
xml.Shipper {
|
143
|
+
xml.Tins {
|
144
|
+
xml.TinType @shipper[:tin_type]
|
145
|
+
xml.Number @shipper[:tin_number]
|
146
|
+
}
|
147
|
+
xml.Contact {
|
148
|
+
xml.PersonName @shipper[:name]
|
149
|
+
xml.CompanyName @shipper[:company]
|
150
|
+
xml.PhoneNumber @shipper[:phone_number]
|
151
|
+
xml.EMailAddress @shipper[:email]
|
152
|
+
}
|
153
|
+
xml.Address {
|
154
|
+
Array(@shipper[:address]).take(2).each do |address_line|
|
155
|
+
xml.StreetLines address_line
|
156
|
+
end
|
157
|
+
xml.City @shipper[:city]
|
158
|
+
xml.StateOrProvinceCode @shipper[:state]
|
159
|
+
xml.PostalCode @shipper[:postal_code]
|
160
|
+
xml.CountryCode @shipper[:country_code]
|
161
|
+
}
|
162
|
+
}
|
163
|
+
end
|
164
|
+
|
165
|
+
# Add shipper to xml request
|
166
|
+
def add_origin(xml)
|
167
|
+
xml.Origin {
|
168
|
+
xml.Contact {
|
169
|
+
xml.PersonName @origin[:name]
|
170
|
+
xml.CompanyName @origin[:company]
|
171
|
+
xml.PhoneNumber @origin[:phone_number]
|
172
|
+
}
|
173
|
+
xml.Address {
|
174
|
+
Array(@origin[:address]).take(2).each do |address_line|
|
175
|
+
xml.StreetLines address_line
|
176
|
+
end
|
177
|
+
xml.City @origin[:city]
|
178
|
+
xml.StateOrProvinceCode @origin[:state]
|
179
|
+
xml.PostalCode @origin[:postal_code]
|
180
|
+
xml.CountryCode @origin[:country_code]
|
181
|
+
}
|
182
|
+
}
|
183
|
+
end
|
184
|
+
|
185
|
+
# Add recipient to xml request
|
186
|
+
def add_recipient(xml)
|
187
|
+
xml.Recipient {
|
188
|
+
if @recipient[:tin_type] && @recipient[:tin_number]
|
189
|
+
xml.Tins {
|
190
|
+
xml.TinType @recipient[:tin_type]
|
191
|
+
xml.Number @recipient[:tin_number]
|
192
|
+
}
|
193
|
+
end
|
194
|
+
xml.Contact {
|
195
|
+
xml.PersonName @recipient[:name]
|
196
|
+
xml.CompanyName @recipient[:company]
|
197
|
+
xml.PhoneNumber @recipient[:phone_number]
|
198
|
+
xml.EMailAddress @recipient[:email]
|
199
|
+
}
|
200
|
+
xml.Address {
|
201
|
+
Array(@recipient[:address]).take(2).each do |address_line|
|
202
|
+
xml.StreetLines address_line
|
203
|
+
end
|
204
|
+
xml.City @recipient[:city]
|
205
|
+
xml.StateOrProvinceCode @recipient[:state]
|
206
|
+
xml.PostalCode @recipient[:postal_code]
|
207
|
+
xml.CountryCode @recipient[:country_code]
|
208
|
+
}
|
209
|
+
}
|
210
|
+
end
|
211
|
+
|
212
|
+
# Add shipping charges to xml request
|
213
|
+
def add_shipping_charges_payment(xml)
|
214
|
+
xml.ShippingChargesPayment {
|
215
|
+
xml.PaymentType @payment_options[:type] || "SENDER"
|
216
|
+
xml.Payor {
|
217
|
+
if service[:version].to_i >= FedexShip::API_VERSION.to_i
|
218
|
+
xml.ResponsibleParty {
|
219
|
+
xml.AccountNumber @payment_options[:account_number] || @credentials.account_number
|
220
|
+
xml.Contact {
|
221
|
+
xml.PersonName @payment_options[:name] || @shipper[:name]
|
222
|
+
xml.CompanyName @payment_options[:company] || @shipper[:company]
|
223
|
+
xml.PhoneNumber @payment_options[:phone_number] || @shipper[:phone_number]
|
224
|
+
}
|
225
|
+
xml.Address {
|
226
|
+
xml.CountryCode @shipper[:country_code]
|
227
|
+
}
|
228
|
+
}
|
229
|
+
else
|
230
|
+
xml.AccountNumber @payment_options[:account_number] || @credentials.account_number
|
231
|
+
xml.CountryCode @payment_options[:country_code] || @shipper[:country_code]
|
232
|
+
end
|
233
|
+
}
|
234
|
+
}
|
235
|
+
end
|
236
|
+
|
237
|
+
# Add Master Tracking Id (for MPS Shipping Labels, this is required when requesting labels 2 through n)
|
238
|
+
def add_master_tracking_id(xml)
|
239
|
+
if @mps.has_key? :master_tracking_id
|
240
|
+
xml.MasterTrackingId {
|
241
|
+
xml.TrackingIdType @mps[:master_tracking_id][:tracking_id_type]
|
242
|
+
xml.TrackingNumber @mps[:master_tracking_id][:tracking_number]
|
243
|
+
}
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Add packages to xml request
|
248
|
+
def add_packages(xml)
|
249
|
+
add_master_tracking_id(xml) if @mps.has_key? :master_tracking_id
|
250
|
+
package_count = @packages.size
|
251
|
+
if @mps.has_key? :package_count
|
252
|
+
xml.PackageCount @mps[:package_count]
|
253
|
+
else
|
254
|
+
xml.PackageCount package_count
|
255
|
+
end
|
256
|
+
@packages.each do |package|
|
257
|
+
xml.RequestedPackageLineItems {
|
258
|
+
if @mps.has_key? :sequence_number
|
259
|
+
xml.SequenceNumber @mps[:sequence_number]
|
260
|
+
xml.GroupPackageCount 1
|
261
|
+
else
|
262
|
+
xml.GroupPackageCount 1
|
263
|
+
end
|
264
|
+
if package[:insured_value]
|
265
|
+
xml.InsuredValue {
|
266
|
+
xml.Currency package[:insured_value][:currency]
|
267
|
+
xml.Amount package[:insured_value][:amount]
|
268
|
+
}
|
269
|
+
end
|
270
|
+
xml.Weight {
|
271
|
+
xml.Units package[:weight][:units]
|
272
|
+
xml.Value package[:weight][:value]
|
273
|
+
}
|
274
|
+
if package[:dimensions]
|
275
|
+
xml.Dimensions {
|
276
|
+
xml.Length package[:dimensions][:length]
|
277
|
+
xml.Width package[:dimensions][:width]
|
278
|
+
xml.Height package[:dimensions][:height]
|
279
|
+
xml.Units package[:dimensions][:units]
|
280
|
+
}
|
281
|
+
end
|
282
|
+
add_customer_references(xml, package, @customs_clearance_detail[:commercial_invoice][:customer_references])
|
283
|
+
if package[:special_services_requested]
|
284
|
+
xml.SpecialServicesRequested {
|
285
|
+
if package[:special_services_requested][:special_service_types]
|
286
|
+
if package[:special_services_requested][:special_service_types].is_a? Array
|
287
|
+
package[:special_services_requested][:special_service_types].each do |type|
|
288
|
+
xml.SpecialServiceTypes type
|
289
|
+
end
|
290
|
+
else
|
291
|
+
xml.SpecialServiceTypes package[:special_services_requested][:special_service_types]
|
292
|
+
end
|
293
|
+
end
|
294
|
+
# Handle COD Options
|
295
|
+
if package[:special_services_requested][:cod_detail]
|
296
|
+
xml.CodDetail {
|
297
|
+
xml.CodCollectionAmount {
|
298
|
+
xml.Currency package[:special_services_requested][:cod_detail][:cod_collection_amount][:currency]
|
299
|
+
xml.Amount package[:special_services_requested][:cod_detail][:cod_collection_amount][:amount]
|
300
|
+
}
|
301
|
+
if package[:special_services_requested][:cod_detail][:add_transportation_charges]
|
302
|
+
xml.AddTransportationCharges package[:special_services_requested][:cod_detail][:add_transportation_charges]
|
303
|
+
end
|
304
|
+
xml.CollectionType package[:special_services_requested][:cod_detail][:collection_type]
|
305
|
+
xml.CodRecipient {
|
306
|
+
add_shipper(xml)
|
307
|
+
}
|
308
|
+
if package[:special_services_requested][:cod_detail][:reference_indicator]
|
309
|
+
xml.ReferenceIndicator package[:special_services_requested][:cod_detail][:reference_indicator]
|
310
|
+
end
|
311
|
+
}
|
312
|
+
end
|
313
|
+
# DangerousGoodsDetail goes here
|
314
|
+
if package[:special_services_requested][:dry_ice_weight]
|
315
|
+
xml.DryIceWeight {
|
316
|
+
xml.Units package[:special_services_requested][:dry_ice_weight][:units]
|
317
|
+
xml.Value package[:special_services_requested][:dry_ice_weight][:value]
|
318
|
+
}
|
319
|
+
end
|
320
|
+
if package[:special_services_requested][:signature_option_detail]
|
321
|
+
xml.SignatureOptionDetail {
|
322
|
+
xml.OptionType package[:special_services_requested][:signature_option_detail][:signature_option_type]
|
323
|
+
}
|
324
|
+
end
|
325
|
+
if package[:special_services_requested][:priority_alert_detail]
|
326
|
+
xml.PriorityAlertDetail package[:special_services_requested][:priority_alert_detail]
|
327
|
+
end
|
328
|
+
}
|
329
|
+
end
|
330
|
+
}
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def add_customer_references(xml, package, customer_references)
|
335
|
+
# customer_refrences is a legacy misspelling
|
336
|
+
if !customer_references.empty? && customer_references.is_a?(Array)
|
337
|
+
customer_references.each do |ref|
|
338
|
+
xml.CustomerReferences {
|
339
|
+
if ref.is_a?(Hash)
|
340
|
+
# :type can specify custom type:
|
341
|
+
#
|
342
|
+
# BILL_OF_LADING, CUSTOMER_REFERENCE, DEPARTMENT_NUMBER,
|
343
|
+
# ELECTRONIC_PRODUCT_CODE, INTRACOUNTRY_REGULATORY_REFERENCE,
|
344
|
+
# INVOICE_NUMBER, P_O_NUMBER, RMA_ASSOCIATION,
|
345
|
+
# SHIPMENT_INTEGRITY, STORE_NUMBER
|
346
|
+
xml.CustomerReferenceType ref[:customer_reference_type]
|
347
|
+
xml.Value ref[:value]
|
348
|
+
else
|
349
|
+
xml.CustomerReferenceType 'CUSTOMER_REFERENCE'
|
350
|
+
xml.Value ref
|
351
|
+
end
|
352
|
+
}
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
# Add customs clearance(for international shipments)
|
358
|
+
def add_customs_clearance(xml)
|
359
|
+
xml.CustomsClearanceDetail {
|
360
|
+
hash_to_xml(xml, @customs_clearance_detail)
|
361
|
+
# customer_references_hash = {}
|
362
|
+
# customer_references_hash[:customer_references] = @customs_clearance_detail[:commercial_invoice][:customer_references]
|
363
|
+
# add_customer_references(xml, customer_references_hash)
|
364
|
+
|
365
|
+
}
|
366
|
+
end
|
367
|
+
|
368
|
+
# Fedex Web Service Api
|
369
|
+
def api_url
|
370
|
+
@credentials.mode == "production" ? PRODUCTION_URL : TEST_URL
|
371
|
+
end
|
372
|
+
|
373
|
+
# Build xml Fedex Web Service request
|
374
|
+
# Implemented by each subclass
|
375
|
+
def build_xml
|
376
|
+
raise NotImplementedError, "Override build_xml in subclass"
|
377
|
+
end
|
378
|
+
|
379
|
+
# Build xml nodes dynamically from the hash keys and values
|
380
|
+
def hash_to_xml(xml, hash)
|
381
|
+
hash.each do |key, value|
|
382
|
+
key_s_down = key.to_s.downcase
|
383
|
+
if key_s_down.match(/^commodities_\d{1,}$/)
|
384
|
+
element = 'Commodities'
|
385
|
+
elsif key_s_down.match(/^masked_data_\d{1,}$/)
|
386
|
+
element = 'MaskedData'
|
387
|
+
else
|
388
|
+
element = camelize(key)
|
389
|
+
end
|
390
|
+
if value.is_a?(Hash)
|
391
|
+
xml.send element do |x|
|
392
|
+
hash_to_xml(x, value)
|
393
|
+
end
|
394
|
+
elsif value.is_a?(Array)
|
395
|
+
value.each do |v|
|
396
|
+
xml.send element do |x|
|
397
|
+
hash_to_xml(x, v)
|
398
|
+
end
|
399
|
+
end
|
400
|
+
else
|
401
|
+
xml.send element, value
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
|
406
|
+
# Parse response, convert keys to underscore symbols
|
407
|
+
def parse_response(response)
|
408
|
+
response = sanitize_response_keys(response.parsed_response)
|
409
|
+
end
|
410
|
+
|
411
|
+
# Recursively sanitizes the response object by cleaning up any hash keys.
|
412
|
+
def sanitize_response_keys(response)
|
413
|
+
if response.is_a?(Hash)
|
414
|
+
response.inject({}) {|result, (key, value)| result[underscorize(key).to_sym] = sanitize_response_keys(value); result}
|
415
|
+
elsif response.is_a?(Array)
|
416
|
+
response.collect {|result| sanitize_response_keys(result)}
|
417
|
+
else
|
418
|
+
response
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
def service
|
423
|
+
raise NotImplementedError,
|
424
|
+
"Override service in subclass: {:id => 'service', :version => 1}"
|
425
|
+
end
|
426
|
+
|
427
|
+
# Use GROUND_HOME_DELIVERY for shipments going to a residential address within the US.
|
428
|
+
def service_type
|
429
|
+
if @recipient[:residential].to_s =~ /true/i and @service_type =~ /GROUND/i and @recipient[:country_code] =~ /US/i
|
430
|
+
"GROUND_HOME_DELIVERY"
|
431
|
+
else
|
432
|
+
@service_type
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
# Successful request
|
437
|
+
def success?(response)
|
438
|
+
(!response[:rate_reply].nil? and %w{SUCCESS WARNING NOTE}.include? response[:rate_reply][:highest_severity])
|
439
|
+
end
|
440
|
+
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'fedex_ship/request/base'
|
2
|
+
require 'fedex_ship/request/logs_fedex'
|
3
|
+
|
4
|
+
module FedexShip
|
5
|
+
module Request
|
6
|
+
class Delete < LogsFedex
|
7
|
+
|
8
|
+
attr_reader :tracking_number
|
9
|
+
|
10
|
+
def initialize(credentials, options = {})
|
11
|
+
requires!(options, :tracking_number)
|
12
|
+
|
13
|
+
@tracking_number = options[:tracking_number]
|
14
|
+
@deletion_control = options[:deletion_control] || 'DELETE_ALL_PACKAGES'
|
15
|
+
@credentials = credentials
|
16
|
+
end
|
17
|
+
|
18
|
+
def process_request
|
19
|
+
@build_xml = build_xml
|
20
|
+
delete_ship_serv_log('Final XML Request : ' + @build_xml.to_s)
|
21
|
+
api_url_srv = api_url + "/ship"
|
22
|
+
delete_ship_serv_log('URL for API : ' + api_url_srv.to_s)
|
23
|
+
api_response = self.class.post(api_url_srv, :body => build_xml)
|
24
|
+
delete_ship_serv_log('API Response : ' + api_response.to_s)
|
25
|
+
puts api_response if @debug == true
|
26
|
+
response = parse_response(api_response)
|
27
|
+
unless success?(response)
|
28
|
+
delete_ship_serv_log('Successfully Done : ' + response.to_s)
|
29
|
+
error_message = if response[:envelope][:body][:shipment_reply]
|
30
|
+
[response[:envelope][:body][:shipment_reply][:notifications]].flatten.first[:message]
|
31
|
+
else
|
32
|
+
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n
|
33
|
+
--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
|
34
|
+
end rescue $1
|
35
|
+
raise RateError, error_message
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Build xml Fedex Web Service request
|
42
|
+
def build_xml
|
43
|
+
ns = "http://fedex.com/ws/ship/v21"
|
44
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
45
|
+
xml.Envelope("xmlns" => "http://fedex.com/ws/ship/v21") {
|
46
|
+
xml.parent.namespace = xml.parent.add_namespace_definition("soapenv", "http://schemas.xmlsoap.org/soap/envelope/")
|
47
|
+
xml['soapenv'].Header {}
|
48
|
+
xml['soapenv'].Body {
|
49
|
+
xml.DeleteShipmentRequest() {
|
50
|
+
add_web_authentication_detail(xml)
|
51
|
+
add_client_detail(xml)
|
52
|
+
add_version(xml)
|
53
|
+
xml.TrackingId {
|
54
|
+
xml.TrackingIdType 'FEDEX'
|
55
|
+
xml.TrackingNumber @tracking_number
|
56
|
+
}
|
57
|
+
xml.DeletionControl @deletion_control
|
58
|
+
}
|
59
|
+
}
|
60
|
+
}
|
61
|
+
end
|
62
|
+
builder.doc.root.to_xml
|
63
|
+
end
|
64
|
+
|
65
|
+
def service
|
66
|
+
{:id => 'ship', :version => "21"}
|
67
|
+
end
|
68
|
+
|
69
|
+
# Successful request
|
70
|
+
def success?(response)
|
71
|
+
response[:envelope][:body][:shipment_reply] &&
|
72
|
+
%w{SUCCESS WARNING NOTE}.include?(response[:envelope][:body][:shipment_reply][:highest_severity])
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|