rvgp 0.3.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.
- 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
|