tax_cloud 0.1.4 → 0.2.0
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/.travis.yml +9 -0
- data/CHANGELOG.rdoc +28 -0
- data/CONTRIBUTORS.txt +20 -0
- data/Gemfile +1 -1
- data/LICENSE.rdoc +22 -0
- data/README.rdoc +112 -28
- data/Rakefile +10 -2
- data/lib/config/locales/en.yml +34 -0
- data/lib/hash.rb +8 -6
- data/lib/savon_soap_xml.rb +33 -0
- data/lib/tasks/tax_cloud.rake +20 -0
- data/lib/tasks/tax_code_groups.rake +39 -0
- data/lib/tasks/tax_codes.rake +45 -0
- data/lib/tax_cloud.rb +43 -16
- data/lib/tax_cloud/address.rb +30 -17
- data/lib/tax_cloud/cart_item.rb +13 -19
- data/lib/tax_cloud/client.rb +48 -0
- data/lib/tax_cloud/configuration.rb +17 -2
- data/lib/tax_cloud/errors.rb +6 -0
- data/lib/tax_cloud/errors/api_error.rb +18 -0
- data/lib/tax_cloud/errors/missing_config_error.rb +13 -0
- data/lib/tax_cloud/errors/missing_config_option_error.rb +19 -0
- data/lib/tax_cloud/errors/soap_error.rb +33 -0
- data/lib/tax_cloud/errors/tax_cloud_error.rb +86 -0
- data/lib/tax_cloud/errors/unexpected_soap_response_error.rb +23 -0
- data/lib/tax_cloud/record.rb +14 -0
- data/lib/tax_cloud/responses.rb +13 -0
- data/lib/tax_cloud/responses/authorized.rb +10 -0
- data/lib/tax_cloud/responses/authorized_with_capture.rb +10 -0
- data/lib/tax_cloud/responses/base.rb +88 -0
- data/lib/tax_cloud/responses/captured.rb +11 -0
- data/lib/tax_cloud/responses/cart_item.rb +24 -0
- data/lib/tax_cloud/responses/generic.rb +35 -0
- data/lib/tax_cloud/responses/lookup.rb +41 -0
- data/lib/tax_cloud/responses/ping.rb +10 -0
- data/lib/tax_cloud/responses/returned.rb +10 -0
- data/lib/tax_cloud/responses/tax_code_groups.rb +33 -0
- data/lib/tax_cloud/responses/tax_codes.rb +33 -0
- data/lib/tax_cloud/responses/tax_codes_by_group.rb +33 -0
- data/lib/tax_cloud/responses/verify_address.rb +29 -0
- data/lib/tax_cloud/tax_code.rb +11 -0
- data/lib/tax_cloud/tax_code_constants.rb +562 -0
- data/lib/tax_cloud/tax_code_group.rb +30 -0
- data/lib/tax_cloud/tax_code_group_constants.rb +31 -0
- data/lib/tax_cloud/tax_code_groups.rb +28 -0
- data/lib/tax_cloud/tax_codes.rb +24 -47
- data/lib/tax_cloud/transaction.rb +39 -34
- data/lib/tax_cloud/version.rb +3 -3
- data/tax_cloud.gemspec +7 -5
- data/test/cassettes/authorized.yml +70 -45
- data/test/cassettes/authorized_with_capture.yml +70 -45
- data/test/cassettes/captured.yml +101 -67
- data/test/cassettes/get_tic_groups.yml +656 -0
- data/test/cassettes/get_tics.yml +952 -0
- data/test/cassettes/get_tics_by_group.yml +49 -0
- data/test/cassettes/invalid_soap_call.yml +651 -0
- data/test/cassettes/lookup.yml +644 -25
- data/test/cassettes/lookup_ny.yml +651 -0
- data/test/cassettes/ping.yml +647 -0
- data/test/cassettes/ping_with_invalid_credentials.yml +647 -0
- data/test/cassettes/ping_with_invalid_response.yml +647 -0
- data/test/cassettes/returned.yml +101 -67
- data/test/cassettes/verify_bad_address.yml +578 -976
- data/test/cassettes/verify_good_address.yml +36 -23
- data/test/helper.rb +4 -19
- data/test/test_address.rb +25 -7
- data/test/test_client.rb +29 -0
- data/test/test_configuration.rb +33 -0
- data/test/test_setup.rb +18 -0
- data/test/test_soap.rb +13 -0
- data/test/test_tax_code_groups.rb +31 -0
- data/test/test_tax_codes.rb +19 -0
- data/test/test_transaction.rb +22 -11
- data/test/test_transaction_ny.rb +27 -0
- data/test/vcr_setup.rb +9 -0
- metadata +134 -24
- data/lib/savon_xml_override.rb +0 -30
data/lib/tax_cloud.rb
CHANGED
@@ -1,44 +1,71 @@
|
|
1
1
|
require 'savon'
|
2
|
-
require '
|
2
|
+
require 'i18n'
|
3
|
+
require 'hash'
|
4
|
+
require 'savon_soap_xml'
|
5
|
+
require 'active_support/core_ext'
|
6
|
+
|
3
7
|
require 'tax_cloud/version'
|
8
|
+
require 'tax_cloud/errors'
|
9
|
+
require 'tax_cloud/responses'
|
10
|
+
require 'tax_cloud/record'
|
4
11
|
require 'tax_cloud/transaction'
|
5
12
|
require 'tax_cloud/address'
|
6
13
|
require 'tax_cloud/cart_item'
|
14
|
+
require 'tax_cloud/tax_code'
|
15
|
+
require 'tax_cloud/tax_code_group'
|
7
16
|
require 'tax_cloud/tax_codes'
|
17
|
+
require 'tax_cloud/tax_code_constants'
|
18
|
+
require 'tax_cloud/tax_code_groups'
|
19
|
+
require 'tax_cloud/tax_code_group_constants'
|
8
20
|
require 'tax_cloud/configuration'
|
9
|
-
require '
|
21
|
+
require 'tax_cloud/client'
|
22
|
+
|
23
|
+
I18n.load_path << File.join(File.dirname(__FILE__), "config", "locales", "en.yml")
|
10
24
|
|
11
25
|
# TaxCloud is a web service to calculate and track sales tax for your ecommerce platform. Integration is easy to use.
|
12
26
|
# For information on configuring and using the TaxCloud API, look at the <tt>README</tt> file.
|
13
27
|
module TaxCloud
|
14
|
-
|
28
|
+
|
29
|
+
# WSDL location for TaxCloud API.
|
15
30
|
WSDL_URL = 'https://api.taxcloud.net/1.0/?wsdl'
|
16
|
-
|
31
|
+
|
32
|
+
# TaxCloud API version.
|
17
33
|
API_VERSION = '1.0'
|
18
34
|
|
19
35
|
class << self
|
36
|
+
|
37
|
+
# TaxCloud gem configuration.
|
20
38
|
attr_accessor :configuration
|
21
39
|
|
22
|
-
#
|
40
|
+
# Returns true if the gem has been configured.
|
41
|
+
def configured?
|
42
|
+
!! self.configuration
|
43
|
+
end
|
44
|
+
|
45
|
+
# Configure the gem.
|
23
46
|
def configure
|
24
47
|
self.configuration ||= Configuration.new
|
25
48
|
yield configuration
|
26
49
|
end
|
27
50
|
|
28
|
-
#
|
29
|
-
def
|
30
|
-
|
31
|
-
wsdl.document = TaxCloud::WSDL_URL
|
32
|
-
end
|
51
|
+
# Reset the current configuration.
|
52
|
+
def reset!
|
53
|
+
self.configuration = nil
|
33
54
|
end
|
34
55
|
|
35
|
-
#
|
36
|
-
def
|
37
|
-
|
38
|
-
|
39
|
-
'apiKey' => configuration.api_key
|
40
|
-
}
|
56
|
+
# The configured SOAP client to the TaxCloud service.
|
57
|
+
def client
|
58
|
+
check_configuration!
|
59
|
+
@@client ||= TaxCloud::Client.new
|
41
60
|
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def check_configuration!
|
65
|
+
raise TaxCloud::Errors::MissingConfig.new unless self.configuration
|
66
|
+
self.configuration.check!
|
67
|
+
end
|
68
|
+
|
42
69
|
end
|
43
70
|
|
44
71
|
end
|
data/lib/tax_cloud/address.rb
CHANGED
@@ -1,24 +1,37 @@
|
|
1
|
-
module TaxCloud
|
2
|
-
# An address
|
3
|
-
class Address
|
4
|
-
attr_accessor :address1, :address2, :city, :state, :zip5, :zip4
|
1
|
+
module TaxCloud #:nodoc:
|
2
|
+
# An <tt>Address</tt> defines an address in the United States.
|
3
|
+
class Address < Record
|
5
4
|
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
5
|
+
# First line of address.
|
6
|
+
attr_accessor :address1
|
7
|
+
# Second line of adress.
|
8
|
+
attr_accessor :address2
|
9
|
+
# City.
|
10
|
+
attr_accessor :city
|
11
|
+
# State.
|
12
|
+
attr_accessor :state
|
13
|
+
# 5-digit Zip Code.
|
14
|
+
attr_accessor :zip5
|
15
|
+
# 4-digit Zip Code.
|
16
|
+
attr_accessor :zip4
|
13
17
|
|
14
|
-
# Verify
|
18
|
+
# Verify this address.
|
19
|
+
#
|
20
|
+
# Returns a verified TaxCloud::Address.
|
15
21
|
def verify
|
16
|
-
|
17
|
-
|
18
|
-
'apiKey' => TaxCloud.configuration.api_key,
|
22
|
+
params = to_hash.downcase_keys
|
23
|
+
params = params.merge({
|
19
24
|
'uspsUserID' => TaxCloud.configuration.usps_username
|
20
|
-
}.
|
21
|
-
response = TaxCloud.client.request
|
25
|
+
}) if TaxCloud.configuration.usps_username
|
26
|
+
response = TaxCloud.client.request(:verify_address, params)
|
27
|
+
TaxCloud::Responses::VerifyAddress.parse(response)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Complete zip code.
|
31
|
+
# Returns a 9-digit Zip Code, when available.
|
32
|
+
def zip
|
33
|
+
return nil unless zip5 && zip5.length > 0
|
34
|
+
[ zip5, zip4 ].select { |z| z && z.length > 0 }.join("-")
|
22
35
|
end
|
23
36
|
|
24
37
|
# Convert the object to a usable hash for SOAP requests
|
data/lib/tax_cloud/cart_item.rb
CHANGED
@@ -1,25 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
module TaxCloud
|
1
|
+
module TaxCloud #:nodoc:
|
4
2
|
# A <tt>CartItem</tt> defines a single line item for the purchase. Used to calculate the tax amount.
|
5
|
-
|
6
|
-
# === Attributes
|
7
|
-
# * <tt>index</tt> - The unique index number for the line item. Must be unique for the scope of the cart.
|
8
|
-
# * <tt>item_id</tt> - The stock keeping unit (SKU) number
|
9
|
-
# * <tt>tic</tt> - The taxable information code. See <tt>TaxCloud::TaxCodes</tt>.
|
10
|
-
# * <tt>price</tt> - The price of the item. All prices are USD. Do not include currency symbol.
|
11
|
-
# * <tt>quantity</tt> - The total number of items.
|
12
|
-
class CartItem
|
13
|
-
attr_accessor :index, :item_id, :tic, :price, :quantity
|
3
|
+
class CartItem < Record
|
14
4
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
5
|
+
# The unique index number for the line item. Must be unique for the scope of the cart.
|
6
|
+
attr_accessor :index
|
7
|
+
# The stock keeping unit (SKU) number.
|
8
|
+
attr_accessor :item_id
|
9
|
+
# The taxable information code. See TaxCloud::TaxCodes.
|
10
|
+
attr_accessor :tic
|
11
|
+
# The price of the item. All prices are USD. Do not include currency symbol.
|
12
|
+
attr_accessor :price
|
13
|
+
# The total number of items.
|
14
|
+
attr_accessor :quantity
|
21
15
|
|
22
|
-
# Convert the object to a usable hash for SOAP requests
|
16
|
+
# Convert the object to a usable hash for SOAP requests.
|
23
17
|
def to_hash
|
24
18
|
{
|
25
19
|
'Index' => index,
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module TaxCloud #:nodoc:
|
2
|
+
# A <tt>Client</tt> communicates with the TaCloud service.
|
3
|
+
class Client < Savon::Client
|
4
|
+
|
5
|
+
# Create a new client.
|
6
|
+
def initialize
|
7
|
+
super TaxCloud::WSDL_URL
|
8
|
+
end
|
9
|
+
|
10
|
+
# Make a safe SOAP call.
|
11
|
+
# Will raise a TaxCloud::Errors::SoapError on error.
|
12
|
+
#
|
13
|
+
# === Parameters
|
14
|
+
# [method] SOAP method.
|
15
|
+
# [body] Body content.
|
16
|
+
def request(method, body = {})
|
17
|
+
safe do
|
18
|
+
super method, :body => body.merge(auth_params)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Ping the TaxCloud service.
|
23
|
+
#
|
24
|
+
# Returns "OK" or raises an error if the TaxCloud service is unreachable.
|
25
|
+
def ping
|
26
|
+
TaxCloud::Responses::Ping.parse request(:ping)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
# Authorization hash to use with all SOAP requests
|
32
|
+
def auth_params
|
33
|
+
{
|
34
|
+
'apiLoginID' => TaxCloud.configuration.api_login_id,
|
35
|
+
'apiKey' => TaxCloud.configuration.api_key
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def safe &block
|
40
|
+
begin
|
41
|
+
yield
|
42
|
+
rescue Savon::SOAP::Fault => e
|
43
|
+
raise TaxCloud::Errors::SoapError.new(e)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -1,5 +1,20 @@
|
|
1
|
-
module TaxCloud
|
1
|
+
module TaxCloud #:nodoc:
|
2
|
+
# TaxCloud gem configuration.
|
2
3
|
class Configuration
|
3
|
-
|
4
|
+
|
5
|
+
# TaxCloud login ID.
|
6
|
+
attr_accessor :api_login_id
|
7
|
+
# TaxCloud API key.
|
8
|
+
attr_accessor :api_key
|
9
|
+
# Optional USPS username.
|
10
|
+
attr_accessor :usps_username
|
11
|
+
|
12
|
+
# Check the configuration.
|
13
|
+
#
|
14
|
+
# Will raise a TaxCloud::Errors::MissingConfigOption if any of the API login ID or the API key are missing.
|
15
|
+
def check!
|
16
|
+
raise TaxCloud::Errors::MissingConfigOption.new('api_login_id') unless self.api_login_id && self.api_login_id.strip.length > 0
|
17
|
+
raise TaxCloud::Errors::MissingConfigOption.new('api_key') unless self.api_key && self.api_key.strip.length > 0
|
18
|
+
end
|
4
19
|
end
|
5
20
|
end
|
@@ -0,0 +1,6 @@
|
|
1
|
+
require 'tax_cloud/errors/tax_cloud_error'
|
2
|
+
require 'tax_cloud/errors/missing_config_error'
|
3
|
+
require 'tax_cloud/errors/missing_config_option_error'
|
4
|
+
require 'tax_cloud/errors/soap_error'
|
5
|
+
require 'tax_cloud/errors/unexpected_soap_response_error'
|
6
|
+
require 'tax_cloud/errors/api_error'
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TaxCloud #:nodoc:
|
3
|
+
module Errors #:nodoc:
|
4
|
+
# This error is raised when the TaxCloud service
|
5
|
+
# returns an error from an API.
|
6
|
+
class ApiError < TaxCloudError
|
7
|
+
# === Parameters
|
8
|
+
# [message] Error message.
|
9
|
+
# [raw] Raw data from the SOAP response.
|
10
|
+
def initialize(message, raw)
|
11
|
+
super(compose_message("api_error", {
|
12
|
+
:message => message,
|
13
|
+
:raw => raw
|
14
|
+
}))
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TaxCloud #:nodoc:
|
3
|
+
module Errors #:nodoc:
|
4
|
+
# This error is raised when attempting to create a new client
|
5
|
+
# without configuring TaxCloud.
|
6
|
+
class MissingConfig < TaxCloudError
|
7
|
+
# Create a new error.
|
8
|
+
def initialize
|
9
|
+
super(compose_message("missing_config"))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TaxCloud #:nodoc:
|
3
|
+
module Errors #:nodoc:
|
4
|
+
# This error is raised when a configuration
|
5
|
+
# option is missing.
|
6
|
+
class MissingConfigOption < TaxCloudError
|
7
|
+
# === Parameters
|
8
|
+
# [name] The attempted config option name.
|
9
|
+
def initialize(name)
|
10
|
+
super(
|
11
|
+
compose_message(
|
12
|
+
"missing_config_option",
|
13
|
+
{ :name => name }
|
14
|
+
)
|
15
|
+
)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TaxCloud #:nodoc:
|
3
|
+
module Errors #:nodoc:
|
4
|
+
# This error is raised when a SOAP call fails.
|
5
|
+
class SoapError < TaxCloudError
|
6
|
+
|
7
|
+
# Original SOAP failt.
|
8
|
+
attr_reader :fault
|
9
|
+
|
10
|
+
# Create the new error.
|
11
|
+
# === Parameters
|
12
|
+
# [e] SOAP response.
|
13
|
+
def initialize(e)
|
14
|
+
@fault = e
|
15
|
+
e.to_hash.tap do |fault|
|
16
|
+
fault_code = fault[:fault][:faultcode]
|
17
|
+
fault_string = parse_fault(fault[:fault][:faultstring])
|
18
|
+
super(compose_message("soap_error", {
|
19
|
+
:message => fault_string,
|
20
|
+
:code => fault_code
|
21
|
+
}))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def parse_fault(fault_string)
|
28
|
+
fault_string.lines.first.strip
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TaxCloud #:nodoc:
|
3
|
+
module Errors #:nodoc:
|
4
|
+
|
5
|
+
# Default parent TaxCloud error for all custom errors. This handles the base
|
6
|
+
# key for the translations and provides the convenience method for
|
7
|
+
# translating the messages.
|
8
|
+
#
|
9
|
+
# Generously borrowed from Mongoid[https://github.com/mongoid/mongoid/blob/master/lib/mongoid/errors/mongoid_error.rb].
|
10
|
+
class TaxCloudError < StandardError
|
11
|
+
|
12
|
+
# Problem occurred.
|
13
|
+
attr_reader :problem
|
14
|
+
|
15
|
+
# Summary of the problem.
|
16
|
+
attr_reader :summary
|
17
|
+
|
18
|
+
# Suggested problem resolution.
|
19
|
+
attr_reader :resolution
|
20
|
+
|
21
|
+
# Compose the message.
|
22
|
+
# === Parameters
|
23
|
+
# [key] Lookup key in the translation table.
|
24
|
+
# [attributes] The objects to pass to create the message.
|
25
|
+
def compose_message(key, attributes = {})
|
26
|
+
@problem = create_problem(key, attributes)
|
27
|
+
@summary = create_summary(key, attributes)
|
28
|
+
@resolution = create_resolution(key, attributes)
|
29
|
+
|
30
|
+
"\nProblem:\n #{@problem}"+
|
31
|
+
"\nSummary:\n #{@summary}"+
|
32
|
+
"\nResolution:\n #{@resolution}"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
BASE_KEY = "taxcloud.errors.messages" #:nodoc:
|
38
|
+
|
39
|
+
# Given the key of the specific error and the options hash, translate the
|
40
|
+
# message.
|
41
|
+
#
|
42
|
+
# === Parameters
|
43
|
+
# [key] The key of the error in the locales.
|
44
|
+
# [options] The objects to pass to create the message.
|
45
|
+
#
|
46
|
+
# Returns a localized error message string.
|
47
|
+
def translate(key, options)
|
48
|
+
::I18n.translate("#{BASE_KEY}.#{key}", { :locale => :en }.merge(options)).strip
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create the problem.
|
52
|
+
#
|
53
|
+
# === Parameters
|
54
|
+
# [key] The error key.
|
55
|
+
# [attributes] The attributes to interpolate.
|
56
|
+
#
|
57
|
+
# Returns the problem.
|
58
|
+
def create_problem(key, attributes)
|
59
|
+
translate("#{key}.message", attributes)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Create the summary.
|
63
|
+
#
|
64
|
+
# === Parameters
|
65
|
+
# [key] The error key.
|
66
|
+
# [attributes] The attributes to interpolate.
|
67
|
+
#
|
68
|
+
# Returns the summary.
|
69
|
+
def create_summary(key, attributes)
|
70
|
+
translate("#{key}.summary", attributes)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Create the resolution.
|
74
|
+
#
|
75
|
+
# === Parameters
|
76
|
+
# [key] The error key.
|
77
|
+
# [attributes] The attributes to interpolate.
|
78
|
+
#
|
79
|
+
# Returns the resolution.
|
80
|
+
def create_resolution(key, attributes)
|
81
|
+
translate("#{key}.resolution", attributes)
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module TaxCloud #:nodoc:
|
3
|
+
module Errors #:nodoc:
|
4
|
+
|
5
|
+
# This error is raised when TaxCloud returns an
|
6
|
+
# unexpected SOAP response.
|
7
|
+
class UnexpectedSoapResponse < TaxCloudError
|
8
|
+
|
9
|
+
# === Parameters
|
10
|
+
# [raw] Raw data from the SOAP response.
|
11
|
+
# [key] Expected key in the SOAP response.
|
12
|
+
# [chain] Complete SOAP response chain in which the key could not be found.
|
13
|
+
def initialize(raw, key, chain)
|
14
|
+
super(compose_message("unexpected_soap_response", {
|
15
|
+
:key => key,
|
16
|
+
:raw => raw,
|
17
|
+
:chain => chain
|
18
|
+
}))
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|