death_and_taxes 0.0.1 → 0.0.2

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.
data/config/ca.yml ADDED
@@ -0,0 +1,33 @@
1
+ taxes:
2
+ GST:
3
+ versions:
4
+ - starts: 2011-01-01
5
+ ends: 2011-12-31
6
+ percentage: 5
7
+
8
+ states:
9
+ qc:
10
+ rules:
11
+ same_state: [GST, QST]
12
+ same_country: GST
13
+ taxes:
14
+ QST:
15
+ apply_on: GST
16
+ versions:
17
+ - starts: 2011-01-01
18
+ ends: 2011-12-31
19
+ percentage: 8.5
20
+
21
+ - starts: 2012-01-01
22
+ ends: 2012-12-31
23
+ percentage: 9.5
24
+
25
+ on:
26
+ rules:
27
+ same_country: HST
28
+ taxes:
29
+ HST:
30
+ versions:
31
+ - starts: 2011-01-01
32
+ ends: 2012-12-31
33
+ percentage: 5
@@ -0,0 +1,37 @@
1
+ module DeathAndTaxes
2
+ class Country
3
+
4
+ def initialize code, yml = nil
5
+ @taxes = {}
6
+ @states = {}
7
+ @code = code
8
+ parse yml if yml
9
+ end
10
+
11
+ def parse yml
12
+ yml['taxes'].each do |tax_name, infos|
13
+ @taxes[tax_name] = TaxInfo.new(tax_name, infos)
14
+ end
15
+
16
+ yml['states'].each do |state_code, state_yml|
17
+ @states[state_code] = State.new(state_code, state_yml)
18
+ end
19
+ end
20
+
21
+ def applicable_taxes from, to, date
22
+ if from[:country] == to[:country] && to[:country] == @code
23
+ @states[from[:state]].applicable_taxes to[:state], date
24
+ else
25
+ []
26
+ end
27
+ end
28
+
29
+ def applies_taxes? from, to, date
30
+ applicable_taxes(from, to, date).any?
31
+ end
32
+
33
+ def build_tax tax, date
34
+ @taxes[tax].try(:build, date) || @states.map{|t, s| s.build_tax(tax, date)}
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ module DeathAndTaxes
2
+ class State
3
+
4
+ def initialize code, yml = nil
5
+ @taxes = {}
6
+ @code = code
7
+ parse yml if yml
8
+ end
9
+
10
+ def parse yml
11
+ ##
12
+ # Builds a tax list for each taxes defined in this state
13
+ yml['taxes'].each do |tax_name, infos|
14
+ @taxes[tax_name] = TaxInfo.new(tax_name, infos)
15
+ end
16
+
17
+ @rules = yml['rules']
18
+ end
19
+
20
+ def applicable_taxes to_state, date
21
+ taxes = if to_state == @code && @rules['same_state']
22
+ @rules['same_state']
23
+ elsif @rules['same_country']
24
+ @rules['same_country']
25
+ else
26
+ []
27
+ end
28
+ [taxes].flatten
29
+ end
30
+
31
+ def build_tax tax, date
32
+ if i = @taxes[tax] && i.cover?(date)
33
+ i.build(date)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -3,6 +3,9 @@ module DeathAndTaxes
3
3
  belongs_to :taxer, :polymorphic => true
4
4
  belongs_to :tax, :foreign_key => :apply_on_tax
5
5
 
6
+ validates :percentage, :numericality => {:greater_than => 0.01, :less_than => 100}, :format => /^\d+??(?:\.\d*)?$/, :presence => true
7
+ validates_presence_of :name
8
+
6
9
  def apply amount
7
10
  amount * multiplier
8
11
  end
@@ -10,5 +13,9 @@ module DeathAndTaxes
10
13
  def multiplier
11
14
  (percentage / 100) * (1 + tax.try(:multiplier).to_f)
12
15
  end
16
+
17
+ def blank?
18
+ percentage.blank? && name.blank? && account_number.blank?
19
+ end
13
20
  end
14
21
  end
@@ -0,0 +1,27 @@
1
+ module DeathAndTaxes
2
+ class TaxInfo
3
+ def initialize name, yml = nil
4
+ @name = name
5
+ parse yml if yml
6
+ end
7
+
8
+ def parse yml
9
+ @apply_on = yml['apply_on']
10
+ @versions = yml['versions'].collect do |version|
11
+ TaxVersion.new(version)
12
+ end
13
+ end
14
+
15
+ def cover? date
16
+ @versions.any?{ |version| v.cover? date }
17
+ end
18
+
19
+ def build date
20
+ if v = @versions.detect{ |v| v.cover?(date) }
21
+ t = Tax.new :name => @name, :percentage => v['percentage']
22
+ t.tax = DeathAndTaxes.build_tax(@apply_on, date) if @apply_on
23
+ t
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ module DeathAndTaxes
2
+ class TaxVersion
3
+ def initialize yml
4
+ @start = yml['start']
5
+ @end = yml['end']
6
+ @percentage = yml['percentage']
7
+ end
8
+
9
+ def cover? date
10
+ (@start..@end).cover? date
11
+ end
12
+
13
+ def build
14
+
15
+ end
16
+ end
17
+ end
@@ -22,6 +22,11 @@ module DeathAndTaxes
22
22
 
23
23
  include DeathAndTaxes::Taxable::InstanceMethods
24
24
  end
25
+
26
+ def calculate_rate taxes
27
+ taxes.reduce(1) { |rate, tax| rate + tax.apply(1) }
28
+ end
29
+
25
30
  end
26
31
 
27
32
  module InstanceMethods
@@ -29,7 +34,7 @@ module DeathAndTaxes
29
34
  taxes = [taxes] unless taxes.is_a? Array
30
35
 
31
36
  self.taxations = taxes.collect do |tax|
32
- Taxation.new :amount => tax.apply(amount), :percentage => tax.multiplier, :name => tax.name, :account_number => tax.account_number
37
+ Taxation.new :amount => tax.apply(amount), :percentage => tax.percentage, :name => tax.name, :account_number => tax.account_number
33
38
  end
34
39
  end
35
40
  end
@@ -1,3 +1,3 @@
1
1
  module DeathAndTaxes
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
@@ -3,11 +3,25 @@ require 'active_record/version'
3
3
 
4
4
  require 'active_support/core_ext'
5
5
 
6
-
7
- require File.join(File.dirname(__FILE__), 'death_and_taxes/railtie')
6
+ require File.join(File.dirname(__FILE__), 'death_and_taxes', 'railtie')
8
7
 
9
8
  module DeathAndTaxes
10
- %w( Tax Taxable Taxation Taxer ).each do |class_name|
11
- autoload class_name.to_sym, File.join(File.dirname(__FILE__), "death_and_taxes/#{class_name.downcase}")
9
+ %w( Country State Tax Taxable Taxation Taxer TaxInfo TaxVersion ).each do |class_name|
10
+ autoload class_name.to_sym, File.join(File.dirname(__FILE__), "death_and_taxes/#{class_name.underscore}")
11
+ end
12
+
13
+ Dir[File.join(File.dirname(__FILE__), '..', 'config', '*.yml')].each do |filename|
14
+ country_code = filename.match(/\W(?<country_code>[a-z]+)\.yml/)[:country_code]
15
+ @countries = {}
16
+ @countries[country_code] = Country.new( country_code, YAML::load_file(filename) )
17
+ end
18
+
19
+ def self.applicable_taxes from, to, date = nil
20
+ date ||= Date.today
21
+ @countries.map{ |code, country| country.applicable_taxes(from, to, date)}.flatten.compact
22
+ end
23
+
24
+ def self.build tax, date
25
+ @countries.detect {|code, country| country.build_tax(tax, date)}
12
26
  end
13
27
  end
metadata CHANGED
@@ -1,45 +1,60 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: death_and_taxes
3
- version: !ruby/object:Gem::Version
4
- version: 0.0.1
5
- prerelease:
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
6
11
  platform: ruby
7
- authors:
12
+ authors:
8
13
  - Guillaume Malette
9
14
  autorequire:
10
15
  bindir: bin
11
16
  cert_chain: []
12
- date: 2011-08-09 00:00:00.000000000 -04:00
17
+
18
+ date: 2011-08-09 00:00:00 -04:00
13
19
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
16
22
  name: rails
17
- requirement: &2152141700 !ruby/object:Gem::Requirement
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
18
25
  none: false
19
- requirements:
20
- - - ! '>='
21
- - !ruby/object:Gem::Version
22
- version: '0'
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
23
33
  type: :runtime
24
- prerelease: false
25
- version_requirements: *2152141700
26
- - !ruby/object:Gem::Dependency
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
27
36
  name: sqlite3
28
- requirement: &2152141280 !ruby/object:Gem::Requirement
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
29
39
  none: false
30
- requirements:
31
- - - ! '>='
32
- - !ruby/object:Gem::Version
33
- version: '0'
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
34
47
  type: :development
35
- prerelease: false
36
- version_requirements: *2152141280
48
+ version_requirements: *id002
37
49
  description: Death and Taxes allows to 'easily' calculate taxes on a model
38
50
  email: gmalette@gmail.com
39
51
  executables: []
52
+
40
53
  extensions: []
54
+
41
55
  extra_rdoc_files: []
42
- files:
56
+
57
+ files:
43
58
  - .document
44
59
  - .gitignore
45
60
  - Gemfile
@@ -47,10 +62,15 @@ files:
47
62
  - LICENSE.txt
48
63
  - README.rdoc
49
64
  - Rakefile
65
+ - config/ca.yml
50
66
  - death_and_taxes.gemspec
51
67
  - lib/death_and_taxes.rb
68
+ - lib/death_and_taxes/country.rb
52
69
  - lib/death_and_taxes/railtie.rb
70
+ - lib/death_and_taxes/state.rb
53
71
  - lib/death_and_taxes/tax.rb
72
+ - lib/death_and_taxes/tax_info.rb
73
+ - lib/death_and_taxes/tax_version.rb
54
74
  - lib/death_and_taxes/taxable.rb
55
75
  - lib/death_and_taxes/taxation.rb
56
76
  - lib/death_and_taxes/taxer.rb
@@ -60,30 +80,39 @@ files:
60
80
  - test/helper.rb
61
81
  - test/test_death_and_taxes.rb
62
82
  has_rdoc: true
63
- homepage: ''
83
+ homepage: ""
64
84
  licenses: []
85
+
65
86
  post_install_message:
66
87
  rdoc_options: []
67
- require_paths:
88
+
89
+ require_paths:
68
90
  - lib
69
- required_ruby_version: !ruby/object:Gem::Requirement
91
+ required_ruby_version: !ruby/object:Gem::Requirement
70
92
  none: false
71
- requirements:
72
- - - ! '>='
73
- - !ruby/object:Gem::Version
74
- version: '0'
75
- required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ hash: 3
97
+ segments:
98
+ - 0
99
+ version: "0"
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
101
  none: false
77
- requirements:
78
- - - ! '>='
79
- - !ruby/object:Gem::Version
80
- version: '0'
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ hash: 3
106
+ segments:
107
+ - 0
108
+ version: "0"
81
109
  requirements: []
110
+
82
111
  rubyforge_project:
83
- rubygems_version: 1.6.2
112
+ rubygems_version: 1.3.7
84
113
  signing_key:
85
114
  specification_version: 3
86
115
  summary: Tax calculations
87
- test_files:
116
+ test_files:
88
117
  - test/helper.rb
89
118
  - test/test_death_and_taxes.rb