vizjerai-avalara 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +25 -0
  3. data/CHANGELOG.md +14 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE.txt +22 -0
  6. data/README +5 -0
  7. data/Rakefile +10 -0
  8. data/lib/avalara/api.rb +26 -0
  9. data/lib/avalara/configuration.rb +31 -0
  10. data/lib/avalara/errors.rb +8 -0
  11. data/lib/avalara/request/address.rb +15 -0
  12. data/lib/avalara/request/cancel_tax.rb +18 -0
  13. data/lib/avalara/request/detail_level.rb +13 -0
  14. data/lib/avalara/request/invoice.rb +40 -0
  15. data/lib/avalara/request/invoice_address.rb +19 -0
  16. data/lib/avalara/request/line.rb +24 -0
  17. data/lib/avalara/request.rb +13 -0
  18. data/lib/avalara/response/address_validation.rb +24 -0
  19. data/lib/avalara/response/cancel_tax.rb +20 -0
  20. data/lib/avalara/response/cancel_tax_result.rb +20 -0
  21. data/lib/avalara/response/invoice.rb +38 -0
  22. data/lib/avalara/response/message.rb +13 -0
  23. data/lib/avalara/response/tax.rb +24 -0
  24. data/lib/avalara/response/tax_address.rb +16 -0
  25. data/lib/avalara/response/tax_detail.rb +16 -0
  26. data/lib/avalara/response/tax_line.rb +22 -0
  27. data/lib/avalara/response/validation_address.rb +46 -0
  28. data/lib/avalara/response.rb +16 -0
  29. data/lib/avalara/types/date.rb +10 -0
  30. data/lib/avalara/types/stash.rb +28 -0
  31. data/lib/avalara/types.rb +8 -0
  32. data/lib/avalara/version.rb +5 -0
  33. data/lib/avalara.rb +121 -0
  34. data/spec/avalara.yml.example +3 -0
  35. data/spec/factories/addresses.rb +11 -0
  36. data/spec/factories/cancel_taxs.rb +9 -0
  37. data/spec/factories/detail_levels.rb +9 -0
  38. data/spec/factories/invoice_addresses.rb +15 -0
  39. data/spec/factories/invoices.rb +18 -0
  40. data/spec/factories/lines.rb +16 -0
  41. data/spec/fixtures/net/Avalara/_cancel_tax/successful_response.yml +59 -0
  42. data/spec/fixtures/net/Avalara/_cancel_tax/with_missing_doc_code/unsuccessful_response.yml +75 -0
  43. data/spec/fixtures/net/Avalara/_geographical_tax/successful_response.yml +92 -0
  44. data/spec/fixtures/net/Avalara/_geographical_tax/with_incomplete_address/unsuccessful_response.yml +66 -0
  45. data/spec/fixtures/net/Avalara/_get_tax/successful_response.yml +151 -0
  46. data/spec/fixtures/net/Avalara/_get_tax/with_missing_required_field/unsuccessful_response.yml +73 -0
  47. data/spec/fixtures/net/Avalara/_validate_address/successful_response.yml +76 -0
  48. data/spec/fixtures/net/Avalara/_validate_address/with_missing_postal_code/unsuccessful_response.yml +68 -0
  49. data/spec/models/avalara/configuration_spec.rb +55 -0
  50. data/spec/models/avalara/request/address_spec.rb +20 -0
  51. data/spec/models/avalara/request/detail_level_spec.rb +18 -0
  52. data/spec/models/avalara/request/invoice_address_spec.rb +24 -0
  53. data/spec/models/avalara/request/invoice_spec.rb +32 -0
  54. data/spec/models/avalara/request/line_spec.rb +25 -0
  55. data/spec/models/avalara_spec.rb +300 -0
  56. data/spec/spec_helper.rb +19 -0
  57. data/spec/support/avalara.rb +38 -0
  58. data/spec/support/factory_girl.rb +20 -0
  59. data/spec/support/vcr.rb +18 -0
  60. data/vizjerai-avalara.gemspec +31 -0
  61. metadata +257 -0
@@ -0,0 +1,73 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: post
5
+ uri: https://API_BASIC_AUTH@API_ENDPOINT/1.0/tax/get
6
+ body:
7
+ encoding: UTF-8
8
+ string: ! "{\n \"CustomerCode\": null,\n \"DocDate\": \"2013-06-11\",\n \"CompanyCode\":
9
+ 83,\n \"Lines\": [\n {\n \"LineNo\": \"1\",\n \"DestinationCode\":
10
+ \"1\",\n \"OriginCode\": \"1\",\n \"Qty\": \"1\",\n \"Amount\":
11
+ 10\n }\n ],\n \"Addresses\": [\n {\n \"AddressCode\": 1,\n \"Line1\":
12
+ \"435 Ericksen Avenue Northeast\",\n \"Line2\": \"#250\",\n \"PostalCode\":
13
+ \"98110\"\n }\n ]\n}"
14
+ headers:
15
+ Accept:
16
+ - application/json
17
+ Content-Type:
18
+ - text/json
19
+ Date:
20
+ - Tue, 11 Jun 2013 14:25:21 GMT
21
+ User-Agent:
22
+ - avalara/0.0.2 (Rubygems; Ruby 1.9.3 x86_64-freebsd8.1)
23
+ Content-Length:
24
+ - '374'
25
+ response:
26
+ status:
27
+ code: 500
28
+ message: Internal Server Error
29
+ headers:
30
+ Cache-Control:
31
+ - private
32
+ Content-Type:
33
+ - text/json; charset=utf-8
34
+ X-Aspnet-Version:
35
+ - 4.0.30319
36
+ X-Powered-By:
37
+ - ASP.NET
38
+ Date:
39
+ - Tue, 11 Jun 2013 14:25:21 GMT
40
+ Content-Length:
41
+ - '215'
42
+ Set-Cookie:
43
+ - TS6198e2=6b8238c1ba9f886ffe742e8d094cc3fa3a10d0eeb29e4ce851b73351; Path=/
44
+ - TS6198e2_28=adb1456b4bb2ed5d9ad7bfb38be60cdc3a10d0eeb29e4ce800000000000000000051b73351cf2467f60807d03e;
45
+ Path=/
46
+ body:
47
+ encoding: US-ASCII
48
+ string: ! '{
49
+
50
+ "ResultCode": "Error",
51
+
52
+ "Messages": [
53
+
54
+ {
55
+
56
+ "Summary": "CustomerCode is required.",
57
+
58
+ "Details": "This value must be specified.",
59
+
60
+ "RefersTo": "CustomerCode",
61
+
62
+ "Severity": "Error",
63
+
64
+ "Source": "Avalara.AvaTax.Services"}
65
+
66
+ ]
67
+
68
+ }
69
+
70
+ '
71
+ http_version:
72
+ recorded_at: Tue, 11 Jun 2013 14:25:22 GMT
73
+ recorded_with: VCR 2.5.0
@@ -0,0 +1,76 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://API_BASIC_AUTH@API_ENDPOINT/1.0/address/validate?Line1=435%20Ericksen%20Avenue%20Northeast&Line2=%23250&PostalCode=98110
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/json
12
+ Content-Type:
13
+ - text/json
14
+ Date:
15
+ - Tue, 11 Jun 2013 19:07:56 GMT
16
+ User-Agent:
17
+ - avalara/0.0.2 (Rubygems; Ruby 1.9.3 x86_64-freebsd8.1)
18
+ Content-Length:
19
+ - '0'
20
+ response:
21
+ status:
22
+ code: 200
23
+ message: OK
24
+ headers:
25
+ Cache-Control:
26
+ - private
27
+ Content-Type:
28
+ - text/json; charset=utf-8
29
+ X-Aspnet-Version:
30
+ - 4.0.30319
31
+ X-Powered-By:
32
+ - ASP.NET
33
+ Date:
34
+ - Tue, 11 Jun 2013 19:07:56 GMT
35
+ Content-Length:
36
+ - '297'
37
+ Set-Cookie:
38
+ - TS6198e2=636d7e08a1ba939e74f09679205034ff17aef1bec375cc4e51b7758d; Path=/
39
+ - TS6198e2_28=d504e6ad8384879a66c8f948ac046f1317aef1bec375cc4e00000000000000000051b7758c8a73661fdfaad314;
40
+ Path=/
41
+ body:
42
+ encoding: US-ASCII
43
+ string: ! '{
44
+
45
+ "ResultCode": "Success",
46
+
47
+ "Address": {
48
+
49
+ "Line1": "250",
50
+
51
+ "Line2": "435 Ericksen Ave NE",
52
+
53
+ "City": "Bainbridge Island",
54
+
55
+ "Region": "WA",
56
+
57
+ "PostalCode": "98110-1896",
58
+
59
+ "Country": "US",
60
+
61
+ "County": "Kitsap",
62
+
63
+ "FipsCode": "5303500000",
64
+
65
+ "CarrierRoute": "C051",
66
+
67
+ "PostNet": "981101896999",
68
+
69
+ "AddressType": "H"}
70
+
71
+ }
72
+
73
+ '
74
+ http_version:
75
+ recorded_at: Tue, 11 Jun 2013 19:07:57 GMT
76
+ recorded_with: VCR 2.5.0
@@ -0,0 +1,68 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://API_BASIC_AUTH@API_ENDPOINT/1.0/address/validate?Line1=435%20Ericksen%20Avenue%20Northeast&Line2=%23250&PostalCode=
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - application/json
12
+ Content-Type:
13
+ - text/json
14
+ Date:
15
+ - Tue, 11 Jun 2013 19:07:54 GMT
16
+ User-Agent:
17
+ - avalara/0.0.2 (Rubygems; Ruby 1.9.3 x86_64-freebsd8.1)
18
+ Content-Length:
19
+ - '0'
20
+ response:
21
+ status:
22
+ code: 500
23
+ message: Internal Server Error
24
+ headers:
25
+ Cache-Control:
26
+ - private
27
+ Content-Type:
28
+ - text/json; charset=utf-8
29
+ X-Aspnet-Version:
30
+ - 4.0.30319
31
+ X-Powered-By:
32
+ - ASP.NET
33
+ Date:
34
+ - Tue, 11 Jun 2013 19:07:55 GMT
35
+ Content-Length:
36
+ - '253'
37
+ Set-Cookie:
38
+ - TS6198e2=927716e1d83dd5e3fa61af30db5ced304c62f84ba8d8a59b51b7758b; Path=/
39
+ - TS6198e2_28=22ec2375be97493e8f7a884bc8271dc54c62f84ba8d8a59b00000000000000000051b7758b2814d6a370a5270d;
40
+ Path=/
41
+ body:
42
+ encoding: US-ASCII
43
+ string: ! '{
44
+
45
+ "ResultCode": "Error",
46
+
47
+ "Messages": [
48
+
49
+ {
50
+
51
+ "Summary": "Insufficient address information",
52
+
53
+ "Details": "You must specify at least Line/ZIP, or Line/City/State",
54
+
55
+ "RefersTo": "PostalCode",
56
+
57
+ "Severity": "Error",
58
+
59
+ "Source": "Avalara.AvaTax.Services.Address"}
60
+
61
+ ]
62
+
63
+ }
64
+
65
+ '
66
+ http_version:
67
+ recorded_at: Tue, 11 Jun 2013 19:07:56 GMT
68
+ recorded_with: VCR 2.5.0
@@ -0,0 +1,55 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Avalara::Configuration do
6
+ let(:configuration) { Avalara::Configuration.new }
7
+
8
+ context '#endpoint' do
9
+ it 'defaults to https://rest.avalara.net' do
10
+ configuration.endpoint.should == 'https://rest.avalara.net'
11
+ end
12
+
13
+ it 'may be overridden' do
14
+ expect {
15
+ configuration.endpoint = 'https://example.local/'
16
+ }.to change(configuration, :endpoint).to('https://example.local/')
17
+ end
18
+ end
19
+
20
+ context '#version' do
21
+ it 'defaults to 1.0' do
22
+ configuration.version.should == '1.0'
23
+ end
24
+
25
+ it 'may be overridden' do
26
+ expect {
27
+ configuration.version = '2.0'
28
+ }.to change(configuration, :version).to('2.0')
29
+ end
30
+ end
31
+
32
+ context '#username' do
33
+ it 'is unset by default' do
34
+ configuration.username.should be_nil
35
+ end
36
+
37
+ it 'may be set' do
38
+ expect {
39
+ configuration.username = 'abcdefg'
40
+ }.to change(configuration, :username).to('abcdefg')
41
+ end
42
+ end
43
+
44
+ context '#password' do
45
+ it 'is unset by default' do
46
+ configuration.password.should be_nil
47
+ end
48
+
49
+ it 'may be set' do
50
+ expect {
51
+ configuration.password = 'abcdefg'
52
+ }.to change(configuration, :password).to('abcdefg')
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Avalara::Request::Address do
6
+ let(:params) { FactoryGirl.attributes_for(:address) }
7
+ let(:address) { FactoryGirl.build_via_new(:address) }
8
+
9
+ context 'sets all attributes' do
10
+ subject { address }
11
+
12
+ its(:Line1) { should == params[:line_1] }
13
+ its(:Line2) { should == params[:line_2] }
14
+ its(:Line3) { should == params[:line_3] }
15
+ its(:City) { should == params[:city] }
16
+ its(:Region) { should == params[:region] }
17
+ its(:Country) { should == params[:country] }
18
+ its(:PostalCode) { should == params[:postal_code] }
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Avalara::Request::DetailLevel do
6
+ let(:params) { FactoryGirl.attributes_for(:detail_level) }
7
+ let(:detail_level) { FactoryGirl.build_via_new(:detail_level) }
8
+
9
+ context 'sets all attributes' do
10
+ subject { detail_level }
11
+
12
+ its(:Line) { should == params[:line] }
13
+ its(:Summary) { should == params[:summary] }
14
+ its(:Document) { should == params[:document] }
15
+ its(:Tax) { should == params[:tax] }
16
+ its(:Diagnostic) { should == params[:diagnostic] }
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Avalara::Request::InvoiceAddress do
6
+ let(:params) { FactoryGirl.attributes_for(:invoice_address) }
7
+ let(:invoice_address) { FactoryGirl.build_via_new(:invoice_address) }
8
+
9
+ context 'sets all attributes' do
10
+ subject { invoice_address }
11
+
12
+ its(:AddressCode) { should == params[:address_code] }
13
+ its(:Line1) { should == params[:line_1] }
14
+ its(:Line2) { should == params[:line_2] }
15
+ its(:Line3) { should == params[:line_3] }
16
+ its(:City) { should == params[:city] }
17
+ its(:Region) { should == params[:region] }
18
+ its(:Country) { should == params[:country] }
19
+ its(:PostalCode) { should == params[:postal_code] }
20
+ its(:Latitude) { should == params[:latitude] }
21
+ its(:Longitude) { should == params[:longitude] }
22
+ its(:TaxRegionId) { should == params[:tax_region_id] }
23
+ end
24
+ end
@@ -0,0 +1,32 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Avalara::Request::Invoice do
6
+ let(:params) { FactoryGirl.attributes_for(:invoice) }
7
+ let(:invoice) { FactoryGirl.build_via_new(:invoice) }
8
+
9
+ context 'sets all attributes' do
10
+ subject { invoice }
11
+
12
+ its(:CustomerCode) { should == params[:customer_code] }
13
+ its(:DocDate) { should == Avalara::Types::Date.coerce(params[:doc_date]) }
14
+ its(:CompanyCode) { should == params[:company_code] }
15
+ its(:Commit) { should == params[:commit] }
16
+ its(:CustomerUsageType) { should == params[:customer_usage_type] }
17
+ its(:Discount) { should == params[:discount] }
18
+ its(:DocCode) { should == params[:doc_code] }
19
+ its(:PurchaseOrderNo) { should == params[:purchase_order_no] }
20
+ its(:ExemptionNo) { should == params[:exemption_no] }
21
+ its(:DetailLevel) { should == params[:detail_level] }
22
+ its(:DocType) { should == params[:doc_type] }
23
+ its(:Lines) { should == params[:lines] }
24
+ its(:Addresses) { should == params[:addresses] }
25
+ its(:ReferenceCode) { should == params[:reference_code] }
26
+ end
27
+
28
+ context 'converts nested objects to json' do
29
+ subject { invoice.to_json }
30
+ it { should_not be_nil }
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Avalara::Request::Line do
6
+ let(:params) { FactoryGirl.attributes_for(:line) }
7
+ let(:line) { FactoryGirl.build_via_new(:line) }
8
+
9
+ context 'sets all attributes' do
10
+ subject { line }
11
+
12
+ its(:LineNo) { should == params[:line_no] }
13
+ its(:DestinationCode) { should == params[:destination_code] }
14
+ its(:OriginCode) { should == params[:origin_code] }
15
+ its(:ItemCode) { should == params[:item_code] }
16
+ its(:TaxCode) { should == params[:tax_code] }
17
+ its(:CustomerUsageType) { should == params[:customer_usage_type] }
18
+ its(:Qty) { should == params[:qty] }
19
+ its(:Amount) { should == params[:amount] }
20
+ its(:Discounted) { should == params[:discounted] }
21
+ its(:TaxIncluded) { should == params[:tax_included] }
22
+ its(:Ref1) { should == params[:ref_1] }
23
+ its(:Ref2) { should == params[:ref_2] }
24
+ end
25
+ end
@@ -0,0 +1,300 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Avalara do
6
+ maintain_contactology_configuration
7
+
8
+ let(:configuration) { Avalara.configuration }
9
+
10
+ describe '.configuration' do
11
+ it 'yields a Avalara::Configuration instance' do
12
+ Avalara.configuration do |yielded|
13
+ yielded.should be_kind_of Avalara::Configuration
14
+ end
15
+ end
16
+
17
+ it 'yields the same configuration instance across multiple calls' do
18
+ Avalara.configuration do |config|
19
+ Avalara.configuration do |config2|
20
+ config.object_id.should == config2.object_id
21
+ end
22
+ end
23
+ end
24
+
25
+ it 'returns the configuration when queried' do
26
+ Avalara.configuration do |config|
27
+ Avalara.configuration.object_id.should == config.object_id
28
+ end
29
+ end
30
+
31
+ it 'may be explicitly overridden' do
32
+ configuration = Avalara::Configuration.new
33
+ expect {
34
+ Avalara.configuration = configuration
35
+ }.to change(Avalara, :configuration).to(configuration)
36
+ end
37
+
38
+ it 'raises an ArgumentError when set to a non-Configuration object' do
39
+ expect {
40
+ Avalara.configuration = 'bad'
41
+ }.to raise_error(ArgumentError)
42
+ end
43
+ end
44
+
45
+ describe '.endpoint' do
46
+ it 'returns the configuration endpoint' do
47
+ Avalara.endpoint.should == configuration.endpoint
48
+ end
49
+
50
+ it 'overrides the configuration endpoint' do
51
+ expect {
52
+ Avalara.endpoint = 'https://example.local/'
53
+ }.to change(configuration, :endpoint).to('https://example.local/')
54
+ end
55
+ end
56
+
57
+ describe '.username' do
58
+ it 'returns the configuration username' do
59
+ configuration.username = 'username'
60
+ Avalara.username.should == configuration.username
61
+ end
62
+
63
+ it 'overrides the configuration username' do
64
+ expect {
65
+ Avalara.username = 'username'
66
+ }.to change(configuration, :username).to('username')
67
+ end
68
+ end
69
+
70
+ describe '.password' do
71
+ it 'returns the configuration password' do
72
+ configuration.password = 'password'
73
+ Avalara.password.should == configuration.password
74
+ end
75
+
76
+ it 'overrides the configuration password' do
77
+ expect {
78
+ Avalara.password = 'password'
79
+ }.to change(configuration, :password).to('password')
80
+ end
81
+ end
82
+
83
+ describe '.version' do
84
+ it 'returns the configuration version' do
85
+ configuration.version = 'version'
86
+ Avalara.version.should == configuration.version
87
+ end
88
+
89
+ it 'overrides the configuration version' do
90
+ expect {
91
+ Avalara.version = 'version'
92
+ }.to change(configuration, :version).to('version')
93
+ end
94
+ end
95
+
96
+ describe '.cancel_tax' do
97
+ let(:cancel_tax) { FactoryGirl.build_via_new(:cancel_tax) }
98
+ let(:request) { Avalara.cancel_tax(cancel_tax) }
99
+
100
+ it 'successful response', :vcr do
101
+ expect(request).to be_a Avalara::Response::CancelTax
102
+ expect(request).to be_success
103
+
104
+ expect(request.transaction_id).to be 431361022
105
+ expect(request.doc_id).to eq '34279849'
106
+ expect(request.result_code).to eq 'Success'
107
+ expect(request.messages).to be_nil
108
+ end
109
+
110
+ context 'with missing doc code', :vcr do
111
+ let(:cancel_tax) { FactoryGirl.build_via_new(:cancel_tax, :doc_code => nil) }
112
+ it 'unsuccessful response' do
113
+ expect(request).to be_a Avalara::Response::CancelTax
114
+ expect(request).to_not be_success
115
+
116
+ expect(request.transaction_id).to be 431377420
117
+ expect(request.doc_id).to be_nil
118
+ expect(request.result_code).to eq 'Error'
119
+ expect(request).to have(1).message
120
+
121
+ message = request.messages.first
122
+ expect(message.details).to eq "This value must be specified."
123
+ expect(message.refers_to).to eq "DocCode"
124
+ expect(message.severity).to eq "Error"
125
+ expect(message.source).to eq "Avalara.AvaTax.Services"
126
+ expect(message.summary).to eq "DocCode is required."
127
+ end
128
+ end
129
+
130
+ context 'on timeout' do
131
+ it 'raises an avalara timeout error' do
132
+ Avalara::API.should_receive(:post).and_raise(Timeout::Error)
133
+ expect { request }.to raise_error(Avalara::TimeoutError)
134
+ end
135
+ end
136
+ end
137
+
138
+ describe '.get_tax' do
139
+ let(:doc_date) { Date.parse("January 1, 2012") }
140
+ let(:invoice) { FactoryGirl.build_via_new(:invoice, :doc_date => doc_date) }
141
+ let(:request) { Avalara.get_tax(invoice) }
142
+
143
+ it 'successful response', :vcr do
144
+ expect(request).to be_kind_of Avalara::Response::Invoice
145
+ expect(request).to be_success
146
+
147
+ expect(request.doc_code).to_not be_nil
148
+ expect(request.doc_date).to eq "2012-01-01"
149
+ expect(request.result_code).to eq "Success"
150
+ expect(request.tax_date).to_not be_nil
151
+ expect(request.timestamp).to_not be_nil
152
+ expect(request.total_amount).to eq "10"
153
+ expect(request.total_discount).to eq "0"
154
+ expect(request.total_exemption).to eq "10"
155
+ expect(request.total_tax).to eq "0"
156
+ expect(request.total_tax_calculated).to eq "0"
157
+ expect(request.tax_lines.length).to be 1
158
+
159
+ tax_line = request.tax_lines.first
160
+ expect(tax_line.line_no).to eq "1"
161
+ expect(tax_line.tax_code).to eq "P0000000"
162
+ expect(tax_line.taxability).to eq "true"
163
+ expect(tax_line.taxable).to eq "0"
164
+ expect(tax_line.rate).to eq "0"
165
+ expect(tax_line.tax).to eq "0"
166
+ expect(tax_line.discount).to eq "0"
167
+ expect(tax_line.tax_calculated).to eq "0"
168
+ expect(tax_line.exemption).to eq "10"
169
+ expect(tax_line.tax_details.length).to be 1
170
+
171
+ tax_detail = tax_line.tax_details.first
172
+ expect(tax_detail.taxable).to eq "0"
173
+ expect(tax_detail.rate).to eq "0"
174
+ expect(tax_detail.tax).to eq "0"
175
+ expect(tax_detail.region).to eq "WA"
176
+ expect(tax_detail.country).to eq "US"
177
+ expect(tax_detail.juris_type).to eq "State"
178
+ expect(tax_detail.juris_name).to eq "WASHINGTON"
179
+ expect(tax_detail.tax_name).to eq "WA STATE TAX"
180
+
181
+ expect(request.tax_addresses.length).to be 1
182
+ end
183
+
184
+ context 'with missing required field', :vcr do
185
+ let(:invoice) { FactoryGirl.build_via_new(:invoice, customer_code: nil) }
186
+
187
+ it 'unsuccessful response' do
188
+ expect(request).to be_kind_of Avalara::Response::Invoice
189
+ expect(request).to_not be_success
190
+
191
+ message = request.messages.first
192
+ expect(message.details).to eq "This value must be specified."
193
+ expect(message.refers_to).to eq "CustomerCode"
194
+ expect(message.severity).to eq "Error"
195
+ expect(message.source).to eq "Avalara.AvaTax.Services"
196
+ expect(message.summary).to eq "CustomerCode is required."
197
+ end
198
+ end
199
+
200
+ context 'on timeout' do
201
+ it 'raises an avalara timeout error' do
202
+ Avalara::API.should_receive(:post).and_raise(Timeout::Error)
203
+ expect { request }.to raise_error(Avalara::TimeoutError)
204
+ end
205
+ end
206
+ end
207
+
208
+ describe '.geographical_tax' do
209
+ let(:latitude) { '47.627935' }
210
+ let(:longitude) { '-122.51702' }
211
+ let(:sales_amount) { 100 }
212
+ let(:request) { Avalara.geographical_tax(latitude, longitude, sales_amount) }
213
+
214
+ it 'successful response', :vcr do
215
+ expect(request).to be_kind_of Avalara::Response::Tax
216
+ expect(request).to be_success
217
+
218
+ expect(request.rate).to eq 0.086
219
+ expect(request.tax).to eq 8.6
220
+ expect(request.tax_details).to have(2).items
221
+
222
+ state_tax_detail = request.tax_details.first
223
+ expect(state_tax_detail.rate).to eq 0.065
224
+ expect(state_tax_detail.tax).to eq 6.5
225
+ expect(state_tax_detail.juris_type).to eq 'State'
226
+
227
+ city_tax_detail = request.tax_details.last
228
+ expect(city_tax_detail.rate).to eq 0.021
229
+ expect(city_tax_detail.tax).to eq 2.1
230
+ expect(city_tax_detail.juris_type).to eq 'City'
231
+ end
232
+
233
+ context 'with incomplete address', :vcr do
234
+ let(:longitude) { '122.51702' }
235
+
236
+ it 'unsuccessful response' do
237
+ expect(request).to be_kind_of Avalara::Response::Tax
238
+ expect(request).to_not be_success
239
+
240
+ expect(request.rate).to be_nil
241
+ expect(request.tax).to be_nil
242
+ expect(request.tax_details).to be_nil
243
+ end
244
+ end
245
+
246
+ context 'on timeout' do
247
+ let(:longitude) { '-122.51702' }
248
+ it 'raises an avalara timeout error' do
249
+ Avalara::API.should_receive(:get).and_raise(Timeout::Error)
250
+ expect { request }.to raise_error(Avalara::TimeoutError)
251
+ end
252
+ end
253
+ end
254
+
255
+ describe '.validate_address' do
256
+ let(:address) { FactoryGirl.build_via_new(:address) }
257
+ let(:request) { Avalara.validate_address(address) }
258
+
259
+ it 'successful response', :vcr do
260
+ expect(request).to be_kind_of Avalara::Response::AddressValidation
261
+ expect(request).to be_success
262
+
263
+ expect(request.address).to be_a Avalara::Response::ValidationAddress
264
+
265
+ address = request.address
266
+ expect(address.line_1).to eq '250'
267
+ expect(address.line_2).to eq '435 Ericksen Ave NE'
268
+ expect(address.line_3).to be_nil
269
+ expect(address.city).to eq 'Bainbridge Island'
270
+ expect(address.region).to eq 'WA'
271
+ expect(address.country).to eq 'US'
272
+ expect(address.address_type).to eq 'H'
273
+ expect(address.address_type_name).to eq 'High-rise or business complex'
274
+ expect(address.postal_code).to eq '98110-1896'
275
+ expect(address.county).to eq 'Kitsap'
276
+ expect(address.fips_code).to eq '5303500000'
277
+ expect(address.carrier_route).to eq 'C051'
278
+ expect(address.carrier_route_name).to eq 'City delivery'
279
+ expect(address.tax_region_id).to be_nil
280
+ expect(address.post_net).to eq '981101896999'
281
+ end
282
+
283
+ context 'with missing postal code', :vcr do
284
+ let(:address) { FactoryGirl.build_via_new(:address, :postal_code => nil) }
285
+
286
+ it 'unsuccessful response' do
287
+ expect(request).to be_kind_of Avalara::Response::AddressValidation
288
+ expect(request).to_not be_success
289
+ end
290
+ end
291
+
292
+ context 'on timeout' do
293
+ it 'raises an avalara timeout error' do
294
+ Avalara::API.should_receive(:get).and_raise(Timeout::Error)
295
+ expect { request }.to raise_error(Avalara::TimeoutError)
296
+ end
297
+ end
298
+ end
299
+
300
+ end