death_and_taxes 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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