active_shipping 0.9.11 → 0.9.12

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ 2011/04/21:
2
+ * USPS updated to use new APIs [james]
3
+ * new :gift boolean option for Package [james]
4
+ * Location's :address_type can be "po_box" [james]
5
+
6
+ Earlier:
1
7
  * New Zealand Post [AbleTech]
2
8
  * Include address name for rate requests to Shipwire if provided [dennis]
3
9
  * Add support for address name to Location [dennis]
@@ -29,8 +29,8 @@ module ActiveMerchant
29
29
  TEST_RESOURCE = 'ShippingAPITest.dll'
30
30
 
31
31
  API_CODES = {
32
- :us_rates => 'RateV3',
33
- :world_rates => 'IntlRate',
32
+ :us_rates => 'RateV4',
33
+ :world_rates => 'IntlRateV2',
34
34
  :test => 'CarrierPickupAvailability'
35
35
  }
36
36
  USE_SSL = {
@@ -180,7 +180,7 @@ module ActiveMerchant
180
180
  end
181
181
 
182
182
  def world_rates(origin, destination, packages, options={})
183
- request = build_world_rate_request(packages, destination.country)
183
+ request = build_world_rate_request(packages, destination)
184
184
  # never use test mode; rate requests just won't work on test servers
185
185
  parse_rate_response origin, destination, packages, commit(:world_rates,request,false), options
186
186
  end
@@ -204,7 +204,7 @@ module ActiveMerchant
204
204
  # "machinability" entirely.
205
205
  def build_us_rate_request(packages, origin_zip, destination_zip, options={})
206
206
  packages = Array(packages)
207
- request = XmlNode.new('RateV3Request', :USERID => @options[:login]) do |rate_request|
207
+ request = XmlNode.new('RateV4Request', :USERID => @options[:login]) do |rate_request|
208
208
  packages.each_with_index do |p,id|
209
209
  rate_request << XmlNode.new('Package', :ID => id.to_s) do |package|
210
210
  package << XmlNode.new('Service', US_SERVICES[options[:service] || :all])
@@ -212,9 +212,7 @@ module ActiveMerchant
212
212
  package << XmlNode.new('ZipDestination', strip_zip(destination_zip))
213
213
  package << XmlNode.new('Pounds', 0)
214
214
  package << XmlNode.new('Ounces', "%0.1f" % [p.ounces,1].max)
215
- if p.options[:container] and [nil,:all,:express,:priority].include? p.service
216
- package << XmlNode.new('Container', CONTAINERS[p.options[:container]])
217
- end
215
+ package << XmlNode.new('Container', CONTAINERS[p.options[:container]])
218
216
  package << XmlNode.new('Size', USPS.size_code_for(p))
219
217
  package << XmlNode.new('Width', p.inches(:width))
220
218
  package << XmlNode.new('Length', p.inches(:length))
@@ -241,21 +239,34 @@ module ActiveMerchant
241
239
  #
242
240
  # package.options[:mail_type] -- one of [:package, :postcard, :matter_for_the_blind, :envelope].
243
241
  # Defaults to :package.
244
- def build_world_rate_request(packages, destination_country)
245
- country = COUNTRY_NAME_CONVERSIONS[destination_country.code(:alpha2).value] || destination_country.name
246
- request = XmlNode.new('IntlRateRequest', :USERID => @options[:login]) do |rate_request|
242
+ def build_world_rate_request(packages, destination)
243
+ country = COUNTRY_NAME_CONVERSIONS[destination.country.code(:alpha2).value] || destination.country.name
244
+ request = XmlNode.new('IntlRateV2Request', :USERID => @options[:login]) do |rate_request|
247
245
  packages.each_index do |id|
248
246
  p = packages[id]
249
247
  rate_request << XmlNode.new('Package', :ID => id.to_s) do |package|
250
248
  package << XmlNode.new('Pounds', 0)
251
249
  package << XmlNode.new('Ounces', [p.ounces,1].max.ceil) #takes an integer for some reason, must be rounded UP
252
250
  package << XmlNode.new('MailType', MAIL_TYPES[p.options[:mail_type]] || 'Package')
253
- if p.value && (p.value > 0) && (p.currency == 'USD')
254
- package << XmlNode.new('ValueOfContents', p.value / 100.0)
251
+ package << XmlNode.new('GXG') do |gxg|
252
+ gxg << XmlNode.new('POBoxFlag', destination.po_box? ? 'Y' : 'N')
253
+ gxg << XmlNode.new('GiftFlag', p.gift? ? 'Y' : 'N')
254
+ end
255
+ value = if p.value && p.value > 0 && p.currency && p.currency != 'USD'
256
+ 0.0
257
+ else
258
+ (p.value || 0) / 100.0
255
259
  end
260
+ package << XmlNode.new('ValueOfContents', value)
256
261
  package << XmlNode.new('Country') do |node|
257
262
  node.cdata = country
258
263
  end
264
+ package << XmlNode.new('Container', p.cylinder? ? 'NONRECTANGULAR' : 'RECTANGULAR')
265
+ package << XmlNode.new('Size', USPS.size_code_for(p))
266
+ package << XmlNode.new('Width', [p.inches(:width), 0.01].max)
267
+ package << XmlNode.new('Length', [p.inches(:length), 0.01].max)
268
+ package << XmlNode.new('Height', [p.inches(:height), 0.01].max)
269
+ package << XmlNode.new('Girth', [p.inches(:girth), 0.01].max)
259
270
  end
260
271
  end
261
272
  end
@@ -285,41 +296,49 @@ module ActiveMerchant
285
296
  rate_hash = rates_from_response_node(xml, packages)
286
297
  unless rate_hash
287
298
  success = false
288
- message = "Unknown root node in XML response: '#{root_node_name}'"
299
+ message = "Unknown root node in XML response: '#{xml.root.name}'"
289
300
  end
290
301
  end
291
302
 
292
303
  end
293
304
 
294
- rate_estimates = rate_hash.keys.map do |service_name|
295
- RateEstimate.new(origin,destination,@@name,"USPS #{service_name}",
296
- :package_rates => rate_hash[service_name][:package_rates],
297
- :service_code => rate_hash[service_name][:service_code],
298
- :currency => 'USD')
305
+ if success
306
+ rate_estimates = rate_hash.keys.map do |service_name|
307
+ RateEstimate.new(origin,destination,@@name,"USPS #{service_name}",
308
+ :package_rates => rate_hash[service_name][:package_rates],
309
+ :service_code => rate_hash[service_name][:service_code],
310
+ :currency => 'USD')
311
+ end
312
+ rate_estimates.reject! {|e| e.package_count != packages.length}
313
+ rate_estimates = rate_estimates.sort_by(&:total_price)
299
314
  end
300
- rate_estimates.reject! {|e| e.package_count != packages.length}
301
- rate_estimates = rate_estimates.sort_by(&:total_price)
302
315
 
303
316
  RateResponse.new(success, message, Hash.from_xml(response), :rates => rate_estimates, :xml => response, :request => last_request)
304
317
  end
305
318
 
306
319
  def rates_from_response_node(response_node, packages)
307
320
  rate_hash = {}
308
- return false unless (root_node = response_node.elements['/IntlRateResponse | /RateV3Response'])
309
- domestic = (root_node.name == 'RateV3Response')
321
+ return false unless (root_node = response_node.elements['/IntlRateV2Response | /RateV4Response'])
322
+ domestic = (root_node.name == 'RateV4Response')
310
323
 
311
324
  domestic_elements = ['Postage', 'CLASSID', 'MailService', 'Rate']
312
325
  international_elements = ['Service', 'ID', 'SvcDescription', 'Postage']
313
326
  service_node, service_code_node, service_name_node, rate_node = domestic ? domestic_elements : international_elements
314
327
 
315
328
  root_node.each_element('Package') do |package_node|
316
- package_index = package_node.attributes['ID'].to_i
329
+ this_package = packages[package_node.attributes['ID'].to_i]
317
330
 
318
331
  package_node.each_element(service_node) do |service_response_node|
319
332
  service_name = service_response_node.get_text(service_name_node).to_s
320
333
 
321
- # workaround for USPS messing up and including unescaped html and asterisks in their rate names since Jan 2, 2011
322
- service_name.gsub!(/&amp;lt;sup&amp;gt;&amp;amp;reg;&amp;lt;\/sup&amp;gt;|\*+$/, '')
334
+ # strips the double-escaped HTML for trademark symbols from service names
335
+ service_name.gsub!(/&amp;lt;\S*&amp;gt;/,'')
336
+ # ...leading "USPS"
337
+ service_name.gsub!(/^USPS/,'')
338
+ # ...trailing asterisks
339
+ service_name.gsub!(/\*+$/,'')
340
+ # ...surrounding spaces
341
+ service_name.strip!
323
342
 
324
343
  # aggregate specific package rates into a service-centric RateEstimate
325
344
  # first package with a given service name will initialize these;
@@ -327,7 +346,7 @@ module ActiveMerchant
327
346
  this_service = rate_hash[service_name] ||= {}
328
347
  this_service[:service_code] ||= service_response_node.attributes[service_code_node]
329
348
  package_rates = this_service[:package_rates] ||= []
330
- this_package_rate = {:package => (this_package = packages[package_index]),
349
+ this_package_rate = {:package => this_package,
331
350
  :rate => Package.cents_from(service_response_node.get_text(rate_node).to_s.to_f)}
332
351
 
333
352
  package_rates << this_package_rate if package_valid_for_service(this_package,service_response_node)
@@ -1,6 +1,7 @@
1
1
  module ActiveMerchant #:nodoc:
2
2
  module Shipping #:nodoc:
3
3
  class Location
4
+ ADDRESS_TYPES = %w{residential commercial po_box}
4
5
 
5
6
  attr_reader :options,
6
7
  :country,
@@ -34,8 +35,12 @@ module ActiveMerchant #:nodoc:
34
35
  @address3 = options[:address3]
35
36
  @phone = options[:phone]
36
37
  @fax = options[:fax]
37
- raise ArgumentError.new('address_type must be either "residential" or "commercial"') if options[:address_type] and not (["residential", "commercial", ""]).include?(options[:address_type].to_s)
38
- @address_type = options[:address_type].nil? ? nil : options[:address_type].to_s
38
+ if options[:address_type].present?
39
+ @address_type = options[:address_type].to_s
40
+ unless ADDRESS_TYPES.include?(@address_type)
41
+ raise ArgumentError.new("address_type must be one of #{ADDRESS_TYPES.map(&:inspect).join(', ')}")
42
+ end
43
+ end
39
44
  end
40
45
 
41
46
  def self.from(object, options={})
@@ -68,7 +73,7 @@ module ActiveMerchant #:nodoc:
68
73
  end
69
74
  end
70
75
  end
71
- attributes.delete(:address_type) unless %w{residential commercial}.include?(attributes[:address_type].to_s)
76
+ attributes.delete(:address_type) unless ADDRESS_TYPES.include?(attributes[:address_type].to_s)
72
77
  self.new(attributes.update(options))
73
78
  end
74
79
 
@@ -76,8 +81,9 @@ module ActiveMerchant #:nodoc:
76
81
  @country.nil? ? nil : @country.code(format).value
77
82
  end
78
83
 
79
- def residential?; (@address_type == 'residential') end
80
- def commercial?; (@address_type == 'commercial') end
84
+ def residential?; @address_type == 'residential' end
85
+ def commercial?; @address_type == 'commercial' end
86
+ def po_box?; @address_type == 'po_box' end
81
87
 
82
88
  def to_s
83
89
  prettyprint.gsub(/\n/, ' ')
@@ -32,6 +32,7 @@ module ActiveMerchant #:nodoc:
32
32
  @value = Package.cents_from(options[:value])
33
33
  @currency = options[:currency] || (options[:value].currency if options[:value].respond_to?(:currency))
34
34
  @cylinder = (options[:cylinder] || options[:tube]) ? true : false
35
+ @gift = options[:gift] ? true : false
35
36
  end
36
37
 
37
38
  def cylinder?
@@ -39,6 +40,8 @@ module ActiveMerchant #:nodoc:
39
40
  end
40
41
  alias_method :tube?, :cylinder?
41
42
 
43
+ def gift?; @gift end
44
+
42
45
  def ounces(options={})
43
46
  weight(options).in_ounces.amount
44
47
  end
@@ -1,3 +1,3 @@
1
1
  module ActiveShipping
2
- VERSION = "0.9.11"
2
+ VERSION = "0.9.12"
3
3
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_shipping
3
3
  version: !ruby/object:Gem::Version
4
- hash: 45
5
- prerelease: false
4
+ hash: 35
5
+ prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 9
9
- - 11
10
- version: 0.9.11
9
+ - 12
10
+ version: 0.9.12
11
11
  platform: ruby
12
12
  authors:
13
13
  - James MacAulay
@@ -18,7 +18,7 @@ autorequire:
18
18
  bindir: bin
19
19
  cert_chain: []
20
20
 
21
- date: 2011-03-30 00:00:00 -04:00
21
+ date: 2011-04-21 00:00:00 -04:00
22
22
  default_executable:
23
23
  dependencies:
24
24
  - !ruby/object:Gem::Dependency
@@ -146,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
146
146
  requirements: []
147
147
 
148
148
  rubyforge_project: active_shipping
149
- rubygems_version: 1.3.7
149
+ rubygems_version: 1.6.2
150
150
  signing_key:
151
151
  specification_version: 3
152
152
  summary: Shipping API extension for Active Merchant