avalara 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/.rvmrc +1 -0
- data/CHANGELOG +5 -0
- data/Gemfile +8 -0
- data/Gemfile.lock +71 -0
- data/LICENSE +20 -0
- data/README +5 -0
- data/avalara.gemspec +28 -0
- data/lib/avalara.rb +101 -0
- data/lib/avalara/api.rb +23 -0
- data/lib/avalara/configuration.rb +29 -0
- data/lib/avalara/errors.rb +8 -0
- data/lib/avalara/parser.rb +41 -0
- data/lib/avalara/request.rb +11 -0
- data/lib/avalara/request/address.rb +19 -0
- data/lib/avalara/request/detail_level.rb +13 -0
- data/lib/avalara/request/invoice.rb +46 -0
- data/lib/avalara/request/line.rb +21 -0
- data/lib/avalara/response.rb +11 -0
- data/lib/avalara/response/invoice.rb +48 -0
- data/lib/avalara/response/message.rb +13 -0
- data/lib/avalara/response/tax_address.rb +16 -0
- data/lib/avalara/response/tax_detail.rb +16 -0
- data/lib/avalara/response/tax_line.rb +25 -0
- data/lib/avalara/types.rb +8 -0
- data/lib/avalara/types/date.rb +10 -0
- data/lib/avalara/types/stash.rb +28 -0
- data/lib/avalara/version.rb +5 -0
- data/spec/avalara.yml.example +2 -0
- data/spec/factories/addresses.rb +13 -0
- data/spec/factories/detail_levels.rb +7 -0
- data/spec/factories/invoices.rb +17 -0
- data/spec/factories/lines.rb +14 -0
- data/spec/fixtures/net/geographical_tax_no_sales.yml +95 -0
- data/spec/fixtures/net/get_tax/failure.yml +68 -0
- data/spec/fixtures/net/get_tax/success.yml +106 -0
- data/spec/models/avalara/configuration_spec.rb +55 -0
- data/spec/models/avalara/request/address_spec.rb +24 -0
- data/spec/models/avalara/request/detail_level_spec.rb +18 -0
- data/spec/models/avalara/request/invoice_spec.rb +33 -0
- data/spec/models/avalara/request/line_spec.rb +25 -0
- data/spec/models/avalara_spec.rb +204 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/support/avalara.rb +38 -0
- data/spec/support/factory_girl.rb +20 -0
- data/spec/support/vcr.rb +14 -0
- metadata +204 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
spec/avalara.yml
|
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use 1.9.2-p290@avalara
|
data/CHANGELOG
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
= 0.0.1 - February 3, 2012 - Initial Release with get_tax call
|
2
|
+
|
3
|
+
Not much in the way of features so far, but the rough draft of the get_tax call is in,
|
4
|
+
as well as error and warning message handling. Still need to update the docs with it
|
5
|
+
once an API to the gem is finalized.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
GIT
|
2
|
+
remote: https://github.com/intridea/hashie.git
|
3
|
+
revision: 6728ed259b4e8e679901b1d64e1d791f28ec186a
|
4
|
+
specs:
|
5
|
+
hashie (2.0.0.beta)
|
6
|
+
|
7
|
+
PATH
|
8
|
+
remote: .
|
9
|
+
specs:
|
10
|
+
avalara (0.0.1)
|
11
|
+
hashie
|
12
|
+
httparty
|
13
|
+
multi_json (~> 1.0.4)
|
14
|
+
|
15
|
+
GEM
|
16
|
+
remote: http://rubygems.org/
|
17
|
+
specs:
|
18
|
+
activesupport (3.2.1)
|
19
|
+
i18n (~> 0.6)
|
20
|
+
multi_json (~> 1.0)
|
21
|
+
addressable (2.2.6)
|
22
|
+
archive-tar-minitar (0.5.2)
|
23
|
+
columnize (0.3.6)
|
24
|
+
crack (0.3.1)
|
25
|
+
diff-lcs (1.1.3)
|
26
|
+
factory_girl (2.5.0)
|
27
|
+
activesupport
|
28
|
+
httparty (0.8.1)
|
29
|
+
multi_json
|
30
|
+
multi_xml
|
31
|
+
i18n (0.6.0)
|
32
|
+
linecache19 (0.5.12)
|
33
|
+
ruby_core_source (>= 0.1.4)
|
34
|
+
multi_json (1.0.4)
|
35
|
+
multi_xml (0.4.1)
|
36
|
+
rake (0.9.2.2)
|
37
|
+
rspec (2.8.0)
|
38
|
+
rspec-core (~> 2.8.0)
|
39
|
+
rspec-expectations (~> 2.8.0)
|
40
|
+
rspec-mocks (~> 2.8.0)
|
41
|
+
rspec-core (2.8.0)
|
42
|
+
rspec-expectations (2.8.0)
|
43
|
+
diff-lcs (~> 1.1.2)
|
44
|
+
rspec-mocks (2.8.0)
|
45
|
+
ruby-debug-base19 (0.11.25)
|
46
|
+
columnize (>= 0.3.1)
|
47
|
+
linecache19 (>= 0.5.11)
|
48
|
+
ruby_core_source (>= 0.1.4)
|
49
|
+
ruby-debug19 (0.11.6)
|
50
|
+
columnize (>= 0.3.1)
|
51
|
+
linecache19 (>= 0.5.11)
|
52
|
+
ruby-debug-base19 (>= 0.11.19)
|
53
|
+
ruby_core_source (0.1.5)
|
54
|
+
archive-tar-minitar (>= 0.5.2)
|
55
|
+
vcr (1.11.3)
|
56
|
+
webmock (1.7.10)
|
57
|
+
addressable (~> 2.2, > 2.2.5)
|
58
|
+
crack (>= 0.1.7)
|
59
|
+
|
60
|
+
PLATFORMS
|
61
|
+
ruby
|
62
|
+
|
63
|
+
DEPENDENCIES
|
64
|
+
avalara!
|
65
|
+
factory_girl
|
66
|
+
hashie!
|
67
|
+
rake
|
68
|
+
rspec
|
69
|
+
ruby-debug19
|
70
|
+
vcr
|
71
|
+
webmock
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Adam Fortuna
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
data/avalara.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'avalara/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'avalara'
|
7
|
+
s.version = Avalara::VERSION
|
8
|
+
s.authors = ['Adam Fortuna']
|
9
|
+
s.email = ['adam@envylabs.com']
|
10
|
+
s.homepage = 'https://github.com/adamfortuna/avalara'
|
11
|
+
s.summary = %q{A Ruby interface to the Avalara Tax API}
|
12
|
+
s.description = %q{This library provides Ruby calls to interact with the Avalara Tax API}
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.require_paths = ['lib']
|
18
|
+
|
19
|
+
s.add_dependency 'hashie'
|
20
|
+
s.add_dependency 'httparty'
|
21
|
+
s.add_dependency 'multi_json', '~> 1.0.4'
|
22
|
+
|
23
|
+
s.add_development_dependency 'vcr'
|
24
|
+
s.add_development_dependency 'webmock'
|
25
|
+
s.add_development_dependency 'rspec'
|
26
|
+
s.add_development_dependency 'factory_girl'
|
27
|
+
s.add_development_dependency 'ruby-debug19'
|
28
|
+
end
|
data/lib/avalara.rb
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'avalara/version'
|
4
|
+
require 'avalara/errors'
|
5
|
+
require 'avalara/configuration'
|
6
|
+
|
7
|
+
require 'avalara/api'
|
8
|
+
|
9
|
+
require 'avalara/types'
|
10
|
+
require 'avalara/request'
|
11
|
+
require 'avalara/response'
|
12
|
+
|
13
|
+
module Avalara
|
14
|
+
|
15
|
+
def self.configuration
|
16
|
+
@@_configuration ||= Avalara::Configuration.new
|
17
|
+
yield @@_configuration if block_given?
|
18
|
+
@@_configuration
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.configuration=(configuration)
|
22
|
+
raise ArgumentError, 'Expected a Avalara::Configuration instance' unless configuration.kind_of?(Configuration)
|
23
|
+
@@_configuration = configuration
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.configure(&block)
|
27
|
+
configuration(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.endpoint
|
31
|
+
configuration.endpoint
|
32
|
+
end
|
33
|
+
def self.endpoint=(endpoint)
|
34
|
+
configuration.endpoint = endpoint
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.username
|
38
|
+
configuration.username
|
39
|
+
end
|
40
|
+
def self.username=(username)
|
41
|
+
configuration.username = username
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.password
|
45
|
+
configuration.password
|
46
|
+
end
|
47
|
+
def self.password=(password)
|
48
|
+
configuration.password = password
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.version
|
52
|
+
configuration.version
|
53
|
+
end
|
54
|
+
def self.version=(version)
|
55
|
+
configuration.version = version
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.geographical_tax(latitude, longitude, sales_amount)
|
59
|
+
raise NotImplementedError
|
60
|
+
uri = [configuration.endpoint, configuration.version, "tax", "#{latitude},#{longitude}", "get"].join["/"]
|
61
|
+
|
62
|
+
response = API.get(uri,
|
63
|
+
:headers => API.headers_for('0'),
|
64
|
+
:query => { :saleamount => sales_amount }
|
65
|
+
)
|
66
|
+
rescue Timeout::Error
|
67
|
+
puts "Timed out"
|
68
|
+
raise TimeoutError
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.get_tax(invoice)
|
72
|
+
uri = [endpoint, version, 'tax', 'get'].join('/')
|
73
|
+
|
74
|
+
response = API.post(uri,
|
75
|
+
:body => invoice.to_json,
|
76
|
+
:headers => API.headers_for(invoice.to_json.length),
|
77
|
+
:basic_auth => authentication
|
78
|
+
)
|
79
|
+
|
80
|
+
return case response.code
|
81
|
+
when 200..299
|
82
|
+
Response::Invoice.new(response)
|
83
|
+
when 400..599
|
84
|
+
raise ApiError.new(Response::Invoice.new(response))
|
85
|
+
else
|
86
|
+
raise ApiError.new(response)
|
87
|
+
end
|
88
|
+
rescue Timeout::Error => e
|
89
|
+
raise TimeoutError.new(e)
|
90
|
+
rescue ApiError => e
|
91
|
+
raise e
|
92
|
+
rescue Exception => e
|
93
|
+
raise Error.new(e)
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def self.authentication
|
99
|
+
{ :username => username, :password => password}
|
100
|
+
end
|
101
|
+
end
|
data/lib/avalara/api.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'httparty'
|
3
|
+
require 'avalara/parser'
|
4
|
+
|
5
|
+
module Avalara
|
6
|
+
class API
|
7
|
+
include HTTParty
|
8
|
+
headers 'Accept' => 'application/json', 'Content-Type' => 'text/json'
|
9
|
+
format :json
|
10
|
+
parser Parser
|
11
|
+
|
12
|
+
def self.headers_for(length)
|
13
|
+
{ 'Date' => Time.now.httpdate(), 'User-Agent' => user_agent_string, "Content-Length" => length.to_s }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def self.user_agent_string
|
19
|
+
"avalara/#{Avalara::VERSION} (Rubygems; Ruby #{RUBY_VERSION} #{RUBY_PLATFORM})"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
class Avalara::Configuration
|
4
|
+
|
5
|
+
attr_writer :endpoint
|
6
|
+
attr_accessor :password
|
7
|
+
attr_accessor :username
|
8
|
+
attr_writer :version
|
9
|
+
|
10
|
+
##
|
11
|
+
# Public: Get the API endpoint used by the configuration. Unless explicitly
|
12
|
+
# set, the endpoint will default to the official production endpoint at
|
13
|
+
# 'https://avatax.avalara.net'.
|
14
|
+
#
|
15
|
+
# Returns the String for the API endpoint.
|
16
|
+
#
|
17
|
+
def endpoint
|
18
|
+
@endpoint ||= 'https://rest.avalara.net'
|
19
|
+
end
|
20
|
+
|
21
|
+
##
|
22
|
+
# Public: Get the API version. Defaults to 1.0.
|
23
|
+
#
|
24
|
+
# Returns the String for the API version.
|
25
|
+
#
|
26
|
+
def version
|
27
|
+
@version ||= '1.0'
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
# To be removed when Crack is updated, see below.
|
4
|
+
require 'multi_json'
|
5
|
+
require 'httparty'
|
6
|
+
|
7
|
+
module Avalara
|
8
|
+
class Parser < ::HTTParty::Parser
|
9
|
+
SupportedFormats.merge!({'text/plain' => :json})
|
10
|
+
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
|
15
|
+
##
|
16
|
+
# Private: This was put in place entirely to appease Ruby 1.9.2-p290's more
|
17
|
+
# strict YAML parsing, which breaks Crack 0.1.8's JSON decoding.
|
18
|
+
#
|
19
|
+
# This will be removed when Crack supports decoding the API's returned
|
20
|
+
# values.
|
21
|
+
#
|
22
|
+
def json
|
23
|
+
decode_json(body)
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Private: This was put in place entirely to appease Ruby 1.9.2-p290's more
|
28
|
+
# strict YAML parsing, which breaks Crack 0.1.8's JSON decoding.
|
29
|
+
#
|
30
|
+
# This will be removed when Crack supports decoding the API's returned
|
31
|
+
# values.
|
32
|
+
#
|
33
|
+
def decode_json(body)
|
34
|
+
if body.to_s =~ /^(?:[\d]+|null|true|false)$/
|
35
|
+
MultiJson.decode("[#{body}]").first
|
36
|
+
else
|
37
|
+
MultiJson.decode body
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Avalara
|
4
|
+
module Request
|
5
|
+
autoload :Address, 'avalara/request/address'
|
6
|
+
autoload :DetailLevel, 'avalara/request/detail_level'
|
7
|
+
autoload :Invoice, 'avalara/request/invoice'
|
8
|
+
autoload :Line, 'avalara/request/line'
|
9
|
+
autoload :Message, 'avalara/request/message'
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Avalara
|
4
|
+
module Request
|
5
|
+
class Address < Avalara::Types::Stash
|
6
|
+
property :AddressCode, :from => :address_code, :required => true
|
7
|
+
property :Line1, :from => :line_1
|
8
|
+
property :Line2, :from => :line_2
|
9
|
+
property :Line3, :from => :line_3
|
10
|
+
property :City, :from => :city
|
11
|
+
property :Region, :from => :region
|
12
|
+
property :Country, :from => :country
|
13
|
+
property :PostalCode, :from => :postal_code
|
14
|
+
property :Latitude, :from => :latitude
|
15
|
+
property :Longitude, :from => :longitude
|
16
|
+
property :TaxRegionId, :from => :tax_region_id
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Avalara
|
4
|
+
module Request
|
5
|
+
class DetailLevel < Avalara::Types::Stash
|
6
|
+
property :Line, :from => :line
|
7
|
+
property :Summary, :from => :summary
|
8
|
+
property :Document, :from => :document
|
9
|
+
property :Tax, :from => :tax
|
10
|
+
property :Diagnostic, :from => :diagnostic
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'multi_json'
|
3
|
+
|
4
|
+
module Avalara
|
5
|
+
module Request
|
6
|
+
# Same as GetTaxRequest
|
7
|
+
class Invoice < Avalara::Types::Stash
|
8
|
+
coerce_key :DocDate, Avalara::Types::Date
|
9
|
+
|
10
|
+
# Set outgoing
|
11
|
+
property :CustomerCode, :from => :customer_code
|
12
|
+
property :DocDate, :from => :doc_date
|
13
|
+
property :CompanyCode, :from => :company_code
|
14
|
+
property :Commit, :from => :commit
|
15
|
+
property :CustomerUsageType, :from => :customer_usage_type
|
16
|
+
property :Discount, :from => :discount
|
17
|
+
property :DocCode, :from => :doc_code
|
18
|
+
property :PurchaseOrderNo, :from => :purchase_order_no
|
19
|
+
property :ExemptionNo, :from => :exemption_no
|
20
|
+
property :DetailLevel, :from => :detail_level
|
21
|
+
property :DocType, :from => :doc_type
|
22
|
+
property :PaymentDate, :from => :payment_date
|
23
|
+
property :Lines, :from => :lines
|
24
|
+
property :Addresses, :from => :addresses
|
25
|
+
property :ReferenceCode, :from => :reference_code
|
26
|
+
|
27
|
+
def addresses=(addresses)
|
28
|
+
self.Addresses = []
|
29
|
+
addresses.each do |address|
|
30
|
+
self.Addresses << Address.new(address)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def lines=(lines)
|
35
|
+
self.Lines = []
|
36
|
+
lines.each do |line|
|
37
|
+
self.Lines << Line.new(line)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def to_json
|
42
|
+
MultiJson.encode(self.to_hash, :pretty => true)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|