rvgp 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +23 -0
- data/LICENSE +504 -0
- data/README.md +223 -0
- data/Rakefile +32 -0
- data/bin/rvgp +8 -0
- data/lib/rvgp/application/config.rb +159 -0
- data/lib/rvgp/application/descendant_registry.rb +122 -0
- data/lib/rvgp/application/status_output.rb +139 -0
- data/lib/rvgp/application.rb +170 -0
- data/lib/rvgp/base/command.rb +457 -0
- data/lib/rvgp/base/grid.rb +531 -0
- data/lib/rvgp/base/reader.rb +29 -0
- data/lib/rvgp/base/reconciler.rb +434 -0
- data/lib/rvgp/base/validation.rb +261 -0
- data/lib/rvgp/commands/cashflow.rb +160 -0
- data/lib/rvgp/commands/grid.rb +70 -0
- data/lib/rvgp/commands/ireconcile.rb +95 -0
- data/lib/rvgp/commands/new_project.rb +296 -0
- data/lib/rvgp/commands/plot.rb +41 -0
- data/lib/rvgp/commands/publish_gsheets.rb +83 -0
- data/lib/rvgp/commands/reconcile.rb +58 -0
- data/lib/rvgp/commands/rotate_year.rb +202 -0
- data/lib/rvgp/commands/validate_journal.rb +59 -0
- data/lib/rvgp/commands/validate_system.rb +44 -0
- data/lib/rvgp/commands.rb +160 -0
- data/lib/rvgp/dashboard.rb +252 -0
- data/lib/rvgp/fakers/fake_feed.rb +245 -0
- data/lib/rvgp/fakers/fake_journal.rb +57 -0
- data/lib/rvgp/fakers/fake_reconciler.rb +88 -0
- data/lib/rvgp/fakers/faker_helpers.rb +25 -0
- data/lib/rvgp/gem.rb +80 -0
- data/lib/rvgp/journal/commodity.rb +453 -0
- data/lib/rvgp/journal/complex_commodity.rb +214 -0
- data/lib/rvgp/journal/currency.rb +101 -0
- data/lib/rvgp/journal/journal.rb +141 -0
- data/lib/rvgp/journal/posting.rb +156 -0
- data/lib/rvgp/journal/pricer.rb +267 -0
- data/lib/rvgp/journal.rb +24 -0
- data/lib/rvgp/plot/gnuplot.rb +478 -0
- data/lib/rvgp/plot/google-drive/output_csv.rb +44 -0
- data/lib/rvgp/plot/google-drive/output_google_sheets.rb +434 -0
- data/lib/rvgp/plot/google-drive/sheet.rb +67 -0
- data/lib/rvgp/plot.rb +293 -0
- data/lib/rvgp/pta/hledger.rb +237 -0
- data/lib/rvgp/pta/ledger.rb +308 -0
- data/lib/rvgp/pta.rb +311 -0
- data/lib/rvgp/reconcilers/csv_reconciler.rb +424 -0
- data/lib/rvgp/reconcilers/journal_reconciler.rb +41 -0
- data/lib/rvgp/reconcilers/shorthand/finance_gem_hacks.rb +48 -0
- data/lib/rvgp/reconcilers/shorthand/international_atm.rb +152 -0
- data/lib/rvgp/reconcilers/shorthand/investment.rb +144 -0
- data/lib/rvgp/reconcilers/shorthand/mortgage.rb +195 -0
- data/lib/rvgp/utilities/grid_query.rb +190 -0
- data/lib/rvgp/utilities/yaml.rb +131 -0
- data/lib/rvgp/utilities.rb +44 -0
- data/lib/rvgp/validations/balance_validation.rb +68 -0
- data/lib/rvgp/validations/duplicate_tags_validation.rb +48 -0
- data/lib/rvgp/validations/uncategorized_validation.rb +15 -0
- data/lib/rvgp.rb +66 -0
- data/resources/README.MD/2022-cashflow-google.png +0 -0
- data/resources/README.MD/2022-cashflow.png +0 -0
- data/resources/README.MD/all-wealth-growth-google.png +0 -0
- data/resources/README.MD/all-wealth-growth.png +0 -0
- data/resources/gnuplot/default.yml +80 -0
- data/resources/i18n/en.yml +192 -0
- data/resources/iso-4217-currencies.json +171 -0
- data/resources/skel/Rakefile +5 -0
- data/resources/skel/app/grids/cashflow_grid.rb +27 -0
- data/resources/skel/app/grids/monthly_income_and_expenses_grid.rb +25 -0
- data/resources/skel/app/grids/wealth_growth_grid.rb +35 -0
- data/resources/skel/app/plots/cashflow.yml +33 -0
- data/resources/skel/app/plots/monthly-income-and-expenses.yml +17 -0
- data/resources/skel/app/plots/wealth-growth.yml +20 -0
- data/resources/skel/config/csv-format-acme-checking.yml +9 -0
- data/resources/skel/config/google-secrets.yml +5 -0
- data/resources/skel/config/rvgp.yml +0 -0
- data/resources/skel/journals/prices.db +0 -0
- data/rvgp.gemspec +6 -0
- data/test/assets/ledger_total_monthly_liabilities_with_empty.xml +383 -0
- data/test/assets/ledger_total_monthly_liabilities_with_empty2.xml +428 -0
- data/test/test_command_base.rb +61 -0
- data/test/test_commodity.rb +270 -0
- data/test/test_csv_reconciler.rb +60 -0
- data/test/test_currency.rb +24 -0
- data/test/test_fake_feed.rb +228 -0
- data/test/test_fake_journal.rb +98 -0
- data/test/test_fake_reconciler.rb +60 -0
- data/test/test_journal_parse.rb +545 -0
- data/test/test_ledger.rb +102 -0
- data/test/test_plot.rb +133 -0
- data/test/test_posting.rb +50 -0
- data/test/test_pricer.rb +139 -0
- data/test/test_pta_adapter.rb +575 -0
- data/test/test_utilities.rb +45 -0
- metadata +268 -0
@@ -0,0 +1,98 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
require_relative '../lib/rvgp'
|
7
|
+
require_relative '../lib/rvgp/fakers/fake_journal'
|
8
|
+
|
9
|
+
# Minitest class, used to test RVGP::Fakers::FakeJournal
|
10
|
+
class TestFakeJournal < Minitest::Test
|
11
|
+
def test_basic_cash
|
12
|
+
journal = RVGP::Fakers::FakeJournal.basic_cash from: Date.new(2020, 1, 1), to: Date.new(2020, 1, 10)
|
13
|
+
|
14
|
+
assert_kind_of RVGP::Journal, journal
|
15
|
+
assert_equal 10, journal.postings.length
|
16
|
+
assert_equal %w[2020-01-01 2020-01-02 2020-01-03 2020-01-04 2020-01-05
|
17
|
+
2020-01-06 2020-01-07 2020-01-08 2020-01-09 2020-01-10],
|
18
|
+
journal.postings.map(&:date).map(&:to_s)
|
19
|
+
journal.postings.each do |posting|
|
20
|
+
assert_kind_of String, posting.description
|
21
|
+
assert !posting.description.empty?
|
22
|
+
end
|
23
|
+
assert_equal [2], journal.postings.map { |posting| posting.transfers.length }.uniq
|
24
|
+
assert_equal ['$ 10.00'], journal.postings.map { |posting| posting.transfers[0].commodity.to_s }.uniq
|
25
|
+
assert_equal ['Expense'], journal.postings.map { |posting| posting.transfers[0].account.to_s }.uniq
|
26
|
+
assert_equal ['Cash'], journal.postings.map { |posting| posting.transfers[1].account.to_s }.uniq
|
27
|
+
|
28
|
+
balance = RVGP::Pta::Ledger.new.balance 'Expense', from_s: journal.to_s
|
29
|
+
assert_equal 1, balance.accounts.length
|
30
|
+
assert_equal 'Expense', balance.accounts[0].fullname
|
31
|
+
assert_equal 1, balance.accounts[0].amounts.length
|
32
|
+
assert_equal '$ 100.00', balance.accounts[0].amounts[0].to_s
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_basic_cash_balance
|
36
|
+
10.times.map { rand(1..10_000_000) }.each do |i|
|
37
|
+
amount = format('$ %.2f', i.to_f / 100).to_commodity
|
38
|
+
|
39
|
+
journal = RVGP::Fakers::FakeJournal.basic_cash from: Date.new(2020, 1, 1), sum: amount
|
40
|
+
|
41
|
+
assert_kind_of RVGP::Journal, journal
|
42
|
+
assert_equal(10, journal.postings.length)
|
43
|
+
|
44
|
+
balance = RVGP::Pta::Ledger.new.balance 'Expense', from_s: journal.to_s
|
45
|
+
|
46
|
+
assert_equal 1, balance.accounts.length
|
47
|
+
assert_equal 'Expense', balance.accounts[0].fullname
|
48
|
+
assert_equal 1, balance.accounts[0].amounts.length
|
49
|
+
assert_equal amount.to_s, balance.accounts[0].amounts[0].to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_basic_cash_dates
|
54
|
+
assert_equal %w[2020-01-01 2020-01-03 2020-01-05 2020-01-07 2020-01-09
|
55
|
+
2020-01-12 2020-01-14 2020-01-16 2020-01-18 2020-01-20],
|
56
|
+
RVGP::Fakers::FakeJournal.basic_cash(from: Date.new(2020, 1, 1), to: Date.new(2020, 1, 20))
|
57
|
+
.postings.map(&:date).map(&:to_s)
|
58
|
+
|
59
|
+
assert_equal %w[2020-01-01 2020-02-09 2020-03-19 2020-04-28 2020-06-06
|
60
|
+
2020-07-16 2020-08-24 2020-10-03 2020-11-11 2020-12-20],
|
61
|
+
RVGP::Fakers::FakeJournal.basic_cash(from: Date.new(2020, 1, 1), to: Date.new(2020, 12, 20))
|
62
|
+
.postings.map(&:date).map(&:to_s)
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_basic_cash_postings
|
66
|
+
postings = [
|
67
|
+
RVGP::Journal::Posting.new(
|
68
|
+
Date.new(2019, 12, 31), 'Posting from Dec 2019',
|
69
|
+
transfers: [
|
70
|
+
RVGP::Journal::Posting::Transfer.new('Personal:Expenses:Testing', commodity: '$ 9.00'.to_commodity),
|
71
|
+
RVGP::Journal::Posting::Transfer.new('Cash')
|
72
|
+
]
|
73
|
+
),
|
74
|
+
RVGP::Journal::Posting.new(
|
75
|
+
Date.new(2020, 2, 1), 'Posting from Feb 2020',
|
76
|
+
transfers: [
|
77
|
+
RVGP::Journal::Posting::Transfer.new('Personal:Expenses:Testing', commodity: '$ 8.00'.to_commodity),
|
78
|
+
RVGP::Journal::Posting::Transfer.new('Cash')
|
79
|
+
]
|
80
|
+
)
|
81
|
+
]
|
82
|
+
|
83
|
+
journal = RVGP::Fakers::FakeJournal.basic_cash from: Date.new(2020, 1, 1),
|
84
|
+
to: Date.new(2020, 1, 10),
|
85
|
+
postings: postings
|
86
|
+
# Just make sure these postings are part of the output, and in the expected alphabetical order
|
87
|
+
assert_equal 12, journal.postings.length
|
88
|
+
|
89
|
+
assert_equal ['2019-12-31 Posting from Dec 2019',
|
90
|
+
' Personal:Expenses:Testing $ 9.00',
|
91
|
+
' Cash'].join("\n"),
|
92
|
+
journal.postings.first.to_ledger
|
93
|
+
assert_equal ['2020-02-01 Posting from Feb 2020',
|
94
|
+
' Personal:Expenses:Testing $ 8.00',
|
95
|
+
' Cash'].join("\n"),
|
96
|
+
journal.postings.last.to_ledger
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
require_relative '../lib/rvgp'
|
7
|
+
require_relative '../lib/rvgp/fakers/fake_reconciler'
|
8
|
+
|
9
|
+
# Minitest class, used to test RVGP::Fakers::FakeReconciler
|
10
|
+
class TestFakeReconciler < Minitest::Test
|
11
|
+
def test_basic_reconciler_with_format_path
|
12
|
+
reconciler = Psych.load RVGP::Fakers::FakeReconciler.basic_checking(
|
13
|
+
label: 'Personal AcmeBank:Checking (2020)',
|
14
|
+
input_path: '2020-personal-basic-checking.csv',
|
15
|
+
output_path: '2020-personal-basic-checking.journal',
|
16
|
+
format_path: 'config/csv-format-acme-checking.yml',
|
17
|
+
income: incomes,
|
18
|
+
expense: expenses
|
19
|
+
)
|
20
|
+
|
21
|
+
assert_equal 'Personal:Assets:AcmeBank:Checking', reconciler['from']
|
22
|
+
assert_equal 'Personal AcmeBank:Checking (2020)', reconciler['label']
|
23
|
+
assert_equal 'config/csv-format-acme-checking.yml', reconciler['format'].path
|
24
|
+
assert_equal '2020-personal-basic-checking.csv', reconciler['input']
|
25
|
+
assert_equal '2020-personal-basic-checking.journal', reconciler['output']
|
26
|
+
assert_nil reconciler['balances']
|
27
|
+
|
28
|
+
assert_equal incomes + [{ 'match' => '/.*/', 'to' => 'Personal:Income:Unknown' }], reconciler['income']
|
29
|
+
assert_equal expenses + [{ 'match' => '/.*/', 'to' => 'Personal:Expenses:Unknown' }], reconciler['expense']
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_basic_reconciler_without_format
|
33
|
+
reconciler = Psych.load RVGP::Fakers::FakeReconciler.basic_checking(
|
34
|
+
label: 'Personal AcmeBank:Checking (2020)',
|
35
|
+
input_path: '2020-personal-basic-checking.csv',
|
36
|
+
output_path: '2020-personal-basic-checking.journal',
|
37
|
+
income: incomes,
|
38
|
+
expense: expenses
|
39
|
+
)
|
40
|
+
|
41
|
+
assert_equal true, reconciler['format']['csv_headers']
|
42
|
+
assert_equal true, reconciler['format']['reverse_order']
|
43
|
+
assert_equal '$', reconciler['format']['default_currency']
|
44
|
+
assert_equal %w[date amount description], reconciler['format']['fields'].keys
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def incomes
|
50
|
+
[{ 'match' => '/MC DONALDS INC DIRECT DEP/', 'to' => 'Personal:Income:McDonalds' },
|
51
|
+
{ 'match' => '/Airbnb Payments/', 'to' => 'Personal:Income:AirBNB' }]
|
52
|
+
end
|
53
|
+
|
54
|
+
def expenses
|
55
|
+
[{ 'match' => '/(?:Burger King|KFC|Taco Bell)/', 'to' => 'Personal:Expenses:Food:Restaurants' },
|
56
|
+
{ 'match' => '/(?:Publix|Kroger|Trader Joe)/', 'to' => 'Personal:Expenses:Food:Groceries' },
|
57
|
+
{ 'match' => '/CHECK (?:100|105|110)/', 'to' => 'Personal:Expenses:Rent' },
|
58
|
+
{ 'match' => '/T[-]?Mobile/i', 'to' => 'Personal:Expenses:Phone' }]
|
59
|
+
end
|
60
|
+
end
|