fedex 3.6.1 → 3.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Readme.md +155 -2
- data/fedex.gemspec +4 -4
- data/lib/fedex/ground_manifest.rb +25 -0
- data/lib/fedex/label.rb +21 -6
- data/lib/fedex/rate.rb +3 -2
- data/lib/fedex/request/base.rb +41 -5
- data/lib/fedex/request/delete.rb +62 -0
- data/lib/fedex/request/ground_close.rb +73 -0
- data/lib/fedex/request/pickup.rb +121 -0
- data/lib/fedex/request/pickup_availability.rb +89 -0
- data/lib/fedex/request/rate.rb +7 -0
- data/lib/fedex/request/service_availability.rb +86 -0
- data/lib/fedex/request/shipment.rb +31 -7
- data/lib/fedex/request/tracking_information.rb +11 -2
- data/lib/fedex/shipment.rb +47 -5
- data/lib/fedex/tracking_information.rb +2 -1
- data/lib/fedex/tracking_information/event.rb +5 -2
- data/lib/fedex/version.rb +3 -1
- data/spec/lib/fedex/address_spec.rb +4 -4
- data/spec/lib/fedex/delete_spec.rb +26 -0
- data/spec/lib/fedex/document_spec.rb +9 -9
- data/spec/lib/fedex/ground_close_spec.rb +42 -0
- data/spec/lib/fedex/label_spec.rb +4 -4
- data/spec/lib/fedex/pickup_availability_spec.rb +19 -0
- data/spec/lib/fedex/pickup_spec.rb +32 -0
- data/spec/lib/fedex/rate_spec.rb +14 -10
- data/spec/lib/fedex/service_availability_spec.rb +20 -0
- data/spec/lib/fedex/shipment_spec.rb +7 -2
- data/spec/lib/fedex/track_spec.rb +21 -6
- data/spec/spec_helper.rb +3 -0
- data/spec/support/vcr.rb +0 -1
- metadata +46 -19
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'fedex/request/base'
|
2
|
+
require 'fedex/ground_manifest'
|
3
|
+
|
4
|
+
module Fedex
|
5
|
+
module Request
|
6
|
+
class GroundClose < Base
|
7
|
+
|
8
|
+
attr_reader :up_to_time, :filename
|
9
|
+
|
10
|
+
def initialize(credentials, options={})
|
11
|
+
requires!(options, :up_to_time)
|
12
|
+
|
13
|
+
@credentials = credentials
|
14
|
+
@up_to_time = options[:up_to_time]
|
15
|
+
@filename = options[:filename]
|
16
|
+
@debug = ENV['DEBUG'] == 'true'
|
17
|
+
end
|
18
|
+
|
19
|
+
def process_request
|
20
|
+
api_response = self.class.post(api_url, :body => build_xml)
|
21
|
+
puts api_response if @debug == true
|
22
|
+
response = parse_response(api_response)
|
23
|
+
if success?(response)
|
24
|
+
success_response(response)
|
25
|
+
else
|
26
|
+
error_message = if response[:ground_close_reply]
|
27
|
+
[response[:ground_close_reply][:notifications]].flatten.first[:message]
|
28
|
+
else
|
29
|
+
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n
|
30
|
+
--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
|
31
|
+
end rescue $1
|
32
|
+
raise RateError, error_message
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def success_response(response)
|
39
|
+
manifest_details = {
|
40
|
+
:filename => filename,
|
41
|
+
:manifest => response[:ground_close_reply][:manifest]
|
42
|
+
}
|
43
|
+
manifest = Fedex::GroundManifest.new(manifest_details)
|
44
|
+
puts "manifest written to #{filename}" if @debug == true
|
45
|
+
manifest
|
46
|
+
end
|
47
|
+
|
48
|
+
# Build xml Fedex Web Service request
|
49
|
+
def build_xml
|
50
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
51
|
+
xml.GroundCloseRequest(:xmlns => "http://fedex.com/ws/close/v2"){
|
52
|
+
add_web_authentication_detail(xml)
|
53
|
+
add_client_detail(xml)
|
54
|
+
add_version(xml)
|
55
|
+
|
56
|
+
xml.TimeUpToWhichShipmentsAreToBeClosed up_to_time.utc.iso8601(2)
|
57
|
+
}
|
58
|
+
end
|
59
|
+
builder.doc.root.to_xml
|
60
|
+
end
|
61
|
+
|
62
|
+
def service
|
63
|
+
{ :id => 'clos', :version => '2' }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Successful request
|
67
|
+
def success?(response)
|
68
|
+
response[:ground_close_reply] &&
|
69
|
+
%w{SUCCESS WARNING NOTE}.include?(response[:ground_close_reply][:highest_severity])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'fedex/request/base'
|
2
|
+
|
3
|
+
module Fedex
|
4
|
+
module Request
|
5
|
+
class Pickup < Base
|
6
|
+
def initialize(credentials, options={})
|
7
|
+
requires!(options, :packages, :ready_timestamp, :close_time, :carrier_code, :country_relationship)
|
8
|
+
@debug = ENV['DEBUG'] == 'true'
|
9
|
+
|
10
|
+
@credentials = credentials
|
11
|
+
@packages = options[:packages]
|
12
|
+
@ready_timestamp = options[:ready_timestamp]
|
13
|
+
@close_time = options[:close_time]
|
14
|
+
@carrier_code = options[:carrier_code]
|
15
|
+
@remarks = options[:remarks] if options[:remarks]
|
16
|
+
@pickup_location = options[:pickup_location]
|
17
|
+
@commodity_description = options[:commodity_description] if options[:commodity_description]
|
18
|
+
@country_relationship = options[:country_relationship]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Sends post request to Fedex web service and parse the response, a Pickup object is created if the response is successful
|
22
|
+
def process_request
|
23
|
+
api_response = self.class.post(api_url, :body => build_xml)
|
24
|
+
puts api_response if @debug
|
25
|
+
response = parse_response(api_response)
|
26
|
+
if success?(response)
|
27
|
+
success_response(api_response, response)
|
28
|
+
else
|
29
|
+
failure_response(api_response, response)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# Build xml Fedex Web Service request
|
36
|
+
def build_xml
|
37
|
+
ns = "http://fedex.com/ws/pickup/v#{service[:version]}"
|
38
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
39
|
+
xml.CreatePickupRequest(:xmlns => ns) {
|
40
|
+
add_web_authentication_detail(xml)
|
41
|
+
add_client_detail(xml)
|
42
|
+
add_version(xml)
|
43
|
+
add_origin_detail(xml)
|
44
|
+
add_package_details(xml)
|
45
|
+
}
|
46
|
+
end
|
47
|
+
builder.doc.root.to_xml
|
48
|
+
end
|
49
|
+
|
50
|
+
def service
|
51
|
+
{ :id => 'pickup', :version => Fedex::PICKUP_API_VERSION }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add shipper to xml request
|
55
|
+
def add_origin_detail(xml)
|
56
|
+
xml.OriginDetail {
|
57
|
+
if @pickup_location
|
58
|
+
xml.UseAccountAddress false
|
59
|
+
add_pickup_location(xml)
|
60
|
+
else
|
61
|
+
xml.UseAccountAddress true
|
62
|
+
end
|
63
|
+
xml.ReadyTimestamp @ready_timestamp
|
64
|
+
xml.CompanyCloseTime @close_time.strftime("%H:%M:%S")
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_package_details(xml)
|
69
|
+
xml.PackageCount @packages[:count]
|
70
|
+
xml.TotalWeight {
|
71
|
+
xml.Units @packages[:weight][:units]
|
72
|
+
xml.Value @packages[:weight][:value]
|
73
|
+
}
|
74
|
+
xml.CarrierCode @carrier_code
|
75
|
+
xml.Remarks @remarks if @remarks
|
76
|
+
xml.CommodityDescription @commodity_description if @commodity_description
|
77
|
+
xml.CountryRelationship @country_relationship
|
78
|
+
end
|
79
|
+
|
80
|
+
def add_pickup_location(xml)
|
81
|
+
xml.PickupLocation {
|
82
|
+
xml.Contact {
|
83
|
+
xml.PersonName @pickup_location[:name]
|
84
|
+
xml.CompanyName @pickup_location[:company]
|
85
|
+
xml.PhoneNumber @pickup_location[:phone_number]
|
86
|
+
}
|
87
|
+
xml.Address {
|
88
|
+
Array(@pickup_location[:address]).take(2).each do |address_line|
|
89
|
+
xml.StreetLines address_line
|
90
|
+
end
|
91
|
+
xml.City @pickup_location[:city]
|
92
|
+
xml.StateOrProvinceCode @pickup_location[:state]
|
93
|
+
xml.PostalCode @pickup_location[:postal_code]
|
94
|
+
xml.CountryCode @pickup_location[:country_code]
|
95
|
+
}
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
# Callback used after a failed pickup response.
|
100
|
+
def failure_response(api_response, response)
|
101
|
+
error_message = if response[:create_pickup_reply]
|
102
|
+
[response[:create_pickup_reply][:notifications]].flatten.first[:message]
|
103
|
+
else
|
104
|
+
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{Array(api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"]).join("\n--")}"
|
105
|
+
end rescue $1
|
106
|
+
raise RateError, error_message
|
107
|
+
end
|
108
|
+
|
109
|
+
# Callback used after a successful pickup response.
|
110
|
+
def success_response(api_response, response)
|
111
|
+
@response_details = response[:create_pickup_reply]
|
112
|
+
end
|
113
|
+
|
114
|
+
# Successful request
|
115
|
+
def success?(response)
|
116
|
+
response[:create_pickup_reply] &&
|
117
|
+
%w{SUCCESS WARNING NOTE}.include?(response[:create_pickup_reply][:highest_severity])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'fedex/request/base'
|
2
|
+
|
3
|
+
module Fedex
|
4
|
+
module Request
|
5
|
+
class PickupAvailability < Base
|
6
|
+
|
7
|
+
def initialize(credentials, options={})
|
8
|
+
requires!(options, :country_code, :request_type, :carrier_code)
|
9
|
+
@debug = ENV['DEBUG'] == 'true'
|
10
|
+
|
11
|
+
@credentials = credentials
|
12
|
+
|
13
|
+
@country_code = options[:country_code]
|
14
|
+
@postal_code = options[:postal_code] if options[:postal_code]
|
15
|
+
@state_code = options[:state_code] if options[:state_code]
|
16
|
+
@request_type = options[:request_type]
|
17
|
+
@carrier_code = options[:carrier_code]
|
18
|
+
@dispatch_date = options[:dispatch_date] if options[:dispatch_date]
|
19
|
+
end
|
20
|
+
|
21
|
+
def process_request
|
22
|
+
api_response = self.class.post(api_url, :body => build_xml)
|
23
|
+
puts api_response if @debug == true
|
24
|
+
response = parse_response(api_response)
|
25
|
+
if success?(response)
|
26
|
+
success_response(api_response, response)
|
27
|
+
else
|
28
|
+
failure_response(api_response, response)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# Build xml Fedex Web Service request
|
35
|
+
def build_xml
|
36
|
+
ns = "http://fedex.com/ws/pickup/v#{service[:version]}"
|
37
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
38
|
+
xml.PickupAvailabilityRequest(:xmlns => ns) {
|
39
|
+
add_web_authentication_detail(xml)
|
40
|
+
add_client_detail(xml)
|
41
|
+
add_version(xml)
|
42
|
+
add_pickup_address(xml)
|
43
|
+
add_other_pickup_details(xml)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
builder.doc.root.to_xml
|
47
|
+
end
|
48
|
+
|
49
|
+
def service
|
50
|
+
{ :id => 'pickup', :version => Fedex::PICKUP_API_VERSION }
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_pickup_address(xml)
|
54
|
+
xml.PickupAddress{
|
55
|
+
xml.PostalCode @postal_code if @postal_code
|
56
|
+
xml.CountryCode @country_code
|
57
|
+
xml.StateOrProvinceCode @state_code if @state_code
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def add_other_pickup_details(xml)
|
62
|
+
xml.PickupRequestType @request_type
|
63
|
+
xml.DispatchDate @dispatch_date if @dispatch_date
|
64
|
+
xml.Carriers @carrier_code
|
65
|
+
end
|
66
|
+
|
67
|
+
# Callback used after a failed pickup response.
|
68
|
+
def failure_response(api_response, response)
|
69
|
+
error_message = if response[:pickup_availability_reply]
|
70
|
+
[response[:pickup_availability_reply][:notifications]].flatten.first[:message]
|
71
|
+
else
|
72
|
+
"#{api_response["Fault"]["detail"]["fault"]["reason"]}\n--#{api_response["Fault"]["detail"]["fault"]["details"]["ValidationFailureDetail"]["message"].join("\n--")}"
|
73
|
+
end rescue $1
|
74
|
+
raise RateError, error_message
|
75
|
+
end
|
76
|
+
|
77
|
+
# Callback used after a successful pickup response.
|
78
|
+
def success_response(api_response, response)
|
79
|
+
@response_details = response[:pickup_availability_reply]
|
80
|
+
end
|
81
|
+
|
82
|
+
# Successful request
|
83
|
+
def success?(response)
|
84
|
+
response[:pickup_availability_reply] &&
|
85
|
+
%w{SUCCESS WARNING NOTE}.include?(response[:pickup_availability_reply][:highest_severity])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/fedex/request/rate.rb
CHANGED
@@ -15,6 +15,7 @@ module Fedex
|
|
15
15
|
rate_reply_details.map do |rate_reply|
|
16
16
|
rate_details = [rate_reply[:rated_shipment_details]].flatten.first[:shipment_rate_detail]
|
17
17
|
rate_details.merge!(service_type: rate_reply[:service_type])
|
18
|
+
rate_details.merge!(transit_time: rate_reply[:transit_time])
|
18
19
|
Fedex::Rate.new(rate_details)
|
19
20
|
end
|
20
21
|
else
|
@@ -44,6 +45,11 @@ module Fedex
|
|
44
45
|
}
|
45
46
|
end
|
46
47
|
|
48
|
+
# Add transite time options
|
49
|
+
def add_transit_time(xml)
|
50
|
+
xml.ReturnTransitAndCommit true
|
51
|
+
end
|
52
|
+
|
47
53
|
# Build xml Fedex Web Service request
|
48
54
|
def build_xml
|
49
55
|
ns = "http://fedex.com/ws/rate/v#{service[:version]}"
|
@@ -52,6 +58,7 @@ module Fedex
|
|
52
58
|
add_web_authentication_detail(xml)
|
53
59
|
add_client_detail(xml)
|
54
60
|
add_version(xml)
|
61
|
+
add_transit_time(xml)
|
55
62
|
add_requested_shipment(xml)
|
56
63
|
}
|
57
64
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'fedex/request/base'
|
2
|
+
|
3
|
+
module Fedex
|
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 => Fedex::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
|
@@ -15,6 +15,7 @@ module Fedex
|
|
15
15
|
:label_stock_type => 'PAPER_LETTER'
|
16
16
|
}
|
17
17
|
@label_specification.merge! options[:label_specification] if options[:label_specification]
|
18
|
+
@customer_specified_detail = options[:customer_specified_detail] if options[:customer_specified_detail]
|
18
19
|
end
|
19
20
|
|
20
21
|
# Sends post request to Fedex web service and parse the response.
|
@@ -41,10 +42,11 @@ module Fedex
|
|
41
42
|
xml.DropoffType @shipping_options[:drop_off_type] ||= "REGULAR_PICKUP"
|
42
43
|
xml.ServiceType service_type
|
43
44
|
xml.PackagingType @shipping_options[:packaging_type] ||= "YOUR_PACKAGING"
|
45
|
+
add_total_weight(xml) if @mps.has_key? :total_weight
|
44
46
|
add_shipper(xml)
|
45
47
|
add_recipient(xml)
|
46
48
|
add_shipping_charges_payment(xml)
|
47
|
-
add_special_services(xml) if @shipping_options[:return_reason]
|
49
|
+
add_special_services(xml) if @shipping_options[:return_reason] || @shipping_options[:cod]
|
48
50
|
add_customs_clearance(xml) if @customs_clearance_detail
|
49
51
|
add_custom_components(xml)
|
50
52
|
xml.RateRequestTypes "ACCOUNT"
|
@@ -52,6 +54,15 @@ module Fedex
|
|
52
54
|
}
|
53
55
|
end
|
54
56
|
|
57
|
+
def add_total_weight(xml)
|
58
|
+
if @mps.has_key? :total_weight
|
59
|
+
xml.TotalWeight{
|
60
|
+
xml.Units @mps[:total_weight][:units]
|
61
|
+
xml.Value @mps[:total_weight][:value]
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
55
66
|
# Hook that can be used to add custom parts.
|
56
67
|
def add_custom_components(xml)
|
57
68
|
add_label_specification xml
|
@@ -63,18 +74,31 @@ module Fedex
|
|
63
74
|
xml.LabelFormatType @label_specification[:label_format_type]
|
64
75
|
xml.ImageType @label_specification[:image_type]
|
65
76
|
xml.LabelStockType @label_specification[:label_stock_type]
|
77
|
+
xml.CustomerSpecifiedDetail{ hash_to_xml(xml, @customer_specified_detail) } if @customer_specified_detail
|
66
78
|
}
|
67
79
|
end
|
68
80
|
|
69
81
|
def add_special_services(xml)
|
70
82
|
xml.SpecialServicesRequested {
|
71
|
-
|
72
|
-
|
73
|
-
xml.
|
74
|
-
|
75
|
-
xml.
|
83
|
+
if @shipping_options[:return_reason]
|
84
|
+
xml.SpecialServiceTypes "RETURN_SHIPMENT"
|
85
|
+
xml.ReturnShipmentDetail {
|
86
|
+
xml.ReturnType "PRINT_RETURN_LABEL"
|
87
|
+
xml.Rma {
|
88
|
+
xml.Reason "#{@shipping_options[:return_reason]}"
|
89
|
+
}
|
76
90
|
}
|
77
|
-
|
91
|
+
end
|
92
|
+
if @shipping_options[:cod]
|
93
|
+
xml.SpecialServiceTypes "COD"
|
94
|
+
xml.CodDetail {
|
95
|
+
xml.CodCollectionAmount {
|
96
|
+
xml.Currency @shipping_options[:cod][:currency].upcase if @shipping_options[:cod][:currency]
|
97
|
+
xml.Amount @shipping_options[:cod][:amount] if @shipping_options[:cod][:amount]
|
98
|
+
}
|
99
|
+
xml.CollectionType @shipping_options[:cod][:collection_type] if @shipping_options[:cod][:collection_type]
|
100
|
+
}
|
101
|
+
end
|
78
102
|
}
|
79
103
|
end
|
80
104
|
|