mudrat_projector 0.9.0
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 +7 -0
- data/.gitignore +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +49 -0
- data/README.md +46 -0
- data/Rakefile +26 -0
- data/bin/testrb +3 -0
- data/finance_fu.txt +41 -0
- data/lib/mudrat_projector/account.rb +75 -0
- data/lib/mudrat_projector/amortizer.rb +103 -0
- data/lib/mudrat_projector/banker_rounding.rb +11 -0
- data/lib/mudrat_projector/chart_of_accounts.rb +119 -0
- data/lib/mudrat_projector/date_diff.rb +169 -0
- data/lib/mudrat_projector/projection.rb +45 -0
- data/lib/mudrat_projector/projector.rb +71 -0
- data/lib/mudrat_projector/schedule.rb +45 -0
- data/lib/mudrat_projector/scheduled_transaction.rb +45 -0
- data/lib/mudrat_projector/tax_calculation.rb +154 -0
- data/lib/mudrat_projector/tax_calculator.rb +144 -0
- data/lib/mudrat_projector/tax_values_by_year.yml +102 -0
- data/lib/mudrat_projector/transaction.rb +71 -0
- data/lib/mudrat_projector/transaction_entry.rb +126 -0
- data/lib/mudrat_projector/transaction_handler.rb +19 -0
- data/lib/mudrat_projector/validator.rb +49 -0
- data/lib/mudrat_projector/version.rb +3 -0
- data/lib/mudrat_projector.rb +27 -0
- data/mudrat_projector.gemspec +28 -0
- data/test/integrations/long_term_projection_test.rb +42 -0
- data/test/integrations/mortgage_test.rb +85 -0
- data/test/integrations/self_employed_tax_calculation_test.rb +44 -0
- data/test/models/accounts_test.rb +39 -0
- data/test/models/chart_of_accounts_test.rb +170 -0
- data/test/models/date_diff_test.rb +117 -0
- data/test/models/projection_test.rb +62 -0
- data/test/models/projector_test.rb +168 -0
- data/test/models/schedule_test.rb +68 -0
- data/test/models/scheduled_transaction_test.rb +47 -0
- data/test/models/tax_calculator_test.rb +145 -0
- data/test/models/transaction_entry_test.rb +37 -0
- data/test/models/transaction_handler_test.rb +67 -0
- data/test/models/transaction_test.rb +66 -0
- data/test/models/validator_test.rb +45 -0
- data/test/test_helper.rb +69 -0
- metadata +204 -0
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TransactionHandlerTest < Minitest::Test
|
4
|
+
def setup
|
5
|
+
@chart = ChartOfAccounts.new.tap do |c|
|
6
|
+
c.add_account :checking, type: :asset
|
7
|
+
c.add_account :job, type: :revenue
|
8
|
+
end
|
9
|
+
@projection = Projection.new range: (jan_1_2000..dec_31_2000), chart: @chart
|
10
|
+
@transaction_handler = TransactionHandler.new projection: @projection
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_routes_non_scheduled_transactions_in_range_to_projection
|
14
|
+
@transaction_handler << fixed_transaction
|
15
|
+
assert_equal 1, @projection.transaction_sequence.size
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_does_not_route_non_scheduled_transactions_after_range_to_projection
|
19
|
+
@transaction_handler << fixed_transaction(date: jan_1_2001)
|
20
|
+
assert_equal 0, @projection.transaction_sequence.size
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_routes_non_scheduled_transactions_after_range_to_next_projector
|
24
|
+
@transaction_handler.next_projector = next_projector
|
25
|
+
@transaction_handler << fixed_transaction(date: jan_1_2001)
|
26
|
+
assert_equal 1, next_projector.transactions.size
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_routes_a_scheduled_transaction_into_projector
|
30
|
+
@transaction_handler << scheduled_transaction
|
31
|
+
assert_equal 12, @projection.transaction_sequence.size
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_routes_any_remainder_of_a_scheduled_transaction_into_next_projector
|
35
|
+
@transaction_handler.next_projector = next_projector
|
36
|
+
@transaction_handler << scheduled_transaction(end_date: july_1_2001)
|
37
|
+
assert_equal 1, next_projector.transactions.size
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def fixed_transaction date: jan_1_2000, amount: 1000
|
43
|
+
Transaction.new(
|
44
|
+
date: date,
|
45
|
+
debit: { amount: amount, account_id: :checking },
|
46
|
+
credit: { amount: amount, account_id: :job },
|
47
|
+
)
|
48
|
+
end
|
49
|
+
|
50
|
+
def scheduled_transaction start_date: jan_1_2000, end_date: dec_31_2000, amount: 1000
|
51
|
+
ScheduledTransaction.new(
|
52
|
+
date: start_date,
|
53
|
+
debit: { amount: amount, account_id: :checking },
|
54
|
+
credit: { amount: amount, account_id: :job },
|
55
|
+
schedule: every_month(until: end_date),
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def next_projector
|
60
|
+
@next_projector ||=
|
61
|
+
Struct.new :transactions do
|
62
|
+
def add_transaction transaction
|
63
|
+
transactions.push transaction
|
64
|
+
end
|
65
|
+
end.new(Array.new)
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TransactionTest < Minitest::Test
|
4
|
+
def test_balanced_on_fixed_transactions
|
5
|
+
assert Transaction.new(
|
6
|
+
date: jan_1_2000,
|
7
|
+
credits: [{ amount: 1, account_id: :checking }],
|
8
|
+
debits: [{ amount: 1, account_id: :bills }],
|
9
|
+
).balanced?
|
10
|
+
|
11
|
+
refute Transaction.new(
|
12
|
+
date: jan_1_2000,
|
13
|
+
credits: [{ amount: 1, account_id: :checking }],
|
14
|
+
debits: [{ amount: 2, account_id: :bills }],
|
15
|
+
).balanced?
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_balanced_on_percentage_transactions
|
19
|
+
refute Transaction.new(
|
20
|
+
date: jan_1_2000,
|
21
|
+
credit: { percent: 25.0, of: :savings, account_id: :checking },
|
22
|
+
debit: { amount: 25, account_id: :bills },
|
23
|
+
).balanced?
|
24
|
+
|
25
|
+
refute Transaction.new(
|
26
|
+
date: jan_1_2000,
|
27
|
+
credit: { percent: 25.0, of: :savings, account_id: :checking },
|
28
|
+
debit: { percent: 24.0, of: :savings, account_id: :bills },
|
29
|
+
).balanced?
|
30
|
+
|
31
|
+
assert Transaction.new(
|
32
|
+
date: jan_1_2000,
|
33
|
+
credit: { percent: 25.0, of: :savings , account_id: :checking },
|
34
|
+
debits: [{percent: 12.5, of: :savings , account_id: :bills },
|
35
|
+
{percent: 12.5, of: :savings , account_id: :bills }],
|
36
|
+
).balanced?
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_can_supply_a_single_credit_or_debit
|
40
|
+
Transaction.new(
|
41
|
+
date: jan_1_2000,
|
42
|
+
credit: { amount: 1, account_id: :checking },
|
43
|
+
debit: { amount: 1, account_id: :bills },
|
44
|
+
)
|
45
|
+
# Can't supply both a :credit and :credits
|
46
|
+
assert_raises ArgumentError do
|
47
|
+
Transaction.new(
|
48
|
+
date: jan_1_2000,
|
49
|
+
credit: { amount: 1, account_id: :checking },
|
50
|
+
credits: [{ amount: 2, account_id: :checking }],
|
51
|
+
debits: [{ amount: 1, account_id: :bills }],
|
52
|
+
)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def valid_transaction schedule: nil
|
59
|
+
Transaction.new(
|
60
|
+
date: jan_1_2000,
|
61
|
+
credits: [{ amount: 1, account_id: :checking }],
|
62
|
+
debits: [{ amount: 1, account_id: :bills }],
|
63
|
+
schedule: schedule,
|
64
|
+
)
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ValidatorTest < Minitest::Test
|
4
|
+
def setup
|
5
|
+
@projector = Projector.new from: jan_1_2000
|
6
|
+
@projector.add_account :checking, type: :asset
|
7
|
+
@projector.add_account :job, type: :revenue
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_cannot_add_transaction_without_entriess
|
11
|
+
assert_raises Projector::InvalidTransaction do
|
12
|
+
@projector.add_transaction(
|
13
|
+
date: jan_1_2000
|
14
|
+
)
|
15
|
+
end
|
16
|
+
assert_raises Projector::InvalidTransaction do
|
17
|
+
@projector.add_transaction(
|
18
|
+
date: jan_1_2000,
|
19
|
+
credits: [],
|
20
|
+
debits: [],
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_past_transaction
|
26
|
+
assert_raises Projector::InvalidTransaction do
|
27
|
+
@projector.add_transaction(
|
28
|
+
date: dec_31_1999,
|
29
|
+
credit: { amount: 1000, account_id: :job },
|
30
|
+
debit: { amount: 1000, account_id: :checking },
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_cannot_add_single_transaction_which_does_not_balance
|
36
|
+
assert_raises Projector::BalanceError do
|
37
|
+
@projector.add_transaction(
|
38
|
+
date: jan_1_2000,
|
39
|
+
credit: { amount: 1000, account_id: :job },
|
40
|
+
debit: { amount: 999, account_id: :checking },
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'simplecov' if ENV['COVERAGE']
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'minitest/unit'
|
5
|
+
require 'minitest/reporters'
|
6
|
+
|
7
|
+
require 'ostruct'
|
8
|
+
require 'pp'
|
9
|
+
|
10
|
+
require 'mudrat_projector'
|
11
|
+
|
12
|
+
# Require pry library on demand to avoid eating its >500ms start up penalty
|
13
|
+
class Binding
|
14
|
+
def method_missing sym, *args
|
15
|
+
if sym == :pry && args.empty? && !block_given?
|
16
|
+
require 'pry'
|
17
|
+
pry
|
18
|
+
else
|
19
|
+
super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Dates need friendlier output
|
25
|
+
class Date
|
26
|
+
def inspect
|
27
|
+
strftime
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# BigDecimal needs friendlier output
|
32
|
+
class BigDecimal < Numeric
|
33
|
+
def inspect
|
34
|
+
"#<BigDecmial:#{round(10).to_f.inspect}>"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Minitest::Reporters.use! MiniTest::Reporters::DefaultReporter.new
|
39
|
+
|
40
|
+
class Minitest::Test
|
41
|
+
include MudratProjector
|
42
|
+
|
43
|
+
# Enable you to just call "jan_1_2000" to new up a date.
|
44
|
+
def method_missing sym, *args, &block
|
45
|
+
return super if block_given? || args.size > 0
|
46
|
+
begin
|
47
|
+
Date.parse sym.to_s
|
48
|
+
rescue ArgumentError => ae
|
49
|
+
super
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def epoch
|
56
|
+
jan_1_1970
|
57
|
+
end
|
58
|
+
|
59
|
+
def every_month **params
|
60
|
+
end_date = params[:until]
|
61
|
+
if end_date
|
62
|
+
from = params[:from] || jan_1_2000
|
63
|
+
count = DateDiff.date_diff unit: :month, from: from, to: end_date
|
64
|
+
{ unit: :month, scalar: 1, count: count }
|
65
|
+
else
|
66
|
+
{ unit: :month, scalar: 1 }
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
metadata
ADDED
@@ -0,0 +1,204 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mudrat_projector
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- ntl
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-15 00:00:00.000000000 Z
|
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: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest-reporters
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: simplecov
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: timecop
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
description: Mudrat Projector is a simple financial projection engine.
|
112
|
+
email:
|
113
|
+
- nathanladd+github@gmail.com
|
114
|
+
executables:
|
115
|
+
- testrb
|
116
|
+
extensions: []
|
117
|
+
extra_rdoc_files: []
|
118
|
+
files:
|
119
|
+
- .gitignore
|
120
|
+
- .ruby-version
|
121
|
+
- .travis.yml
|
122
|
+
- Gemfile
|
123
|
+
- Gemfile.lock
|
124
|
+
- README.md
|
125
|
+
- Rakefile
|
126
|
+
- bin/testrb
|
127
|
+
- finance_fu.txt
|
128
|
+
- lib/mudrat_projector.rb
|
129
|
+
- lib/mudrat_projector/account.rb
|
130
|
+
- lib/mudrat_projector/amortizer.rb
|
131
|
+
- lib/mudrat_projector/banker_rounding.rb
|
132
|
+
- lib/mudrat_projector/chart_of_accounts.rb
|
133
|
+
- lib/mudrat_projector/date_diff.rb
|
134
|
+
- lib/mudrat_projector/projection.rb
|
135
|
+
- lib/mudrat_projector/projector.rb
|
136
|
+
- lib/mudrat_projector/schedule.rb
|
137
|
+
- lib/mudrat_projector/scheduled_transaction.rb
|
138
|
+
- lib/mudrat_projector/tax_calculation.rb
|
139
|
+
- lib/mudrat_projector/tax_calculator.rb
|
140
|
+
- lib/mudrat_projector/tax_values_by_year.yml
|
141
|
+
- lib/mudrat_projector/transaction.rb
|
142
|
+
- lib/mudrat_projector/transaction_entry.rb
|
143
|
+
- lib/mudrat_projector/transaction_handler.rb
|
144
|
+
- lib/mudrat_projector/validator.rb
|
145
|
+
- lib/mudrat_projector/version.rb
|
146
|
+
- mudrat_projector.gemspec
|
147
|
+
- test/integrations/long_term_projection_test.rb
|
148
|
+
- test/integrations/mortgage_test.rb
|
149
|
+
- test/integrations/self_employed_tax_calculation_test.rb
|
150
|
+
- test/models/accounts_test.rb
|
151
|
+
- test/models/chart_of_accounts_test.rb
|
152
|
+
- test/models/date_diff_test.rb
|
153
|
+
- test/models/projection_test.rb
|
154
|
+
- test/models/projector_test.rb
|
155
|
+
- test/models/schedule_test.rb
|
156
|
+
- test/models/scheduled_transaction_test.rb
|
157
|
+
- test/models/tax_calculator_test.rb
|
158
|
+
- test/models/transaction_entry_test.rb
|
159
|
+
- test/models/transaction_handler_test.rb
|
160
|
+
- test/models/transaction_test.rb
|
161
|
+
- test/models/validator_test.rb
|
162
|
+
- test/test_helper.rb
|
163
|
+
homepage: https://github.com/ntl/mudrat-projector
|
164
|
+
licenses:
|
165
|
+
- MIT
|
166
|
+
metadata: {}
|
167
|
+
post_install_message:
|
168
|
+
rdoc_options: []
|
169
|
+
require_paths:
|
170
|
+
- lib
|
171
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - '>='
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
requirements: []
|
182
|
+
rubyforge_project:
|
183
|
+
rubygems_version: 2.0.14
|
184
|
+
signing_key:
|
185
|
+
specification_version: 4
|
186
|
+
summary: Mudrat Projector is a simple financial projection engine designed for personal
|
187
|
+
finance computations.
|
188
|
+
test_files:
|
189
|
+
- test/integrations/long_term_projection_test.rb
|
190
|
+
- test/integrations/mortgage_test.rb
|
191
|
+
- test/integrations/self_employed_tax_calculation_test.rb
|
192
|
+
- test/models/accounts_test.rb
|
193
|
+
- test/models/chart_of_accounts_test.rb
|
194
|
+
- test/models/date_diff_test.rb
|
195
|
+
- test/models/projection_test.rb
|
196
|
+
- test/models/projector_test.rb
|
197
|
+
- test/models/schedule_test.rb
|
198
|
+
- test/models/scheduled_transaction_test.rb
|
199
|
+
- test/models/tax_calculator_test.rb
|
200
|
+
- test/models/transaction_entry_test.rb
|
201
|
+
- test/models/transaction_handler_test.rb
|
202
|
+
- test/models/transaction_test.rb
|
203
|
+
- test/models/validator_test.rb
|
204
|
+
- test/test_helper.rb
|