active_shipping 0.1.4 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +2 -0
- data/README.markdown +0 -3
- data/lib/active_merchant/common.rb +14 -0
- data/lib/{active_shipping/lib → active_merchant/common}/connection.rb +2 -0
- data/lib/{active_shipping/lib → active_merchant/common}/country.rb +0 -0
- data/lib/active_merchant/common/error.rb +26 -0
- data/lib/active_merchant/common/post_data.rb +24 -0
- data/lib/{active_shipping/lib → active_merchant/common}/posts_data.rb +0 -0
- data/lib/{active_shipping/lib → active_merchant/common}/requires_parameters.rb +0 -0
- data/lib/{active_shipping/lib → active_merchant/common}/utils.rb +0 -0
- data/lib/{active_shipping/lib → active_merchant/common}/validateable.rb +0 -0
- data/lib/active_shipping.rb +10 -22
- data/lib/active_shipping/shipping/base.rb +2 -1
- data/lib/active_shipping/shipping/carrier.rb +0 -5
- data/lib/active_shipping/shipping/carriers.rb +2 -1
- data/lib/active_shipping/shipping/carriers/fedex.rb +7 -191
- data/lib/active_shipping/shipping/carriers/kunaki.rb +165 -0
- data/lib/active_shipping/shipping/carriers/ups.rb +2 -1
- data/lib/active_shipping/shipping/carriers/usps.rb +0 -85
- data/lib/active_shipping/shipping/location.rb +1 -4
- data/lib/active_shipping/shipping/response.rb +1 -4
- data/lib/active_shipping/version.rb +3 -0
- data/lib/vendor/quantified/lib/quantified.rb +5 -3
- metadata +68 -102
- data/.gitignore +0 -8
- data/Rakefile +0 -51
- data/VERSION +0 -1
- data/active_shipping.gemspec +0 -152
- data/init.rb +0 -1
- data/lib/active_shipping/lib/error.rb +0 -4
- data/lib/active_shipping/lib/post_data.rb +0 -22
- data/lib/active_shipping/shipping/contact.rb +0 -18
- data/lib/active_shipping/shipping/label.rb +0 -31
- data/lib/active_shipping/shipping/location_response.rb +0 -14
- data/lib/active_shipping/shipping/party.rb +0 -15
- data/lib/active_shipping/shipping/return_label_response.rb +0 -14
- data/lib/active_shipping/shipping/return_shipment.rb +0 -14
- data/lib/active_shipping/shipping/shipment.rb +0 -73
- data/test/fixtures.example.yml +0 -13
- data/test/fixtures/xml/fedex/ottawa_to_beverly_hills_rate_request.xml +0 -67
- data/test/fixtures/xml/fedex/ottawa_to_beverly_hills_rate_response.xml +0 -213
- data/test/fixtures/xml/fedex/tracking_request.xml +0 -27
- data/test/fixtures/xml/fedex/tracking_response.xml +0 -153
- data/test/fixtures/xml/shipwire/international_rates_response.xml +0 -17
- data/test/fixtures/xml/shipwire/invalid_credentials_response.xml +0 -4
- data/test/fixtures/xml/shipwire/new_carrier_rate_response.xml +0 -18
- data/test/fixtures/xml/shipwire/no_rates_response.xml +0 -7
- data/test/fixtures/xml/shipwire/rates_response.xml +0 -36
- data/test/fixtures/xml/ups/example_tracking_response.xml +0 -53
- data/test/fixtures/xml/ups/shipment_from_tiger_direct.xml +0 -222
- data/test/fixtures/xml/ups/test_real_home_as_residential_destination_response.xml +0 -1
- data/test/fixtures/xml/usps/beverly_hills_to_ottawa_book_rate_response.xml +0 -85
- data/test/fixtures/xml/usps/beverly_hills_to_ottawa_book_wii_rate_response.xml +0 -168
- data/test/fixtures/xml/usps/beverly_hills_to_ottawa_wii_rate_response.xml +0 -85
- data/test/fixtures/xml/usps/example_tracking_response.xml +0 -104
- data/test/fixtures/xml/usps/multi_tracking_example.xml +0 -105
- data/test/party_factory.rb +0 -29
- data/test/remote/fedex_test.rb +0 -160
- data/test/remote/shipwire_test.rb +0 -88
- data/test/remote/ups_test.rb +0 -207
- data/test/remote/usps_test.rb +0 -184
- data/test/shipment_factory.rb +0 -27
- data/test/test_helper.rb +0 -171
- data/test/unit/base_test.rb +0 -18
- data/test/unit/carriers/fedex_test.rb +0 -78
- data/test/unit/carriers/shipwire_test.rb +0 -130
- data/test/unit/carriers/ups_test.rb +0 -81
- data/test/unit/carriers/usps_test.rb +0 -206
- data/test/unit/location_test.rb +0 -46
- data/test/unit/package_test.rb +0 -65
- data/test/unit/party_test.rb +0 -20
- data/test/unit/response_test.rb +0 -10
- data/test/unit/shipment_test.rb +0 -43
data/CHANGELOG
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
* Assume test_helper is in load path when running tests [cody]
|
2
|
+
* Add support Kunaki rating service [cody]
|
1
3
|
* Require active_support instead of activesupport to avoid deprecation warning in Rails 2.3.5 [cody]
|
2
4
|
* Remove ftools for Rails 1.9 compatibility and remove xml logging, as logging is now included in the connection [cody]
|
3
5
|
* Update connection code from ActiveMerchant [cody]
|
data/README.markdown
CHANGED
@@ -38,8 +38,6 @@ You will need to get [Git][] if you don't have it. Then:
|
|
38
38
|
|
39
39
|
Active Shipping includes an init.rb file. This means that Rails will automatically load it on startup. Check out [git-archive][] for exporting the file tree from your repository to your vendor directory.
|
40
40
|
|
41
|
-
Gem and tarball forthcoming on rubyforge.
|
42
|
-
|
43
41
|
[Git]:http://git.or.cz/
|
44
42
|
[git-archive]:http://www.kernel.org/pub/software/scm/git/docs/git-archive.html
|
45
43
|
|
@@ -144,7 +142,6 @@ Gem and tarball forthcoming on rubyforge.
|
|
144
142
|
|
145
143
|
* proper documentation
|
146
144
|
* proper offline testing for carriers in addition to the remote tests
|
147
|
-
* package into a gem
|
148
145
|
* carrier code template generator
|
149
146
|
* more carriers
|
150
147
|
* integrate with ActiveMerchant
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ActiveMerchant
|
2
|
+
autoload :Connection, 'active_merchant/common/connection'
|
3
|
+
autoload :Country, 'active_merchant/common/country'
|
4
|
+
autoload :ActiveMerchantError, 'active_merchant/common/error'
|
5
|
+
autoload :ConnectionError, 'active_merchant/common/error'
|
6
|
+
autoload :RetriableConnectionError, 'active_merchant/common/error'
|
7
|
+
autoload :ResponseError, 'active_merchant/common/error'
|
8
|
+
autoload :ClientCertificateError, 'active_merchant/common/error'
|
9
|
+
autoload :PostData, 'active_merchant/common/post_data'
|
10
|
+
autoload :PostsData, 'active_merchant/common/posts_data'
|
11
|
+
autoload :RequiresParameters, 'active_merchant/common/requires_parameters'
|
12
|
+
autoload :Utils, 'active_merchant/common/utils'
|
13
|
+
autoload :Validateable, 'active_merchant/common/validateable'
|
14
|
+
end
|
@@ -80,6 +80,8 @@ module ActiveMerchant
|
|
80
80
|
raise ConnectionError, "The remote server reset the connection"
|
81
81
|
rescue Errno::ECONNREFUSED => e
|
82
82
|
raise RetriableConnectionError, "The remote server refused the connection"
|
83
|
+
rescue OpenSSL::X509::CertificateError => e
|
84
|
+
raise ClientCertificateError, "The remote server did not accept the provided SSL certificate"
|
83
85
|
rescue Timeout::Error, Errno::ETIMEDOUT => e
|
84
86
|
raise ConnectionError, "The connection to the remote server timed out"
|
85
87
|
end
|
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ActiveMerchant #:nodoc:
|
2
|
+
class ActiveMerchantError < StandardError #:nodoc:
|
3
|
+
end
|
4
|
+
|
5
|
+
class ConnectionError < ActiveMerchantError # :nodoc:
|
6
|
+
end
|
7
|
+
|
8
|
+
class RetriableConnectionError < ConnectionError # :nodoc:
|
9
|
+
end
|
10
|
+
|
11
|
+
class ResponseError < ActiveMerchantError # :nodoc:
|
12
|
+
attr_reader :response
|
13
|
+
|
14
|
+
def initialize(response, message = nil)
|
15
|
+
@response = response
|
16
|
+
@message = message
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_s
|
20
|
+
"Failed with #{response.code} #{response.message if response.respond_to?(:message)}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class ClientCertificateError < ActiveMerchantError # :nodoc
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module ActiveMerchant
|
4
|
+
class PostData < Hash
|
5
|
+
class_inheritable_accessor :required_fields, :instance_writer => false
|
6
|
+
self.required_fields = []
|
7
|
+
|
8
|
+
def []=(key, value)
|
9
|
+
return if value.blank? && !required?(key)
|
10
|
+
super
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_post_data
|
14
|
+
collect { |key, value| "#{key}=#{CGI.escape(value.to_s)}" }.join("&")
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method :to_s, :to_post_data
|
18
|
+
|
19
|
+
private
|
20
|
+
def required?(key)
|
21
|
+
required_fields.include?(key)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/active_shipping.rb
CHANGED
@@ -23,20 +23,19 @@
|
|
23
23
|
|
24
24
|
$:.unshift File.dirname(__FILE__)
|
25
25
|
|
26
|
-
|
27
|
-
require 'active_support'
|
26
|
+
begin
|
27
|
+
require 'active_support/all'
|
28
|
+
rescue LoadError => e
|
29
|
+
require 'rubygems'
|
30
|
+
gem "activesupport", ">= 2.3.5"
|
31
|
+
require "active_support/all"
|
32
|
+
end
|
28
33
|
|
29
|
-
|
30
|
-
|
31
|
-
require 'quantified/mass'
|
32
|
-
require 'quantified/length'
|
34
|
+
autoload :XmlNode, 'vendor/xml_node/lib/xml_node'
|
35
|
+
autoload :Quantified, 'vendor/quantified/lib/quantified'
|
33
36
|
|
34
37
|
require 'net/https'
|
35
|
-
require '
|
36
|
-
require 'active_shipping/lib/requires_parameters'
|
37
|
-
require 'active_shipping/lib/connection'
|
38
|
-
require 'active_shipping/lib/posts_data'
|
39
|
-
require 'active_shipping/lib/country'
|
38
|
+
require 'active_merchant/common'
|
40
39
|
|
41
40
|
require 'active_shipping/shipping/base'
|
42
41
|
require 'active_shipping/shipping/response'
|
@@ -45,17 +44,6 @@ require 'active_shipping/shipping/tracking_response'
|
|
45
44
|
require 'active_shipping/shipping/package'
|
46
45
|
require 'active_shipping/shipping/location'
|
47
46
|
require 'active_shipping/shipping/rate_estimate'
|
48
|
-
require 'active_shipping/shipping/location_response'
|
49
|
-
require 'active_shipping/shipping/return_label_response'
|
50
|
-
|
51
|
-
require 'active_shipping/shipping/contact'
|
52
|
-
require 'active_shipping/shipping/party'
|
53
|
-
require 'active_shipping/shipping/label'
|
54
|
-
|
55
|
-
require 'active_shipping/shipping/shipment'
|
56
|
-
require 'active_shipping/shipping/return_shipment'
|
57
|
-
|
58
47
|
require 'active_shipping/shipping/shipment_event'
|
59
48
|
require 'active_shipping/shipping/carrier'
|
60
49
|
require 'active_shipping/shipping/carriers'
|
61
|
-
|
@@ -5,7 +5,8 @@ module ActiveMerchant
|
|
5
5
|
self.mode = :production
|
6
6
|
|
7
7
|
def self.carrier(name)
|
8
|
-
ActiveMerchant::Shipping::Carriers.all.find {|c| c.name.downcase == name}
|
8
|
+
ActiveMerchant::Shipping::Carriers.all.find {|c| c.name.downcase == name.to_s.downcase} ||
|
9
|
+
raise(NameError, "unknown carrier #{name}")
|
9
10
|
end
|
10
11
|
end
|
11
12
|
end
|
@@ -3,13 +3,14 @@ require 'active_shipping/shipping/carriers/ups'
|
|
3
3
|
require 'active_shipping/shipping/carriers/usps'
|
4
4
|
require 'active_shipping/shipping/carriers/fedex'
|
5
5
|
require 'active_shipping/shipping/carriers/shipwire'
|
6
|
+
require 'active_shipping/shipping/carriers/kunaki'
|
6
7
|
|
7
8
|
module ActiveMerchant
|
8
9
|
module Shipping
|
9
10
|
module Carriers
|
10
11
|
class <<self
|
11
12
|
def all
|
12
|
-
[BogusCarrier, UPS, USPS, FedEx, Shipwire]
|
13
|
+
[BogusCarrier, UPS, USPS, FedEx, Shipwire, Kunaki]
|
13
14
|
end
|
14
15
|
end
|
15
16
|
end
|
@@ -106,152 +106,8 @@ module ActiveMerchant
|
|
106
106
|
response = commit(save_request(tracking_request), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
|
107
107
|
parse_tracking_response(response, options)
|
108
108
|
end
|
109
|
-
|
110
|
-
def get_return_label(shipment, options = {})
|
111
|
-
req = build_return_label_request(shipment)
|
112
|
-
response = commit(save_request(req), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
|
113
|
-
|
114
|
-
parse_return_label_response(response, shipment)
|
115
|
-
end
|
116
|
-
|
117
|
-
def validate_location(location, options = {})
|
118
|
-
req = build_location_validation_request(location)
|
119
|
-
response = commit(save_request(req), (options[:test] || false)).gsub(/<(\/)?.*?\:(.*?)>/, '<\1\2>')
|
120
|
-
|
121
|
-
parse_location_validation_request(response, location)
|
122
|
-
end
|
123
|
-
|
109
|
+
|
124
110
|
protected
|
125
|
-
def build_location_validation_request(location)
|
126
|
-
xml_request = XmlNode.new('AddressValidationRequest',
|
127
|
-
'xmlns' => 'http://fedex.com/ws/addressvalidation/v2') do |root_node|
|
128
|
-
|
129
|
-
root_node << build_request_header
|
130
|
-
root_node << XmlNode.new('Version') do |version_node|
|
131
|
-
version_node << XmlNode.new('ServiceId', 'aval')
|
132
|
-
version_node << XmlNode.new('Major', '2')
|
133
|
-
version_node << XmlNode.new('Intermediate', '0')
|
134
|
-
version_node << XmlNode.new('Minor', '0')
|
135
|
-
end
|
136
|
-
|
137
|
-
root_node << XmlNode.new('RequestTimestamp', Time.now)
|
138
|
-
root_node << XmlNode.new('Options', '')
|
139
|
-
root_node << XmlNode.new('AddressesToValidate') do |v|
|
140
|
-
v << build_party_location_node('Address', location)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
xml_request.to_s
|
145
|
-
end
|
146
|
-
|
147
|
-
def parse_location_validation_request(response, location)
|
148
|
-
xml = REXML::Document.new(response)
|
149
|
-
success = response_success?(xml)
|
150
|
-
message = response_message(xml)
|
151
|
-
|
152
|
-
if success
|
153
|
-
parent = xml.elements.first
|
154
|
-
location.valid = parent.elements['//DeliveryPointValidation'][0] == 'CONFIRMED'
|
155
|
-
location.score = parent.elements['//Score'][0].to_s.to_i
|
156
|
-
else
|
157
|
-
location.valid = false
|
158
|
-
location.score = 0
|
159
|
-
message
|
160
|
-
end
|
161
|
-
LocationResponse.new(success, message, Hash.from_xml(response),
|
162
|
-
:xml => response,
|
163
|
-
:request => last_request,
|
164
|
-
:location => location
|
165
|
-
)
|
166
|
-
end
|
167
|
-
|
168
|
-
def parse_return_label_response(response, shipment)
|
169
|
-
xml = REXML::Document.new(response)
|
170
|
-
success = response_success?(xml)
|
171
|
-
message = response_message(xml)
|
172
|
-
|
173
|
-
if success
|
174
|
-
parent = xml.elements.first
|
175
|
-
shipment.label.image = Base64.decode64(parent.elements['//Image'][0].to_s)
|
176
|
-
shipment.tracking_number = parent.elements['//TrackingNumber'][0].to_s
|
177
|
-
shipment.transit_time = parent.elements['//TransitTime'][0].to_s
|
178
|
-
|
179
|
-
shipment
|
180
|
-
end
|
181
|
-
ReturnLabelResponse.new(success, message, Hash.from_xml(response),
|
182
|
-
:xml => response,
|
183
|
-
:request => last_request,
|
184
|
-
:shipment => shipment
|
185
|
-
)
|
186
|
-
end
|
187
|
-
|
188
|
-
def build_return_label_request(shipment)
|
189
|
-
xml_request = XmlNode.new('ProcessShipmentRequest',
|
190
|
-
'xmlns' => 'http://fedex.com/ws/ship/v7') do |root_node|
|
191
|
-
root_node << build_request_header
|
192
|
-
root_node << XmlNode.new('Version') do |version_node|
|
193
|
-
version_node << XmlNode.new('ServiceId', 'ship')
|
194
|
-
version_node << XmlNode.new('Major', '7')
|
195
|
-
version_node << XmlNode.new('Intermediate', '0')
|
196
|
-
version_node << XmlNode.new('Minor', '0')
|
197
|
-
end
|
198
|
-
|
199
|
-
root_node << XmlNode.new('RequestedShipment') do |rs|
|
200
|
-
rs << XmlNode.new('ShipTimestamp', shipment.ship_at || Time.now)
|
201
|
-
rs << XmlNode.new('DropoffType', shipment.dropoff_type)
|
202
|
-
rs << XmlNode.new('ServiceType', shipment.service)
|
203
|
-
rs << XmlNode.new('PackagingType', shipment.packaging_type)
|
204
|
-
rs << XmlNode.new('TotalWeight') do |t|
|
205
|
-
t << XmlNode.new('Units', shipment.total_weight_units)
|
206
|
-
t << XmlNode.new('Value', shipment.total_weight_value)
|
207
|
-
end
|
208
|
-
if shipment.total_insured_amount
|
209
|
-
rs << XmlNode.new('TotalInsuredValue') do |ins|
|
210
|
-
ins << XmlNode.new('Currency', shipment.total_insured_currency)
|
211
|
-
ins << XmlNode.new('Amount', shipment.total_insured_amount)
|
212
|
-
end
|
213
|
-
end
|
214
|
-
rs << build_party_node('Shipper', shipment.shipper)
|
215
|
-
rs << build_party_node('Recipient', shipment.recipient)
|
216
|
-
rs << XmlNode.new('ShippingChargesPayment') do |payment|
|
217
|
-
payment << XmlNode.new('PaymentType', shipment.payment_type)
|
218
|
-
payment << XmlNode.new('Payor') do |payor|
|
219
|
-
payor << XmlNode.new('AccountNumber', shipment.payor_account_number)
|
220
|
-
payor << XmlNode.new('CountryCode', shipment.payor_account_country.code(:alpha2))
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
rs << XmlNode.new('SpecialServicesRequested') do |s|
|
225
|
-
s << XmlNode.new('SpecialServiceTypes', 'RETURN_SHIPMENT')
|
226
|
-
s << XmlNode.new('ReturnShipmentDetail') do |d|
|
227
|
-
d << XmlNode.new('ReturnType', shipment.return_type)
|
228
|
-
d << XmlNode.new('Rma') do |rma|
|
229
|
-
rma << XmlNode.new('Number', shipment.rma_number)
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
rs << XmlNode.new('LabelSpecification') do |spec|
|
235
|
-
spec << XmlNode.new('LabelFormatType',
|
236
|
-
shipment.label.format_type || 'COMMON2D')
|
237
|
-
spec << XmlNode.new('ImageType', shipment.label.image_type)
|
238
|
-
spec << XmlNode.new('LabelStockType', 'PAPER_4X6')
|
239
|
-
end
|
240
|
-
rs << XmlNode.new('RateRequestTypes', shipment.rate_request_type)
|
241
|
-
rs << XmlNode.new('PackageCount', shipment.package_count)
|
242
|
-
rs << XmlNode.new('PackageDetail', 'PACKAGE_SUMMARY')
|
243
|
-
(1..shipment.package_count).each do |package_index|
|
244
|
-
rs << XmlNode.new('RequestedPackageLineItems') do |p|
|
245
|
-
p << XmlNode.new('SequenceNumber', package_index)
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
250
|
-
|
251
|
-
xml_request.to_s
|
252
|
-
end
|
253
|
-
|
254
|
-
|
255
111
|
def build_rate_request(origin, destination, packages, options={})
|
256
112
|
imperial = ['US','LR','MM'].include?(origin.country_code(:alpha2))
|
257
113
|
|
@@ -349,47 +205,6 @@ module ActiveMerchant
|
|
349
205
|
[web_authentication_detail, client_detail, trasaction_detail]
|
350
206
|
end
|
351
207
|
|
352
|
-
def build_party_node(name, party)
|
353
|
-
XmlNode.new(name) do |xml_node|
|
354
|
-
if party.account_number
|
355
|
-
xml_node << XmlNode.new('AccountNumber', party.account_number)
|
356
|
-
end
|
357
|
-
|
358
|
-
if party.contact
|
359
|
-
xml_node << XmlNode.new('Contact') do |c|
|
360
|
-
c << XmlNode.new('PersonName', party.contact.name)
|
361
|
-
c << XmlNode.new('Title', party.contact.title)
|
362
|
-
c << XmlNode.new('CompanyName', party.contact.company_name)
|
363
|
-
c << XmlNode.new('PhoneNumber', party.contact.phone_number)
|
364
|
-
c << XmlNode.new('FaxNumber', party.contact.fax_number)
|
365
|
-
c << XmlNode.new('EMailAddress', party.contact.email_address)
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
|
370
|
-
if party.location
|
371
|
-
xml_node << build_party_location_node('Address', party.location)
|
372
|
-
end
|
373
|
-
end
|
374
|
-
end
|
375
|
-
|
376
|
-
def build_party_location_node(name, location)
|
377
|
-
XmlNode.new(name) do |a|
|
378
|
-
if location.address1
|
379
|
-
a << XmlNode.new('StreetLines', location.address1)
|
380
|
-
end
|
381
|
-
|
382
|
-
if location.address2
|
383
|
-
a << XmlNode.new('StreetLines', location.address2)
|
384
|
-
end
|
385
|
-
|
386
|
-
a << XmlNode.new('City', location.city)
|
387
|
-
a << XmlNode.new('StateOrProvinceCode', location.state)
|
388
|
-
a << XmlNode.new('PostalCode', location.zip)
|
389
|
-
a << XmlNode.new('CountryCode', location.country.code(:alpha2))
|
390
|
-
end
|
391
|
-
end
|
392
|
-
|
393
208
|
def build_location_node(name, location)
|
394
209
|
location_node = XmlNode.new(name) do |xml_node|
|
395
210
|
xml_node << XmlNode.new('Address') do |address_node|
|
@@ -446,9 +261,8 @@ module ActiveMerchant
|
|
446
261
|
tracking_number = tracking_details.get_text('TrackingNumber').to_s
|
447
262
|
|
448
263
|
destination_node = tracking_details.elements['DestinationAddress']
|
449
|
-
|
450
264
|
destination = Location.new(
|
451
|
-
:country => destination_node.get_text('CountryCode').to_s
|
265
|
+
:country => destination_node.get_text('CountryCode').to_s,
|
452
266
|
:province => destination_node.get_text('StateOrProvinceCode').to_s,
|
453
267
|
:city => destination_node.get_text('City').to_s
|
454
268
|
)
|
@@ -458,12 +272,14 @@ module ActiveMerchant
|
|
458
272
|
:city => event.elements['Address'].get_text('City').to_s,
|
459
273
|
:state => event.elements['Address'].get_text('StateOrProvinceCode').to_s,
|
460
274
|
:postal_code => event.elements['Address'].get_text('PostalCode').to_s,
|
461
|
-
:country => event.elements['Address'].get_text('CountryCode').to_s
|
275
|
+
:country => event.elements['Address'].get_text('CountryCode').to_s)
|
462
276
|
description = event.get_text('EventDescription').to_s
|
463
277
|
|
464
|
-
|
278
|
+
# for now, just assume UTC, even though it probably isn't
|
279
|
+
time = Time.parse("#{event.get_text('Timestamp').to_s}")
|
280
|
+
zoneless_time = Time.utc(time.year, time.month, time.mday, time.hour, time.min, time.sec)
|
465
281
|
|
466
|
-
shipment_events << ShipmentEvent.new(description,
|
282
|
+
shipment_events << ShipmentEvent.new(description, zoneless_time, location)
|
467
283
|
end
|
468
284
|
shipment_events = shipment_events.sort_by(&:time)
|
469
285
|
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'builder'
|
2
|
+
|
3
|
+
module ActiveMerchant
|
4
|
+
module Shipping
|
5
|
+
class Kunaki < Carrier
|
6
|
+
self.retry_safe = true
|
7
|
+
|
8
|
+
cattr_reader :name
|
9
|
+
@@name = "Kunaki"
|
10
|
+
|
11
|
+
URL = 'https://Kunaki.com/XMLService.ASP'
|
12
|
+
|
13
|
+
CARRIERS = [ "UPS", "USPS", "FedEx", "Royal Mail", "Parcelforce", "Pharos", "Eurotrux", "Canada Post", "DHL" ]
|
14
|
+
|
15
|
+
COUNTRIES = {
|
16
|
+
'AR' => 'Argentina',
|
17
|
+
'AU' => 'Australia',
|
18
|
+
'AT' => 'Austria',
|
19
|
+
'BE' => 'Belgium',
|
20
|
+
'BR' => 'Brazil',
|
21
|
+
'BG' => 'Bulgaria',
|
22
|
+
'CA' => 'Canada',
|
23
|
+
'CN' => 'China',
|
24
|
+
'CY' => 'Cyprus',
|
25
|
+
'CZ' => 'Czech Republic',
|
26
|
+
'DK' => 'Denmark',
|
27
|
+
'EE' => 'Estonia',
|
28
|
+
'FI' => 'Finland',
|
29
|
+
'FR' => 'France',
|
30
|
+
'DE' => 'Germany',
|
31
|
+
'GI' => 'Gibraltar',
|
32
|
+
'GR' => 'Greece',
|
33
|
+
'GL' => 'Greenland',
|
34
|
+
'HK' => 'Hong Kong',
|
35
|
+
'HU' => 'Hungary',
|
36
|
+
'IS' => 'Iceland',
|
37
|
+
'IE' => 'Ireland',
|
38
|
+
'IL' => 'Israel',
|
39
|
+
'IT' => 'Italy',
|
40
|
+
'JP' => 'Japan',
|
41
|
+
'LV' => 'Latvia',
|
42
|
+
'LI' => 'Liechtenstein',
|
43
|
+
'LT' => 'Lithuania',
|
44
|
+
'LU' => 'Luxembourg',
|
45
|
+
'MX' => 'Mexico',
|
46
|
+
'NL' => 'Netherlands',
|
47
|
+
'NZ' => 'New Zealand',
|
48
|
+
'NO' => 'Norway',
|
49
|
+
'PL' => 'Poland',
|
50
|
+
'PT' => 'Portugal',
|
51
|
+
'RO' => 'Romania',
|
52
|
+
'RU' => 'Russia',
|
53
|
+
'SG' => 'Singapore',
|
54
|
+
'SK' => 'Slovakia',
|
55
|
+
'SI' => 'Slovenia',
|
56
|
+
'ES' => 'Spain',
|
57
|
+
'SE' => 'Sweden',
|
58
|
+
'CH' => 'Switzerland',
|
59
|
+
'TW' => 'Taiwan',
|
60
|
+
'TR' => 'Turkey',
|
61
|
+
'UA' => 'Ukraine',
|
62
|
+
'GB' => 'United Kingdom',
|
63
|
+
'US' => 'United States',
|
64
|
+
'VA' => 'Vatican City',
|
65
|
+
'RS' => 'Yugoslavia',
|
66
|
+
'ME' => 'Yugoslavia'
|
67
|
+
}
|
68
|
+
|
69
|
+
def find_rates(origin, destination, packages, options = {})
|
70
|
+
requires!(options, :items)
|
71
|
+
commit(origin, destination, options)
|
72
|
+
end
|
73
|
+
|
74
|
+
def valid_credentials?
|
75
|
+
true
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def build_request(destination, options)
|
80
|
+
xml = Builder::XmlMarkup.new
|
81
|
+
xml.instruct!
|
82
|
+
xml.tag! 'ShippingOptions' do
|
83
|
+
xml.tag! 'AddressInfo' do
|
84
|
+
xml.tag! 'Country', COUNTRIES[destination.country_code]
|
85
|
+
|
86
|
+
state = ['US', 'CA'].include?(destination.country_code.to_s) ? destination.state : ''
|
87
|
+
|
88
|
+
xml.tag! 'State_Province', state
|
89
|
+
xml.tag! 'PostalCode', destination.zip
|
90
|
+
end
|
91
|
+
|
92
|
+
options[:items].each do |item|
|
93
|
+
xml.tag! 'Product' do
|
94
|
+
xml.tag! 'ProductId', item[:sku]
|
95
|
+
xml.tag! 'Quantity', item[:quantity]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
xml.target!
|
100
|
+
end
|
101
|
+
|
102
|
+
def commit(origin, destination, options)
|
103
|
+
request = build_request(destination, options)
|
104
|
+
|
105
|
+
response = parse( ssl_post(URL, request, "Content-Type" => "text/xml") )
|
106
|
+
|
107
|
+
RateResponse.new(success?(response), message_from(response), response,
|
108
|
+
:rates => build_rate_estimates(response, origin, destination)
|
109
|
+
)
|
110
|
+
end
|
111
|
+
|
112
|
+
def build_rate_estimates(response, origin, destination)
|
113
|
+
response["Options"].collect do |quote|
|
114
|
+
RateEstimate.new(origin, destination, carrier_for(quote["Description"]), quote["Description"],
|
115
|
+
:total_price => quote["Price"],
|
116
|
+
:currency => "USD"
|
117
|
+
)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def carrier_for(service)
|
122
|
+
CARRIERS.dup.find{ |carrier| service.to_s =~ /^#{carrier}/i } || service.to_s.split(" ").first
|
123
|
+
end
|
124
|
+
|
125
|
+
def parse(xml)
|
126
|
+
response = {}
|
127
|
+
response["Options"] = []
|
128
|
+
|
129
|
+
document = REXML::Document.new(sanitize(xml))
|
130
|
+
|
131
|
+
response["ErrorCode"] = parse_child_text(document.root, "ErrorCode")
|
132
|
+
response["ErrorText"] = parse_child_text(document.root, "ErrorText")
|
133
|
+
|
134
|
+
document.root.elements.each("Option") do |e|
|
135
|
+
rate = {}
|
136
|
+
rate["Description"] = parse_child_text(e, "Description")
|
137
|
+
rate["Price"] = parse_child_text(e, "Price")
|
138
|
+
response["Options"] << rate
|
139
|
+
end
|
140
|
+
response
|
141
|
+
end
|
142
|
+
|
143
|
+
def sanitize(response)
|
144
|
+
result = response.to_s
|
145
|
+
result.gsub!("\r\n", "")
|
146
|
+
result.gsub!(/<(\/)?(BODY|HTML)>/, '')
|
147
|
+
result
|
148
|
+
end
|
149
|
+
|
150
|
+
def parse_child_text(parent, name)
|
151
|
+
if element = parent.elements[name]
|
152
|
+
element.text
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def success?(response)
|
157
|
+
response["ErrorCode"] == "0"
|
158
|
+
end
|
159
|
+
|
160
|
+
def message_from(response)
|
161
|
+
response["ErrorText"]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|