avalara 0.0.1

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