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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +15 -0
  3. data/.idea/.rakeTasks +7 -0
  4. data/.idea/fedex_ship-0.1.0.iml +22 -0
  5. data/.idea/misc.xml +7 -0
  6. data/.idea/modules.xml +8 -0
  7. data/.idea/vcs.xml +6 -0
  8. data/.idea/workspace.xml +56 -0
  9. data/.rspec +2 -0
  10. data/Gemfile +5 -0
  11. data/Rakefile +7 -0
  12. data/Readme.md +496 -0
  13. data/fedex_ship.gemspec +28 -0
  14. data/lib/fedex_ship.rb +55 -0
  15. data/lib/fedex_ship/address.rb +31 -0
  16. data/lib/fedex_ship/credentials.rb +26 -0
  17. data/lib/fedex_ship/document.rb +51 -0
  18. data/lib/fedex_ship/ground_manifest.rb +25 -0
  19. data/lib/fedex_ship/helpers.rb +20 -0
  20. data/lib/fedex_ship/label.rb +71 -0
  21. data/lib/fedex_ship/rate.rb +38 -0
  22. data/lib/fedex_ship/request/address.rb +97 -0
  23. data/lib/fedex_ship/request/base.rb +443 -0
  24. data/lib/fedex_ship/request/delete.rb +76 -0
  25. data/lib/fedex_ship/request/document.rb +45 -0
  26. data/lib/fedex_ship/request/ground_close.rb +73 -0
  27. data/lib/fedex_ship/request/label.rb +29 -0
  28. data/lib/fedex_ship/request/logs_fedex.rb +74 -0
  29. data/lib/fedex_ship/request/pickup.rb +135 -0
  30. data/lib/fedex_ship/request/pickup_availability.rb +102 -0
  31. data/lib/fedex_ship/request/rate.rb +94 -0
  32. data/lib/fedex_ship/request/service_availability.rb +86 -0
  33. data/lib/fedex_ship/request/shipment.rb +249 -0
  34. data/lib/fedex_ship/request/tracking_information.rb +119 -0
  35. data/lib/fedex_ship/shipment.rb +115 -0
  36. data/lib/fedex_ship/tracking_information.rb +54 -0
  37. data/lib/fedex_ship/tracking_information/event.rb +24 -0
  38. data/lib/fedex_ship/version.rb +6 -0
  39. data/spec/config/fedex_credentials.example.yml +13 -0
  40. data/spec/lib/fedex_ship/address_spec.rb +59 -0
  41. data/spec/lib/fedex_ship/delete_spec.rb +26 -0
  42. data/spec/lib/fedex_ship/document_spec.rb +177 -0
  43. data/spec/lib/fedex_ship/ground_close_spec.rb +42 -0
  44. data/spec/lib/fedex_ship/label_spec.rb +73 -0
  45. data/spec/lib/fedex_ship/pickup_availability_spec.rb +19 -0
  46. data/spec/lib/fedex_ship/pickup_spec.rb +32 -0
  47. data/spec/lib/fedex_ship/rate_spec.rb +216 -0
  48. data/spec/lib/fedex_ship/service_availability_spec.rb +20 -0
  49. data/spec/lib/fedex_ship/shipment_spec.rb +86 -0
  50. data/spec/lib/fedex_ship/track_spec.rb +67 -0
  51. data/spec/spec_helper.rb +12 -0
  52. data/spec/support/credentials.rb +15 -0
  53. data/spec/support/vcr.rb +14 -0
  54. 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