shipping-calc 0.1.6 → 0.1.7

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.
@@ -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