shipping 1.3.0 → 1.5.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 +10 -6
- data/lib/shipping.rb +2 -0
- data/lib/shipping/base.rb +159 -156
- data/lib/shipping/fedex.rb +463 -438
- data/lib/shipping/ups.rb +432 -83
- data/test/base/base_test.rb +10 -4
- data/test/fedex/fedex_test.rb +55 -22
- data/test/ups/ups_test.rb +62 -6
- metadata +42 -36
data/README
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
== Welcome to Shipping
|
2
2
|
|
3
|
-
Shipping is a module that connects APIs for various shippers like UPS and FedEx.
|
3
|
+
Shipping is a module that connects APIs for various shippers like UPS and FedEx. Shipping currently supports pricing, shipping, labeling and voiding for UPS and FedEx through their XML APIs.
|
4
4
|
|
5
5
|
== Download
|
6
6
|
|
@@ -15,16 +15,19 @@ There is going to be some data that will persist for all connections. For exampl
|
|
15
15
|
fedex_account: 1234556
|
16
16
|
fedex_meter: 387878
|
17
17
|
|
18
|
-
|
18
|
+
ups_url: https://wwwcie.ups.com/ups.app/xml
|
19
|
+
ups_license_number: 7B4F74E3075AEEFF
|
19
20
|
ups_user: username
|
20
21
|
ups_password: password
|
22
|
+
ups_shipper_number: 855AA0
|
23
|
+
|
21
24
|
|
22
25
|
You can set as many default values as you would like in this file.
|
23
26
|
|
24
27
|
require 'shipping'
|
25
28
|
|
26
|
-
ups = Shipping::UPS.new :zip => 97202, :sender_zip => 10001, :weight => 2
|
27
|
-
ups.price => 8
|
29
|
+
ups = Shipping::UPS.new :zip => 97202, :state => "OR", :sender_zip => 10001, :weight => 2
|
30
|
+
ups.price => 5.8
|
28
31
|
ups.valid_address? => false
|
29
32
|
|
30
33
|
ups.city = "Portland"
|
@@ -33,14 +36,15 @@ You can set as many default values as you would like in this file.
|
|
33
36
|
Alternately, you can instantiate the base class and then see both UPS and FedEx information.
|
34
37
|
|
35
38
|
ship = Shipping::Base.new :zip => 97202, :state => "OR", :sender_zip => 10001, :weight => 2
|
36
|
-
ship.ups.price => 8
|
37
|
-
ship.fedex.price => 5.
|
39
|
+
ship.ups.price => 5.8
|
40
|
+
ship.fedex.price => 5.8
|
38
41
|
|
39
42
|
ship.city = "Portland"
|
40
43
|
ship.ups.valid_address? => true
|
41
44
|
|
42
45
|
== Authors
|
43
46
|
* Lucas Carlson (mailto:lucas@rufy.com)
|
47
|
+
* Noah Zoschke (mailto:noah@bitscribe.net)
|
44
48
|
|
45
49
|
This library is released under the terms of the GNU LGPL.
|
46
50
|
|
data/lib/shipping.rb
CHANGED
data/lib/shipping/base.rb
CHANGED
@@ -3,159 +3,162 @@
|
|
3
3
|
# License:: LGPL
|
4
4
|
|
5
5
|
module Shipping
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
6
|
+
VERSION = "1.5.0"
|
7
|
+
|
8
|
+
class ShippingError < StandardError; end
|
9
|
+
class ShippingRequiredFieldError < StandardError; end
|
10
|
+
|
11
|
+
class Base
|
12
|
+
attr_reader :data, :response, :plain_response, :required
|
13
|
+
|
14
|
+
attr_writer :ups_license_number, :ups_shipper_number, :ups_user, :ups_password, :ups_url, :ups_tool
|
15
|
+
attr_writer :fedex_account, :fedex_meter, :fedex_url
|
16
|
+
|
17
|
+
attr_accessor :name, :phone, :company, :email, :address, :address2, :city, :state, :zip, :country
|
18
|
+
attr_accessor :sender_name, :sender_phone, :sender_company, :sender_email, :sender_address, :sender_city, :sender_state, :sender_zip, :sender_country
|
19
|
+
|
20
|
+
attr_accessor :weight, :weight_units, :insured_value, :declared_value, :transaction_type, :description
|
21
|
+
attr_accessor :measure_units, :measure_length, :measure_width, :measure_height
|
22
|
+
attr_accessor :package_total, :packaging_type, :service_type
|
23
|
+
|
24
|
+
attr_accessor :ship_date, :dropoff_type, :pay_type, :currency_code, :image_type, :label_type
|
25
|
+
|
26
|
+
def initialize(options = {})
|
27
|
+
prefs = File.expand_path(options[:prefs] || "~/.shipping.yml")
|
28
|
+
YAML.load(File.open(prefs)).each {|pref, value| eval("@#{pref} = #{value.inspect}")} if File.exists?(prefs)
|
29
|
+
|
30
|
+
@required = Array.new
|
31
|
+
|
32
|
+
# include all provided data
|
33
|
+
options.each do |method, value|
|
34
|
+
instance_variable_set("@#{method}", value)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# Initializes an instance of Shipping::FedEx with the same instance variables as the base object
|
40
|
+
def fedex
|
41
|
+
Shipping::FedEx.new prepare_vars
|
42
|
+
end
|
43
|
+
|
44
|
+
# Initializes an instance of Shipping::UPS with the same instance variables as the base object
|
45
|
+
def ups
|
46
|
+
Shipping::UPS.new prepare_vars
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.state_from_zip(zip)
|
50
|
+
zip = zip.to_i
|
51
|
+
{
|
52
|
+
(99500...99929) => "AK",
|
53
|
+
(35000...36999) => "AL",
|
54
|
+
(71600...72999) => "AR",
|
55
|
+
(75502...75505) => "AR",
|
56
|
+
(85000...86599) => "AZ",
|
57
|
+
(90000...96199) => "CA",
|
58
|
+
(80000...81699) => "CO",
|
59
|
+
(6000...6999) => "CT",
|
60
|
+
(20000...20099) => "DC",
|
61
|
+
(20200...20599) => "DC",
|
62
|
+
(19700...19999) => "DE",
|
63
|
+
(32000...33999) => "FL",
|
64
|
+
(34100...34999) => "FL",
|
65
|
+
(30000...31999) => "GA",
|
66
|
+
(96700...96798) => "HI",
|
67
|
+
(96800...96899) => "HI",
|
68
|
+
(50000...52999) => "IA",
|
69
|
+
(83200...83899) => "ID",
|
70
|
+
(60000...62999) => "IL",
|
71
|
+
(46000...47999) => "IN",
|
72
|
+
(66000...67999) => "KS",
|
73
|
+
(40000...42799) => "KY",
|
74
|
+
(45275...45275) => "KY",
|
75
|
+
(70000...71499) => "LA",
|
76
|
+
(71749...71749) => "LA",
|
77
|
+
(1000...2799) => "MA",
|
78
|
+
(20331...20331) => "MD",
|
79
|
+
(20600...21999) => "MD",
|
80
|
+
(3801...3801) => "ME",
|
81
|
+
(3804...3804) => "ME",
|
82
|
+
(3900...4999) => "ME",
|
83
|
+
(48000...49999) => "MI",
|
84
|
+
(55000...56799) => "MN",
|
85
|
+
(63000...65899) => "MO",
|
86
|
+
(38600...39799) => "MS",
|
87
|
+
(59000...59999) => "MT",
|
88
|
+
(27000...28999) => "NC",
|
89
|
+
(58000...58899) => "ND",
|
90
|
+
(68000...69399) => "NE",
|
91
|
+
(3000...3803) => "NH",
|
92
|
+
(3809...3899) => "NH",
|
93
|
+
(7000...8999) => "NJ",
|
94
|
+
(87000...88499) => "NM",
|
95
|
+
(89000...89899) => "NV",
|
96
|
+
(400...599) => "NY",
|
97
|
+
(6390...6390) => "NY",
|
98
|
+
(9000...14999) => "NY",
|
99
|
+
(43000...45999) => "OH",
|
100
|
+
(73000...73199) => "OK",
|
101
|
+
(73400...74999) => "OK",
|
102
|
+
(97000...97999) => "OR",
|
103
|
+
(15000...19699) => "PA",
|
104
|
+
(2800...2999) => "RI",
|
105
|
+
(6379...6379) => "RI",
|
106
|
+
(29000...29999) => "SC",
|
107
|
+
(57000...57799) => "SD",
|
108
|
+
(37000...38599) => "TN",
|
109
|
+
(72395...72395) => "TN",
|
110
|
+
(73300...73399) => "TX",
|
111
|
+
(73949...73949) => "TX",
|
112
|
+
(75000...79999) => "TX",
|
113
|
+
(88501...88599) => "TX",
|
114
|
+
(84000...84799) => "UT",
|
115
|
+
(20105...20199) => "VA",
|
116
|
+
(20301...20301) => "VA",
|
117
|
+
(20370...20370) => "VA",
|
118
|
+
(22000...24699) => "VA",
|
119
|
+
(5000...5999) => "VT",
|
120
|
+
(98000...99499) => "WA",
|
121
|
+
(49936...49936) => "WI",
|
122
|
+
(53000...54999) => "WI",
|
123
|
+
(24700...26899) => "WV",
|
124
|
+
(82000...83199) => "WY"
|
125
|
+
}.each do |range, state|
|
126
|
+
return state if range.include? zip
|
127
|
+
end
|
128
|
+
|
129
|
+
raise ShippingError, "Invalid zip code"
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def prepare_vars #:nodoc:
|
135
|
+
h = eval(%q{instance_variables.map {|var| "#{var.gsub("@",":")} => #{eval(var+'.inspect')}"}.join(", ").chomp(", ")})
|
136
|
+
return eval("{#{h}}")
|
137
|
+
end
|
138
|
+
|
139
|
+
# Goes out, posts the data, and sets the @response variable with the information
|
140
|
+
def get_response(url)
|
141
|
+
check_required
|
142
|
+
uri = URI.parse url
|
143
|
+
http = Net::HTTP.new uri.host, uri.port
|
144
|
+
if uri.port == 443
|
145
|
+
http.use_ssl = true
|
146
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
147
|
+
end
|
148
|
+
@response_plain = http.post(uri.path, @data).body
|
149
|
+
@response = @response_plain.include?('<?xml') ? REXML::Document.new(@response_plain) : @response_plain
|
150
|
+
|
151
|
+
@response.instance_variable_set "@response_plain", @response_plain
|
152
|
+
def @response.plain; @response_plain; end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Make sure that the required fields are not empty
|
156
|
+
def check_required
|
157
|
+
for var in @required
|
158
|
+
raise ShippingRequiredFieldError, "The #{var} variable needs to be set" if eval("@#{var}").nil?
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
STATES = {"al" => "alabama", "ne" => "nebraska", "ak" => "alaska", "nv" => "nevada", "az" => "arizona", "nh" => "new hampshire", "ar" => "arkansas", "nj" => "new jersey", "ca" => "california", "nm" => "new mexico", "co" => "colorado", "ny" => "new york", "ct" => "connecticut", "nc" => "north carolina", "de" => "delaware", "nd" => "north dakota", "fl" => "florida", "oh" => "ohio", "ga" => "georgia", "ok" => "oklahoma", "hi" => "hawaii", "or" => "oregon", "id" => "idaho", "pa" => "pennsylvania", "il" => "illinois", "pr" => "puerto rico", "in" => "indiana", "ri" => "rhode island", "ia" => "iowa", "sc" => "south carolina", "ks" => "kansas", "sd" => "south dakota", "ky" => "kentucky", "tn" => "tennessee", "la" => "louisiana", "tx" => "texas", "me" => "maine", "ut" => "utah", "md" => "maryland", "vt" => "vermont", "ma" => "massachusetts", "va" => "virginia", "mi" => "michigan", "wa" => "washington", "mn" => "minnesota", "dc" => "district of columbia", "ms" => "mississippi", "wv" => "west virginia", "mo" => "missouri", "wi" => "wisconsin", "mt" => "montana", "wy" => "wyoming"}
|
163
|
+
end
|
164
|
+
end
|
data/lib/shipping/fedex.rb
CHANGED
@@ -6,29 +6,28 @@
|
|
6
6
|
# See http://www.fedex.com/us/solutions/wis/pdf/xml_transguide.pdf?link=4 for the full XML-based API
|
7
7
|
|
8
8
|
module Shipping
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
9
|
+
class FedEx < Base
|
10
|
+
# Gets the list price the regular consumer would have to pay. Discount price is what the
|
11
|
+
# person with this particular account number will pay
|
12
|
+
def price
|
13
|
+
get_price
|
14
|
+
return REXML::XPath.first(@response, "//FDXRateReply/EstimatedCharges/ListCharges/NetCharge").text.to_f
|
15
|
+
rescue ShippingError
|
16
|
+
raise ShippingError, get_error
|
17
|
+
end
|
18
|
+
|
19
|
+
# Gets the discount price of the shipping (with discounts taken into consideration).
|
20
|
+
# "base price" doesn't include surcharges like fuel cost, so I don't think it is the correct price to get
|
21
|
+
def discount_price
|
22
|
+
get_price
|
23
|
+
return REXML::XPath.first(@response, "//FDXRateReply/EstimatedCharges/DiscountedCharges/NetCharge").text.to_f
|
24
|
+
rescue ShippingError
|
25
|
+
raise ShippingError, get_error
|
26
|
+
end
|
27
|
+
|
28
28
|
# still not sure what the best way to handle this transaction's return data would be. Possibly a hash of the form {service => delivery_estimate}?
|
29
29
|
def express_service_availability
|
30
|
-
|
31
|
-
@data = String.new
|
30
|
+
@data = String.new
|
32
31
|
b = Builder::XmlMarkup.new :target => @data
|
33
32
|
b.instruct!
|
34
33
|
b.FDXServiceAvailabilityRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXServiceAvailabilityRequest.xsd') { |b|
|
@@ -47,452 +46,478 @@ module Shipping
|
|
47
46
|
b.ShipDate @ship_date unless @ship_date.blank?
|
48
47
|
b.PackageCount @package_total || '1'
|
49
48
|
}
|
50
|
-
|
49
|
+
|
51
50
|
get_response @fedex_url
|
52
51
|
end
|
53
52
|
|
54
53
|
def register
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
state = STATES.has_value?(@state.downcase) ? STATES.index(@state.downcase).upcase : @state.upcase rescue nil
|
54
|
+
@required = [:name, :company, :phone, :email, :address, :city, :state, :zip]
|
55
|
+
@required += [:fedex_account, :fedex_url]
|
59
56
|
|
60
|
-
|
61
|
-
|
57
|
+
state = STATES.has_value?(@state.downcase) ? STATES.index(@state.downcase).upcase : @state.upcase rescue nil
|
58
|
+
|
59
|
+
@data = String.new
|
60
|
+
b = Builder::XmlMarkup.new :target => @data
|
62
61
|
b.instruct!
|
63
62
|
b.FDXSubscriptionRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXSubscriptionRequest.xsd') { |b|
|
64
63
|
b.RequestHeader { |b|
|
65
|
-
|
64
|
+
b.CustomerTransactionIdentifier @transaction_identifier if @transaction_identifier # optional
|
66
65
|
b.AccountNumber @fedex_account
|
67
66
|
}
|
68
67
|
b.Contact { |b|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
68
|
+
b.PersonName @name
|
69
|
+
b.CompanyName @company
|
70
|
+
b.Department @department if @department
|
71
|
+
b.PhoneNumber @phone.gsub(/[^\d]/,"")
|
72
|
+
b.tag! :"E-MailAddress", @email
|
74
73
|
}
|
75
74
|
b.Address { |b|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
75
|
+
b.Line1 @address
|
76
|
+
b.Line2 @address2 if @address2
|
77
|
+
b.City @city
|
78
|
+
b.StateOrProvinceCode state
|
79
|
+
b.PostalCode @zip
|
80
|
+
b.CountryCode @country || 'US'
|
82
81
|
}
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
82
|
+
}
|
83
|
+
|
84
|
+
get_response @fedex_url
|
85
|
+
|
86
|
+
return REXML::XPath.first(@response, "//FDXSubscriptionReply/MeterNumber").text
|
88
87
|
end
|
89
|
-
|
88
|
+
|
90
89
|
# require 'fileutils'
|
91
90
|
# fedex = Shipping::FedEx.new :name => 'John Doe', ... , :sender_zip => 97202
|
92
91
|
# label = fedex.label
|
93
92
|
# puts label.tracking_number
|
94
|
-
|
93
|
+
# FileUtils.cp label.image.path, '/path/to/my/images/directory/'
|
95
94
|
#
|
96
95
|
# There are several types of labels that can be returned by changing @image_type.
|
97
96
|
# PNG is selected by default.
|
98
97
|
#
|
99
98
|
def label
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
response.freeze
|
99
|
+
@required = [:phone, :email, :address, :city, :state, :zip ]
|
100
|
+
@required += [:sender_phone, :sender_email, :sender_address, :sender_city, :sender_state, :sender_zip ]
|
101
|
+
@required += [:fedex_account, :fedex_url, :fedex_meter]
|
102
|
+
|
103
|
+
@transaction_type ||= 'ship_ground'
|
104
|
+
@weight = (@weight.to_f*10).round/10.0
|
105
|
+
@declared_value = (@declared_value.to_f*100).round/100.0 unless @declared_value.blank?
|
106
|
+
state = STATES.has_value?(@state.downcase) ? STATES.index(@state.downcase).upcase : @state.upcase
|
107
|
+
sender_state = STATES.has_value?(@sender_state.downcase) ? STATES.index(@sender_state.downcase).upcase : @sender_state.upcase
|
108
|
+
|
109
|
+
@data = String.new
|
110
|
+
b = Builder::XmlMarkup.new :target => @data
|
111
|
+
b.instruct!
|
112
|
+
b.FDXShipRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXShipRequest.xsd') { |b|
|
113
|
+
b.RequestHeader { |b|
|
114
|
+
b.AccountNumber @fedex_account
|
115
|
+
b.MeterNumber @fedex_meter
|
116
|
+
b.CarrierCode TransactionTypes[@transaction_type][1]
|
117
|
+
}
|
118
|
+
b.ShipDate((Time.now).strftime("%Y-%m-%d"))
|
119
|
+
b.ShipTime((Time.now).strftime("%H:%M:%S"))
|
120
|
+
b.DropoffType @dropoff_type || 'REGULARPICKUP'
|
121
|
+
b.Service ServiceTypes[@service_type] || ServiceTypes['ground_service'] # default to ground service
|
122
|
+
b.Packaging PackageTypes[@packaging_type] || 'YOURPACKAGING'
|
123
|
+
b.WeightUnits @weight_units || 'LBS' # or KGS
|
124
|
+
b.Weight @weight
|
125
|
+
b.CurrencyCode @currency_code || 'USD'
|
126
|
+
b.Origin { |b|
|
127
|
+
b.Contact { |b|
|
128
|
+
if @sender_name.to_s.size > 2
|
129
|
+
b.PersonName @sender_name
|
130
|
+
b.CompanyName @sender_company unless @sender_company.blank?
|
131
|
+
elsif @sender_company.to_s.size > 2
|
132
|
+
b.PersonName @sender_name unless @sender_name.blank?
|
133
|
+
b.CompanyName @sender_company
|
134
|
+
else
|
135
|
+
raise ShippingError, "Either the sender_name or the sender_company value must be bigger than 2 characters."
|
136
|
+
end
|
137
|
+
b.Department @sender_department unless @sender_department.blank?
|
138
|
+
b.PhoneNumber @sender_phone.gsub(/[^\d]/,"")
|
139
|
+
b.PagerNumber @sender_pager.gsub(/[^\d]/,"") if @sender_pager.class == String
|
140
|
+
b.FaxNumber @sender_fax.gsub(/[^\d]/,"") if @sender_fax.class == String
|
141
|
+
b.tag! :"E-MailAddress", @sender_email
|
142
|
+
}
|
143
|
+
b.Address { |b|
|
144
|
+
b.Line1 @sender_address
|
145
|
+
b.Line2 @sender_address2 unless @sender_address2.blank?
|
146
|
+
b.City @sender_city
|
147
|
+
b.StateOrProvinceCode sender_state
|
148
|
+
b.PostalCode @sender_zip
|
149
|
+
b.CountryCode @sender_country || 'US'
|
150
|
+
}
|
151
|
+
}
|
152
|
+
b.Destination { |b|
|
153
|
+
b.Contact { |b|
|
154
|
+
if @name.to_s.size > 2
|
155
|
+
b.PersonName @name
|
156
|
+
b.CompanyName @company unless @company.blank?
|
157
|
+
elsif @company.to_s.size > 2
|
158
|
+
b.PersonName @name unless @name.blank?
|
159
|
+
b.CompanyName @company
|
160
|
+
else
|
161
|
+
raise ShippingError, "Either the name or the company value must be bigger than 2 characters."
|
162
|
+
end
|
163
|
+
b.Department @department unless @department.blank?
|
164
|
+
b.PhoneNumber @phone.gsub(/[^\d]/,"")
|
165
|
+
b.PagerNumber @pager.gsub(/[^\d]/,"") if @pager.class == String
|
166
|
+
b.FaxNumber @fax.gsub(/[^\d]/,"") if @fax.class == String
|
167
|
+
b.tag! :"E-MailAddress", @email
|
168
|
+
}
|
169
|
+
b.Address { |b|
|
170
|
+
b.Line1 @address
|
171
|
+
b.Line2 @address2 unless @address2.blank?
|
172
|
+
b.City @city
|
173
|
+
b.StateOrProvinceCode state
|
174
|
+
b.PostalCode @zip
|
175
|
+
b.CountryCode @country || 'US'
|
176
|
+
}
|
177
|
+
}
|
178
|
+
b.Payment { |b|
|
179
|
+
b.PayorType PaymentTypes[@pay_type] || 'SENDER'
|
180
|
+
b.Payor { |b|
|
181
|
+
b.AccountNumber @payor_account_number
|
182
|
+
b.CountryCode @payor_country_code unless @payor_country_code.blank?
|
183
|
+
} unless @payor_account_number.blank?
|
184
|
+
}
|
185
|
+
b.RMA { |b|
|
186
|
+
b.Number @rma_number
|
187
|
+
} unless @rma_number.blank?
|
188
|
+
b.SpecialServices { |b|
|
189
|
+
b.EMailNotification { |b|
|
190
|
+
b.ShipAlertOptionalMessage @message
|
191
|
+
b.Shipper { |b|
|
192
|
+
b.ShipAlert @shipper_ship_alert ? 'true' : 'false'
|
193
|
+
b.LanguageCode @shipper_language || 'EN' # FR also available
|
194
|
+
}
|
195
|
+
b.Recipient { |b|
|
196
|
+
b.ShipAlert @recipient_ship_alert ? 'true' : 'false'
|
197
|
+
b.LanguageCode @recipient_language || 'EN' # FR also available
|
198
|
+
}
|
199
|
+
b.Other { |b|
|
200
|
+
b.tag! :"E-MailAddress", @other_email
|
201
|
+
b.ShipAlert @other_ship_alert ? 'true' : 'false'
|
202
|
+
b.LanguageCode @other_language || 'EN' # FR also available
|
203
|
+
} unless @other_email.blank?
|
204
|
+
}
|
205
|
+
} unless @message.blank?
|
206
|
+
b.Label { |b|
|
207
|
+
b.Type @label_type || '2DCOMMON'
|
208
|
+
b.ImageType @image_type || 'PNG'
|
209
|
+
}
|
210
|
+
}
|
211
|
+
get_response @fedex_url
|
212
|
+
|
213
|
+
begin
|
214
|
+
response = Hash.new
|
215
|
+
response[:tracking_number] = REXML::XPath.first(@response, "//FDXShipReply/Tracking/TrackingNumber").text
|
216
|
+
response[:encoded_image] = REXML::XPath.first(@response, "//FDXShipReply/Labels/OutboundLabel").text
|
217
|
+
response[:image] = Tempfile.new("shipping_label")
|
218
|
+
response[:image].write Base64.decode64( response[:encoded_image] )
|
219
|
+
response[:image].rewind
|
220
|
+
rescue
|
221
|
+
raise ShippingError, get_error
|
222
|
+
end
|
223
|
+
|
224
|
+
# allows for things like fedex.label.url
|
225
|
+
def response.method_missing(name, *args)
|
226
|
+
has_key?(name) ? self[name] : super
|
227
|
+
end
|
228
|
+
|
229
|
+
# don't allow people to edit the response
|
230
|
+
response.freeze
|
233
231
|
end
|
234
232
|
|
235
|
-
#
|
236
|
-
#
|
237
|
-
#
|
238
|
-
#
|
239
|
-
#
|
240
|
-
#
|
233
|
+
# require 'fileutils'
|
234
|
+
# fedex = Shipping::FedEx.new :name => 'John Doe', ... , :sender_zip => 97202
|
235
|
+
# label = fedex.email_label
|
236
|
+
# puts label.url
|
237
|
+
# puts label.tracking_number
|
238
|
+
#
|
241
239
|
def return_label
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
b = Builder::XmlMarkup.new :target => @data
|
240
|
+
@required = [:phone, :email, :address, :city, :state, :zip ]
|
241
|
+
@required += [:sender_phone, :sender_email, :sender_address, :sender_city, :sender_state, :sender_zip ]
|
242
|
+
@required += [:fedex_account, :fedex_url, :fedex_meter, :weight ]
|
243
|
+
|
244
|
+
@transaction_type ||= 'ship_ground'
|
245
|
+
@weight = (@weight.to_f*10).round/10.0
|
246
|
+
@declared_value = (@declared_value.to_f*100).round/100.0 unless @declared_value.blank?
|
247
|
+
|
248
|
+
state = STATES.has_value?(@state.downcase) ? STATES.index(@state.downcase).upcase : @state.upcase rescue nil
|
249
|
+
sender_state = STATES.has_value?(@sender_state.downcase) ? STATES.index(@sender_state.downcase).upcase : @sender_state.upcase rescue nil
|
250
|
+
|
251
|
+
@data = String.new
|
252
|
+
b = Builder::XmlMarkup.new :target => @data
|
256
253
|
b.instruct!
|
257
254
|
b.FDXEmailLabelRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXEmailLabelRequest.xsd') { |b|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
b.Service ServiceTypes[@service_type] || ServiceTypes['ground_service'] # default to ground service
|
402
|
-
b.Packaging PackageTypes[@packaging_type] || 'YOURPACKAGING'
|
403
|
-
b.WeightUnits @weight_units || 'LBS'
|
404
|
-
b.Weight @weight
|
405
|
-
b.ListRate true #tells fedex to return list rates as well as discounted rates
|
406
|
-
b.OriginAddress { |b|
|
407
|
-
b.StateOrProvinceCode self.class.state_from_zip(@sender_zip)
|
408
|
-
b.PostalCode @sender_zip
|
409
|
-
b.CountryCode @sender_country_code || "US"
|
410
|
-
}
|
411
|
-
b.DestinationAddress { |b|
|
412
|
-
b.StateOrProvinceCode self.class.state_from_zip(@zip)
|
413
|
-
b.PostalCode @zip
|
414
|
-
b.CountryCode @country || "US"
|
415
|
-
}
|
416
|
-
b.Payment { |b|
|
417
|
-
b.PayorType PaymentTypes[@pay_type] || 'SENDER'
|
418
|
-
}
|
419
|
-
b.PackageCount @package_total || '1'
|
420
|
-
}
|
421
|
-
|
422
|
-
get_response @fedex_url
|
423
|
-
end
|
424
|
-
|
425
|
-
def get_error
|
426
|
-
return if @response.class != REXML::Document
|
255
|
+
b.RequestHeader { |b|
|
256
|
+
b.AccountNumber @fedex_account
|
257
|
+
b.MeterNumber @fedex_meter
|
258
|
+
b.CarrierCode TransactionTypes[@transaction_type][1]
|
259
|
+
}
|
260
|
+
b.URLExpirationDate((Time.now + 3600*24).strftime("%Y-%m-%d"))
|
261
|
+
b.tag! :"URLNotificationE-MailAddress", @sender_email
|
262
|
+
b.MerchantPhoneNumber @sender_phone.gsub(/[^\d]/,"")
|
263
|
+
b.Service ServiceTypes[@service_type] || ServiceTypes['ground_service'] # default to ground service
|
264
|
+
b.Packaging PackageTypes[@packaging_type] || 'YOURPACKAGING'
|
265
|
+
b.WeightUnits @weight_units || 'LBS' # or KGS
|
266
|
+
b.CurrencyCode @currency_code || 'USD'
|
267
|
+
b.Origin { |b|
|
268
|
+
b.Contact { |b|
|
269
|
+
if @sender_name.to_s.size > 2
|
270
|
+
b.PersonName @sender_name
|
271
|
+
b.CompanyName @sender_company unless @sender_company.blank?
|
272
|
+
elsif @sender_company.to_s.size > 2
|
273
|
+
b.PersonName @sender_name unless @sender_name.blank?
|
274
|
+
b.CompanyName @sender_company
|
275
|
+
else
|
276
|
+
raise ShippingError, "Either the sender_name or the sender_company value must be bigger than 2 characters."
|
277
|
+
end
|
278
|
+
b.Department @sender_department unless @sender_department.blank?
|
279
|
+
b.PhoneNumber @sender_phone.gsub(/[^\d]/,"")
|
280
|
+
b.PagerNumber @sender_pager.gsub(/[^\d]/,"") if @sender_pager.class == String
|
281
|
+
b.FaxNumber @sender_fax.gsub(/[^\d]/,"") if @sender_fax.class == String
|
282
|
+
b.tag! :"E-MailAddress", @sender_email
|
283
|
+
}
|
284
|
+
b.Address { |b|
|
285
|
+
b.Line1 @sender_address
|
286
|
+
b.Line2 @sender_address2 unless @sender_address2.blank?
|
287
|
+
b.City @sender_city
|
288
|
+
b.StateOrProvinceCode sender_state
|
289
|
+
b.PostalCode @sender_zip
|
290
|
+
b.CountryCode @sender_country || 'US'
|
291
|
+
}
|
292
|
+
}
|
293
|
+
b.Destination { |b|
|
294
|
+
b.Contact { |b|
|
295
|
+
if @name.to_s.size > 2
|
296
|
+
b.PersonName @name
|
297
|
+
b.CompanyName @company unless @company.blank?
|
298
|
+
elsif @company.to_s.size > 2
|
299
|
+
b.PersonName @name unless @name.blank?
|
300
|
+
b.CompanyName @company
|
301
|
+
else
|
302
|
+
raise ShippingError, "Either the name or the company value must be bigger than 2 characters."
|
303
|
+
end
|
304
|
+
b.Department @department unless @department.blank?
|
305
|
+
b.PhoneNumber @phone.gsub(/[^\d]/,"")
|
306
|
+
b.PagerNumber @pager.gsub(/[^\d]/,"") if @pager.class == String
|
307
|
+
b.FaxNumber @fax.gsub(/[^\d]/,"") if @fax.class == String
|
308
|
+
b.tag! :"E-MailAddress", @email
|
309
|
+
}
|
310
|
+
b.Address { |b|
|
311
|
+
b.Line1 @address
|
312
|
+
b.Line2 @address2 unless @address2.blank?
|
313
|
+
b.City @city
|
314
|
+
b.StateOrProvinceCode state
|
315
|
+
b.PostalCode @zip
|
316
|
+
b.CountryCode @country || 'US'
|
317
|
+
}
|
318
|
+
}
|
319
|
+
|
320
|
+
b.Payment { |b|
|
321
|
+
b.PayorType PaymentTypes[@pay_type] || 'SENDER'
|
322
|
+
b.Payor { |b|
|
323
|
+
b.AccountNumber @payor_account_number
|
324
|
+
b.CountryCode @payor_country_code unless @payor_country_code.blank?
|
325
|
+
} unless @payor_account_number.blank?
|
326
|
+
}
|
327
|
+
|
328
|
+
b.RMA { |b|
|
329
|
+
b.Number @rma_number
|
330
|
+
} unless @rma_number.blank?
|
331
|
+
|
332
|
+
b.Package { |b|
|
333
|
+
b.Weight @weight
|
334
|
+
b.DeclaredValue @declared_value || '99.00'
|
335
|
+
b.ReferenceInfo { |b|
|
336
|
+
b.CustomerReference @customer_reference
|
337
|
+
} unless @customer_reference.blank?
|
338
|
+
b.ItemDescription @description || "Shipment"
|
339
|
+
}
|
340
|
+
|
341
|
+
b.SpecialServices { |b|
|
342
|
+
b.EMailNotification { |b|
|
343
|
+
b.ShipAlertOptionalMessage @message
|
344
|
+
b.Shipper { |b|
|
345
|
+
b.ShipAlert @shipper_ship_alert ? 'true' : 'false'
|
346
|
+
b.LanguageCode @shipper_language || 'EN' # FR also available
|
347
|
+
}
|
348
|
+
b.Recipient { |b|
|
349
|
+
b.ShipAlert @recipient_ship_alert ? 'true' : 'false'
|
350
|
+
b.LanguageCode @recipient_language || 'EN' # FR also available
|
351
|
+
}
|
352
|
+
b.Other { |b|
|
353
|
+
b.tag! :"E-MailAddress", @other_email
|
354
|
+
b.ShipAlert @other_ship_alert ? 'true' : 'false'
|
355
|
+
b.LanguageCode @other_language || 'EN' # FR also available
|
356
|
+
} unless @other_email.blank?
|
357
|
+
}
|
358
|
+
} unless @message.blank?
|
359
|
+
}
|
360
|
+
|
361
|
+
get_response @fedex_url
|
362
|
+
|
363
|
+
begin
|
364
|
+
response = Hash.new
|
365
|
+
response[:url] = REXML::XPath.first(@response, "//FDXEmailLabelReply/URL").text
|
366
|
+
response[:userid] = REXML::XPath.first(@response, "//FDXEmailLabelReply/UserID").text
|
367
|
+
response[:password] = REXML::XPath.first(@response, "//FDXEmailLabelReply/Password").text
|
368
|
+
response[:tracking_number] = REXML::XPath.first(@response, "//FDXEmailLabelReply/Package/TrackingNumber").text
|
369
|
+
rescue
|
370
|
+
raise ShippingError, get_error
|
371
|
+
end
|
372
|
+
|
373
|
+
# allows for things like fedex.label.url
|
374
|
+
def response.method_missing(name, *args)
|
375
|
+
has_key?(name) ? self[name] : super
|
376
|
+
end
|
377
|
+
|
378
|
+
# don't allow people to edit the response
|
379
|
+
return response.freeze
|
380
|
+
end
|
381
|
+
|
382
|
+
def void(tracking_number)
|
383
|
+
@required = [:fedex_account, :fedex_url, :fedex_meter]
|
384
|
+
|
385
|
+
@transaction_type ||= 'ship_ground'
|
386
|
+
|
387
|
+
@data = String.new
|
388
|
+
b = Builder::XmlMarkup.new :target => @data
|
389
|
+
b.instruct!
|
390
|
+
b.FDXShipDeleteRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXShipDeleteRequest.xsd') { |b|
|
391
|
+
b.RequestHeader { |b|
|
392
|
+
b.AccountNumber @fedex_account
|
393
|
+
b.MeterNumber @fedex_meter
|
394
|
+
b.CarrierCode TransactionTypes[@transaction_type][1]
|
395
|
+
}
|
396
|
+
b.TrackingNumber tracking_number
|
397
|
+
}
|
427
398
|
|
428
|
-
|
429
|
-
|
399
|
+
get_response @fedex_url
|
400
|
+
raise ShippingError, get_error if get_error
|
401
|
+
return true
|
402
|
+
end
|
403
|
+
|
404
|
+
private
|
405
|
+
|
406
|
+
def get_price #:nodoc:
|
407
|
+
@required = [:zip, :sender_zip, :weight]
|
408
|
+
@required += [:transaction_type, :fedex_account, :fedex_meter, :fedex_url]
|
409
|
+
|
410
|
+
@transaction_type ||= 'rate_ground'
|
411
|
+
@weight = (@weight.to_f*10).round/10.0
|
412
|
+
|
413
|
+
@data = String.new
|
414
|
+
b = Builder::XmlMarkup.new(:target => @data)
|
415
|
+
b.instruct!
|
416
|
+
b.FDXRateRequest('xmlns:api' => 'http://www.fedex.com/fsmapi', 'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance', 'xsi:noNamespaceSchemaLocation' => 'FDXRateRequest.xsd') { |b|
|
417
|
+
b.RequestHeader { |b|
|
418
|
+
b.AccountNumber @fedex_account
|
419
|
+
b.MeterNumber @fedex_meter
|
420
|
+
b.CarrierCode TransactionTypes[@transaction_type][1]
|
421
|
+
}
|
422
|
+
b.ShipDate @ship_date unless @ship_date.blank?
|
423
|
+
b.DropoffType @dropoff_type || 'REGULARPICKUP'
|
424
|
+
b.Service ServiceTypes[@service_type] || ServiceTypes['ground_service'] # default to ground service
|
425
|
+
b.Packaging PackageTypes[@packaging_type] || 'YOURPACKAGING'
|
426
|
+
b.WeightUnits @weight_units || 'LBS'
|
427
|
+
b.Weight @weight
|
428
|
+
b.ListRate true #tells fedex to return list rates as well as discounted rates
|
429
|
+
b.OriginAddress { |b|
|
430
|
+
b.StateOrProvinceCode self.class.state_from_zip(@sender_zip)
|
431
|
+
b.PostalCode @sender_zip
|
432
|
+
b.CountryCode @sender_country_code || "US"
|
433
|
+
}
|
434
|
+
b.DestinationAddress { |b|
|
435
|
+
b.StateOrProvinceCode self.class.state_from_zip(@zip)
|
436
|
+
b.PostalCode @zip
|
437
|
+
b.CountryCode @country || "US"
|
438
|
+
}
|
439
|
+
b.Payment { |b|
|
440
|
+
b.PayorType PaymentTypes[@pay_type] || 'SENDER'
|
441
|
+
}
|
442
|
+
b.PackageCount @package_total || '1'
|
443
|
+
}
|
444
|
+
|
445
|
+
get_response @fedex_url
|
446
|
+
end
|
447
|
+
|
448
|
+
def get_error
|
449
|
+
return if @response.class != REXML::Document
|
450
|
+
error = REXML::XPath.first(@response, "//Error")
|
451
|
+
return if !error
|
430
452
|
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
453
|
+
code = REXML::XPath.first(error, "//Code").text
|
454
|
+
message = REXML::XPath.first(error, "//Message").text
|
455
|
+
|
456
|
+
return "Error #{code}: #{message}"
|
457
|
+
end
|
458
|
+
|
459
|
+
# The following type hashes are to allow cross-api data retrieval
|
460
|
+
|
461
|
+
ServiceTypes = {
|
462
|
+
"priority" => "PRIORITYOVERNIGHT",
|
463
|
+
"2day" => "FEDEX2DAY",
|
464
|
+
"standard_overnight" => "STANDARDOVERNIGHT",
|
465
|
+
"first_overnight" => "FIRSTOVERNIGHT",
|
466
|
+
"express_saver" => "FEDEXEXPRESSSAVER",
|
467
|
+
"1day_freight" => "FEDEX1DAYFREIGHT",
|
468
|
+
"2day_freight" => "FEDEX2DAYFREIGHT",
|
469
|
+
"3day_freight" => "FEDEX3DAYFREIGHT",
|
470
|
+
"international_priority" => "INTERNATIONALPRIORITY",
|
471
|
+
"international_economy" => "INTERNATIONALECONOMY",
|
472
|
+
"international_first" => "INTERNATIONALFIRST",
|
473
|
+
"international_priority_freight" => "INTERNATIONALPRIORITYFREIGHT",
|
474
|
+
"international_economy_freight" => "INTERNATIONALECONOMYFREIGHT",
|
475
|
+
"home_delivery" => "GROUNDHOMEDELIVERY",
|
476
|
+
"ground_service" => "FEDEXGROUND",
|
477
|
+
"international_ground_service" => "INTERNATIONALGROUND"
|
478
|
+
}
|
479
|
+
|
480
|
+
PackageTypes = {
|
481
|
+
"fedex_envelope" => "FEDEXENVELOPE",
|
482
|
+
"fedex_pak" => "FEDEXPAK",
|
483
|
+
"fedex_box" => "FEDEXBOX",
|
484
|
+
"fedex_tube" => "FEDEXTUBE",
|
485
|
+
"fedex_10_kg_box" => "FEDEX10KGBOX",
|
486
|
+
"fedex_25_kg_box" => "FEDEX25KGBOX",
|
487
|
+
"your_packaging" => "YOURPACKAGING"
|
488
|
+
}
|
489
|
+
|
490
|
+
DropoffTypes = {
|
491
|
+
'regular_pickup' => 'REGULARPICKUP',
|
492
|
+
'request_courier' => 'REQUESTCOURIER',
|
493
|
+
'dropbox' => 'DROPBOX',
|
494
|
+
'business_service_center' => 'BUSINESSSERVICECENTER',
|
495
|
+
'station' => 'STATION'
|
496
|
+
}
|
497
|
+
|
498
|
+
PaymentTypes = {
|
499
|
+
'sender' => 'SENDER',
|
500
|
+
'recipient' => 'RECIPIENT',
|
501
|
+
'third_party' => 'THIRDPARTY',
|
502
|
+
'collect' => 'COLLECT'
|
503
|
+
}
|
504
|
+
|
505
|
+
|
506
|
+
TransactionTypes = {
|
507
|
+
'rate_ground' => ['022','FDXG'],
|
508
|
+
'rate_express' => ['022','FDXE'],
|
509
|
+
'rate_services' => ['025',''],
|
510
|
+
'ship_ground' => ['021','FDXG'],
|
511
|
+
'ship_express' => ['021','FDXE'],
|
512
|
+
'cancel_express' => ['023','FDXE'],
|
513
|
+
'cancel_ground' => ['023','FDXG'],
|
514
|
+
'close_ground' => ['007','FDXG'],
|
515
|
+
'service_available' => ['019','FDXE'],
|
516
|
+
'fedex_locater' => ['410',''],
|
517
|
+
'subscribe' => ['211',''],
|
518
|
+
'sig_proof_delivery' => ['402',''],
|
519
|
+
'track' => ['405',''],
|
520
|
+
'ref_track' => ['403','']
|
521
|
+
}
|
522
|
+
end
|
498
523
|
end
|