omniship 0.3.2.2 → 0.4.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/lib/omniship.rb +13 -12
- data/lib/omniship/address.rb +36 -36
- data/lib/omniship/carriers/fedex.rb +343 -188
- data/lib/omniship/carriers/ups.rb +156 -95
- data/lib/omniship/notification.rb +18 -0
- data/lib/omniship/response.rb +7 -7
- data/lib/omniship/ship_response.rb +12 -0
- data/lib/omniship/version.rb +2 -2
- data/lib/rails/generators/omniship/setup/setup_generator.rb +19 -0
- data/lib/rails/generators/omniship/setup/templates/initializer.rb +2 -0
- data/lib/rails/generators/omniship/setup/templates/omniship.yml +33 -0
- metadata +58 -73
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d9f5ce46f050f67d8b750e29ae85c457dc30bde0
|
4
|
+
data.tar.gz: fedf5e0c3e0408636c1ba156781eb9088df4f3ea
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 473d38ea1ef885e530317a3b68ae013d0717406dd176ee8bca0e9ff2abfcfbc45780c211da29a62567ac2c462a8290ea6c5e9ff34de8eadc247bcdf27650b068
|
7
|
+
data.tar.gz: 999466fd9b82fb309c5327af767df136a364066b2a64c7755abb99f440d9535d31a156772fcb1844ee800bad661d41837ad5cc0abdd1de3ee4a61bafed925631
|
data/lib/omniship.rb
CHANGED
@@ -26,23 +26,23 @@
|
|
26
26
|
require 'omniship/base'
|
27
27
|
|
28
28
|
def Omniship.setup
|
29
|
-
@root = Rails.root
|
30
|
-
if @root
|
29
|
+
@root = Rails.root
|
30
|
+
if @root
|
31
31
|
@boot = File.join(@root, "config", "boot.rb").freeze
|
32
32
|
@config = File.join(@root, "config", "omniship.yml").freeze
|
33
|
-
@keys = %w{ username password key }.map { |v| v.freeze }.freeze
|
33
|
+
@keys = %w{ username password key account meter }.map { |v| v.freeze }.freeze
|
34
34
|
require boot unless defined? Rails.env
|
35
|
-
|
36
|
-
|
37
|
-
raise "Invalid
|
38
|
-
if (
|
39
|
-
|
40
|
-
"ups" =>
|
41
|
-
"fedex" =>
|
42
|
-
"usps" =>
|
35
|
+
if File.exists? @config
|
36
|
+
@config = YAML.load_file(@config)
|
37
|
+
raise "Invalid omniship configuration file: #{@config}" unless @config.is_a?(Hash)
|
38
|
+
if (@config.keys & @keys).sort == @keys.sort and !@config.has_key?(Rails.env)
|
39
|
+
@config[Rails.env] = {
|
40
|
+
"ups" => @config["ups"],
|
41
|
+
"fedex" => @config["fedex"],
|
42
|
+
"usps" => @config["usps"]
|
43
43
|
}
|
44
44
|
end
|
45
|
-
|
45
|
+
@config[Rails.env].freeze
|
46
46
|
end
|
47
47
|
end
|
48
48
|
end
|
@@ -75,3 +75,4 @@ require 'omniship/rate_estimate'
|
|
75
75
|
require 'omniship/carrier'
|
76
76
|
require 'omniship/carriers'
|
77
77
|
require 'omniship/shipment_event'
|
78
|
+
require 'omniship/ship_response'
|
data/lib/omniship/address.rb
CHANGED
@@ -28,16 +28,16 @@ module Omniship #:nodoc:
|
|
28
28
|
@country = (options[:country].nil? or options[:country].is_a?(ActiveMerchant::Country)) ?
|
29
29
|
options[:country] :
|
30
30
|
ActiveMerchant::Country.find(options[:country])
|
31
|
-
@postal_code
|
32
|
-
@province
|
33
|
-
@city
|
34
|
-
@name
|
35
|
-
@address1
|
36
|
-
@address2
|
37
|
-
@address3
|
38
|
-
@phone
|
39
|
-
@fax
|
40
|
-
@company_name
|
31
|
+
@postal_code = options[:postal_code] || options[:postal] || options[:zip]
|
32
|
+
@province = options[:province] || options[:state] || options[:territory] || options[:region]
|
33
|
+
@city = options[:city]
|
34
|
+
@name = options[:name]
|
35
|
+
@address1 = options[:address1]
|
36
|
+
@address2 = options[:address2]
|
37
|
+
@address3 = options[:address3]
|
38
|
+
@phone = options[:phone]
|
39
|
+
@fax = options[:fax]
|
40
|
+
@company_name = options[:company_name] || options[:company]
|
41
41
|
@attention_name = options[:attention_name]
|
42
42
|
|
43
43
|
self.address_type = options[:address_type]
|
@@ -46,19 +46,19 @@ module Omniship #:nodoc:
|
|
46
46
|
def self.from(object, options={})
|
47
47
|
return object if object.is_a? Omniship::Address
|
48
48
|
attr_mappings = {
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
49
|
+
:name => [:name],
|
50
|
+
:attention_name => [:attention_name],
|
51
|
+
:country => [:country_code, :country],
|
52
|
+
:postal_code => [:postal_code, :zip, :postal],
|
53
|
+
:province => [:province_code, :state_code, :territory_code, :region_code, :province, :state, :territory, :region],
|
54
|
+
:city => [:city, :town],
|
55
|
+
:address1 => [:address1, :address, :street],
|
56
|
+
:address2 => [:address2],
|
57
|
+
:address3 => [:address3],
|
58
|
+
:phone => [:phone, :phone_number],
|
59
|
+
:fax => [:fax, :fax_number],
|
60
|
+
:address_type => [:address_type],
|
61
|
+
:company_name => [:company, :company_name]
|
62
62
|
}
|
63
63
|
attributes = {}
|
64
64
|
hash_access = begin
|
@@ -95,19 +95,19 @@ module Omniship #:nodoc:
|
|
95
95
|
|
96
96
|
def to_hash
|
97
97
|
{
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
98
|
+
:country => country_code,
|
99
|
+
:postal_code => postal_code,
|
100
|
+
:province => province,
|
101
|
+
:city => city,
|
102
|
+
:name => name,
|
103
|
+
:address1 => address1,
|
104
|
+
:address2 => address2,
|
105
|
+
:address3 => address3,
|
106
|
+
:phone => phone,
|
107
|
+
:fax => fax,
|
108
|
+
:address_type => address_type,
|
109
|
+
:company_name => company_name,
|
110
|
+
:attention_name => attention_name
|
111
111
|
}
|
112
112
|
end
|
113
113
|
|
@@ -1,5 +1,4 @@
|
|
1
|
-
# FedEx module by
|
2
|
-
# http://github.com/jimmyebaker
|
1
|
+
# FedEx module by Donavan White
|
3
2
|
|
4
3
|
module Omniship
|
5
4
|
# :key is your developer API key
|
@@ -8,79 +7,79 @@ module Omniship
|
|
8
7
|
# :login is your meter number
|
9
8
|
class FedEx < Carrier
|
10
9
|
self.retry_safe = true
|
11
|
-
|
10
|
+
|
12
11
|
cattr_reader :name
|
13
12
|
@@name = "FedEx"
|
14
|
-
|
13
|
+
|
15
14
|
TEST_URL = 'https://gatewaybeta.fedex.com:443/xml'
|
16
15
|
LIVE_URL = 'https://gateway.fedex.com:443/xml'
|
17
|
-
|
16
|
+
|
18
17
|
CarrierCodes = {
|
19
|
-
"fedex_ground"
|
18
|
+
"fedex_ground" => "FDXG",
|
20
19
|
"fedex_express" => "FDXE"
|
21
20
|
}
|
22
|
-
|
21
|
+
|
23
22
|
ServiceTypes = {
|
24
|
-
"PRIORITY_OVERNIGHT"
|
25
|
-
"PRIORITY_OVERNIGHT_SATURDAY_DELIVERY"
|
26
|
-
"FEDEX_2_DAY"
|
27
|
-
"FEDEX_2_DAY_SATURDAY_DELIVERY"
|
28
|
-
"STANDARD_OVERNIGHT"
|
29
|
-
"FIRST_OVERNIGHT"
|
30
|
-
"FIRST_OVERNIGHT_SATURDAY_DELIVERY"
|
31
|
-
"FEDEX_EXPRESS_SAVER"
|
32
|
-
"FEDEX_1_DAY_FREIGHT"
|
33
|
-
"FEDEX_1_DAY_FREIGHT_SATURDAY_DELIVERY"
|
34
|
-
"FEDEX_2_DAY_FREIGHT"
|
35
|
-
"FEDEX_2_DAY_FREIGHT_SATURDAY_DELIVERY"
|
36
|
-
"FEDEX_3_DAY_FREIGHT"
|
37
|
-
"FEDEX_3_DAY_FREIGHT_SATURDAY_DELIVERY"
|
38
|
-
"INTERNATIONAL_PRIORITY"
|
23
|
+
"PRIORITY_OVERNIGHT" => "FedEx Priority Overnight",
|
24
|
+
"PRIORITY_OVERNIGHT_SATURDAY_DELIVERY" => "FedEx Priority Overnight Saturday Delivery",
|
25
|
+
"FEDEX_2_DAY" => "FedEx 2 Day",
|
26
|
+
"FEDEX_2_DAY_SATURDAY_DELIVERY" => "FedEx 2 Day Saturday Delivery",
|
27
|
+
"STANDARD_OVERNIGHT" => "FedEx Standard Overnight",
|
28
|
+
"FIRST_OVERNIGHT" => "FedEx First Overnight",
|
29
|
+
"FIRST_OVERNIGHT_SATURDAY_DELIVERY" => "FedEx First Overnight Saturday Delivery",
|
30
|
+
"FEDEX_EXPRESS_SAVER" => "FedEx Express Saver",
|
31
|
+
"FEDEX_1_DAY_FREIGHT" => "FedEx 1 Day Freight",
|
32
|
+
"FEDEX_1_DAY_FREIGHT_SATURDAY_DELIVERY" => "FedEx 1 Day Freight Saturday Delivery",
|
33
|
+
"FEDEX_2_DAY_FREIGHT" => "FedEx 2 Day Freight",
|
34
|
+
"FEDEX_2_DAY_FREIGHT_SATURDAY_DELIVERY" => "FedEx 2 Day Freight Saturday Delivery",
|
35
|
+
"FEDEX_3_DAY_FREIGHT" => "FedEx 3 Day Freight",
|
36
|
+
"FEDEX_3_DAY_FREIGHT_SATURDAY_DELIVERY" => "FedEx 3 Day Freight Saturday Delivery",
|
37
|
+
"INTERNATIONAL_PRIORITY" => "FedEx International Priority",
|
39
38
|
"INTERNATIONAL_PRIORITY_SATURDAY_DELIVERY" => "FedEx International Priority Saturday Delivery",
|
40
|
-
"INTERNATIONAL_ECONOMY"
|
41
|
-
"INTERNATIONAL_FIRST"
|
42
|
-
"INTERNATIONAL_PRIORITY_FREIGHT"
|
43
|
-
"INTERNATIONAL_ECONOMY_FREIGHT"
|
44
|
-
"GROUND_HOME_DELIVERY"
|
45
|
-
"FEDEX_GROUND"
|
46
|
-
"INTERNATIONAL_GROUND"
|
39
|
+
"INTERNATIONAL_ECONOMY" => "FedEx International Economy",
|
40
|
+
"INTERNATIONAL_FIRST" => "FedEx International First",
|
41
|
+
"INTERNATIONAL_PRIORITY_FREIGHT" => "FedEx International Priority Freight",
|
42
|
+
"INTERNATIONAL_ECONOMY_FREIGHT" => "FedEx International Economy Freight",
|
43
|
+
"GROUND_HOME_DELIVERY" => "FedEx Ground Home Delivery",
|
44
|
+
"FEDEX_GROUND" => "FedEx Ground",
|
45
|
+
"INTERNATIONAL_GROUND" => "FedEx International Ground"
|
47
46
|
}
|
48
47
|
|
49
48
|
PackageTypes = {
|
50
|
-
"fedex_envelope"
|
51
|
-
"fedex_pak"
|
52
|
-
"fedex_box"
|
53
|
-
"fedex_tube"
|
49
|
+
"fedex_envelope" => "FEDEX_ENVELOPE",
|
50
|
+
"fedex_pak" => "FEDEX_PAK",
|
51
|
+
"fedex_box" => "FEDEX_BOX",
|
52
|
+
"fedex_tube" => "FEDEX_TUBE",
|
54
53
|
"fedex_10_kg_box" => "FEDEX_10KG_BOX",
|
55
54
|
"fedex_25_kg_box" => "FEDEX_25KG_BOX",
|
56
|
-
"your_packaging"
|
55
|
+
"your_packaging" => "YOUR_PACKAGING"
|
57
56
|
}
|
58
57
|
|
59
58
|
DropoffTypes = {
|
60
|
-
'regular_pickup'
|
61
|
-
'request_courier'
|
62
|
-
'dropbox'
|
59
|
+
'regular_pickup' => 'REGULAR_PICKUP',
|
60
|
+
'request_courier' => 'REQUEST_COURIER',
|
61
|
+
'dropbox' => 'DROP_BOX',
|
63
62
|
'business_service_center' => 'BUSINESS_SERVICE_CENTER',
|
64
|
-
'station'
|
63
|
+
'station' => 'STATION'
|
65
64
|
}
|
66
65
|
|
67
66
|
PaymentTypes = {
|
68
|
-
'sender'
|
69
|
-
'recipient'
|
67
|
+
'sender' => 'SENDER',
|
68
|
+
'recipient' => 'RECIPIENT',
|
70
69
|
'third_party' => 'THIRDPARTY',
|
71
|
-
'collect'
|
70
|
+
'collect' => 'COLLECT'
|
72
71
|
}
|
73
|
-
|
72
|
+
|
74
73
|
PackageIdentifierTypes = {
|
75
|
-
'tracking_number'
|
76
|
-
'door_tag'
|
77
|
-
'rma'
|
78
|
-
'ground_shipment_id'
|
79
|
-
'ground_invoice_number'
|
74
|
+
'tracking_number' => 'TRACKING_NUMBER_OR_DOORTAG',
|
75
|
+
'door_tag' => 'TRACKING_NUMBER_OR_DOORTAG',
|
76
|
+
'rma' => 'RMA',
|
77
|
+
'ground_shipment_id' => 'GROUND_SHIPMENT_ID',
|
78
|
+
'ground_invoice_number' => 'GROUND_INVOICE_NUMBER',
|
80
79
|
'ground_customer_reference' => 'GROUND_CUSTOMER_REFERENCE',
|
81
|
-
'ground_po'
|
82
|
-
'express_reference'
|
83
|
-
'express_mps_master'
|
80
|
+
'ground_po' => 'GROUND_PO',
|
81
|
+
'express_reference' => 'EXPRESS_REFERENCE',
|
82
|
+
'express_mps_master' => 'EXPRESS_MPS_MASTER'
|
84
83
|
}
|
85
84
|
|
86
85
|
def self.service_name_for_code(service_code)
|
@@ -89,88 +88,214 @@ module Omniship
|
|
89
88
|
"FedEx #{name.sub(/Fedex /, '')}"
|
90
89
|
end
|
91
90
|
end
|
92
|
-
|
91
|
+
|
93
92
|
def requirements
|
94
|
-
[:key, :
|
93
|
+
[:key, :account, :meter, :password]
|
95
94
|
end
|
96
|
-
|
95
|
+
|
97
96
|
def find_rates(origin, destination, packages, options = {})
|
98
|
-
options
|
99
|
-
|
100
|
-
|
101
|
-
rate_request
|
102
|
-
|
103
|
-
response = commit(save_request(rate_request), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
|
104
|
-
|
97
|
+
options = @options.merge(options)
|
98
|
+
options[:test] = options[:test].nil? ? true : options[:test]
|
99
|
+
packages = Array(packages)
|
100
|
+
rate_request = build_rate_request(origin, destination, packages, options)
|
101
|
+
response = commit(save_request(rate_request.gsub("\n", "")), options[:test])
|
105
102
|
parse_rate_response(origin, destination, packages, response, options)
|
106
103
|
end
|
107
|
-
|
104
|
+
|
105
|
+
def create_shipment(origin, destination, packages, options={})
|
106
|
+
options = @options.merge(options)
|
107
|
+
options[:test] = options[:test].nil? ? true : options[:test]
|
108
|
+
packages = Array(packages)
|
109
|
+
ship_request = build_ship_request(origin, destination, packages, options)
|
110
|
+
response = commit(save_request(ship_request.gsub("\n", "")), options[:test])
|
111
|
+
parse_ship_response(response, options)
|
112
|
+
end
|
113
|
+
|
114
|
+
def delete_shipment(tracking_number, shipment_type, options={})
|
115
|
+
options = @options.merge(options)
|
116
|
+
delete_shipment_request = build_delete_request(tracking_number, shipment_type, options)
|
117
|
+
response = commit(save_request(delete_shipment_request.gsub("\n", "")), options[:test])
|
118
|
+
parse_delete_response(response, options)
|
119
|
+
end
|
120
|
+
|
108
121
|
def find_tracking_info(tracking_number, options={})
|
109
|
-
options
|
110
|
-
|
122
|
+
options = @options.update(options)
|
111
123
|
tracking_request = build_tracking_request(tracking_number, options)
|
112
|
-
response
|
124
|
+
response = commit(save_request(tracking_request), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
|
113
125
|
parse_tracking_response(response, options)
|
114
126
|
end
|
115
|
-
|
127
|
+
|
116
128
|
protected
|
117
129
|
def build_rate_request(origin, destination, packages, options={})
|
118
130
|
imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
|
119
131
|
|
120
|
-
|
121
|
-
|
132
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
133
|
+
xml.RateRequest('xmlns' => 'http://fedex.com/ws/rate/v12') {
|
134
|
+
build_access_request(xml)
|
135
|
+
xml.Version {
|
136
|
+
xml.ServiceId "crs"
|
137
|
+
xml.Major "12"
|
138
|
+
xml.Intermediate "0"
|
139
|
+
xml.Minor "0"
|
140
|
+
}
|
141
|
+
xml.RequestedShipment {
|
142
|
+
xml.ShipTimestamp options[:ship_date] || DateTime.now.strftime
|
143
|
+
xml.DropoffType options[:dropoff_type] || 'REGULAR_PICKUP'
|
144
|
+
xml.PackagingType options[:packaging_type] || 'YOUR_PACKAGING'
|
145
|
+
build_location_node(['Shipper'], (options[:shipper] || origin), xml)
|
146
|
+
build_location_node(['Recipient'], destination, xml)
|
147
|
+
if options[:shipper] && options[:shipper] != origin
|
148
|
+
build_location_node(['Origin'], origin, xml)
|
149
|
+
end
|
150
|
+
xml.RateRequestTypes 'ACCOUNT'
|
151
|
+
xml.PackageCount packages.size
|
152
|
+
packages.each do |pkg|
|
153
|
+
xml.RequestedPackageLineItems {
|
154
|
+
xml.SequenceNumber 1
|
155
|
+
xml.GroupPackageCount 1
|
156
|
+
xml.Weight {
|
157
|
+
xml.Units (imperial ? 'LB' : 'KG')
|
158
|
+
xml.Value ((imperial ? pkg.weight : pkg.weight/2.2).to_f)
|
159
|
+
}
|
160
|
+
# xml.Dimensions {
|
161
|
+
# [:length, :width, :height].each do |axis|
|
162
|
+
# name = axis.to_s.capitalize
|
163
|
+
# value = ((imperial ? pkg.inches(axis) : pkg.cm(axis)).to_f*1000).round/1000.0
|
164
|
+
# xml.name value
|
165
|
+
# end
|
166
|
+
# xml.Units (imperial ? 'IN' : 'CM')
|
167
|
+
# }
|
168
|
+
}
|
169
|
+
end
|
170
|
+
}
|
171
|
+
}
|
172
|
+
end
|
173
|
+
builder.to_xml
|
174
|
+
end
|
122
175
|
|
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
|
-
|
176
|
+
def build_ship_request(origin, destination, packages, options={})
|
177
|
+
imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
|
178
|
+
|
179
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
180
|
+
xml.ProcessShipmentRequest('xmlns' => 'http://fedex.com/ws/ship/v12') {
|
181
|
+
build_access_request(xml)
|
182
|
+
xml.Version {
|
183
|
+
xml.ServiceId "ship"
|
184
|
+
xml.Major "12"
|
185
|
+
xml.Intermediate "0"
|
186
|
+
xml.Minor "0"
|
187
|
+
}
|
188
|
+
xml.RequestedShipment {
|
189
|
+
xml.ShipTimestamp options[:ship_date] || DateTime.now.strftime
|
190
|
+
xml.DropoffType options[:dropoff_type] || 'REGULAR_PICKUP'
|
191
|
+
xml.ServiceType options[:service_type] || 'GROUND_HOME_DELIVERY'
|
192
|
+
xml.PackagingType options[:package_type] || 'YOUR_PACKAGING'
|
193
|
+
build_location_node(["Shipper"], (options[:shipper] || origin), xml)
|
194
|
+
build_location_node(["Recipient"], destination, xml)
|
195
|
+
if options[:shipper] && options[:shipper] != origin
|
196
|
+
build_location_node(["Origin"], origin, xml)
|
197
|
+
end
|
198
|
+
xml.ShippingChargesPayment {
|
199
|
+
xml.PaymentType "SENDER"
|
200
|
+
xml.Payor {
|
201
|
+
xml.ResponsibleParty {
|
202
|
+
xml.AccountNumber @options[:account]
|
203
|
+
xml.Contact nil
|
204
|
+
}
|
205
|
+
}
|
206
|
+
}
|
207
|
+
xml.SpecialServicesRequested {
|
208
|
+
xml.SpecialServiceTypes "SATURDAY_DELIVERY" if options[:saturday_delivery]
|
209
|
+
if options[:return_shipment]
|
210
|
+
xml.SpecialServiceTypes "RETURN_SHIPMENT"
|
211
|
+
xml.ReturnShipmentDetail {
|
212
|
+
xml.ReturnType "PRINT_RETURN_LABEL"
|
213
|
+
}
|
161
214
|
end
|
215
|
+
}
|
216
|
+
xml.LabelSpecification {
|
217
|
+
xml.LabelFormatType 'COMMON2D'
|
218
|
+
xml.ImageType 'PDF'
|
219
|
+
xml.LabelStockType 'PAPER_7X4.75'
|
220
|
+
}
|
221
|
+
xml.RateRequestTypes 'ACCOUNT'
|
222
|
+
xml.PackageCount packages.size
|
223
|
+
packages.each do |pkg|
|
224
|
+
xml.RequestedPackageLineItems {
|
225
|
+
xml.SequenceNumber 1
|
226
|
+
xml.Weight {
|
227
|
+
xml.Units (imperial ? 'LB' : 'KG')
|
228
|
+
xml.Value ((imperial ? pkg.weight : pkg.weight/2.2).to_f)
|
229
|
+
}
|
230
|
+
# xml.Dimensions {
|
231
|
+
# [:length, :width, :height].each do |axis|
|
232
|
+
# name = axis.to_s.capitalize
|
233
|
+
# value = ((imperial ? pkg.inches(axis) : pkg.cm(axis)).to_f*1000).round/1000.0
|
234
|
+
# xml.send name, value.to_s
|
235
|
+
# end
|
236
|
+
# xml.Units (imperial ? 'IN' : 'CM')
|
237
|
+
# }
|
238
|
+
}
|
162
239
|
end
|
163
|
-
|
164
|
-
|
165
|
-
|
240
|
+
|
241
|
+
if !!@options[:notifications]
|
242
|
+
xml.SpecialServicesRequested {
|
243
|
+
xml.SpecialServiceTypes "EMAIL_NOTIFICATION"
|
244
|
+
xml.EmailNotificationDetail {
|
245
|
+
xml.PersonalMessage # Personal Message to be sent to all recipients
|
246
|
+
@options[:notifications].each do |email|
|
247
|
+
xml.Recipients {
|
248
|
+
xml.EmailAddress email.address
|
249
|
+
xml.NotificationEventsRequested {
|
250
|
+
xml.EmailNotificationEventType{
|
251
|
+
xml.ON_DELIVERY if email.on_delivery
|
252
|
+
xml.ON_EXCEPTION if email.on_exception
|
253
|
+
xml.ON_SHIPMENT if email.on_shipment
|
254
|
+
xml.ON_TENDER if email.on_tender
|
255
|
+
}
|
256
|
+
}
|
257
|
+
xml.Format email.format || "HTML" # options are "HTML" "Text" "Wireless"
|
258
|
+
xml.Localization {
|
259
|
+
xml.Language email.language || "EN" # Default to EN (English)
|
260
|
+
xml.LocaleCode email.locale_code if !email.locale_code.nil?
|
261
|
+
}
|
262
|
+
}
|
263
|
+
end
|
264
|
+
xml.EMailNotificationAggregationType @options[:notification_aggregation_type] if @options.has_key?(:notification_aggregation_type)
|
265
|
+
}
|
266
|
+
}
|
267
|
+
end
|
268
|
+
}
|
269
|
+
}
|
166
270
|
end
|
167
|
-
|
271
|
+
builder.to_xml
|
168
272
|
end
|
169
|
-
|
273
|
+
|
274
|
+
def build_delete_request(tracking_number, shipment_type, options={})
|
275
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
276
|
+
xml.DeleteShipmentRequest('xmlns' => 'http://fedex.com/ws/ship/v12') {
|
277
|
+
build_access_request(xml)
|
278
|
+
xml.Version {
|
279
|
+
xml.ServiceId "ship"
|
280
|
+
xml.Major "12"
|
281
|
+
xml.Intermediate "0"
|
282
|
+
xml.Minor "0"
|
283
|
+
}
|
284
|
+
xml.ShipTimestamp options[:ship_timestamp] if options[:ship_timestamp]
|
285
|
+
xml.TrackingId {
|
286
|
+
xml.TrackingIdType shipment_type
|
287
|
+
xml.TrackingNumber tracking_number
|
288
|
+
}
|
289
|
+
xml.DeletionControl options[:deletion_type] || "DELETE_ALL_PACKAGES"
|
290
|
+
}
|
291
|
+
end
|
292
|
+
builder.to_xml
|
293
|
+
end
|
294
|
+
|
170
295
|
def build_tracking_request(tracking_number, options={})
|
171
296
|
xml_request = XmlNode.new('TrackRequest', 'xmlns' => 'http://fedex.com/ws/track/v3') do |root_node|
|
172
297
|
root_node << build_request_header
|
173
|
-
|
298
|
+
|
174
299
|
# Version
|
175
300
|
root_node << XmlNode.new('Version') do |version_node|
|
176
301
|
version_node << XmlNode.new('ServiceId', 'trck')
|
@@ -178,74 +303,81 @@ module Omniship
|
|
178
303
|
version_node << XmlNode.new('Intermediate', '0')
|
179
304
|
version_node << XmlNode.new('Minor', '0')
|
180
305
|
end
|
181
|
-
|
306
|
+
|
182
307
|
root_node << XmlNode.new('PackageIdentifier') do |package_node|
|
183
308
|
package_node << XmlNode.new('Value', tracking_number)
|
184
309
|
package_node << XmlNode.new('Type', PackageIdentifierTypes[options['package_identifier_type'] || 'tracking_number'])
|
185
310
|
end
|
186
|
-
|
311
|
+
|
187
312
|
root_node << XmlNode.new('ShipDateRangeBegin', options['ship_date_range_begin']) if options['ship_date_range_begin']
|
188
313
|
root_node << XmlNode.new('ShipDateRangeEnd', options['ship_date_range_end']) if options['ship_date_range_end']
|
189
314
|
root_node << XmlNode.new('IncludeDetailedScans', 1)
|
190
315
|
end
|
191
316
|
xml_request.to_s
|
192
317
|
end
|
193
|
-
|
194
|
-
def
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
[web_authentication_detail, client_detail, trasaction_detail]
|
318
|
+
|
319
|
+
def build_access_request(xml)
|
320
|
+
xml.WebAuthenticationDetail {
|
321
|
+
xml.UserCredential {
|
322
|
+
xml.Key @options[:key]
|
323
|
+
xml.Password @options[:password]
|
324
|
+
}
|
325
|
+
}
|
326
|
+
|
327
|
+
xml.ClientDetail {
|
328
|
+
xml.AccountNumber @options[:account]
|
329
|
+
xml.MeterNumber @options[:meter]
|
330
|
+
}
|
331
|
+
|
332
|
+
xml.TransactionDetail {
|
333
|
+
xml.CustomerTransactionId 'Omniship' # TODO: Need to do something better with this...
|
334
|
+
}
|
212
335
|
end
|
213
|
-
|
214
|
-
def build_location_node(name, location)
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
336
|
+
|
337
|
+
def build_location_node(name, location, xml)
|
338
|
+
for name in name
|
339
|
+
xml.send(name) {
|
340
|
+
xml.Contact {
|
341
|
+
xml.PersonName location.name unless location.name == "" || location.name == nil
|
342
|
+
xml.CompanyName location.company unless location.company == "" || location.name == nil
|
343
|
+
xml.PhoneNumber location.phone
|
344
|
+
}
|
345
|
+
xml.Address {
|
346
|
+
xml.StreetLines location.address1
|
347
|
+
xml.StreetLines location.address2 unless location.address2.nil?
|
348
|
+
xml.City location.city
|
349
|
+
xml.StateOrProvinceCode location.state
|
350
|
+
xml.PostalCode location.postal_code
|
351
|
+
xml.CountryCode location.country_code(:alpha2)
|
352
|
+
xml.Residential true unless location.commercial?
|
353
|
+
}
|
354
|
+
}
|
222
355
|
end
|
223
356
|
end
|
224
|
-
|
357
|
+
|
225
358
|
def parse_rate_response(origin, destination, packages, response, options)
|
226
|
-
rate_estimates
|
359
|
+
rate_estimates = []
|
227
360
|
success, message = nil
|
228
|
-
|
229
|
-
xml =
|
230
|
-
|
231
|
-
|
361
|
+
|
362
|
+
xml = Nokogiri::XML(response).remove_namespaces!
|
363
|
+
|
232
364
|
success = response_success?(xml)
|
233
365
|
message = response_message(xml)
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
366
|
+
|
367
|
+
|
368
|
+
service_code = xml.xpath('//ServiceType').text == options[:service_type]
|
369
|
+
is_saturday_delivery = xml.xpath('//AppliedOptions').text == 'SATURDAY_DELIVERY'
|
370
|
+
service_type = is_saturday_delivery ? "#{service_code}_SATURDAY_DELIVERY" : service_code
|
371
|
+
|
372
|
+
currency = handle_uk_currency(xml.xpath('//RatedShipmentDetails/ShipmentRateDetail/TotalNetCharge/Currency').text)
|
373
|
+
rate_estimates << RateEstimate.new(origin, destination, @@name,
|
374
|
+
self.class.service_name_for_code(service_type),
|
375
|
+
:service_code => service_code,
|
376
|
+
:total_price => xml.xpath('//RatedShipmentDetails/ShipmentRateDetail/TotalNetCharge/Amount').text.to_f,
|
377
|
+
:currency => currency,
|
378
|
+
:packages => packages,
|
379
|
+
:delivery_range => [xml.xpath('//DeliveryTimestamp').text] * 2)
|
380
|
+
|
249
381
|
|
250
382
|
if rate_estimates.empty?
|
251
383
|
success = false
|
@@ -254,28 +386,52 @@ module Omniship
|
|
254
386
|
|
255
387
|
RateResponse.new(success, message, Hash.from_xml(response), :rates => rate_estimates, :xml => response, :request => last_request, :log_xml => options[:log_xml])
|
256
388
|
end
|
257
|
-
|
389
|
+
|
390
|
+
def parse_ship_response(response, options)
|
391
|
+
xml = Nokogiri::XML(response).remove_namespaces!
|
392
|
+
success = response_success?(xml)
|
393
|
+
message = response_message(xml)
|
394
|
+
label = nil
|
395
|
+
tracking_number = nil
|
396
|
+
|
397
|
+
if success
|
398
|
+
label = xml.xpath("//Image").text
|
399
|
+
tracking_number = xml.xpath("//TrackingNumber").text
|
400
|
+
else
|
401
|
+
success = false
|
402
|
+
message = "Shipment was not succcessful." if message.blank?
|
403
|
+
end
|
404
|
+
ShipResponse.new(success, message, :tracking_number => tracking_number, :label_encoded => label)
|
405
|
+
end
|
406
|
+
|
407
|
+
def parse_delete_response(response, options={})
|
408
|
+
xml = Nokogiri::XML(response).remove_namespaces!
|
409
|
+
success = response_success?(xml)
|
410
|
+
message = response_message(xml)
|
411
|
+
return [success, message]
|
412
|
+
end
|
413
|
+
|
258
414
|
def parse_tracking_response(response, options)
|
259
415
|
xml = REXML::Document.new(response)
|
260
416
|
root_node = xml.elements['TrackReply']
|
261
|
-
|
417
|
+
|
262
418
|
success = response_success?(xml)
|
263
419
|
message = response_message(xml)
|
264
|
-
|
420
|
+
|
265
421
|
if success
|
266
422
|
tracking_number, origin, destination = nil
|
267
423
|
shipment_events = []
|
268
|
-
|
424
|
+
|
269
425
|
tracking_details = root_node.elements['TrackDetails']
|
270
426
|
tracking_number = tracking_details.get_text('TrackingNumber').to_s
|
271
|
-
|
427
|
+
|
272
428
|
destination_node = tracking_details.elements['DestinationAddress']
|
273
429
|
destination = Address.new(
|
274
430
|
:country => destination_node.get_text('CountryCode').to_s,
|
275
431
|
:province => destination_node.get_text('StateOrProvinceCode').to_s,
|
276
432
|
:city => destination_node.get_text('City').to_s
|
277
433
|
)
|
278
|
-
|
434
|
+
|
279
435
|
tracking_details.elements.each('Events') do |event|
|
280
436
|
address = event.elements['Address']
|
281
437
|
|
@@ -284,45 +440,44 @@ module Omniship
|
|
284
440
|
zip_code = address.get_text('PostalCode').to_s
|
285
441
|
country = address.get_text('CountryCode').to_s
|
286
442
|
next if country.blank?
|
287
|
-
|
443
|
+
|
288
444
|
location = Address.new(:city => city, :state => state, :postal_code => zip_code, :country => country)
|
289
445
|
description = event.get_text('EventDescription').to_s
|
290
|
-
|
446
|
+
|
291
447
|
# for now, just assume UTC, even though it probably isn't
|
292
448
|
time = Time.parse("#{event.get_text('Timestamp').to_s}")
|
293
449
|
zoneless_time = Time.utc(time.year, time.month, time.mday, time.hour, time.min, time.sec)
|
294
|
-
|
450
|
+
|
295
451
|
shipment_events << ShipmentEvent.new(description, zoneless_time, location)
|
296
452
|
end
|
297
453
|
shipment_events = shipment_events.sort_by(&:time)
|
298
454
|
end
|
299
|
-
|
455
|
+
|
300
456
|
TrackingResponse.new(success, message, Hash.from_xml(response),
|
301
|
-
:xml
|
302
|
-
:request
|
457
|
+
:xml => response,
|
458
|
+
:request => last_request,
|
303
459
|
:shipment_events => shipment_events,
|
304
|
-
:destination
|
460
|
+
:destination => destination,
|
305
461
|
:tracking_number => tracking_number
|
306
462
|
)
|
307
463
|
end
|
308
|
-
|
309
|
-
def response_status_node(
|
310
|
-
|
464
|
+
|
465
|
+
def response_status_node(xml)
|
466
|
+
xml.ProcessShipmentReply
|
311
467
|
end
|
312
|
-
|
313
|
-
def response_success?(
|
314
|
-
%w{SUCCESS WARNING NOTE}.include?
|
468
|
+
|
469
|
+
def response_success?(xml)
|
470
|
+
%w{SUCCESS WARNING NOTE}.include? xml.xpath('//Notifications/Severity').text
|
315
471
|
end
|
316
|
-
|
317
|
-
def response_message(
|
318
|
-
|
319
|
-
"#{response_status_node(document).get_text('Severity').to_s} - #{response_node.get_text('Code').to_s}: #{response_node.get_text('Message').to_s}"
|
472
|
+
|
473
|
+
def response_message(xml)
|
474
|
+
"#{xml.xpath('//Notifications/Severity').text} - #{xml.xpath('//Notifications/Code').text}: #{xml.xpath('//Notifications/Message').text}"
|
320
475
|
end
|
321
|
-
|
476
|
+
|
322
477
|
def commit(request, test = false)
|
323
|
-
ssl_post(test ? TEST_URL : LIVE_URL, request
|
478
|
+
ssl_post(test ? TEST_URL : LIVE_URL, request)
|
324
479
|
end
|
325
|
-
|
480
|
+
|
326
481
|
def handle_uk_currency(currency)
|
327
482
|
currency =~ /UKL/i ? 'GBP' : currency
|
328
483
|
end
|