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.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.rubocop.yml +23 -0
  4. data/LICENSE +504 -0
  5. data/README.md +223 -0
  6. data/Rakefile +32 -0
  7. data/bin/rvgp +8 -0
  8. data/lib/rvgp/application/config.rb +159 -0
  9. data/lib/rvgp/application/descendant_registry.rb +122 -0
  10. data/lib/rvgp/application/status_output.rb +139 -0
  11. data/lib/rvgp/application.rb +170 -0
  12. data/lib/rvgp/base/command.rb +457 -0
  13. data/lib/rvgp/base/grid.rb +531 -0
  14. data/lib/rvgp/base/reader.rb +29 -0
  15. data/lib/rvgp/base/reconciler.rb +434 -0
  16. data/lib/rvgp/base/validation.rb +261 -0
  17. data/lib/rvgp/commands/cashflow.rb +160 -0
  18. data/lib/rvgp/commands/grid.rb +70 -0
  19. data/lib/rvgp/commands/ireconcile.rb +95 -0
  20. data/lib/rvgp/commands/new_project.rb +296 -0
  21. data/lib/rvgp/commands/plot.rb +41 -0
  22. data/lib/rvgp/commands/publish_gsheets.rb +83 -0
  23. data/lib/rvgp/commands/reconcile.rb +58 -0
  24. data/lib/rvgp/commands/rotate_year.rb +202 -0
  25. data/lib/rvgp/commands/validate_journal.rb +59 -0
  26. data/lib/rvgp/commands/validate_system.rb +44 -0
  27. data/lib/rvgp/commands.rb +160 -0
  28. data/lib/rvgp/dashboard.rb +252 -0
  29. data/lib/rvgp/fakers/fake_feed.rb +245 -0
  30. data/lib/rvgp/fakers/fake_journal.rb +57 -0
  31. data/lib/rvgp/fakers/fake_reconciler.rb +88 -0
  32. data/lib/rvgp/fakers/faker_helpers.rb +25 -0
  33. data/lib/rvgp/gem.rb +80 -0
  34. data/lib/rvgp/journal/commodity.rb +453 -0
  35. data/lib/rvgp/journal/complex_commodity.rb +214 -0
  36. data/lib/rvgp/journal/currency.rb +101 -0
  37. data/lib/rvgp/journal/journal.rb +141 -0
  38. data/lib/rvgp/journal/posting.rb +156 -0
  39. data/lib/rvgp/journal/pricer.rb +267 -0
  40. data/lib/rvgp/journal.rb +24 -0
  41. data/lib/rvgp/plot/gnuplot.rb +478 -0
  42. data/lib/rvgp/plot/google-drive/output_csv.rb +44 -0
  43. data/lib/rvgp/plot/google-drive/output_google_sheets.rb +434 -0
  44. data/lib/rvgp/plot/google-drive/sheet.rb +67 -0
  45. data/lib/rvgp/plot.rb +293 -0
  46. data/lib/rvgp/pta/hledger.rb +237 -0
  47. data/lib/rvgp/pta/ledger.rb +308 -0
  48. data/lib/rvgp/pta.rb +311 -0
  49. data/lib/rvgp/reconcilers/csv_reconciler.rb +424 -0
  50. data/lib/rvgp/reconcilers/journal_reconciler.rb +41 -0
  51. data/lib/rvgp/reconcilers/shorthand/finance_gem_hacks.rb +48 -0
  52. data/lib/rvgp/reconcilers/shorthand/international_atm.rb +152 -0
  53. data/lib/rvgp/reconcilers/shorthand/investment.rb +144 -0
  54. data/lib/rvgp/reconcilers/shorthand/mortgage.rb +195 -0
  55. data/lib/rvgp/utilities/grid_query.rb +190 -0
  56. data/lib/rvgp/utilities/yaml.rb +131 -0
  57. data/lib/rvgp/utilities.rb +44 -0
  58. data/lib/rvgp/validations/balance_validation.rb +68 -0
  59. data/lib/rvgp/validations/duplicate_tags_validation.rb +48 -0
  60. data/lib/rvgp/validations/uncategorized_validation.rb +15 -0
  61. data/lib/rvgp.rb +66 -0
  62. data/resources/README.MD/2022-cashflow-google.png +0 -0
  63. data/resources/README.MD/2022-cashflow.png +0 -0
  64. data/resources/README.MD/all-wealth-growth-google.png +0 -0
  65. data/resources/README.MD/all-wealth-growth.png +0 -0
  66. data/resources/gnuplot/default.yml +80 -0
  67. data/resources/i18n/en.yml +192 -0
  68. data/resources/iso-4217-currencies.json +171 -0
  69. data/resources/skel/Rakefile +5 -0
  70. data/resources/skel/app/grids/cashflow_grid.rb +27 -0
  71. data/resources/skel/app/grids/monthly_income_and_expenses_grid.rb +25 -0
  72. data/resources/skel/app/grids/wealth_growth_grid.rb +35 -0
  73. data/resources/skel/app/plots/cashflow.yml +33 -0
  74. data/resources/skel/app/plots/monthly-income-and-expenses.yml +17 -0
  75. data/resources/skel/app/plots/wealth-growth.yml +20 -0
  76. data/resources/skel/config/csv-format-acme-checking.yml +9 -0
  77. data/resources/skel/config/google-secrets.yml +5 -0
  78. data/resources/skel/config/rvgp.yml +0 -0
  79. data/resources/skel/journals/prices.db +0 -0
  80. data/rvgp.gemspec +6 -0
  81. data/test/assets/ledger_total_monthly_liabilities_with_empty.xml +383 -0
  82. data/test/assets/ledger_total_monthly_liabilities_with_empty2.xml +428 -0
  83. data/test/test_command_base.rb +61 -0
  84. data/test/test_commodity.rb +270 -0
  85. data/test/test_csv_reconciler.rb +60 -0
  86. data/test/test_currency.rb +24 -0
  87. data/test/test_fake_feed.rb +228 -0
  88. data/test/test_fake_journal.rb +98 -0
  89. data/test/test_fake_reconciler.rb +60 -0
  90. data/test/test_journal_parse.rb +545 -0
  91. data/test/test_ledger.rb +102 -0
  92. data/test/test_plot.rb +133 -0
  93. data/test/test_posting.rb +50 -0
  94. data/test/test_pricer.rb +139 -0
  95. data/test/test_pta_adapter.rb +575 -0
  96. data/test/test_utilities.rb +45 -0
  97. 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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rvgp'
4
+
5
+ RVGP.initialize_app(File.dirname(__FILE__)).initialize_rake! self
@@ -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']
@@ -0,0 +1,5 @@
1
+ client_id: "841596637004-vcljkfs9o45s12nq6g3r54vtu6htp8b9.apps.googleusercontent.com"
2
+ project_id: "ruby-rake-accounting"
3
+ client_secret: "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"
4
+ token_path: "google-token.yml"
5
+ application_name: "rvgp"
File without changes
File without changes
data/rvgp.gemspec ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env gem build
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'lib/rvgp/gem'
5
+
6
+ RVGP::Gem.specification