little_finger 0.0.0 → 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.
- checksums.yaml +8 -8
- data/README.md +45 -0
- data/lib/little_finger.rb +12 -4
- data/lib/little_finger/avatax.rb +96 -0
- data/lib/little_finger/configuration.rb +31 -0
- data/spec/config.yml +8 -0
- data/spec/little_finger_spec.rb +10 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/helpers/avatax_helper.rb +24 -0
- metadata +152 -5
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
MmYzZTlkNGNjY2Q4YWQ5ZTM0OTc1NDMzZjJkYzA4YmRiYmY3ZGUxYQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
OGIwNWI1YmIyZGZkNDY0N2Q5OTJlZWJmZjI0ZTgxODJkNTRmMWVjYg==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
ZGExYTM4NTUwZmE1NTEzNjUwMmM2NjRmMGQwN2EyYTZlYmFlMjVhODRiNGJj
|
10
|
+
NjI2YzNmMDkyYWI3ZmI0MTUzNzYwMThlMzQ3YjU1MGE4NjUyNTNkMzNlZmM2
|
11
|
+
NWU1YWI2OWFiMmQ1YTc2NDJhNWUwMzNjNjY2N2FkZTUxNmE5NjY=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZjhkYWFiZjI3MTgyNzIyMWQ3YWM0YWU3NjJlZTkzOWUxYjZlMDBkNTA4MDdl
|
14
|
+
ODkyNjUwODQzZWE2ZGUxZDlmNTYyZTA0NzMxMDZjNWJmYTk2NWEzYzI4YmQ4
|
15
|
+
ODdlZjcxZTRjMmU5N2NhNmVmNGQwNzg1MzA4NjYyMTIzMjE5Yzg=
|
data/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
little_finger
|
2
|
+
============
|
3
|
+
[](https://travis-ci.org/honest/little_finger)
|
4
|
+
[](http://badge.fury.io/rb/little_finger)
|
5
|
+
|
6
|
+
Little Finger is an AvaTax REST API wrapper plus backup tax calculation.
|
7
|
+
|
8
|
+
Please visit the AvaTax [developer site](http://developer.avalara.com/) for more information on their REST API.
|
9
|
+
|
10
|
+
Dependencies
|
11
|
+
-----------
|
12
|
+
TBD
|
13
|
+
|
14
|
+
Requirements
|
15
|
+
----------
|
16
|
+
- Ruby 1.9.3 or later
|
17
|
+
|
18
|
+
Getting Started
|
19
|
+
----------
|
20
|
+
- Add the `little_finger` gem to your Gemfile with `gem 'little_finger'`
|
21
|
+
- Run `bundle install` to retrieve `little_finger` and all its dependencies
|
22
|
+
- Authentication requires an valid **Account Number** and **License Key**. If you do not have an AvaTax account, a free trial account can be acquired through the [developer site](http://developer.avalara.com/api-get-started)
|
23
|
+
- Specify your authentication credentials in the YAML file (see `little_finger.yml.example`) and place it in your config folder
|
24
|
+
|
25
|
+
Usage
|
26
|
+
----------
|
27
|
+
```
|
28
|
+
To get the connection status to AvaTax:
|
29
|
+
LittleFinger::Avatax.new.status
|
30
|
+
|
31
|
+
To get a tax estimate via geographic coordinates:
|
32
|
+
LittleFinger::Avatax.new.estimate(coordinates, sale_amount)
|
33
|
+
|
34
|
+
To validate an address:
|
35
|
+
LittleFinger::Avatax.new.validate(address)
|
36
|
+
|
37
|
+
To get the tax and optionally create or update a transaction in AvaTax:
|
38
|
+
LittleFinger::Avatax.new.get(payload)
|
39
|
+
|
40
|
+
To cancel an existing transaction:
|
41
|
+
LittleFinger::Avatax.new.cancel(cancel_code, lookup_opts)
|
42
|
+
|
43
|
+
Backup: TBD
|
44
|
+
|
45
|
+
```
|
data/lib/little_finger.rb
CHANGED
@@ -1,6 +1,14 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require "active_support/all"
|
2
|
+
require "awesome_print"
|
3
|
+
require "yaml"
|
4
|
+
require "hashie"
|
5
|
+
require_relative "little_finger/configuration"
|
6
|
+
|
7
|
+
module LittleFinger
|
8
|
+
|
9
|
+
def self.yolo
|
10
|
+
"#yolo"
|
4
11
|
end
|
5
|
-
|
12
|
+
|
6
13
|
end
|
14
|
+
require_relative "little_finger/avatax"
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require "json"
|
2
|
+
require "base64"
|
3
|
+
require "rest-client"
|
4
|
+
require_relative "configuration"
|
5
|
+
|
6
|
+
class LittleFinger::Avatax
|
7
|
+
attr_accessor :company_code, :account_number, :license_key, :credentials, :tax_service_url, :address_service_url
|
8
|
+
|
9
|
+
def initialize()
|
10
|
+
@company_code = LittleFinger::Configuration.configuration[:avatax][:company_code]
|
11
|
+
@account_number = LittleFinger::Configuration.configuration[:avatax][:account_number]
|
12
|
+
@license_key = LittleFinger::Configuration.configuration[:avatax][:license_key]
|
13
|
+
@credentials = ["Basic ",Base64.encode64(@account_number + ":"+ @license_key)].join
|
14
|
+
@tax_service_url = [LittleFinger::Configuration.configuration[:avatax][:service_url], LittleFinger::Configuration.configuration[:avatax][:tax_service_path]].join
|
15
|
+
@address_service_url = [LittleFinger::Configuration.configuration[:avatax][:service_url], LittleFinger::Configuration.configuration[:avatax][:address_service_path]].join
|
16
|
+
end
|
17
|
+
|
18
|
+
# Since the REST API does not provide an explicit ping function, the estimate method can also be used to test connectivity to the service.
|
19
|
+
# @return [Fixnum] response code
|
20
|
+
def status
|
21
|
+
coordinates = { latitude: "34.030291", longitude: "-118.468925"}
|
22
|
+
uri = [@tax_service_url,coordinates[:latitude].to_s,",",coordinates[:longitude].to_s,"/get?saleamount=",0].join
|
23
|
+
response = send_request(:get, uri)
|
24
|
+
response.code
|
25
|
+
end
|
26
|
+
|
27
|
+
# Calculates taxes on a document such as a sales order, sales invoice, purchase order, purchase invoice, or credit memo.
|
28
|
+
# http://developer.avalara.com/api-docs/api-reference/rest-curl/gettax
|
29
|
+
# @param [Hash] payload
|
30
|
+
def get(payload)
|
31
|
+
uri = [@tax_service_url,"get"].join
|
32
|
+
response = send_request(:post, uri, payload)
|
33
|
+
JSON.parse(response.body)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Voids or deletes and existing transaction record from the AvaTax system.
|
37
|
+
# http://developer.avalara.com/api-docs/api-reference/rest-curl/canceltax
|
38
|
+
# http://developer.avalara.com/api-docs/rest/tax/cancel
|
39
|
+
# @param [String] cancel_code The reason for cancelling the tax record: Unspecified, PostFailed, DocDeleted, DocVoided, AdjustmentCancelled
|
40
|
+
# @param [Hash] lookup_opts options to identify Avatax transactions by DocId or by the set of values CompanyCode, DocCode, and DocType.
|
41
|
+
# @option lookup_opts [String] DocId Avatax-assigned unique Document Id
|
42
|
+
# @option lookup_opts [String] CompanyCode Client application company reference code
|
43
|
+
# @option lookup_opts [String] DocCode Client application identifier describing this tax transaction (i.e. invoice number, sales order number, etc.)
|
44
|
+
# @option lookup_opts [String] DocType The reason for cancelling the tax record.
|
45
|
+
# @return [Hash] JSON parsed response body
|
46
|
+
def cancel(cancel_code, lookup_opts = {})
|
47
|
+
payload = lookup_opts.merge({CancelCode: cancel_code})
|
48
|
+
uri = [@tax_service_url, "cancel"].join
|
49
|
+
response = send_request(:post, uri, payload)
|
50
|
+
JSON.parse(response.body).try(:[],"CancelTaxResult")
|
51
|
+
end
|
52
|
+
|
53
|
+
# Retrieves tax rate details for the supplied geographic coordinates and sale amount.
|
54
|
+
# http://developer.avalara.com/api-docs/api-reference/rest-curl/estimatetax
|
55
|
+
# @param [Hash] coordinates geographic coordinates
|
56
|
+
# @option coordinates [String] latitude
|
57
|
+
# @option coordinates [String] longitude
|
58
|
+
# @param [Float] sale_amount
|
59
|
+
# @return [Hash] JSON parsed response body
|
60
|
+
def estimate(coordinates, sale_amount = 0)
|
61
|
+
return unless coordinates.present? && coordinates.has_key?(:latitude) && coordinates.has_key?(:longitude)
|
62
|
+
uri = [@tax_service_url,coordinates[:latitude].to_s,",",coordinates[:longitude].to_s,"/get?saleamount=",sale_amount.to_s].join
|
63
|
+
response = send_request(:get, uri)
|
64
|
+
JSON.parse(response.body)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Normalizes a single US or Canadian address, providing a non-ambiguous address match.
|
68
|
+
# http://developer.avalara.com/api-docs/api-reference/rest-curl/validate
|
69
|
+
# @param [Hash] address physical shipping address to validate
|
70
|
+
# @option address [String] :Line1
|
71
|
+
# @option address [String] :Line2
|
72
|
+
# @option address [String] :Line3
|
73
|
+
# @option address [String] :City
|
74
|
+
# @option address [String] :Region
|
75
|
+
# @option address [String] :PostalCode
|
76
|
+
# @option address [String] :Country
|
77
|
+
# @return [Hash] JSON parsed response body
|
78
|
+
def validate(address)
|
79
|
+
uri = [@address_service_url, "validate?", address.to_query].join
|
80
|
+
response = send_request(:get, uri)
|
81
|
+
JSON.parse(response.body)
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
# Forms and sends the request
|
87
|
+
# @param [Symbol] method request method
|
88
|
+
# @param [String] uri Avatax service endpoint
|
89
|
+
# @param optional [Hash] payload
|
90
|
+
# @param optional [Hash] custom_headers
|
91
|
+
def send_request(method, uri, payload = {}, custom_headers = {})
|
92
|
+
headers = {authorization: @credentials, content_type: "application/json"}.merge(custom_headers)
|
93
|
+
RestClient::Request.execute(method: method.to_sym, url: uri, payload: JSON.generate(payload), headers: headers){|response, request, result| response }
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module LittleFinger
|
2
|
+
module Configuration
|
3
|
+
@@conf = nil
|
4
|
+
|
5
|
+
def self.configure_with(environment_name, yaml_file = nil)
|
6
|
+
@@yaml_file = yaml_file.nil? ? "config/little_finger.yml" : yaml_file
|
7
|
+
@@all_conf = Hashie::Mash.new(YAML.load_file(@@yaml_file) )
|
8
|
+
@@conf = @@all_conf[environment_name]
|
9
|
+
self
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.configuration
|
13
|
+
self.configure_with(self.environment) if @@conf.nil?
|
14
|
+
@@conf
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.environment
|
18
|
+
val = ENV["RAILS_ENV"] || ENV["RACK_ENV"]
|
19
|
+
val = if val.present?
|
20
|
+
val.to_sym
|
21
|
+
else
|
22
|
+
if defined?(Rails)
|
23
|
+
Rails.env
|
24
|
+
else
|
25
|
+
raise "No environment can be found for configuration!"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/spec/config.yml
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
require "little_finger"
|
9
|
+
ENV["RAILS_ENV"] ||= "test"
|
10
|
+
require "timecop"
|
11
|
+
require "factory_girl"
|
12
|
+
require_relative "support/helpers/avatax_helper"
|
13
|
+
|
14
|
+
FactoryGirl.definition_file_paths = [File.expand_path("../factories", __FILE__)]
|
15
|
+
FactoryGirl.find_definitions
|
16
|
+
|
17
|
+
RSpec.configure do |config|
|
18
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
19
|
+
config.run_all_when_everything_filtered = true
|
20
|
+
config.filter_run :focus
|
21
|
+
|
22
|
+
# Run specs in random order to surface order dependencies. If you find an
|
23
|
+
# order dependency and want to debug it, you can fix the order by providing
|
24
|
+
# the seed, which is printed after each run.
|
25
|
+
# --seed 1234
|
26
|
+
config.order = "random"
|
27
|
+
end
|
28
|
+
|
29
|
+
LittleFinger::Configuration.configure_with("test", File.expand_path("../config.yml", __FILE__))
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module AvataxHelper
|
2
|
+
|
3
|
+
def error_messages(messages)
|
4
|
+
messages.map{|m| m["Summary"] }
|
5
|
+
end
|
6
|
+
|
7
|
+
def simple_calculation(tax_lines)
|
8
|
+
# given no tax amount overrides
|
9
|
+
tax_sum = 0
|
10
|
+
tax_lines.each do |line|
|
11
|
+
tax_sum += line["Taxable"].to_f * line["Rate"].to_f
|
12
|
+
end
|
13
|
+
tax_sum.round(2)
|
14
|
+
end
|
15
|
+
|
16
|
+
def sum_taxes(tax_lines)
|
17
|
+
tax_sum = 0
|
18
|
+
tax_lines.each do |line|
|
19
|
+
tax_sum += line["Tax"].to_f
|
20
|
+
end
|
21
|
+
tax_sum.round(2)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: little_finger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michelle Yung
|
@@ -9,14 +9,161 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2014-12-04 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
13
|
-
|
14
|
-
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 3.0.0
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 3.0.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: activesupport
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.2'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.2'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: awesome_print
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.2.0
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.2.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: airbrake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.1'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ! '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.1'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: hashie
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.2'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.2'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: json
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.8'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '1.8'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rest-client
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 1.6.7
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 1.6.7
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: yard
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ! '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :runtime
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ! '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: AvaTax REST API wrapper plus backup tax calculation
|
154
|
+
email: michelle@honest.com
|
15
155
|
executables: []
|
16
156
|
extensions: []
|
17
157
|
extra_rdoc_files: []
|
18
158
|
files:
|
159
|
+
- README.md
|
19
160
|
- lib/little_finger.rb
|
161
|
+
- lib/little_finger/avatax.rb
|
162
|
+
- lib/little_finger/configuration.rb
|
163
|
+
- spec/config.yml
|
164
|
+
- spec/little_finger_spec.rb
|
165
|
+
- spec/spec_helper.rb
|
166
|
+
- spec/support/helpers/avatax_helper.rb
|
20
167
|
homepage: https://github.com/honest/little_finger
|
21
168
|
licenses:
|
22
169
|
- MIT
|
@@ -40,6 +187,6 @@ rubyforge_project:
|
|
40
187
|
rubygems_version: 2.2.2
|
41
188
|
signing_key:
|
42
189
|
specification_version: 4
|
43
|
-
summary:
|
190
|
+
summary: AvaTax with backup tax calculation
|
44
191
|
test_files: []
|
45
192
|
has_rdoc:
|