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,94 @@
|
|
1
|
+
require 'fedex_ship/request/base'
|
2
|
+
require 'fedex_ship/request/logs_fedex'
|
3
|
+
|
4
|
+
module FedexShip
|
5
|
+
module Request
|
6
|
+
class Rate < LogsFedex
|
7
|
+
# Sends post request to Fedex web service and parse the response, a Rate object is created if the response is successful
|
8
|
+
def process_request
|
9
|
+
@build_xml = build_xml
|
10
|
+
rate_serv_log('Final XML Request : ' + @build_xml.to_s)
|
11
|
+
api_url_srv = api_url + "/rate"
|
12
|
+
rate_serv_log('URL for API : ' + api_url_srv.to_s)
|
13
|
+
api_response = self.class.post(api_url_srv, :body => @build_xml)
|
14
|
+
rate_serv_log('API Response : ' + api_response.to_s)
|
15
|
+
puts api_response if @debug
|
16
|
+
response = parse_response(api_response)
|
17
|
+
if success?(response)
|
18
|
+
rate_serv_log('Successfully Done : ' + response.to_s)
|
19
|
+
rate_reply_details = response[:envelope][:body][:rate_reply][:rate_reply_details] || []
|
20
|
+
rate_reply_details = [rate_reply_details] if rate_reply_details.is_a?(Hash)
|
21
|
+
|
22
|
+
rate_reply_details.map do |rate_reply|
|
23
|
+
rate_details = [rate_reply[:rated_shipment_details]].flatten.first[:shipment_rate_detail]
|
24
|
+
rate_details.merge!(service_type: rate_reply[:service_type])
|
25
|
+
rate_details.merge!(transit_time: rate_reply[:delivery_timestamp])
|
26
|
+
FedexShip::Rate.new(rate_details)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
error_message = if response[:envelope][:body][:rate_reply]
|
30
|
+
[response[:envelope][:body][:rate_reply][:notifications]].flatten.first[:message]
|
31
|
+
else
|
32
|
+
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
|
33
|
+
end rescue $1
|
34
|
+
raise RateError, error_message
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
# Add information for shipments
|
41
|
+
def add_requested_shipment(xml)
|
42
|
+
xml.RequestedShipment {
|
43
|
+
xml.DropoffType @shipping_options[:drop_off_type] ||= "REGULAR_PICKUP"
|
44
|
+
xml.ServiceType service_type if service_type
|
45
|
+
xml.PackagingType @shipping_options[:packaging_type] ||= "YOUR_PACKAGING"
|
46
|
+
xml.PreferredCurrency @preferred_currency
|
47
|
+
add_shipper(xml)
|
48
|
+
add_recipient(xml)
|
49
|
+
add_shipping_charges_payment(xml)
|
50
|
+
add_customs_clearance(xml) if @customs_clearance_detail
|
51
|
+
xml.RateRequestTypes "ACCOUNT"
|
52
|
+
add_packages(xml)
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add transite time options
|
57
|
+
def add_transit_time(xml)
|
58
|
+
xml.ReturnTransitAndCommit true
|
59
|
+
end
|
60
|
+
|
61
|
+
# Build xml Fedex Web Service request
|
62
|
+
def build_xml
|
63
|
+
ns = "http://fedex.com/ws/rate/v#{service[:version]}"
|
64
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
65
|
+
xml.Envelope("xmlns" => "http://fedex.com/ws/rate/v13") {
|
66
|
+
xml.parent.namespace = xml.parent.add_namespace_definition("soapenv", "http://schemas.xmlsoap.org/soap/envelope/")
|
67
|
+
xml['soapenv'].Header {}
|
68
|
+
xml['soapenv'].Body {
|
69
|
+
xml.RateRequest(:xmlns => ns) {
|
70
|
+
add_web_authentication_detail(xml)
|
71
|
+
add_client_detail(xml)
|
72
|
+
add_version(xml)
|
73
|
+
add_transit_time(xml)
|
74
|
+
add_requested_shipment(xml)
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
end
|
79
|
+
return builder.doc.root.to_xml
|
80
|
+
end
|
81
|
+
|
82
|
+
def service
|
83
|
+
{:id => 'crs', :version => "13"}
|
84
|
+
end
|
85
|
+
|
86
|
+
# Successful request
|
87
|
+
def success?(response)
|
88
|
+
response[:envelope][:body][:rate_reply] &&
|
89
|
+
%w{SUCCESS}.include?(response[:envelope][:body][:rate_reply][:highest_severity])
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'fedex_ship/request/base'
|
2
|
+
|
3
|
+
module FedexShip
|
4
|
+
module Request
|
5
|
+
class ServiceAvailability < Base
|
6
|
+
def initialize(credentials, options={})
|
7
|
+
requires!(options, :origin, :destination, :ship_date, :carrier_code)
|
8
|
+
|
9
|
+
@credentials = credentials
|
10
|
+
@origin = options[:origin]
|
11
|
+
@destination = options[:destination]
|
12
|
+
@ship_date = options[:ship_date]
|
13
|
+
@carrier_code = options[:carrier_code]
|
14
|
+
end
|
15
|
+
|
16
|
+
def process_request
|
17
|
+
api_response = self.class.post api_url, :body => build_xml
|
18
|
+
puts api_response if @debug
|
19
|
+
response = parse_response(api_response)
|
20
|
+
if success?(response)
|
21
|
+
success_response(api_response, response)
|
22
|
+
else
|
23
|
+
failure_response(api_response, response)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_xml
|
28
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
29
|
+
xml.ServiceAvailabilityRequest(:xmlns => "http://fedex.com/ws/packagemovementinformationservice/v#{service[:version]}"){
|
30
|
+
add_web_authentication_detail(xml)
|
31
|
+
add_client_detail(xml)
|
32
|
+
add_version(xml)
|
33
|
+
add_origin(xml)
|
34
|
+
add_destination(xml)
|
35
|
+
add_other_details(xml)
|
36
|
+
}
|
37
|
+
end
|
38
|
+
builder.doc.root.to_xml
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_origin(xml)
|
42
|
+
xml.Origin{
|
43
|
+
xml.PostalCode @origin[:postal_code]
|
44
|
+
xml.CountryCode @origin[:country_code]
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def add_destination(xml)
|
49
|
+
xml.Destination{
|
50
|
+
xml.PostalCode @destination[:postal_code]
|
51
|
+
xml.CountryCode @destination[:country_code]
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_other_details(xml)
|
56
|
+
xml.ShipDate @ship_date
|
57
|
+
xml.CarrierCode @carrier_code
|
58
|
+
end
|
59
|
+
|
60
|
+
# Callback used after a failed shipment response.
|
61
|
+
def failure_response(api_response, response)
|
62
|
+
error_message = if response[:service_availability_reply]
|
63
|
+
[response[:service_availability_reply][:notifications]].flatten.first[:message]
|
64
|
+
else
|
65
|
+
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
|
66
|
+
end rescue $1
|
67
|
+
raise RateError, error_message
|
68
|
+
end
|
69
|
+
|
70
|
+
# Callback used after a successful shipment response.
|
71
|
+
def success_response(api_response, response)
|
72
|
+
@response_details = response[:service_availability_reply]
|
73
|
+
end
|
74
|
+
|
75
|
+
def service
|
76
|
+
{ :id => 'pmis', :version => FedexShip::SERVICE_AVAILABILITY_API_VERSION }
|
77
|
+
end
|
78
|
+
|
79
|
+
# Successful request
|
80
|
+
def success?(response)
|
81
|
+
response[:service_availability_reply] &&
|
82
|
+
%w{SUCCESS WARNING NOTE}.include?(response[:service_availability_reply][:highest_severity])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,249 @@
|
|
1
|
+
require 'fedex_ship/request/base'
|
2
|
+
require 'fedex_ship/request/logs_fedex'
|
3
|
+
|
4
|
+
module FedexShip
|
5
|
+
module Request
|
6
|
+
class Shipment < LogsFedex
|
7
|
+
attr_reader :response_details
|
8
|
+
|
9
|
+
def initialize(credentials, options = {})
|
10
|
+
super
|
11
|
+
requires!(options, :service_type)
|
12
|
+
# Label specification is required even if we're not using it.
|
13
|
+
@label_specification = {
|
14
|
+
:label_format_type => 'COMMON2D',
|
15
|
+
:image_type => 'PDF',
|
16
|
+
:label_stock_type => 'PAPER_LETTER'
|
17
|
+
}
|
18
|
+
@label_specification.merge! options[:label_specification] if options[:label_specification]
|
19
|
+
@customer_specified_detail = options[:customer_specified_detail] if options[:customer_specified_detail]
|
20
|
+
end
|
21
|
+
|
22
|
+
# Sends post request to Fedex web service and parse the response.
|
23
|
+
# A label file is created with the label at the specified location.
|
24
|
+
# The parsed Fedex response is available in #response_details
|
25
|
+
# e.g. response_details[:completed_shipment_detail][:completed_package_details][:tracking_ids][:tracking_number]
|
26
|
+
def process_request
|
27
|
+
@build_xml = build_xml
|
28
|
+
ship_serv_log('Final XML Request : ' + @build_xml.to_s)
|
29
|
+
api_url_srv = api_url + "/ship"
|
30
|
+
ship_serv_log('URL for API : ' + api_url_srv.to_s)
|
31
|
+
api_response = self.class.post(api_url_srv, :body => @build_xml)
|
32
|
+
ship_serv_log('API Response : ' + api_response.to_s)
|
33
|
+
puts api_response if @debug
|
34
|
+
response = parse_response(api_response)
|
35
|
+
if success?(response)
|
36
|
+
ship_serv_log('Successfully Done : ' + response.to_s)
|
37
|
+
success_response(api_response, response)
|
38
|
+
else
|
39
|
+
failure_response(api_response, response)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Add information for shipments
|
46
|
+
def add_requested_shipment(xml)
|
47
|
+
xml.RequestedShipment {
|
48
|
+
xml.ShipTimestamp @shipping_options[:ship_timestamp] ||= Time.now.utc.iso8601(2)
|
49
|
+
xml.DropoffType @shipping_options[:drop_off_type] ||= "REGULAR_PICKUP"
|
50
|
+
xml.ServiceType service_type
|
51
|
+
xml.PackagingType @shipping_options[:packaging_type] ||= "YOUR_PACKAGING"
|
52
|
+
add_total_weight(xml) if @mps.has_key? :total_weight
|
53
|
+
add_total_insured_value(xml) if @total_insured_value.has_key? :amount
|
54
|
+
add_shipper(xml)
|
55
|
+
add_origin(xml) if @origin
|
56
|
+
add_recipient(xml)
|
57
|
+
add_shipping_charges_payment(xml)
|
58
|
+
add_special_services(xml) if @shipping_options[:return_reason] || @shipping_options[:cod] || @shipping_options[:event_notification] || @shipping_options[:saturday_delivery]
|
59
|
+
add_customs_clearance(xml) if @customs_clearance_detail
|
60
|
+
add_custom_components(xml)
|
61
|
+
add_shipping_document_specification(xml)
|
62
|
+
xml.RateRequestTypes "ACCOUNT"
|
63
|
+
add_packages(xml)
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
def add_total_weight(xml)
|
68
|
+
if @mps.has_key? :total_weight
|
69
|
+
xml.TotalWeight {
|
70
|
+
xml.Units @packages[0][:weight][:units]
|
71
|
+
xml.Value @mps[:total_weight]
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def add_total_insured_value(xml)
|
77
|
+
if @total_insured_value[:amount] && @total_insured_value[:currency]
|
78
|
+
xml.TotalInsuredValue {
|
79
|
+
xml.Currency @total_insured_value[:currency]
|
80
|
+
xml.Amount @total_insured_value[:amount]
|
81
|
+
}
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Hook that can be used to add custom parts.
|
86
|
+
def add_custom_components(xml)
|
87
|
+
add_label_specification xml
|
88
|
+
end
|
89
|
+
|
90
|
+
# Add the label specification
|
91
|
+
def add_label_specification(xml)
|
92
|
+
xml.LabelSpecification {
|
93
|
+
xml.LabelFormatType @label_specification[:label_format_type]
|
94
|
+
xml.ImageType @label_specification[:image_type]
|
95
|
+
xml.LabelStockType @label_specification[:label_stock_type]
|
96
|
+
xml.CustomerSpecifiedDetail {hash_to_xml(xml, @customer_specified_detail)} if @customer_specified_detail
|
97
|
+
|
98
|
+
if @label_specification[:printed_label_origin] && @label_specification[:printed_label_origin][:address]
|
99
|
+
xml.PrintedLabelOrigin {
|
100
|
+
xml.Contact {
|
101
|
+
xml.PersonName @label_specification[:printed_label_origin][:address][:name]
|
102
|
+
xml.CompanyName @label_specification[:printed_label_origin][:address][:company]
|
103
|
+
xml.PhoneNumber @label_specification[:printed_label_origin][:address][:phone_number]
|
104
|
+
}
|
105
|
+
xml.Address {
|
106
|
+
Array(@label_specification[:printed_label_origin][:address][:address]).each do |address_line|
|
107
|
+
xml.StreetLines address_line
|
108
|
+
end
|
109
|
+
xml.City @label_specification[:printed_label_origin][:address][:city]
|
110
|
+
xml.StateOrProvinceCode @label_specification[:printed_label_origin][:address][:state]
|
111
|
+
xml.PostalCode @label_specification[:printed_label_origin][:address][:postal_code]
|
112
|
+
xml.CountryCode @label_specification[:printed_label_origin][:address][:country_code]
|
113
|
+
}
|
114
|
+
}
|
115
|
+
end
|
116
|
+
}
|
117
|
+
end
|
118
|
+
|
119
|
+
def add_shipping_document_specification(xml)
|
120
|
+
xml.ShippingDocumentSpecification {
|
121
|
+
xml.ShippingDocumentTypes "COMMERCIAL_INVOICE"
|
122
|
+
xml.CommercialInvoiceDetail {
|
123
|
+
xml.Format {
|
124
|
+
xml.ImageType @commercial_invoice_options[:commercial_invoice_detail][:format][:image_type]
|
125
|
+
xml.StockType @commercial_invoice_options[:commercial_invoice_detail][:format][:stock_type]
|
126
|
+
xml.ProvideInstructions "1"
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
def add_special_services(xml)
|
133
|
+
xml.SpecialServicesRequested {
|
134
|
+
|
135
|
+
@shipping_options[:special_services].each do |service|
|
136
|
+
xml.SpecialServiceTypes service
|
137
|
+
end
|
138
|
+
|
139
|
+
=begin
|
140
|
+
if @shipping_options[:return_reason]
|
141
|
+
xml.SpecialServiceTypes "RETURN_SHIPMENT"
|
142
|
+
elsif @shipping_options[:cod]
|
143
|
+
xml.SpecialServiceTypes "COD"
|
144
|
+
elsif @shipping_options[:event_notification]
|
145
|
+
xml.SpecialServiceTypes "EVENT_NOTIFICATION"
|
146
|
+
elsif @shipping_options[:saturday_delivery]
|
147
|
+
xml.SpecialServiceTypes "SATURDAY_DELIVERY"
|
148
|
+
end
|
149
|
+
=end
|
150
|
+
|
151
|
+
if @shipping_options[:return_reason]
|
152
|
+
xml.ReturnShipmentDetail {
|
153
|
+
xml.ReturnType "PRINT_RETURN_LABEL"
|
154
|
+
xml.Rma {
|
155
|
+
xml.Reason "#{@shipping_options[:return_reason]}"
|
156
|
+
}
|
157
|
+
}
|
158
|
+
end
|
159
|
+
|
160
|
+
if @shipping_options[:cod]
|
161
|
+
xml.CodDetail {
|
162
|
+
xml.CodCollectionAmount {
|
163
|
+
xml.Currency @shipping_options[:cod][:currency].upcase if @shipping_options[:cod][:currency]
|
164
|
+
xml.Amount @shipping_options[:cod][:amount] if @shipping_options[:cod][:amount]
|
165
|
+
}
|
166
|
+
xml.CollectionType @shipping_options[:cod][:collection_type] if @shipping_options[:cod][:collection_type]
|
167
|
+
}
|
168
|
+
# add_shipping_document_specification
|
169
|
+
end
|
170
|
+
|
171
|
+
if @shipping_options[:event_notification]
|
172
|
+
xml.EventNotificationDetail {
|
173
|
+
xml.AggregationType "PER_SHIPMENT"
|
174
|
+
xml.PersonalMessage @shipping_options[:event_notification][:personal_message]
|
175
|
+
@shipping_options[:event_notification][:email_address].each_with_index do |email, index|
|
176
|
+
xml.EventNotifications {
|
177
|
+
xml.Role @shipping_options[:event_notification][:role][index].upcase
|
178
|
+
@shipping_options[:event_notification][:events].each do |event|
|
179
|
+
xml.Events event
|
180
|
+
end
|
181
|
+
xml.NotificationDetail {
|
182
|
+
xml.NotificationType "EMAIL"
|
183
|
+
xml.EmailDetail {
|
184
|
+
xml.EmailAddress email
|
185
|
+
}
|
186
|
+
xml.Localization {
|
187
|
+
xml.LanguageCode "EN"
|
188
|
+
}
|
189
|
+
}
|
190
|
+
xml.FormatSpecification {
|
191
|
+
xml.Type @shipping_options[:event_notification][:type]
|
192
|
+
}
|
193
|
+
}
|
194
|
+
end
|
195
|
+
}
|
196
|
+
# add_shipping_document_specification
|
197
|
+
end
|
198
|
+
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
# Callback used after a failed shipment response.
|
203
|
+
def failure_response(api_response, response)
|
204
|
+
error_message = if response[:envelope][:body][:process_shipment_reply]
|
205
|
+
[response[:envelope][:body][:process_shipment_reply][:notifications]].flatten.first[:message]
|
206
|
+
else
|
207
|
+
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
|
208
|
+
end rescue $1
|
209
|
+
raise RateError, error_message
|
210
|
+
end
|
211
|
+
|
212
|
+
# Callback used after a successful shipment response.
|
213
|
+
def success_response(api_response, response)
|
214
|
+
@response_details = response[:envelope][:body][:process_shipment_reply]
|
215
|
+
end
|
216
|
+
|
217
|
+
# Build xml Fedex Web Service request
|
218
|
+
def build_xml
|
219
|
+
ns = "http://fedex.com/ws/ship/v21"
|
220
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
221
|
+
xml.Envelope("xmlns" => "http://fedex.com/ws/ship/v21") {
|
222
|
+
xml.parent.namespace = xml.parent.add_namespace_definition("soapenv", "http://schemas.xmlsoap.org/soap/envelope/")
|
223
|
+
xml['soapenv'].Header {}
|
224
|
+
xml['soapenv'].Body {
|
225
|
+
xml.ProcessShipmentRequest() {
|
226
|
+
add_web_authentication_detail(xml)
|
227
|
+
add_client_detail(xml)
|
228
|
+
add_version(xml)
|
229
|
+
add_requested_shipment(xml)
|
230
|
+
}
|
231
|
+
}
|
232
|
+
}
|
233
|
+
end
|
234
|
+
builder.doc.root.to_xml
|
235
|
+
end
|
236
|
+
|
237
|
+
def service
|
238
|
+
{:id => 'ship', :version => "21"}
|
239
|
+
end
|
240
|
+
|
241
|
+
# Successful request
|
242
|
+
def success?(response)
|
243
|
+
response[:envelope][:body][:process_shipment_reply] &&
|
244
|
+
%w{SUCCESS WARNING NOTE}.include?(response[:envelope][:body][:process_shipment_reply][:highest_severity])
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'fedex_ship/request/base'
|
2
|
+
require 'fedex_ship/tracking_information'
|
3
|
+
require 'fedex_ship/request/logs_fedex'
|
4
|
+
|
5
|
+
module FedexShip
|
6
|
+
module Request
|
7
|
+
class TrackingInformation < LogsFedex
|
8
|
+
|
9
|
+
attr_reader :package_type, :package_id
|
10
|
+
|
11
|
+
def initialize(credentials, options = {})
|
12
|
+
requires!(options, :package_type, :package_id) unless options.has_key?(:tracking_number)
|
13
|
+
|
14
|
+
@package_id = options[:package_id] || options.delete(:tracking_number)
|
15
|
+
@package_type = options[:package_type] || "TRACKING_NUMBER_OR_DOORTAG"
|
16
|
+
@credentials = credentials
|
17
|
+
|
18
|
+
# Optional
|
19
|
+
@include_detailed_scans = options[:include_detailed_scans] || true
|
20
|
+
@uuid = options[:uuid]
|
21
|
+
@paging_token = options[:paging_token]
|
22
|
+
|
23
|
+
unless package_type_valid?
|
24
|
+
raise "Unknown package type '#{package_type}'"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def process_request
|
29
|
+
@build_xml = build_xml
|
30
|
+
track_serv_log('Final XML Request : ' + @build_xml.to_s)
|
31
|
+
api_url_srv = api_url + "/track"
|
32
|
+
track_serv_log('URL for API : ' + api_url_srv.to_s)
|
33
|
+
api_response = self.class.post(api_url_srv, :body => build_xml)
|
34
|
+
track_serv_log('API Response : ' + api_response.to_s)
|
35
|
+
puts api_response if @debug == true
|
36
|
+
response = parse_response(api_response)
|
37
|
+
|
38
|
+
if success?(response)
|
39
|
+
track_serv_log('Successfully Done : ' + response.to_s)
|
40
|
+
options = response[:envelope][:body][:track_reply][:completed_track_details][:track_details]
|
41
|
+
|
42
|
+
if response[:envelope][:body][:track_reply][:completed_track_details][:duplicate_waybill].downcase == 'true'
|
43
|
+
shipments = []
|
44
|
+
[options].flatten.map do |details|
|
45
|
+
options = {:tracking_number => @package_id, :uuid => details[:tracking_number_unique_identifier]}
|
46
|
+
shipments << Request::TrackingInformation.new(@credentials, options).process_request
|
47
|
+
end
|
48
|
+
shipments.flatten
|
49
|
+
elsif [options[:notification]].flatten.first[:severity] == "SUCCESS"
|
50
|
+
[options].flatten.map do |details|
|
51
|
+
FedexShip::TrackingInformation.new(details)
|
52
|
+
end
|
53
|
+
else
|
54
|
+
error_message = if response[:envelope][:body][:track_reply]
|
55
|
+
[options[:notification]].flatten.first[:message]
|
56
|
+
end
|
57
|
+
raise RateError, error_message
|
58
|
+
end
|
59
|
+
else
|
60
|
+
error_message = if response[:envelope][:body][:track_reply]
|
61
|
+
response[:envelope][:body][:track_reply][:notifications][:message]
|
62
|
+
else
|
63
|
+
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
|
64
|
+
end rescue $1
|
65
|
+
raise RateError, error_message
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# Build xml Fedex Web Service request
|
72
|
+
def build_xml
|
73
|
+
ns = "http://fedex.com/ws/track/v16"
|
74
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
75
|
+
xml.Envelope("xmlns" => "http://fedex.com/ws/track/v16") {
|
76
|
+
xml.parent.namespace = xml.parent.add_namespace_definition("soapenv", "http://schemas.xmlsoap.org/soap/envelope/")
|
77
|
+
xml['soapenv'].Header {}
|
78
|
+
xml['soapenv'].Body {
|
79
|
+
xml.TrackRequest() {
|
80
|
+
add_web_authentication_detail(xml)
|
81
|
+
add_client_detail(xml)
|
82
|
+
add_version(xml)
|
83
|
+
add_package_identifier(xml)
|
84
|
+
xml.TrackingNumberUniqueIdentifier @uuid if @uuid
|
85
|
+
xml.PagingToken @paging_token if @paging_token
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
89
|
+
end
|
90
|
+
builder.doc.root.to_xml
|
91
|
+
end
|
92
|
+
|
93
|
+
def service
|
94
|
+
{:id => 'trck', :version => "16"}
|
95
|
+
end
|
96
|
+
|
97
|
+
def add_package_identifier(xml)
|
98
|
+
xml.SelectionDetails {
|
99
|
+
xml.CarrierCode "FDXE"
|
100
|
+
xml.PackageIdentifier {
|
101
|
+
xml.Type package_type
|
102
|
+
xml.Value package_id
|
103
|
+
}
|
104
|
+
}
|
105
|
+
end
|
106
|
+
|
107
|
+
# Successful request
|
108
|
+
def success?(response)
|
109
|
+
response[:envelope][:body][:track_reply] &&
|
110
|
+
%w{SUCCESS WARNING NOTE}.include?(response[:envelope][:body][:track_reply][:highest_severity])
|
111
|
+
end
|
112
|
+
|
113
|
+
def package_type_valid?
|
114
|
+
FedexShip::TrackingInformation::PACKAGE_IDENTIFIER_TYPES.include? package_type
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|