active_shipping 0.9.11 → 0.9.12

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