shipping-calc 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,10 @@
1
+ === 0.1.7 / 2008-06-24
2
+
3
+ Improvements for DHL:
4
+ * Calculations now handle and print descriptive errors when a quote fails.
5
+ * Fixed a bug with the default ship date not being set
6
+ * Tests should now un in Mac OS X.
7
+
1
8
  === 0.1.6 / 2008-05-29
2
9
 
3
10
  * Fixed a bug in FreightQuote where the origin and destination zip code would
@@ -7,8 +7,8 @@ ShippingCalc
7
7
  DESCRIPTION:
8
8
  -----------
9
9
 
10
- Shipping Calculator written in Ruby to get quick quotes from the major
11
- carriers (UPS, DHL, FedEX, FreightQuote).
10
+ Shipping Calculator written in Ruby to get quick quotes from DHL and Freight Carriers.
11
+ We hope to support FedEx and UPS in a near future.
12
12
 
13
13
  FEATURES/PROBLEMS:
14
14
  -----------------
@@ -30,22 +30,21 @@ A simple DHL example:
30
30
  :api_password => "your_pwd",
31
31
  :shipping_key => "your_key",
32
32
  :account_num => "your_accnt",
33
- :date => Time.now, # or something...
34
- :service_code => "E", # check the docs to find out what this means
35
- :shipment_code => "P", # check the docs to find out what this means
33
+ :date => Time.now,
34
+ :service_code => "E", # check the DHL docs to find out what this means
35
+ :shipment_code => "P", # check the DHL docs to find out what this means
36
36
  :weight => 34, # weight in lbs
37
37
  :to_zip => 10001,
38
38
  :to_state => "NY"
39
39
  }
40
40
 
41
41
  d = DHL.new
42
- a = d.quote(opts)
43
- p a
42
+ q = d.quote(opts)
43
+ puts q
44
44
 
45
45
  REQUIREMENTS:
46
46
  ---------------
47
47
  * You must obtain all the DHL ShipIt data (user, password, key and account) from http://www.dhl-usa.com/TechTools/detail/TTDetail.asp?nav=TechnologyTools/Shipping/OwnSoln
48
- * REXML
49
48
 
50
49
  INSTALL:
51
50
  -------
data/README.txt CHANGED
@@ -5,8 +5,8 @@
5
5
 
6
6
  == DESCRIPTION:
7
7
 
8
- Shipping Calculator written in Ruby to get quick quotes from the major
9
- carriers (UPS, DHL, FedEX, FreightQuote).
8
+ Shipping Calculator written in Ruby to get quick quotes from DHL and Freight Carriers.
9
+ We hope to support FedEx and UPS in a near future.
10
10
 
11
11
  == FEATURES/PROBLEMS:
12
12
 
@@ -28,28 +28,27 @@ A simple DHL example:
28
28
  :api_password => "your_pwd",
29
29
  :shipping_key => "your_key",
30
30
  :account_num => "your_accnt",
31
- :date => Time.now, # or something...
32
- :service_code => "E", # check the docs to find out what this means
33
- :shipment_code => "P", # check the docs to find out what this means
31
+ :date => Time.now,
32
+ :service_code => "E", # check the DHL docs to find out what this means
33
+ :shipment_code => "P", # check the DHL docs to find out what this means
34
34
  :weight => 34, # weight in lbs
35
35
  :to_zip => 10001,
36
36
  :to_state => "NY"
37
37
  }
38
38
 
39
39
  d = DHL.new
40
- a = d.quote(opts)
41
- p a
40
+ q = d.quote(opts)
41
+ puts q
42
42
 
43
43
  == REQUIREMENTS:
44
44
 
45
45
  * You must obtain all the DHL ShipIt data (user, password, key and account) from http://www.dhl-usa.com/TechTools/detail/TTDetail.asp?nav=TechnologyTools/Shipping/OwnSoln
46
- * REXML
47
46
 
48
47
  == INSTALL:
49
48
 
50
49
  * sudo gem install shipping-calc
51
50
 
52
- == TEST
51
+ == TEST:
53
52
 
54
53
  To run the DHL tests you'll need to have a .dhl_info.yml file in your home directory with your auth info like this:
55
54
  ~/.dhl_info.yml
@@ -1,12 +1,18 @@
1
1
  require 'rubygems'
2
2
  require 'shipping_calc'
3
3
  require 'yaml'
4
+ require 'rbconfig'
4
5
  include ShippingCalc
5
6
 
6
7
  # This example requires you to have a .dhl_info.yml file in your home dir to
7
8
  # gather the login data. Check the Test secion in README.txt for more information.
8
9
  begin
9
- auth_info = YAML.load_file("/home/#{ENV["USER"]}/.dhl_info.yml")
10
+ os = Config::CONFIG["host_os"]
11
+ if os =~ /darwin/
12
+ auth_info = YAML.load_file("/Users/#{ENV["USER"]}/.dhl_info.yml")
13
+ else
14
+ auth_info = YAML.load_file("/home/#{ENV["USER"]}/.dhl_info.yml")
15
+ end
10
16
  rescue Exception
11
17
  print "You don't have a .dhl_info.yml file in your home directory. Please
12
18
  read the \"Test\" section in README.txt.\n"
@@ -22,23 +22,22 @@
22
22
  $:.unshift(File.dirname(__FILE__))
23
23
 
24
24
  require 'rubygems'
25
- require 'rexml/document'
26
- require 'net/http'
27
- require 'net/https'
28
- require 'uri'
29
-
30
- require 'shipping_calc/dhl'
31
- require 'shipping_calc/freight_quote'
25
+ require File.dirname(__FILE__) + "/shipping_calc/dhl"
26
+ require File.dirname(__FILE__) + "/shipping_calc/freight_quote"
32
27
  module ShippingCalc
33
- class ShippingCalcError < StandardError
34
- end
35
- VERSION = "0.1.6"
36
28
 
37
- US_STATES = ['AK', 'AL', 'AR', 'AZ', 'CA', 'CO', 'CT', 'DC',
29
+ VERSION = "0.1.7"
30
+
31
+ US_STATES = ['AK', 'AL', 'AR', 'AZ', 'CA', 'CO', 'CT', 'DC',
38
32
  'DE', 'FL', 'GA', 'HI', 'IA', 'ID', 'IL', 'IN',
39
33
  'KS', 'KY', 'LA', 'MA', 'MD', 'ME', 'MI', 'MN',
40
34
  'MO', 'MS', 'MT', 'NC', 'ND', 'NE', 'NH', 'NJ',
41
35
  'NM', 'NV', 'NY', 'OH', 'OK', 'OR', 'PA', 'RI',
42
36
  'SC', 'SD', 'TN', 'TX', 'UT', 'VA', 'VT', 'WA',
43
- 'WI', 'WV']
37
+ 'WI', 'WV']
38
+
39
+
40
+ class ShippingCalcError < StandardError
41
+ end
42
+
44
43
  end
@@ -26,13 +26,13 @@ require 'uri'
26
26
  include REXML
27
27
 
28
28
  module ShippingCalc
29
-
30
29
  # Current version on their website is 1.0 and can be found in:
31
30
  # https://eCommerce.airborne.com/ApiLandingTest.asp . To get full access to
32
31
  # all their stuff you have to make sure they certify your application
33
32
  # against their live platform tests. The test bed should be enough to get
34
33
  # simple calculations.
35
34
  # Currently, only shipments made inside the US are available.
35
+
36
36
  class DHL
37
37
 
38
38
  # Obtains an estimate quote from the DHL site.
@@ -53,6 +53,7 @@ module ShippingCalc
53
53
  def quote(params)
54
54
  @xml = xml = Document.new
55
55
  xml << XMLDecl.new("1.0' encoding='UTF-8")
56
+ raise ShippingCalcError.new("Invalid parameters") if params.nil?
56
57
  raise ShippingCalcError.new("Missing shipping parameters") unless params.keys.length == 10
57
58
  auth(params[:api_user], params[:api_password])
58
59
  rate_estimate(params)
@@ -60,25 +61,32 @@ module ShippingCalc
60
61
  end
61
62
 
62
63
  private
63
- # DHL gets the quotes in 2 steps, the first one is authentication. This
64
+
65
+ # DHL gets the quote in 2 steps, the first one is authentication. This
64
66
  # generates the XML for that.
65
67
  def auth(api_user, api_password)
66
- ecomm = Element.new "eCommerce"
67
- ecomm.attributes["action"] = "Request"
68
- ecomm.attributes["version"] = "1.1"
68
+ # <eCommerce action="Request" version="1.1">
69
+ # <Requestor>
70
+ # <ID>username</ID>
71
+ # <Password>password</Password>
72
+ # </Requestor>
73
+ # </eCommerce>
74
+ ecommerce = Element.new "eCommerce"
75
+ ecommerce.attributes["action"] = "Request"
76
+ ecommerce.attributes["version"] = "1.1"
69
77
 
70
78
  user = Element.new "Requestor"
71
- u_id = Element.new "ID"
72
- u_id.text = api_user
79
+ user_id = Element.new "ID"
80
+ user_id.text = api_user
73
81
 
74
- u_pwd = Element.new "Password"
75
- u_pwd.text = api_password
82
+ user_pwd = Element.new "Password"
83
+ user_pwd.text = api_password
76
84
 
77
- user << u_id
78
- user << u_pwd
85
+ user << user_id
86
+ user << user_pwd
79
87
 
80
- ecomm << user
81
- @xml << ecomm
88
+ ecommerce << user
89
+ @xml << ecommerce
82
90
  end
83
91
 
84
92
  # After having the auth message ready, we create the RateEstimate request.
@@ -96,14 +104,20 @@ module ShippingCalc
96
104
  # to_country: Recipient's country. Not used, currently DHL only supports US.
97
105
  # to_state: Recipient's state.
98
106
  def rate_estimate(params)
107
+
108
+ # <Shipment action="RateEstimate" version="1.0">
99
109
  shipment = Element.new "Shipment"
100
110
  shipment.attributes["action"] = "RateEstimate"
101
111
  shipment.attributes["version"] = "1.0"
102
112
 
113
+ # <ShippingCredentials>
114
+ # <ShippingKey>key</ShippingKey>
115
+ # <AccountNbr>number</AccountNbr>
116
+ # </ShippingCredentials>
103
117
  credentials = Element.new "ShippingCredentials"
118
+
104
119
  key = Element.new "ShippingKey"
105
120
  key.text = params[:shipping_key]
106
-
107
121
  account = Element.new "AccountNbr"
108
122
  account.text = params[:account_num]
109
123
 
@@ -111,50 +125,84 @@ module ShippingCalc
111
125
  credentials << account
112
126
  shipment << credentials
113
127
 
114
- detail = Element.new "ShipmentDetail"
115
- date = Element.new "ShipDate"
116
-
117
- date.text = date(params[:date])
118
- detail << date
128
+ # <ShipmentDetail>
129
+ # <ShiptDate>date</ShipDate>
130
+ shipment_detail = Element.new "ShipmentDetail"
131
+ ship_date = Element.new "ShipDate"
132
+ ship_date.text = date(params[:date])
133
+ shipment_detail << ship_date
119
134
 
120
135
  # TODO: Implement SAT and 1030 services
121
- service = Element.new "Service"
122
- s_code = Element.new "Code"
123
- s_code.text = service_code(params[:service_code])
124
- detail << service << s_code
125
-
126
- type = Element.new "ShipmentType"
127
- t_code = Element.new "Code"
128
- t_code.text = shipment_code(params[:shipment_code])
129
- detail << type << t_code
130
136
 
137
+ # <Service>
138
+ # <Code>code</Code>
139
+ # </Service>
140
+ service = Element.new "Service"
141
+ service_code = Element.new "Code"
142
+ service_code.text = service_code(params[:service_code])
143
+ service << service_code
144
+
145
+ shipment_detail << service
146
+
147
+ # <ShipmentType>
148
+ # <Code>code</Code>
149
+ # </ShipmentType>
150
+ shipment_type = Element.new "ShipmentType"
151
+ shipment_type_code = Element.new "Code"
152
+ shipment_type_code.text = shipment_code(params[:shipment_code])
153
+ shipment_type << shipment_type_code
154
+ shipment_detail << shipment_type
155
+
156
+ # <Weight>weight</Weight>
131
157
  weight = Element.new "Weight"
132
158
  weight.text = weight(params[:weight], params[:shipment_code])
133
- shipment << detail << weight
134
159
 
160
+ # </ShipmentDetail>
161
+ shipment_detail << weight
162
+
163
+ shipment << shipment_detail
164
+
165
+ # <Billing>
166
+ # <Party>
167
+ # <Code>S</Code>
168
+ # </Party>
169
+ # </Billing>
135
170
  billing = Element.new "Billing"
136
- b_party = Element.new "Party"
137
- p_code = Element.new "Code"
171
+ billing_party = Element.new "Party"
172
+ billing_party_code = Element.new "Code"
173
+
138
174
  # Since we're just doing some quick calulations we don't want to be
139
175
  # worrying about who's gonna send the package. Just make the calulations
140
176
  # assuming the sender pays for the shipping.
141
- p_code.text = "S"
142
- shipment << billing << b_party << p_code
177
+ billing_party_code.text = "S"
178
+
179
+ billing << billing_party << billing_party_code
143
180
 
181
+ shipment << billing
182
+
183
+ # <Receiver>
184
+ # <Address>
185
+ # <State>state</State>
186
+ # <Country>country</Country>
187
+ # <PostalCode>code</PostalCode>
188
+ # </Address>
189
+ # </Receiver>
144
190
  receiver = Element.new "Receiver"
145
- r_addr = Element.new "Address"
146
- r_state = Element.new "State"
147
- r_country = Element.new "Country"
148
- r_zipcode = Element.new "PostalCode"
191
+ receiver_addr = Element.new "Address"
192
+ receiver_state = Element.new "State"
193
+ receiver_country = Element.new "Country"
194
+ receiver_zipcode = Element.new "PostalCode"
195
+
196
+ receiver_state.text = state(params[:to_state])
197
+ receiver_country.text = "US"
198
+ receiver_zipcode.text = zip_code(params[:to_zip])
149
199
 
150
- r_state.text = state(params[:to_state])
151
- r_country.text = "US"
152
- r_zipcode.text = zip_code(params[:to_zip])
200
+ receiver_addr << receiver_state
201
+ receiver_addr << receiver_country
202
+ receiver_addr << receiver_zipcode
203
+ receiver << receiver_addr
153
204
 
154
- r_addr << r_state
155
- r_addr << r_country
156
- r_addr << r_zipcode
157
- shipment << receiver << r_addr
205
+ shipment << receiver
158
206
 
159
207
  root = @xml.elements["eCommerce"]
160
208
  root.add shipment
@@ -174,12 +222,10 @@ module ShippingCalc
174
222
  # Parses the server's response. Currently, it only returns the estimate
175
223
  # value of the shipping.
176
224
  def parse_response(resp)
177
- p resp
178
225
  doc = Document.new(resp)
179
- if resp =~ /Fault/
180
- raise ShippingCalcError.new("DHL's system is currently offline.")
181
- return
182
- end
226
+
227
+ find_error_and_raise(doc) if errors_exist?(doc)
228
+
183
229
  result = doc.elements["//Shipment/Result/Desc"].text
184
230
 
185
231
  if result == "Shipment estimate successful."
@@ -189,8 +235,45 @@ module ShippingCalc
189
235
  end
190
236
  end
191
237
 
238
+ def errors_exist?(response)
239
+ not response.elements["//Faults"].nil?
240
+ end
241
+
242
+ def find_error_and_raise(response)
243
+ error_code = response.elements["//Code"]
244
+
245
+ # Special Services and Additional Protection are not supported
246
+ case error_code.text.to_i
247
+ when 1000..1009 then msg = "Shipment Headers"
248
+ when 4000..4004 then msg = "ShippingKey"
249
+ when 4007 then msg = "Account Number"
250
+ when 4195..4198 then msg = "Account Number"
251
+ when 4100..4106 then msg = "Shipment Date"
252
+ when 4108..4117 then msg = "Service Type (Code)"
253
+ when 4118..4122 then msg = "Shipment Type Code"
254
+ when 4123..4124 then msg = "Weight"
255
+ when 4128..4131 then msg = "Dimensions"
256
+ when 4116 then msg = "Billing Party (Code)"
257
+ when 4147 then msg = "Billing Party (Code)"
258
+ when 4149..4152 then msg = "Billing Account Number"
259
+ when 4164..4166 then msg = "Receiver City"
260
+ when 4167 then msg = "Receiver State"
261
+ when 4164..4166 then msg = "Receiver City"
262
+ when 4169 then msg = "Receiver Country"
263
+ when 4170..4176 then msg = "Receiver Postal Code"
264
+ else msg = "API Request"
265
+ end
266
+
267
+ raise ShippingCalcError.new("DHL Error #{error_code.text}: Invalid #{msg}")
268
+ end
269
+
192
270
  def date(date)
193
- if date.strftime("%A") == "Sunday"
271
+ date ||= Time.now
272
+ if date.kind_of?(String) && date =~ /\d{4}-\d{2}-\d{2}/ # Suppose it's valid
273
+ return date
274
+ end
275
+
276
+ if date.strftime("%A") == "Sunday"
194
277
  (date + 86400).strftime("%Y-%m-%d") # DHL doesn't ship on Sundays, add 1 day.
195
278
  else
196
279
  date.strftime("%Y-%m-%d")
@@ -1,5 +1,8 @@
1
- require File.dirname(__FILE__) + '/../test_helper'
1
+ require File.dirname(__FILE__) + "/../../lib/shipping_calc"
2
+
3
+ require 'test/unit'
2
4
  require 'yaml'
5
+ require 'rbconfig'
3
6
 
4
7
  include ShippingCalc
5
8
 
@@ -29,39 +32,59 @@ class DHLTest < Test::Unit::TestCase
29
32
  end
30
33
 
31
34
  def test_quote
32
- assert_in_delta "172.5", @d.quote(@opts), 10
35
+ assert_in_delta "172.5", @d.quote(@opts), 45
36
+ end
37
+
38
+ def test_errors
39
+ @opts[:account_num] = "0123456789"
40
+ assert_raises ShippingCalc::ShippingCalcError do
41
+ @d.quote(@opts)
42
+ end
33
43
  end
34
44
 
35
45
  def test_params_empty
36
- assert_raise NoMethodError do
46
+ assert_raises ShippingCalc::ShippingCalcError do
37
47
  @d.quote(nil)
38
48
  end
39
49
  end
40
50
 
51
+ def test_empty_date
52
+ @opts[:date] = nil
53
+ assert_nothing_raised do
54
+ quote = @d.quote(@opts)
55
+ end
56
+ end
57
+
41
58
  def test_not_enough_params
42
59
  @opts.delete(:weight)
43
- assert_raise ShippingCalcError do
60
+ assert_raises ShippingCalc::ShippingCalcError do
44
61
  @d.quote(@opts)
45
62
  end
46
63
  end
47
64
 
48
65
  def test_invalid_weight
49
66
  @opts[:weight] = -1
50
- assert_raise ShippingCalcError do
67
+ assert_raises ShippingCalc::ShippingCalcError do
51
68
  @d.quote(@opts)
52
69
  end
53
70
  end
54
71
 
55
72
  def test_invalid_zip_code
56
73
  @opts[:to_zip] = "10002"
57
- assert_raise ShippingCalcError do
74
+ assert_raises ShippingCalc::ShippingCalcError do
58
75
  @d.quote(@opts)
59
76
  end
60
77
  end
61
78
 
62
79
  # Auth info is private, gotta load it this way.
63
80
  def auth_info_from_file
64
- info = YAML.load_file("/home/#{ENV["USER"]}/.dhl_info.yml")
81
+ os = Config::CONFIG["host_os"]
82
+ if os =~ /darwin/
83
+ info = YAML.load_file("/Users/#{ENV["USER"]}/.dhl_info.yml")
84
+ else
85
+ info = YAML.load_file("/home/#{ENV["USER"]}/.dhl_info.yml")
86
+ end
87
+ info
65
88
  end
66
89
 
67
90
  end
@@ -1,4 +1,6 @@
1
- require File.dirname(__FILE__) + '/../test_helper'
1
+ require File.dirname(__FILE__) + "/../../lib/shipping_calc"
2
+
3
+ require 'test/unit'
2
4
  require 'yaml'
3
5
 
4
6
  include ShippingCalc
@@ -6,7 +8,6 @@ include ShippingCalc
6
8
  class FreightQuoteTest < Test::Unit::TestCase
7
9
 
8
10
  def setup
9
-
10
11
  @opts = {
11
12
  :api_email => "xmltest@FreightQuote.com",
12
13
  :api_password => "XML",
@@ -1,5 +1,5 @@
1
- require 'test/unit'
2
- require 'shipping_calc'
1
+ require File.dirname(__FILE__) + "/freight_quote/freight_quote_test"
2
+ require File.dirname(__FILE__) + "/dhl/dhl_test"
3
+
4
+
3
5
 
4
- require 'dhl/dhl_test'
5
- require 'freight_quote/freight_quote_test'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shipping-calc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Federico Builes
@@ -9,11 +9,12 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-05-29 00:00:00 -05:00
12
+ date: 2008-06-24 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: hoe
17
+ type: :runtime
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
@@ -21,7 +22,7 @@ dependencies:
21
22
  - !ruby/object:Gem::Version
22
23
  version: 1.5.3
23
24
  version:
24
- description: Shipping Calculator written in Ruby to get quick quotes from the major carriers (UPS, DHL, FedEX, FreightQuote).
25
+ description: Shipping Calculator written in Ruby to get quick quotes from DHL and Freight Carriers. We hope to support FedEx and UPS in a near future.
25
26
  email:
26
27
  - federico.builes@gmail.com
27
28
  executables: []
@@ -69,9 +70,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
69
70
  requirements: []
70
71
 
71
72
  rubyforge_project: shipping-calc
72
- rubygems_version: 1.0.1
73
+ rubygems_version: 1.2.0
73
74
  signing_key:
74
75
  specification_version: 2
75
- summary: Shipping Calculator written in Ruby to get quick quotes from the major carriers (UPS, DHL, FedEX, FreightQuote).
76
+ summary: Shipping Calculator written in Ruby to get quick quotes from DHL and Freight Carriers
76
77
  test_files:
77
78
  - test/test_helper.rb