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,171 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"Entity":"UNITED STATES",
|
4
|
+
"Currency":"US Dollar",
|
5
|
+
"Alphabetic Code":"USD",
|
6
|
+
"Numeric Code":"840",
|
7
|
+
"Minor unit":"2",
|
8
|
+
"Symbol":"$"
|
9
|
+
},
|
10
|
+
{
|
11
|
+
"Entity":"EUROPEAN UNION ",
|
12
|
+
"Currency":"Euro",
|
13
|
+
"Alphabetic Code":"EUR",
|
14
|
+
"Numeric Code":"978",
|
15
|
+
"Minor unit":"2"
|
16
|
+
},
|
17
|
+
{
|
18
|
+
"Entity":"HONDURAS",
|
19
|
+
"Currency":"Lempira",
|
20
|
+
"Alphabetic Code":"HNL",
|
21
|
+
"Numeric Code":"340",
|
22
|
+
"Minor unit":"2"
|
23
|
+
},
|
24
|
+
{
|
25
|
+
"Entity":"COLOMBIA",
|
26
|
+
"Currency":"Colombian Peso",
|
27
|
+
"Alphabetic Code":"COP",
|
28
|
+
"Numeric Code":"170",
|
29
|
+
"Minor unit":"2"
|
30
|
+
},
|
31
|
+
{
|
32
|
+
"Entity":"COLOMBIA",
|
33
|
+
"Currency":"Unidad de Valor Real",
|
34
|
+
"Alphabetic Code":"COU",
|
35
|
+
"Numeric Code":"970",
|
36
|
+
"Minor unit":"2"
|
37
|
+
},
|
38
|
+
{
|
39
|
+
"Entity":"THAILAND",
|
40
|
+
"Currency":"Baht",
|
41
|
+
"Alphabetic Code":"THB",
|
42
|
+
"Numeric Code":"764",
|
43
|
+
"Minor unit":"2"
|
44
|
+
},
|
45
|
+
{
|
46
|
+
"Entity":"UNITED KINGDOM",
|
47
|
+
"Currency":"Pound Sterling",
|
48
|
+
"Alphabetic Code":"GBP",
|
49
|
+
"Numeric Code":"826",
|
50
|
+
"Minor unit":"2"
|
51
|
+
},
|
52
|
+
{
|
53
|
+
"Entity":"CANADA",
|
54
|
+
"Currency":"Canadian Dollar",
|
55
|
+
"Alphabetic Code":"CAD",
|
56
|
+
"Numeric Code":"124",
|
57
|
+
"Minor unit":"2"
|
58
|
+
},
|
59
|
+
{
|
60
|
+
"Entity":"ARGENTINA",
|
61
|
+
"Currency":"Argentine Peso",
|
62
|
+
"Alphabetic Code":"ARS",
|
63
|
+
"Numeric Code":"032",
|
64
|
+
"Minor unit":"2"
|
65
|
+
},
|
66
|
+
{
|
67
|
+
"Entity":"CHILE",
|
68
|
+
"Currency":"Unidades de fomento",
|
69
|
+
"Alphabetic Code":"CLF",
|
70
|
+
"Numeric Code":"990",
|
71
|
+
"Minor unit":"0"
|
72
|
+
},
|
73
|
+
{
|
74
|
+
"Entity":"CHILE",
|
75
|
+
"Currency":"Chilean Peso",
|
76
|
+
"Alphabetic Code":"CLP",
|
77
|
+
"Numeric Code":"152",
|
78
|
+
"Minor unit":"0"
|
79
|
+
},
|
80
|
+
{
|
81
|
+
"Entity":"NICARAGUA",
|
82
|
+
"Currency":"Cordoba",
|
83
|
+
"Alphabetic Code":"NIC",
|
84
|
+
"Numeric Code":"—",
|
85
|
+
"Withdrawal Date":"1990-10"
|
86
|
+
},
|
87
|
+
{
|
88
|
+
"Entity":"PANAMA",
|
89
|
+
"Currency":"Balboa",
|
90
|
+
"Alphabetic Code":"PAB",
|
91
|
+
"Numeric Code":"590",
|
92
|
+
"Minor unit":"2"
|
93
|
+
},
|
94
|
+
{
|
95
|
+
"Entity":"COSTA RICA",
|
96
|
+
"Currency":"Costa Rican Colon",
|
97
|
+
"Alphabetic Code":"CRC",
|
98
|
+
"Numeric Code":"188",
|
99
|
+
"Minor unit":"2"
|
100
|
+
},
|
101
|
+
{
|
102
|
+
"Entity":"BRAZIL",
|
103
|
+
"Currency":"Brazilian Real",
|
104
|
+
"Alphabetic Code":"BRL",
|
105
|
+
"Numeric Code":"986",
|
106
|
+
"Minor unit":"2"
|
107
|
+
},
|
108
|
+
{
|
109
|
+
"Entity":"MEXICO",
|
110
|
+
"Currency":"Mexican Peso",
|
111
|
+
"Alphabetic Code":"MXN",
|
112
|
+
"Numeric Code":"484",
|
113
|
+
"Minor unit":"2"
|
114
|
+
},
|
115
|
+
{
|
116
|
+
"Entity":"MEXICO",
|
117
|
+
"Currency":"Mexican Unidad de Inversion (UDI)",
|
118
|
+
"Alphabetic Code":"MXV",
|
119
|
+
"Numeric Code":"979",
|
120
|
+
"Minor unit":"2"
|
121
|
+
},
|
122
|
+
{
|
123
|
+
"Entity":"EL SALVADOR",
|
124
|
+
"Currency":"El Salvador Colon",
|
125
|
+
"Alphabetic Code":"SVC",
|
126
|
+
"Numeric Code":"222",
|
127
|
+
"Minor unit":"2"
|
128
|
+
},
|
129
|
+
{
|
130
|
+
"Entity":"URUGUAY",
|
131
|
+
"Currency":"Uruguay Peso en Unidades Indexadas (URUIURUI)",
|
132
|
+
"Alphabetic Code":"UYI",
|
133
|
+
"Numeric Code":"940",
|
134
|
+
"Minor unit":"0"
|
135
|
+
},
|
136
|
+
{
|
137
|
+
"Entity":"URUGUAY",
|
138
|
+
"Currency":"Peso Uruguayo",
|
139
|
+
"Alphabetic Code":"UYU",
|
140
|
+
"Numeric Code":"858",
|
141
|
+
"Minor unit":"2"
|
142
|
+
},
|
143
|
+
{
|
144
|
+
"Entity":"PARAGUAY",
|
145
|
+
"Currency":"Guarani",
|
146
|
+
"Alphabetic Code":"PYG",
|
147
|
+
"Numeric Code":"600",
|
148
|
+
"Minor unit":"0"
|
149
|
+
},
|
150
|
+
{
|
151
|
+
"Entity":"PERU",
|
152
|
+
"Currency":"Nuevo Sol",
|
153
|
+
"Alphabetic Code":"PEN",
|
154
|
+
"Numeric Code":"604",
|
155
|
+
"Minor unit":"2"
|
156
|
+
},
|
157
|
+
{
|
158
|
+
"Entity":"JAPAN",
|
159
|
+
"Currency":"Yen",
|
160
|
+
"Alphabetic Code":"JPY",
|
161
|
+
"Numeric Code":"392",
|
162
|
+
"Minor unit":"0"
|
163
|
+
},
|
164
|
+
{
|
165
|
+
"Entity":"GUATEMALA",
|
166
|
+
"Currency":"Quetzal",
|
167
|
+
"Alphabetic Code":"GTQ",
|
168
|
+
"Numeric Code":"320",
|
169
|
+
"Minor unit":"2"
|
170
|
+
}
|
171
|
+
]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This class writes the cashflow numbers, by month
|
4
|
+
class CashFlowGrid < RVGP::Base::Grid
|
5
|
+
grid 'cashflow', 'Generate Cashflow Grids', 'Cashflows by month (%s)',
|
6
|
+
output_path_template: '%s-cashflow'
|
7
|
+
|
8
|
+
def sheet_header
|
9
|
+
['Account'] + map_months { |month| month.strftime('%m-%y') }
|
10
|
+
end
|
11
|
+
|
12
|
+
def sheet_body
|
13
|
+
# NOTE: I think it only makes sense to sort these by account name. Mostly
|
14
|
+
# because any other sorting mechanism wouldn't 'line up' with the
|
15
|
+
# other years. But, it's also nice that the git diff's would be
|
16
|
+
# easier to parse.
|
17
|
+
monthly_amounts_by_account
|
18
|
+
.sort_by { |acct, _| acct }
|
19
|
+
.map { |account, by_month| [account] + map_months { |m| by_month[m].round(2) if by_month[m] } }
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def map_months(&block)
|
25
|
+
months_through(starting_at, ending_at).map(&block)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This class writes the monthly income and expense numbers, by month
|
4
|
+
class MonthlyIncomeAndExpensesGrid < RVGP::Base::Grid
|
5
|
+
grid 'income_and_expenses',
|
6
|
+
'Generate Income & Expense Grids',
|
7
|
+
'Income & Expense by month (%s)',
|
8
|
+
output_path_template: '%s-monthly-income-and-expenses'
|
9
|
+
|
10
|
+
def sheet_header
|
11
|
+
%w[Date Income Expense]
|
12
|
+
end
|
13
|
+
|
14
|
+
def sheet_body
|
15
|
+
table = { 'Income' => monthly_amounts('Income'), 'Expense' => monthly_amounts('Expense') }
|
16
|
+
|
17
|
+
# This deserializes our lookup hash(es) into rows:
|
18
|
+
months_through(starting_at, ending_at).collect do |month|
|
19
|
+
[month.strftime('%m-%y')] + %w[Income Expense].map do |direction|
|
20
|
+
cell = table[direction][month]
|
21
|
+
cell ? cell.abs : nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This class writes the wealth growth grid, by month
|
4
|
+
class WealthGrowthGrid < RVGP::Base::Grid
|
5
|
+
grid 'wealth_growth', 'Generate Wealth Growth Grids', 'Wealth Growth by month (%s)',
|
6
|
+
output_path_template: '%s-wealth-growth'
|
7
|
+
|
8
|
+
def sheet_header
|
9
|
+
%w[Date Assets Liabilities]
|
10
|
+
end
|
11
|
+
|
12
|
+
def sheet_body
|
13
|
+
# NOTE: If we're having a problem with prices not matching outputs,
|
14
|
+
# depending on whether we're rebuilding the whole grids directory, as
|
15
|
+
# compared to just rebuilding the newest year, it's probably because of
|
16
|
+
# the notes on prices there at the bottom of prices.db
|
17
|
+
assets, liabilities = *%w[Assets Liabilities].collect do |acct|
|
18
|
+
monthly_totals acct, accrue_before_begin: true
|
19
|
+
end
|
20
|
+
|
21
|
+
months = months_through starting_at, ending_at
|
22
|
+
|
23
|
+
months.collect.with_index do |month, i|
|
24
|
+
# NOTE: The reason we use this last_month hack, is because ledger tends to
|
25
|
+
# omit column values, if there's no 'activity' in a category, at the end
|
26
|
+
# of the display range. hledger doesn't do that, and we can probably nix
|
27
|
+
# this if/when we switch the register command over to hledger...
|
28
|
+
last_month = months[i - 1] if i.positive?
|
29
|
+
|
30
|
+
[month.strftime('%m-%y'),
|
31
|
+
assets[month] || assets[last_month],
|
32
|
+
liabilities[month] || liabilities[last_month]]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
title: "Cashflow Expenses (%{year})"
|
2
|
+
glob: "%{year}-cashflow.csv"
|
3
|
+
grid_hacks:
|
4
|
+
# NOTE: "date"/cornerstone column is the 1 here
|
5
|
+
keystone: "Date"
|
6
|
+
select_rows: !!proc >
|
7
|
+
/\:Expenses\:/.match name
|
8
|
+
# TODO: Change this to keystone:
|
9
|
+
sort_rows_by: !!proc >
|
10
|
+
row[1...].compact.sum * -1
|
11
|
+
truncate_rows: 25 # Sheet::MAX_COLUMNS-1
|
12
|
+
# TODO: Hmmm, I guess Google may need this?
|
13
|
+
switch_rows_columns: true
|
14
|
+
google:
|
15
|
+
chart_type: area
|
16
|
+
axis:
|
17
|
+
left: "Amount"
|
18
|
+
bottom: "Month"
|
19
|
+
stacked_type: "STACKED"
|
20
|
+
series_colors: {}
|
21
|
+
gnuplot:
|
22
|
+
chart_type: area
|
23
|
+
domain: monthly
|
24
|
+
is_stacked: true
|
25
|
+
axis:
|
26
|
+
left: "Amount"
|
27
|
+
bottom: "Month"
|
28
|
+
# TODO: For cashflow, Invert the legend order... why is hotels on bottom right, instead of top left
|
29
|
+
additional_lines: |+
|
30
|
+
set xtics scale 0 rotate by 45 offset -1.4,-1.4
|
31
|
+
set xtics out
|
32
|
+
set tics front
|
33
|
+
set key title ' '
|
@@ -0,0 +1,17 @@
|
|
1
|
+
title: "Monthly Income & Expenses (%{year})"
|
2
|
+
glob: "%{year}-monthly-income-and-expenses.csv"
|
3
|
+
google:
|
4
|
+
chart_type: column
|
5
|
+
axis:
|
6
|
+
left: "Amount"
|
7
|
+
bottom: "Month"
|
8
|
+
gnuplot:
|
9
|
+
chart_type: column
|
10
|
+
is_clustered: true
|
11
|
+
xrange_start: 1
|
12
|
+
axis:
|
13
|
+
left: "Amount"
|
14
|
+
bottom: "Month"
|
15
|
+
additional_lines: |+
|
16
|
+
set xtics scale 0 rotate by 45 offset -1.4,-1.4
|
17
|
+
set key title ' '
|
@@ -0,0 +1,20 @@
|
|
1
|
+
title: "Wealth Growth (%{year})"
|
2
|
+
glob: "%{year}-wealth-growth.csv"
|
3
|
+
grid_hacks:
|
4
|
+
store_cell: !!proc >
|
5
|
+
(cell) ? cell.to_f.abs : nil
|
6
|
+
google:
|
7
|
+
chart_type: area
|
8
|
+
axis:
|
9
|
+
left: "Amount"
|
10
|
+
bottom: "Date"
|
11
|
+
gnuplot:
|
12
|
+
chart_type: area
|
13
|
+
domain: monthly
|
14
|
+
axis:
|
15
|
+
left: "Amount"
|
16
|
+
bottom: "Date"
|
17
|
+
additional_lines: |+
|
18
|
+
set xtics scale 0 rotate by 45 offset -1.4,-1.4
|
19
|
+
set key title ' '
|
20
|
+
set style fill transparent solid 0.7 border
|
@@ -0,0 +1,9 @@
|
|
1
|
+
csv_headers: true
|
2
|
+
reverse_order: true
|
3
|
+
default_currency: $
|
4
|
+
fields:
|
5
|
+
date: !!proc Date.strptime(row['Date'], '%m/%d/%Y')
|
6
|
+
amount: !!proc >
|
7
|
+
withdrawal, deposit = row[3..4].collect {|a| a.to_commodity unless a.empty?};
|
8
|
+
( deposit ? deposit.invert! : withdrawal ).quantity_as_s
|
9
|
+
description: !!proc row['Description']
|
File without changes
|
File without changes
|