fedex 3.6.1 → 3.8.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.
- 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
|
|