strikeiron 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .DS_Store
@@ -0,0 +1 @@
1
+ 1.9.3-p194
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem update --system
4
+ - gem install bundler --pre
5
+ rvm:
6
+ - 1.9.2
7
+ - 1.9.3
8
+ - jruby-19mode
9
+ - jruby-head
10
+ - rbx-18mode
11
+ - rbx-19mode
12
+ script: rake test
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in strikeiron.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Drew Tempelmeyer
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,71 @@
1
+ # strikeiron
2
+
3
+ strikeiron uses the Strikeiron Online Sales Tax API to calculate online sales tax to alleviate maintaining tax codes.
4
+
5
+ For more information on Strikeiron, Inc., go to http://www.strikeiron.com/.
6
+
7
+ [![Build Status](https://secure.travis-ci.org/drewtempelmeyer/strikeiron.png)](http://travis-ci.org/drewtempelmeyer/strikeiron)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'strikeiron'
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install strikeiron
22
+
23
+ ## Usage
24
+
25
+ Usage of strikeiron requires access to Strikeiron Online Sales Tax API.
26
+
27
+ #### 1. Configure the client
28
+
29
+ Strikeiron.configure do |config|
30
+ config.user_id = 'your_strikeiron_user_id'
31
+ config.password = 'your_password'
32
+ end
33
+
34
+ #### 2. Obtaining a list of categories
35
+
36
+ Performing the category lookup does not count against your hits. For performance reasons, I recommend you keep a cached copy. A database is a good start.
37
+
38
+ categories = Strikeiron.tax_categories
39
+ # => [{:category=>"Alcohol", :category_id=>"01051000"}, {:category=>"Alcoholic Beverages", :category_id=>"01050000"}, {:category=>"Beer", :category_id=>"01052000"}, {:category=>"Books", :category_id=>"01501500"}, {:category=>"Charges necessary to complete sale other than delivery and installation", :category_id=>"04800000"}, ...]
40
+
41
+ #### 3. Calculate sales tax
42
+
43
+ from = Strikeiron::Address.new(:street_address => 'One Microsoft Way', :city => 'Redmond', :state => 'WA', :zip_code => '98052')
44
+ to = Strikeiron::Address.new(:street_address => '902 Broadway', :city => 'New York', :state => 'NY', :zip_code => '10010')
45
+ items = [
46
+ Strikeiron::TaxValue.new(:category_id => '01151605', :amount => 239.41),
47
+ Strikeiron::TaxValue.new(:category_id => '01151605', :amount => 239.41)
48
+ ]
49
+
50
+ response = Strikeiron.sales_tax(
51
+ :from => from,
52
+ :to => to,
53
+ :tax_values => items
54
+ )
55
+ # => #<Strikeiron::TaxResult @from=#<Strikeiron::Address @street_address="ONE MICROSOFT WAY", @city="REDMOND", @state="WA", @zip_code="98052">, @to=#<Strikeiron::Address @street_address="902 BROADWAY", @city="NEW YORK", @state="NY", @zip_code="10010-6041">, @tax_values=[#<Strikeiron::TaxValue @category="Computer Software (both prewritten and non-prewritten) delivered electronically", @category_id="01151605", @tax_amount=21.25, @jurisdictions=[#<Strikeiron::Jurisdiction @fips="36", @name="New York", @tax_amount=9.57>, #<Strikeiron::Jurisdiction @fips="CTD51000", @name="METRO COMMUTER TRANS. DISTRICT", @tax_amount=0.89>, #<Strikeiron::Jurisdiction @fips="061", @name="NEW YORK", @tax_amount=10.77>]>, #<Strikeiron::TaxValue @category="Computer Software (both prewritten and non-prewritten) delivered electronically", @category_id="01151605", @tax_amount=21.25, @jurisdictions=[#<Strikeiron::Jurisdiction @fips="36", @name="New York", @tax_amount=9.57>, #<Strikeiron::Jurisdiction @fips="CTD51000", @name="METRO COMMUTER TRANS. DISTRICT", @tax_amount=0.89>, #<Strikeiron::Jurisdiction @fips="061", @name="NEW YORK", @tax_amount=10.77>]>], @total_tax=42.5>
56
+
57
+ #### 4. Profit
58
+
59
+ Hopefully.
60
+
61
+ ## Contributing
62
+
63
+ 1. Fork it
64
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
65
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
66
+ 4. Push to the branch (`git push origin my-new-feature`)
67
+ 5. Create new Pull Request
68
+
69
+ ## Furthermore
70
+
71
+ I am in no way associated with Strikeiron.
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+ require "rdoc/task"
5
+
6
+ Rake::TestTask.new(:test) do |test|
7
+ test.libs << 'lib' << 'test'
8
+ test.pattern = 'test/test_*.rb'
9
+ test.verbose = false
10
+ end
11
+
12
+ RDoc::Task.new do |rd|
13
+ README = 'README.md'
14
+ rd.main = README
15
+ rd.rdoc_dir = 'doc'
16
+ rd.title = 'strikeiron'
17
+ rd.rdoc_files.include(README, 'lib/**/*.rb')
18
+ end
@@ -0,0 +1,109 @@
1
+ require "savon"
2
+ require "strikeiron/version"
3
+ require "strikeiron/configuration"
4
+ require "strikeiron/address"
5
+ require "strikeiron/tax_value"
6
+ require "strikeiron/jurisdiction"
7
+ require "strikeiron/tax_result"
8
+
9
+ # Strikeiron calculates online sales tax for your online service based on your local tax rules.
10
+ module Strikeiron
11
+
12
+ # The location of the Strikeiron Online Sales Tax WSDL
13
+ WSDL = 'https://wsparam.strikeiron.com/SpeedTaxSalesTax3?WSDL'
14
+
15
+ class << self
16
+ attr_accessor :configuration
17
+
18
+ # Configure Strikeiron for your account. See Configuration for more information.
19
+ def configure
20
+ self.configuration ||= Configuration.new
21
+ yield configuration
22
+ end
23
+
24
+ # Singleton Savon client
25
+ def client
26
+ @@client ||= Savon::Client.new do
27
+ wsdl.document = WSDL
28
+ end
29
+ end
30
+
31
+ # Get the calculated online sales tax for a product or service
32
+ #
33
+ # === Options
34
+ # * <tt>from</tt> - The origin Address (or your physical location). Required.
35
+ # * <tt>to</tt> - The destination Address of the customer. Required.
36
+ # * <tt>tax_values</tt> - An array of TaxValueRequest objects. Required.
37
+ def sales_tax(options = {})
38
+ options = options.inject({}) { |memo,(k, v)| memo[k.to_sym] = v; memo }
39
+ required_options = %w(from to tax_values)
40
+
41
+ # Raise an error if the required option is not defined
42
+ required_options.each { |val| raise ArgumentError, "Missing option :#{val}" if !options.include?(val.to_sym) }
43
+
44
+ response = client.request(:get_sales_tax_value) do |soap|
45
+ soap.body = {
46
+ 'UserID' => configuration.user_id,
47
+ 'Password' => configuration.password,
48
+ 'ShipFrom' => options[:from].to_soap,
49
+ 'ShipTo' => options[:to].to_soap,
50
+ 'TaxValueRequests' => { 'TaxValueRequest' => options[:tax_values].map(&:to_soap) }
51
+ }
52
+ end
53
+
54
+ response_code = response[:get_sales_tax_value_response][:get_sales_tax_value_result][:service_status][:status_nbr]
55
+
56
+ # Raise exceptions if there was an error when calculating the tax
57
+ case response_code.to_i
58
+ when 401
59
+ raise RuntimeError, 'Invalid From address.'
60
+ when 402
61
+ raise RuntimeError, 'Invalid To address.'
62
+ when 403
63
+ raise RuntimeError, 'Invalid Taxability category.'
64
+ when 500
65
+ raise RuntimeError, 'Internal Strikeiron server error.'
66
+ end
67
+
68
+ Strikeiron::TaxResult.from_soap(response[:get_sales_tax_value_response][:get_sales_tax_value_result][:service_result])
69
+ end
70
+
71
+ # Performs a request to obtain a list of all available category names and their corresponding ID number.
72
+ #
73
+ # === Example
74
+ # Strikeiron.tax_categories
75
+ # # => [{:category=>"Alcohol", :category_id=>"01051000"}, {:category=>"Alcoholic Beverages", :category_id=>"01050000"}, {:category=>"Beer", :category_id=>"01052000"}, {:category=>"Books", :category_id=>"01501500"}, {:category=>"Charges necessary to complete sale other than delivery and installation", :category_id=>"04800000"}]
76
+ def tax_categories
77
+ response = client.request(:get_sales_tax_categories) do |soap|
78
+ soap.body = {
79
+ 'UserID' => configuration.user_id,
80
+ 'Password' => configuration.password
81
+ }
82
+ end
83
+
84
+ # Return an empty array if the response was not successful
85
+ return [] if response[:get_sales_tax_categories_response][:get_sales_tax_categories_result][:service_status][:status_nbr] != '200'
86
+
87
+ response[:get_sales_tax_categories_response][:get_sales_tax_categories_result][:service_result][:sales_tax_category]
88
+ end
89
+
90
+ # The number of API hits remanining for the configured account
91
+ #
92
+ # === Example
93
+ # Strikeiron.remaining_hits
94
+ # # => 100
95
+ def remaining_hits
96
+ response = client.request(:get_remaining_hits) do |soap|
97
+ soap.body = {
98
+ 'UserID' => configuration.user_id,
99
+ 'Password' => configuration.password
100
+ }
101
+ end
102
+ response[:si_subscription_info][:remaining_hits].to_i
103
+ end
104
+
105
+
106
+
107
+ end
108
+
109
+ end
@@ -0,0 +1,47 @@
1
+ module Strikeiron #:nodoc:
2
+
3
+ # An Address stores information of a physical location for both the seller and customer.
4
+ #
5
+ # === Attributes
6
+ # * <tt>street_address</tt> - The full street address for the seller/customer. Example: 123 Sixth Avenue.
7
+ # * <tt>city</tt> - The city of the address.
8
+ # * <tt>state</tt> - The two letter abbreviation for the state. Example: NY.
9
+ # * <tt>zip_code</tt> - The postal/ZIP code of the address.
10
+ class Address
11
+
12
+ attr_accessor :street_address, :city, :state, :zip_code
13
+
14
+ # Creates an Address with the supplied attributes.
15
+ def initialize(default_values = {})
16
+ safe_keys = %w(street_address city state zip_code)
17
+ # Only permit the keys defined in safe_keys
18
+ default_values.reject! { |key, value| !safe_keys.include?(key.to_s) }
19
+
20
+ default_values.each { |key, value| self.send "#{key}=", value }
21
+ end
22
+
23
+ # Convert the object to a Hash for SOAP
24
+ def to_soap
25
+ {
26
+ 'StreetAddress' => street_address,
27
+ 'City' => city,
28
+ 'State' => state,
29
+ 'ZIPCode' => zip_code
30
+ }
31
+ end
32
+
33
+ class << self
34
+ # Convert the object from a SOAP response to an Address object
35
+ def from_soap(hash = {})
36
+ default_values = {
37
+ :street_address => hash['StreetAddress'],
38
+ :city => hash['City'],
39
+ :state => hash['State'],
40
+ :zip_code => hash['ZIPCode']
41
+ }
42
+ new(default_values)
43
+ end
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,10 @@
1
+ module Strikeiron #:nodoc:
2
+ # Configuration values for Strikeiron such as your UserID and Password.
3
+ #
4
+ # === Attributes
5
+ # * <tt>user_id</tt> - The UserID supplied by Strikeiron for API use.
6
+ # * <tt>password</tt> - The password/key that Strikeiron supplied for API use.
7
+ class Configuration
8
+ attr_accessor :user_id, :password
9
+ end
10
+ end
@@ -0,0 +1,34 @@
1
+ module Strikeiron #:nodoc:
2
+ # Jurisdiction represents a breakdown of the tax calculations for the taxable region(s).
3
+ #
4
+ # === Attributes
5
+ # * <tt>fips</tt> - The code assigned by the Federal Government or state agencies to uniquely identify a location.
6
+ # * <tt>name</tt> - Name associated to the corresponding FIPS code.
7
+ # * <tt>tax_amount</tt> - Sales tax rate for the corresponding FIPS code.
8
+ class Jurisdiction
9
+ attr_accessor :fips, :name, :tax_amount
10
+
11
+ # Creates a Jurisdiction with the supplied attributes.
12
+ def initialize(default_values = {})
13
+ safe_keys = %w(fips name tax_amount)
14
+ # Only permit the keys defined in safe_keys
15
+ default_values.reject! { |key, value| !safe_keys.include?(key.to_s) }
16
+
17
+ default_values.each { |key, value| self.send "#{key}=", value }
18
+ end
19
+
20
+ class << self
21
+ # Convert the SOAP response object to a Jurisdiction
22
+ def from_soap(hash = {})
23
+ default_values = {
24
+ :fips => hash['FIPS'],
25
+ :name => hash['Name'],
26
+ :tax_amount => hash['SalesTaxAmount']
27
+ }
28
+
29
+ new(default_values)
30
+ end
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,39 @@
1
+ module Strikeiron #:nodoc:
2
+ # Returned after performing a tax calculation
3
+ #
4
+ # === Attributes
5
+ # * <tt>from</tt> - The from Address.
6
+ # * <tt>to</tt> - The to Address.
7
+ # * <tt>tax_values</tt> - The TaxValue objects returned from Strikeiron.
8
+ # * <tt>total_tax</tt> - The total tax amount to be applied.
9
+ class TaxResult
10
+ attr_accessor :from, :to, :tax_values, :total_tax
11
+
12
+ # Initialize the TaxResult object with the given options
13
+ def initialize(default_values = {})
14
+ default_values.each { |key, value| self.send "#{key}=", value }
15
+ end
16
+
17
+ class << self
18
+ # Convert the object from the Strikeiron response
19
+ def from_soap(response)
20
+ tax_values = []
21
+ response[:results][:tax_value_record].each do |record|
22
+ tax_values << TaxValue.new(
23
+ :category => record[:category],
24
+ :category_id => record[:category_id],
25
+ :tax_amount => record[:sales_tax_amount].to_f,
26
+ :jurisdictions => record[:jurisdictions][:sales_tax_value_jurisdiction].map { |j| Jurisdiction.new(:fips => j[:fips], :name => j[:name], :tax_amount => j[:sales_tax_amount].to_f) }
27
+ )
28
+ end
29
+
30
+ new(
31
+ :from => Address.new(response[:resolved_from_address]),
32
+ :to => Address.new(response[:resolved_to_address]),
33
+ :tax_values => tax_values,
34
+ :total_tax => tax_values.inject(0) { |sum, tax_value| sum + tax_value.tax_amount }
35
+ )
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,46 @@
1
+ module Strikeiron #:nodoc:
2
+ # Total sales tax rate based on the dollar amount and category from TaxValueRequest.
3
+ # TaxValueRecord should only be rendered from a SOAP response.
4
+ #
5
+ # === Attributes
6
+ # * <tt>category</tt> - The corresponding category name. A category name and/or ID must be supplied.
7
+ # * <tt>category_id</tt> - The corresponding category ID. A category name and/or ID must be supplied.
8
+ # * <tt>amount</tt> - The amount to calculate the tax for.
9
+ # * <tt>tax_amount</tt> - The combined sales tax rate based on the address and tax category provided
10
+ #
11
+ # === Notes
12
+ # See Strikeiron.tax_categories for information on obtaining category information.
13
+ class TaxValue
14
+ attr_accessor :category, :category_id, :amount, :tax_amount, :jurisdictions
15
+
16
+ # Creates a TaxValueRecord with the supplied attributes.
17
+ def initialize(default_values = {})
18
+ safe_keys = %w(category category_id amount tax_amount jurisdictions)
19
+ # Only permit the keys defined in safe_keys
20
+ default_values.reject! { |key, value| !safe_keys.include?(key.to_s) }
21
+
22
+ default_values.each { |key, value| self.send "#{key}=", value }
23
+ end
24
+
25
+ # Convert the object to be valid for the SOAP request
26
+ def to_soap
27
+ {
28
+ 'SalesTaxCategoryOrCategoryID' => category || category_id,
29
+ 'Amount' => amount
30
+ }
31
+ end
32
+
33
+ class << self
34
+ # Convert the SOAP response object to a TaxValueRecord
35
+ def from_soap(hash = {})
36
+ default_values = {
37
+ :category => hash['Category'],
38
+ :category_id => hash['CategoryID'],
39
+ :tax_amount => hash['SalesTaxAmount']
40
+ }
41
+
42
+ new(default_values)
43
+ end
44
+ end
45
+ end
46
+ end