active_shipping 0.12.1 → 0.12.2
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +1 -0
- data/lib/active_shipping/shipping/carriers/fedex.rb +1 -1
- data/lib/active_shipping/version.rb +1 -1
- metadata +48 -28
- metadata.gz.sig +3 -0
- data/lib/active_shipping/shipping/carriers/ups.rb.orig +0 -456
- data/lib/active_shipping/shipping/carriers/usps.rb.orig +0 -616
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3b5f032d7b051953da7d9746cc9ef3fce5e272aa
|
|
4
|
+
data.tar.gz: 74890284fd2a7f865cbbc84cbf6edd9f1a1856a8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3e86722a8a87c0d4f774fe73fed0d2f395bcd5783465031128193283b9012e4eb3746c1df10fb40b6e75e630d0c9006785ca9374003a016fa5abf564249a484a
|
|
7
|
+
data.tar.gz: a50553bdd237ad4da447bbfa7b6e5202eb371b5002c9b8d8eb0231ee29dbbc747fbce844733c4fd142bbce22e0bd94eb9907811ea9d77d69c4dfb3bbcd092683
|
checksums.yaml.gz.sig
ADDED
|
Binary file
|
data.tar.gz.sig
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
j�bJ��ږT��a���$3�둓@�q�_�1,t+�LI�Կ�'R���Q��0jY\׆a�O���͡i�V�'c�ލ'A��휿�p���qg�j�h�{T�������dKS�~�FI�F��X���1�"��0�S��@H�*���C�?�) eB�/y�{���oS�����t�A-!��e�\?�|9�'$��e˒�vho����4xY����^p��'HXXbiw�4�z<��ɯ)5�y�\[�1:n[�
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_shipping
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.12.
|
|
4
|
+
version: 0.12.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James MacAulay
|
|
@@ -10,147 +10,169 @@ authors:
|
|
|
10
10
|
- Jimmy Baker
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
|
-
cert_chain:
|
|
14
|
-
|
|
13
|
+
cert_chain:
|
|
14
|
+
- |
|
|
15
|
+
-----BEGIN CERTIFICATE-----
|
|
16
|
+
MIIDcDCCAligAwIBAgIBATANBgkqhkiG9w0BAQUFADA/MQ8wDQYDVQQDDAZhZG1p
|
|
17
|
+
bnMxFzAVBgoJkiaJk/IsZAEZFgdzaG9waWZ5MRMwEQYKCZImiZPyLGQBGRYDY29t
|
|
18
|
+
MB4XDTE0MDUxNTIwMzM0OFoXDTE1MDUxNTIwMzM0OFowPzEPMA0GA1UEAwwGYWRt
|
|
19
|
+
aW5zMRcwFQYKCZImiZPyLGQBGRYHc2hvcGlmeTETMBEGCgmSJomT8ixkARkWA2Nv
|
|
20
|
+
bTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL0/81O3e1vh5smcwp2G
|
|
21
|
+
MpLQ6q0kejQLa65bPYPxdzWA1SYOKyGfw+yR9LdFzsuKpwWzKq6zX35lj1IckWS4
|
|
22
|
+
bNBEQzxmufUxU0XPM02haFB8fOfDJzdXsWte9Ge4IFwahwn68gpMqN+BvxL+KMYz
|
|
23
|
+
Iut9YmN44d4LZdsENEIO5vmybuG2vYDz7R56qB0PA+Q2P2CdhymsBad2DQs69FBo
|
|
24
|
+
uico9V6VMYYctL9lCYdzu9IXrOYNTt88suKIVzzAlHOKeN0Ng5qdztFoTR8sfxDr
|
|
25
|
+
Ydg3KHl5n47wlpgd8R0f/4b5gGxW+v9pyJCgQnLlRu7DedVSvv7+GMtj3g9r3nhJ
|
|
26
|
+
KqECAwEAAaN3MHUwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFI/o
|
|
27
|
+
maf34HXbUOQsdoLHacEKQgunMB0GA1UdEQQWMBSBEmFkbWluc0BzaG9waWZ5LmNv
|
|
28
|
+
bTAdBgNVHRIEFjAUgRJhZG1pbnNAc2hvcGlmeS5jb20wDQYJKoZIhvcNAQEFBQAD
|
|
29
|
+
ggEBADkK9aj5T0HPExsov4EoMWFnO+G7RQ28C30VAfKxnL2UxG6i4XMHVs6Xi94h
|
|
30
|
+
qXFw1ec9Y2eDUqaolT3bviOk9BB197+A8Vz/k7MC6ci2NE+yDDB7HAC8zU6LAx8Y
|
|
31
|
+
Iqvw7B/PSZ/pz4bUVFlTATif4mi1vO3lidRkdHRtM7UePSn2rUpOi0gtXBP3bLu5
|
|
32
|
+
YjHJN7wx5cugMEyroKITG5gL0Nxtu21qtOlHX4Hc4KdE2JqzCPOsS4zsZGhgwhPs
|
|
33
|
+
fl3hbtVFTqbOlwL9vy1fudXcolIE/ZTcxQ+er07ZFZdKCXayR9PPs64heamfn0fp
|
|
34
|
+
TConQSX2BnZdhIEYW+cKzEC/bLc=
|
|
35
|
+
-----END CERTIFICATE-----
|
|
36
|
+
date: 2014-05-27 00:00:00.000000000 Z
|
|
15
37
|
dependencies:
|
|
16
38
|
- !ruby/object:Gem::Dependency
|
|
17
39
|
name: activesupport
|
|
18
40
|
requirement: !ruby/object:Gem::Requirement
|
|
19
41
|
requirements:
|
|
20
|
-
- -
|
|
42
|
+
- - ">="
|
|
21
43
|
- !ruby/object:Gem::Version
|
|
22
44
|
version: 2.3.5
|
|
23
45
|
type: :runtime
|
|
24
46
|
prerelease: false
|
|
25
47
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
48
|
requirements:
|
|
27
|
-
- -
|
|
49
|
+
- - ">="
|
|
28
50
|
- !ruby/object:Gem::Version
|
|
29
51
|
version: 2.3.5
|
|
30
52
|
- !ruby/object:Gem::Dependency
|
|
31
53
|
name: i18n
|
|
32
54
|
requirement: !ruby/object:Gem::Requirement
|
|
33
55
|
requirements:
|
|
34
|
-
- -
|
|
56
|
+
- - ">="
|
|
35
57
|
- !ruby/object:Gem::Version
|
|
36
58
|
version: '0'
|
|
37
59
|
type: :runtime
|
|
38
60
|
prerelease: false
|
|
39
61
|
version_requirements: !ruby/object:Gem::Requirement
|
|
40
62
|
requirements:
|
|
41
|
-
- -
|
|
63
|
+
- - ">="
|
|
42
64
|
- !ruby/object:Gem::Version
|
|
43
65
|
version: '0'
|
|
44
66
|
- !ruby/object:Gem::Dependency
|
|
45
67
|
name: active_utils
|
|
46
68
|
requirement: !ruby/object:Gem::Requirement
|
|
47
69
|
requirements:
|
|
48
|
-
- - ~>
|
|
70
|
+
- - "~>"
|
|
49
71
|
- !ruby/object:Gem::Version
|
|
50
72
|
version: 2.2.0
|
|
51
73
|
type: :runtime
|
|
52
74
|
prerelease: false
|
|
53
75
|
version_requirements: !ruby/object:Gem::Requirement
|
|
54
76
|
requirements:
|
|
55
|
-
- - ~>
|
|
77
|
+
- - "~>"
|
|
56
78
|
- !ruby/object:Gem::Version
|
|
57
79
|
version: 2.2.0
|
|
58
80
|
- !ruby/object:Gem::Dependency
|
|
59
81
|
name: builder
|
|
60
82
|
requirement: !ruby/object:Gem::Requirement
|
|
61
83
|
requirements:
|
|
62
|
-
- -
|
|
84
|
+
- - ">="
|
|
63
85
|
- !ruby/object:Gem::Version
|
|
64
86
|
version: '0'
|
|
65
87
|
type: :runtime
|
|
66
88
|
prerelease: false
|
|
67
89
|
version_requirements: !ruby/object:Gem::Requirement
|
|
68
90
|
requirements:
|
|
69
|
-
- -
|
|
91
|
+
- - ">="
|
|
70
92
|
- !ruby/object:Gem::Version
|
|
71
93
|
version: '0'
|
|
72
94
|
- !ruby/object:Gem::Dependency
|
|
73
95
|
name: json
|
|
74
96
|
requirement: !ruby/object:Gem::Requirement
|
|
75
97
|
requirements:
|
|
76
|
-
- -
|
|
98
|
+
- - ">="
|
|
77
99
|
- !ruby/object:Gem::Version
|
|
78
100
|
version: 1.5.1
|
|
79
101
|
type: :runtime
|
|
80
102
|
prerelease: false
|
|
81
103
|
version_requirements: !ruby/object:Gem::Requirement
|
|
82
104
|
requirements:
|
|
83
|
-
- -
|
|
105
|
+
- - ">="
|
|
84
106
|
- !ruby/object:Gem::Version
|
|
85
107
|
version: 1.5.1
|
|
86
108
|
- !ruby/object:Gem::Dependency
|
|
87
109
|
name: minitest
|
|
88
110
|
requirement: !ruby/object:Gem::Requirement
|
|
89
111
|
requirements:
|
|
90
|
-
- - ~>
|
|
112
|
+
- - "~>"
|
|
91
113
|
- !ruby/object:Gem::Version
|
|
92
114
|
version: 4.7.5
|
|
93
115
|
type: :development
|
|
94
116
|
prerelease: false
|
|
95
117
|
version_requirements: !ruby/object:Gem::Requirement
|
|
96
118
|
requirements:
|
|
97
|
-
- - ~>
|
|
119
|
+
- - "~>"
|
|
98
120
|
- !ruby/object:Gem::Version
|
|
99
121
|
version: 4.7.5
|
|
100
122
|
- !ruby/object:Gem::Dependency
|
|
101
123
|
name: rake
|
|
102
124
|
requirement: !ruby/object:Gem::Requirement
|
|
103
125
|
requirements:
|
|
104
|
-
- -
|
|
126
|
+
- - ">="
|
|
105
127
|
- !ruby/object:Gem::Version
|
|
106
128
|
version: '0'
|
|
107
129
|
type: :development
|
|
108
130
|
prerelease: false
|
|
109
131
|
version_requirements: !ruby/object:Gem::Requirement
|
|
110
132
|
requirements:
|
|
111
|
-
- -
|
|
133
|
+
- - ">="
|
|
112
134
|
- !ruby/object:Gem::Version
|
|
113
135
|
version: '0'
|
|
114
136
|
- !ruby/object:Gem::Dependency
|
|
115
137
|
name: mocha
|
|
116
138
|
requirement: !ruby/object:Gem::Requirement
|
|
117
139
|
requirements:
|
|
118
|
-
- - ~>
|
|
140
|
+
- - "~>"
|
|
119
141
|
- !ruby/object:Gem::Version
|
|
120
142
|
version: 0.14.0
|
|
121
143
|
type: :development
|
|
122
144
|
prerelease: false
|
|
123
145
|
version_requirements: !ruby/object:Gem::Requirement
|
|
124
146
|
requirements:
|
|
125
|
-
- - ~>
|
|
147
|
+
- - "~>"
|
|
126
148
|
- !ruby/object:Gem::Version
|
|
127
149
|
version: 0.14.0
|
|
128
150
|
- !ruby/object:Gem::Dependency
|
|
129
151
|
name: timecop
|
|
130
152
|
requirement: !ruby/object:Gem::Requirement
|
|
131
153
|
requirements:
|
|
132
|
-
- -
|
|
154
|
+
- - ">="
|
|
133
155
|
- !ruby/object:Gem::Version
|
|
134
156
|
version: '0'
|
|
135
157
|
type: :development
|
|
136
158
|
prerelease: false
|
|
137
159
|
version_requirements: !ruby/object:Gem::Requirement
|
|
138
160
|
requirements:
|
|
139
|
-
- -
|
|
161
|
+
- - ">="
|
|
140
162
|
- !ruby/object:Gem::Version
|
|
141
163
|
version: '0'
|
|
142
164
|
- !ruby/object:Gem::Dependency
|
|
143
165
|
name: nokogiri
|
|
144
166
|
requirement: !ruby/object:Gem::Requirement
|
|
145
167
|
requirements:
|
|
146
|
-
- -
|
|
168
|
+
- - ">="
|
|
147
169
|
- !ruby/object:Gem::Version
|
|
148
170
|
version: '0'
|
|
149
171
|
type: :development
|
|
150
172
|
prerelease: false
|
|
151
173
|
version_requirements: !ruby/object:Gem::Requirement
|
|
152
174
|
requirements:
|
|
153
|
-
- -
|
|
175
|
+
- - ">="
|
|
154
176
|
- !ruby/object:Gem::Version
|
|
155
177
|
version: '0'
|
|
156
178
|
description: Get rates and tracking info from various shipping carriers.
|
|
@@ -177,9 +199,7 @@ files:
|
|
|
177
199
|
- lib/active_shipping/shipping/carriers/shipwire.rb
|
|
178
200
|
- lib/active_shipping/shipping/carriers/stamps.rb
|
|
179
201
|
- lib/active_shipping/shipping/carriers/ups.rb
|
|
180
|
-
- lib/active_shipping/shipping/carriers/ups.rb.orig
|
|
181
202
|
- lib/active_shipping/shipping/carriers/usps.rb
|
|
182
|
-
- lib/active_shipping/shipping/carriers/usps.rb.orig
|
|
183
203
|
- lib/active_shipping/shipping/errors.rb
|
|
184
204
|
- lib/active_shipping/shipping/location.rb
|
|
185
205
|
- lib/active_shipping/shipping/package.rb
|
|
@@ -220,17 +240,17 @@ require_paths:
|
|
|
220
240
|
- lib
|
|
221
241
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
222
242
|
requirements:
|
|
223
|
-
- -
|
|
243
|
+
- - ">="
|
|
224
244
|
- !ruby/object:Gem::Version
|
|
225
245
|
version: '0'
|
|
226
246
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
227
247
|
requirements:
|
|
228
|
-
- -
|
|
248
|
+
- - ">="
|
|
229
249
|
- !ruby/object:Gem::Version
|
|
230
250
|
version: 1.3.6
|
|
231
251
|
requirements: []
|
|
232
252
|
rubyforge_project: active_shipping
|
|
233
|
-
rubygems_version: 2.2.
|
|
253
|
+
rubygems_version: 2.2.0
|
|
234
254
|
signing_key:
|
|
235
255
|
specification_version: 4
|
|
236
256
|
summary: Shipping API extension for Active Merchant
|
metadata.gz.sig
ADDED
|
@@ -1,456 +0,0 @@
|
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
module ActiveMerchant
|
|
4
|
-
module Shipping
|
|
5
|
-
class UPS < Carrier
|
|
6
|
-
self.retry_safe = true
|
|
7
|
-
|
|
8
|
-
cattr_accessor :default_options
|
|
9
|
-
cattr_reader :name
|
|
10
|
-
@@name = "UPS"
|
|
11
|
-
|
|
12
|
-
TEST_URL = 'https://wwwcie.ups.com'
|
|
13
|
-
LIVE_URL = 'https://onlinetools.ups.com'
|
|
14
|
-
|
|
15
|
-
RESOURCES = {
|
|
16
|
-
:rates => 'ups.app/xml/Rate',
|
|
17
|
-
:track => 'ups.app/xml/Track'
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
PICKUP_CODES = HashWithIndifferentAccess.new({
|
|
21
|
-
:daily_pickup => "01",
|
|
22
|
-
:customer_counter => "03",
|
|
23
|
-
:one_time_pickup => "06",
|
|
24
|
-
:on_call_air => "07",
|
|
25
|
-
:suggested_retail_rates => "11",
|
|
26
|
-
:letter_center => "19",
|
|
27
|
-
:air_service_center => "20"
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
CUSTOMER_CLASSIFICATIONS = HashWithIndifferentAccess.new({
|
|
31
|
-
:wholesale => "01",
|
|
32
|
-
:occasional => "03",
|
|
33
|
-
:retail => "04"
|
|
34
|
-
})
|
|
35
|
-
|
|
36
|
-
# these are the defaults described in the UPS API docs,
|
|
37
|
-
# but they don't seem to apply them under all circumstances,
|
|
38
|
-
# so we need to take matters into our own hands
|
|
39
|
-
DEFAULT_CUSTOMER_CLASSIFICATIONS = Hash.new do |hash,key|
|
|
40
|
-
hash[key] = case key.to_sym
|
|
41
|
-
when :daily_pickup then :wholesale
|
|
42
|
-
when :customer_counter then :retail
|
|
43
|
-
else
|
|
44
|
-
:occasional
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
DEFAULT_SERVICES = {
|
|
49
|
-
"01" => "UPS Next Day Air",
|
|
50
|
-
"02" => "UPS Second Day Air",
|
|
51
|
-
"03" => "UPS Ground",
|
|
52
|
-
"07" => "UPS Worldwide Express",
|
|
53
|
-
"08" => "UPS Worldwide Expedited",
|
|
54
|
-
"11" => "UPS Standard",
|
|
55
|
-
"12" => "UPS Three-Day Select",
|
|
56
|
-
"13" => "UPS Next Day Air Saver",
|
|
57
|
-
"14" => "UPS Next Day Air Early A.M.",
|
|
58
|
-
"54" => "UPS Worldwide Express Plus",
|
|
59
|
-
"59" => "UPS Second Day Air A.M.",
|
|
60
|
-
"65" => "UPS Saver",
|
|
61
|
-
"82" => "UPS Today Standard",
|
|
62
|
-
"83" => "UPS Today Dedicated Courier",
|
|
63
|
-
"84" => "UPS Today Intercity",
|
|
64
|
-
"85" => "UPS Today Express",
|
|
65
|
-
"86" => "UPS Today Express Saver"
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
CANADA_ORIGIN_SERVICES = {
|
|
69
|
-
"01" => "UPS Express",
|
|
70
|
-
"02" => "UPS Expedited",
|
|
71
|
-
"14" => "UPS Express Early A.M."
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
MEXICO_ORIGIN_SERVICES = {
|
|
75
|
-
"07" => "UPS Express",
|
|
76
|
-
"08" => "UPS Expedited",
|
|
77
|
-
"54" => "UPS Express Plus"
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
EU_ORIGIN_SERVICES = {
|
|
81
|
-
"07" => "UPS Express",
|
|
82
|
-
"08" => "UPS Expedited"
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
OTHER_NON_US_ORIGIN_SERVICES = {
|
|
86
|
-
"07" => "UPS Express"
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
TRACKING_STATUS_CODES = HashWithIndifferentAccess.new({
|
|
90
|
-
'I' => :in_transit,
|
|
91
|
-
'D' => :delivered,
|
|
92
|
-
'X' => :exception,
|
|
93
|
-
'P' => :pickup,
|
|
94
|
-
'M' => :manifest_pickup
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
# From http://en.wikipedia.org/w/index.php?title=European_Union&oldid=174718707 (Current as of November 30, 2007)
|
|
98
|
-
EU_COUNTRY_CODES = ["GB", "AT", "BE", "BG", "CY", "CZ", "DK", "EE", "FI", "FR", "DE", "GR", "HU", "IE", "IT", "LV", "LT", "LU", "MT", "NL", "PL", "PT", "RO", "SK", "SI", "ES", "SE"]
|
|
99
|
-
|
|
100
|
-
US_TERRITORIES_TREATED_AS_COUNTRIES = ["AS", "FM", "GU", "MH", "MP", "PW", "PR", "VI"]
|
|
101
|
-
|
|
102
|
-
def requirements
|
|
103
|
-
[:key, :login, :password]
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def find_rates(origin, destination, packages, options={})
|
|
107
|
-
origin, destination = upsified_location(origin), upsified_location(destination)
|
|
108
|
-
options = @options.merge(options)
|
|
109
|
-
packages = Array(packages)
|
|
110
|
-
access_request = build_access_request
|
|
111
|
-
rate_request = build_rate_request(origin, destination, packages, options)
|
|
112
|
-
response = commit(:rates, save_request(access_request + rate_request), (options[:test] || false))
|
|
113
|
-
parse_rate_response(origin, destination, packages, response, options)
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
def find_tracking_info(tracking_number, options={})
|
|
117
|
-
options = @options.update(options)
|
|
118
|
-
access_request = build_access_request
|
|
119
|
-
tracking_request = build_tracking_request(tracking_number, options)
|
|
120
|
-
response = commit(:track, save_request(access_request + tracking_request), (options[:test] || false))
|
|
121
|
-
parse_tracking_response(response, options)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
protected
|
|
125
|
-
|
|
126
|
-
def upsified_location(location)
|
|
127
|
-
if location.country_code == 'US' && US_TERRITORIES_TREATED_AS_COUNTRIES.include?(location.state)
|
|
128
|
-
atts = {:country => location.state}
|
|
129
|
-
[:zip, :city, :address1, :address2, :address3, :phone, :fax, :address_type].each do |att|
|
|
130
|
-
atts[att] = location.send(att)
|
|
131
|
-
end
|
|
132
|
-
Location.new(atts)
|
|
133
|
-
else
|
|
134
|
-
location
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
def build_access_request
|
|
139
|
-
xml_request = XmlNode.new('AccessRequest') do |access_request|
|
|
140
|
-
access_request << XmlNode.new('AccessLicenseNumber', @options[:key])
|
|
141
|
-
access_request << XmlNode.new('UserId', @options[:login])
|
|
142
|
-
access_request << XmlNode.new('Password', @options[:password])
|
|
143
|
-
end
|
|
144
|
-
xml_request.to_s
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def build_rate_request(origin, destination, packages, options={})
|
|
148
|
-
packages = Array(packages)
|
|
149
|
-
xml_request = XmlNode.new('RatingServiceSelectionRequest') do |root_node|
|
|
150
|
-
root_node << XmlNode.new('Request') do |request|
|
|
151
|
-
request << XmlNode.new('RequestAction', 'Rate')
|
|
152
|
-
request << XmlNode.new('RequestOption', 'Shop')
|
|
153
|
-
# not implemented: 'Rate' RequestOption to specify a single service query
|
|
154
|
-
# request << XmlNode.new('RequestOption', ((options[:service].nil? or options[:service] == :all) ? 'Shop' : 'Rate'))
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
pickup_type = options[:pickup_type] || :daily_pickup
|
|
158
|
-
|
|
159
|
-
root_node << XmlNode.new('PickupType') do |pickup_type_node|
|
|
160
|
-
pickup_type_node << XmlNode.new('Code', PICKUP_CODES[pickup_type])
|
|
161
|
-
# not implemented: PickupType/PickupDetails element
|
|
162
|
-
end
|
|
163
|
-
cc = options[:customer_classification] || DEFAULT_CUSTOMER_CLASSIFICATIONS[pickup_type]
|
|
164
|
-
root_node << XmlNode.new('CustomerClassification') do |cc_node|
|
|
165
|
-
cc_node << XmlNode.new('Code', CUSTOMER_CLASSIFICATIONS[cc])
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
root_node << XmlNode.new('Shipment') do |shipment|
|
|
169
|
-
# not implemented: Shipment/Description element
|
|
170
|
-
shipment << build_location_node('Shipper', (options[:shipper] || origin), options)
|
|
171
|
-
shipment << build_location_node('ShipTo', destination, options)
|
|
172
|
-
if options[:shipper] and options[:shipper] != origin
|
|
173
|
-
shipment << build_location_node('ShipFrom', origin, options)
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
# not implemented: * Shipment/ShipmentWeight element
|
|
177
|
-
# * Shipment/ReferenceNumber element
|
|
178
|
-
# * Shipment/Service element
|
|
179
|
-
# * Shipment/PickupDate element
|
|
180
|
-
# * Shipment/ScheduledDeliveryDate element
|
|
181
|
-
# * Shipment/ScheduledDeliveryTime element
|
|
182
|
-
# * Shipment/AlternateDeliveryTime element
|
|
183
|
-
# * Shipment/DocumentsOnly element
|
|
184
|
-
|
|
185
|
-
packages.each do |package|
|
|
186
|
-
imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
|
|
187
|
-
|
|
188
|
-
shipment << XmlNode.new("Package") do |package_node|
|
|
189
|
-
|
|
190
|
-
# not implemented: * Shipment/Package/PackagingType element
|
|
191
|
-
# * Shipment/Package/Description element
|
|
192
|
-
|
|
193
|
-
package_node << XmlNode.new("PackagingType") do |packaging_type|
|
|
194
|
-
packaging_type << XmlNode.new("Code", '02')
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
package_node << XmlNode.new("Dimensions") do |dimensions|
|
|
198
|
-
dimensions << XmlNode.new("UnitOfMeasurement") do |units|
|
|
199
|
-
units << XmlNode.new("Code", imperial ? 'IN' : 'CM')
|
|
200
|
-
end
|
|
201
|
-
[:length,:width,:height].each do |axis|
|
|
202
|
-
value = ((imperial ? package.inches(axis) : package.cm(axis)).to_f*1000).round/1000.0 # 3 decimals
|
|
203
|
-
dimensions << XmlNode.new(axis.to_s.capitalize, [value,0.1].max)
|
|
204
|
-
end
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
package_node << XmlNode.new("PackageWeight") do |package_weight|
|
|
208
|
-
package_weight << XmlNode.new("UnitOfMeasurement") do |units|
|
|
209
|
-
units << XmlNode.new("Code", imperial ? 'LBS' : 'KGS')
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
value = ((imperial ? package.lbs : package.kgs).to_f*1000).round/1000.0 # 3 decimals
|
|
213
|
-
package_weight << XmlNode.new("Weight", [value,0.1].max)
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
# not implemented: * Shipment/Package/LargePackageIndicator element
|
|
217
|
-
# * Shipment/Package/ReferenceNumber element
|
|
218
|
-
# * Shipment/Package/PackageServiceOptions element
|
|
219
|
-
# * Shipment/Package/AdditionalHandling element
|
|
220
|
-
end
|
|
221
|
-
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
# not implemented: * Shipment/ShipmentServiceOptions element
|
|
225
|
-
if options[:origin_account]
|
|
226
|
-
shipment << XmlNode.new("RateInformation") do |rate_info_node|
|
|
227
|
-
rate_info_node << XmlNode.new("NegotiatedRatesIndicator")
|
|
228
|
-
end
|
|
229
|
-
end
|
|
230
|
-
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
end
|
|
234
|
-
xml_request.to_s
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
def build_tracking_request(tracking_number, options={})
|
|
238
|
-
xml_request = XmlNode.new('TrackRequest') do |root_node|
|
|
239
|
-
root_node << XmlNode.new('Request') do |request|
|
|
240
|
-
request << XmlNode.new('RequestAction', 'Track')
|
|
241
|
-
request << XmlNode.new('RequestOption', '1')
|
|
242
|
-
end
|
|
243
|
-
root_node << XmlNode.new('TrackingNumber', tracking_number.to_s)
|
|
244
|
-
end
|
|
245
|
-
xml_request.to_s
|
|
246
|
-
end
|
|
247
|
-
|
|
248
|
-
def build_location_node(name,location,options={})
|
|
249
|
-
# not implemented: * Shipment/Shipper/Name element
|
|
250
|
-
# * Shipment/(ShipTo|ShipFrom)/CompanyName element
|
|
251
|
-
# * Shipment/(Shipper|ShipTo|ShipFrom)/AttentionName element
|
|
252
|
-
# * Shipment/(Shipper|ShipTo|ShipFrom)/TaxIdentificationNumber element
|
|
253
|
-
location_node = XmlNode.new(name) do |location_node|
|
|
254
|
-
location_node << XmlNode.new('PhoneNumber', location.phone.gsub(/[^\d]/,'')) unless location.phone.blank?
|
|
255
|
-
location_node << XmlNode.new('FaxNumber', location.fax.gsub(/[^\d]/,'')) unless location.fax.blank?
|
|
256
|
-
|
|
257
|
-
if name == 'Shipper' and (origin_account = @options[:origin_account] || options[:origin_account])
|
|
258
|
-
location_node << XmlNode.new('ShipperNumber', origin_account)
|
|
259
|
-
elsif name == 'ShipTo' and (destination_account = @options[:destination_account] || options[:destination_account])
|
|
260
|
-
location_node << XmlNode.new('ShipperAssignedIdentificationNumber', destination_account)
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
location_node << XmlNode.new('Address') do |address|
|
|
264
|
-
address << XmlNode.new("AddressLine1", location.address1) unless location.address1.blank?
|
|
265
|
-
address << XmlNode.new("AddressLine2", location.address2) unless location.address2.blank?
|
|
266
|
-
address << XmlNode.new("AddressLine3", location.address3) unless location.address3.blank?
|
|
267
|
-
address << XmlNode.new("City", location.city) unless location.city.blank?
|
|
268
|
-
address << XmlNode.new("StateProvinceCode", location.province) unless location.province.blank?
|
|
269
|
-
# StateProvinceCode required for negotiated rates but not otherwise, for some reason
|
|
270
|
-
address << XmlNode.new("PostalCode", location.postal_code) unless location.postal_code.blank?
|
|
271
|
-
address << XmlNode.new("CountryCode", location.country_code(:alpha2)) unless location.country_code(:alpha2).blank?
|
|
272
|
-
address << XmlNode.new("ResidentialAddressIndicator", true) unless location.commercial? # the default should be that UPS returns residential rates for destinations that it doesn't know about
|
|
273
|
-
# not implemented: Shipment/(Shipper|ShipTo|ShipFrom)/Address/ResidentialAddressIndicator element
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
end
|
|
277
|
-
|
|
278
|
-
def parse_rate_response(origin, destination, packages, response, options={})
|
|
279
|
-
rates = []
|
|
280
|
-
File.open('/Users/timmy/code/active_shipping/foo.txt', 'w') {|f| f.write(response) }
|
|
281
|
-
xml = REXML::Document.new(response)
|
|
282
|
-
success = response_success?(xml)
|
|
283
|
-
message = response_message(xml)
|
|
284
|
-
|
|
285
|
-
if success
|
|
286
|
-
rate_estimates = []
|
|
287
|
-
|
|
288
|
-
xml.elements.each('/*/RatedShipment') do |rated_shipment|
|
|
289
|
-
service_code = rated_shipment.get_text('Service/Code').to_s
|
|
290
|
-
days_to_delivery = rated_shipment.get_text('GuaranteedDaysToDelivery').to_s.to_i
|
|
291
|
-
delivery_date = days_to_delivery >= 1 ? days_to_delivery.days.from_now.strftime("%Y-%m-%d") : nil
|
|
292
|
-
rate_estimates << RateEstimate.new(origin, destination, @@name,
|
|
293
|
-
service_name_for(origin, service_code),
|
|
294
|
-
:total_price => rated_shipment.get_text('TotalCharges/MonetaryValue').to_s.to_f,
|
|
295
|
-
:currency => rated_shipment.get_text('TotalCharges/CurrencyCode').to_s,
|
|
296
|
-
:service_code => service_code,
|
|
297
|
-
:packages => packages,
|
|
298
|
-
:delivery_range => [delivery_date],
|
|
299
|
-
:negotiated_rate => rated_shipment.get_text('NegotiatedRates/NetSummaryCharges/GrandTotal/MonetaryValue').to_s.to_f)
|
|
300
|
-
end
|
|
301
|
-
end
|
|
302
|
-
RateResponse.new(success, message, Hash.from_xml(response).values.first, :rates => rate_estimates, :xml => response, :request => last_request)
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
def parse_tracking_response(response, options={})
|
|
306
|
-
xml = REXML::Document.new(response)
|
|
307
|
-
success = response_success?(xml)
|
|
308
|
-
message = response_message(xml)
|
|
309
|
-
|
|
310
|
-
if success
|
|
311
|
-
tracking_number, origin, destination, status_code, status_description = nil
|
|
312
|
-
delivered, exception = false
|
|
313
|
-
exception_event = nil
|
|
314
|
-
shipment_events = []
|
|
315
|
-
status = {}
|
|
316
|
-
scheduled_delivery_date = nil
|
|
317
|
-
|
|
318
|
-
first_shipment = xml.elements['/*/Shipment']
|
|
319
|
-
first_package = first_shipment.elements['Package']
|
|
320
|
-
tracking_number = first_shipment.get_text('ShipmentIdentificationNumber | Package/TrackingNumber').to_s
|
|
321
|
-
|
|
322
|
-
# Build status hash
|
|
323
|
-
status_node = first_package.elements['Activity/Status/StatusType']
|
|
324
|
-
status_code = status_node.get_text('Code').to_s
|
|
325
|
-
status_description = status_node.get_text('Description').to_s
|
|
326
|
-
status = TRACKING_STATUS_CODES[status_code]
|
|
327
|
-
|
|
328
|
-
if status_description =~ /out.*delivery/i
|
|
329
|
-
status = :out_for_delivery
|
|
330
|
-
end
|
|
331
|
-
|
|
332
|
-
origin, destination = %w{Shipper ShipTo}.map do |location|
|
|
333
|
-
location_from_address_node(first_shipment.elements["#{location}/Address"])
|
|
334
|
-
end
|
|
335
|
-
|
|
336
|
-
# Get scheduled delivery date
|
|
337
|
-
unless status == :delivered
|
|
338
|
-
scheduled_delivery_date = parse_ups_datetime({
|
|
339
|
-
:date => first_shipment.get_text('ScheduledDeliveryDate'),
|
|
340
|
-
:time => nil
|
|
341
|
-
})
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
activities = first_package.get_elements('Activity')
|
|
345
|
-
unless activities.empty?
|
|
346
|
-
shipment_events = activities.map do |activity|
|
|
347
|
-
description = activity.get_text('Status/StatusType/Description').to_s
|
|
348
|
-
zoneless_time = if (time = activity.get_text('Time')) &&
|
|
349
|
-
(date = activity.get_text('Date'))
|
|
350
|
-
time, date = time.to_s, date.to_s
|
|
351
|
-
hour, minute, second = time.scan(/\d{2}/)
|
|
352
|
-
year, month, day = date[0..3], date[4..5], date[6..7]
|
|
353
|
-
Time.utc(year, month, day, hour, minute, second)
|
|
354
|
-
end
|
|
355
|
-
location = location_from_address_node(activity.elements['ActivityLocation/Address'])
|
|
356
|
-
ShipmentEvent.new(description, zoneless_time, location)
|
|
357
|
-
end
|
|
358
|
-
|
|
359
|
-
shipment_events = shipment_events.sort_by(&:time)
|
|
360
|
-
|
|
361
|
-
# UPS will sometimes archive a shipment, stripping all shipment activity except for the delivery
|
|
362
|
-
# event (see test/fixtures/xml/delivered_shipment_without_events_tracking_response.xml for an example).
|
|
363
|
-
# This adds an origin event to the shipment activity in such cases.
|
|
364
|
-
if origin && !(shipment_events.count == 1 && status == :delivered)
|
|
365
|
-
first_event = shipment_events[0]
|
|
366
|
-
same_country = origin.country_code(:alpha2) == first_event.location.country_code(:alpha2)
|
|
367
|
-
same_or_blank_city = first_event.location.city.blank? or first_event.location.city == origin.city
|
|
368
|
-
origin_event = ShipmentEvent.new(first_event.name, first_event.time, origin)
|
|
369
|
-
if same_country and same_or_blank_city
|
|
370
|
-
shipment_events[0] = origin_event
|
|
371
|
-
else
|
|
372
|
-
shipment_events.unshift(origin_event)
|
|
373
|
-
end
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
# Has the shipment been delivered?
|
|
377
|
-
if status == :delivered
|
|
378
|
-
if !destination
|
|
379
|
-
destination = shipment_events[-1].location
|
|
380
|
-
end
|
|
381
|
-
shipment_events[-1] = ShipmentEvent.new(shipment_events.last.name, shipment_events.last.time, destination)
|
|
382
|
-
end
|
|
383
|
-
end
|
|
384
|
-
|
|
385
|
-
end
|
|
386
|
-
TrackingResponse.new(success, message, Hash.from_xml(response).values.first,
|
|
387
|
-
:carrier => @@name,
|
|
388
|
-
:xml => response,
|
|
389
|
-
:request => last_request,
|
|
390
|
-
:status => status,
|
|
391
|
-
:status_code => status_code,
|
|
392
|
-
:status_description => status_description,
|
|
393
|
-
:scheduled_delivery_date => scheduled_delivery_date,
|
|
394
|
-
:shipment_events => shipment_events,
|
|
395
|
-
:delivered => delivered,
|
|
396
|
-
:exception => exception,
|
|
397
|
-
:exception_event => exception_event,
|
|
398
|
-
:origin => origin,
|
|
399
|
-
:destination => destination,
|
|
400
|
-
:tracking_number => tracking_number)
|
|
401
|
-
end
|
|
402
|
-
|
|
403
|
-
def location_from_address_node(address)
|
|
404
|
-
return nil unless address
|
|
405
|
-
Location.new(
|
|
406
|
-
:country => node_text_or_nil(address.elements['CountryCode']),
|
|
407
|
-
:postal_code => node_text_or_nil(address.elements['PostalCode']),
|
|
408
|
-
:province => node_text_or_nil(address.elements['StateProvinceCode']),
|
|
409
|
-
:city => node_text_or_nil(address.elements['City']),
|
|
410
|
-
:address1 => node_text_or_nil(address.elements['AddressLine1']),
|
|
411
|
-
:address2 => node_text_or_nil(address.elements['AddressLine2']),
|
|
412
|
-
:address3 => node_text_or_nil(address.elements['AddressLine3'])
|
|
413
|
-
)
|
|
414
|
-
end
|
|
415
|
-
|
|
416
|
-
def parse_ups_datetime(options = {})
|
|
417
|
-
time, date = options[:time].to_s, options[:date].to_s
|
|
418
|
-
if time.nil?
|
|
419
|
-
hour, minute, second = 0
|
|
420
|
-
else
|
|
421
|
-
hour, minute, second = time.scan(/\d{2}/)
|
|
422
|
-
end
|
|
423
|
-
year, month, day = date[0..3], date[4..5], date[6..7]
|
|
424
|
-
|
|
425
|
-
Time.utc(year, month, day, hour, minute, second)
|
|
426
|
-
end
|
|
427
|
-
|
|
428
|
-
def response_success?(xml)
|
|
429
|
-
xml.get_text('/*/Response/ResponseStatusCode').to_s == '1'
|
|
430
|
-
end
|
|
431
|
-
|
|
432
|
-
def response_message(xml)
|
|
433
|
-
xml.get_text('/*/Response/Error/ErrorDescription | /*/Response/ResponseStatusDescription').to_s
|
|
434
|
-
end
|
|
435
|
-
|
|
436
|
-
def commit(action, request, test = false)
|
|
437
|
-
ssl_post("#{test ? TEST_URL : LIVE_URL}/#{RESOURCES[action]}", request)
|
|
438
|
-
end
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
def service_name_for(origin, code)
|
|
442
|
-
origin = origin.country_code(:alpha2)
|
|
443
|
-
|
|
444
|
-
name = case origin
|
|
445
|
-
when "CA" then CANADA_ORIGIN_SERVICES[code]
|
|
446
|
-
when "MX" then MEXICO_ORIGIN_SERVICES[code]
|
|
447
|
-
when *EU_COUNTRY_CODES then EU_ORIGIN_SERVICES[code]
|
|
448
|
-
end
|
|
449
|
-
|
|
450
|
-
name ||= OTHER_NON_US_ORIGIN_SERVICES[code] unless name == 'US'
|
|
451
|
-
name ||= DEFAULT_SERVICES[code]
|
|
452
|
-
end
|
|
453
|
-
|
|
454
|
-
end
|
|
455
|
-
end
|
|
456
|
-
end
|