avalara 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +1 -0
  2. data/.rvmrc +1 -0
  3. data/CHANGELOG +5 -0
  4. data/Gemfile +8 -0
  5. data/Gemfile.lock +71 -0
  6. data/LICENSE +20 -0
  7. data/README +5 -0
  8. data/avalara.gemspec +28 -0
  9. data/lib/avalara.rb +101 -0
  10. data/lib/avalara/api.rb +23 -0
  11. data/lib/avalara/configuration.rb +29 -0
  12. data/lib/avalara/errors.rb +8 -0
  13. data/lib/avalara/parser.rb +41 -0
  14. data/lib/avalara/request.rb +11 -0
  15. data/lib/avalara/request/address.rb +19 -0
  16. data/lib/avalara/request/detail_level.rb +13 -0
  17. data/lib/avalara/request/invoice.rb +46 -0
  18. data/lib/avalara/request/line.rb +21 -0
  19. data/lib/avalara/response.rb +11 -0
  20. data/lib/avalara/response/invoice.rb +48 -0
  21. data/lib/avalara/response/message.rb +13 -0
  22. data/lib/avalara/response/tax_address.rb +16 -0
  23. data/lib/avalara/response/tax_detail.rb +16 -0
  24. data/lib/avalara/response/tax_line.rb +25 -0
  25. data/lib/avalara/types.rb +8 -0
  26. data/lib/avalara/types/date.rb +10 -0
  27. data/lib/avalara/types/stash.rb +28 -0
  28. data/lib/avalara/version.rb +5 -0
  29. data/spec/avalara.yml.example +2 -0
  30. data/spec/factories/addresses.rb +13 -0
  31. data/spec/factories/detail_levels.rb +7 -0
  32. data/spec/factories/invoices.rb +17 -0
  33. data/spec/factories/lines.rb +14 -0
  34. data/spec/fixtures/net/geographical_tax_no_sales.yml +95 -0
  35. data/spec/fixtures/net/get_tax/failure.yml +68 -0
  36. data/spec/fixtures/net/get_tax/success.yml +106 -0
  37. data/spec/models/avalara/configuration_spec.rb +55 -0
  38. data/spec/models/avalara/request/address_spec.rb +24 -0
  39. data/spec/models/avalara/request/detail_level_spec.rb +18 -0
  40. data/spec/models/avalara/request/invoice_spec.rb +33 -0
  41. data/spec/models/avalara/request/line_spec.rb +25 -0
  42. data/spec/models/avalara_spec.rb +204 -0
  43. data/spec/spec_helper.rb +17 -0
  44. data/spec/support/avalara.rb +38 -0
  45. data/spec/support/factory_girl.rb +20 -0
  46. data/spec/support/vcr.rb +14 -0
  47. metadata +204 -0
@@ -0,0 +1 @@
1
+ spec/avalara.yml
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use 1.9.2-p290@avalara
@@ -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
@@ -0,0 +1,8 @@
1
+ source 'http://rubygems.org'
2
+ gemspec
3
+
4
+ gem "hashie", :git => "https://github.com/intridea/hashie.git"
5
+
6
+ group :development, :test do
7
+ gem 'rake'
8
+ end
@@ -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
@@ -0,0 +1,5 @@
1
+ Avalara
2
+ =====
3
+
4
+ This API provides access to the [Avalara](http://www.avalara.com/) AvaTax API.
5
+
@@ -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
@@ -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
@@ -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,8 @@
1
+ # encoding: UTF-8
2
+
3
+ module Avalara
4
+ Error = Class.new(StandardError)
5
+ TimeoutError = Class.new(Error)
6
+ NotImplementedError = Class.new(Error)
7
+ ApiError = Class.new(Error)
8
+ 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