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,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RVGP
4
+ # This module contains helper methods used throughout RVGP. These are just common
5
+ # codepaths, that have little in common, save for their general utility.
6
+ module Utilities
7
+ # This returns each month in a series from the first date, to the last, in the
8
+ # provided array of dates
9
+ # @overload months_through(date, ...)
10
+ # @param [Array<Date>] date A date, that will be used to calculate the range of months to construct a range from.
11
+ # @param [Array<Date>] ... More dates. This method will automatically select the max and min from the sample
12
+ # provided.
13
+ # @return [Array<Date>] An array, containing a Date, set to the first of every month, in the provided range.
14
+ def months_through(*args)
15
+ dates = args.flatten.uniq.sort
16
+
17
+ ret = []
18
+ unless dates.empty?
19
+ d = Date.new dates.first.year, dates.first.month, 1 # start_at
20
+ while d <= Date.new(dates.last.year, dates.last.month, 1) # end_at
21
+ ret << d
22
+ d = d >> 1
23
+ end
24
+ end
25
+ ret
26
+ end
27
+
28
+ # Convert the provided string, into a Regexp. Note that the the ixm suffixes are supported, unlike
29
+ # ruby's Regexp.new(str) method
30
+ # @param [String] str A string, in the 'standard' regexp format: '/Running (?:to|at) the Park/i'
31
+ # @return [Regexp] The conversion to a useable regexp, for the provided string
32
+ def string_to_regex(str)
33
+ if %r{\A/(.*)/([imx]?[imx]?[imx]?)\Z}.match str
34
+ Regexp.new(::Regexp.last_match(1), ::Regexp.last_match(2).chars.map do |c|
35
+ case c
36
+ when 'i' then Regexp::IGNORECASE
37
+ when 'x' then Regexp::EXTENDED
38
+ when 'm' then Regexp::MULTILINE
39
+ end
40
+ end.reduce(:|))
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RVGP
4
+ module Validations
5
+ # This validation asserts that the ledger-reported balance, matches a provided
6
+ # balance, on a given day. These balances, should be stipulated in a section
7
+ # of your reconciler, that looks like this:
8
+ # ```
9
+ # balances:
10
+ # '2022-01-01': $ 105.63
11
+ # '2022-09-01': $ 300.29
12
+ # '2022-10-01': $ 400.33
13
+ # ```
14
+ # These balances are expected to come from a bank statement, and this validation
15
+ # ensures that rvgp is matching the records of your financial institution
16
+ class BalanceValidation < RVGP::Base::JournalValidation
17
+ # If there are no checkpoints in the 'balances' line of the reconciler, this
18
+ # fires a warning. If there are checkpoints, then, we scan the register to
19
+ # ensure that the balance of the reconciler.from, on the checkpoint date,
20
+ # matches the ledger/hledger balance, on that date. (and if it doesnt,
21
+ # fires an error)
22
+ def validate
23
+ if reconciler.balances.nil? || reconciler.balances.empty?
24
+ warning! 'No balance checkpoints found.'
25
+ else
26
+ is_account_valid = true
27
+ cite_balances = reconciler.balances.map do |d, expected_balance_s|
28
+ expected_balance = expected_balance_s.to_commodity
29
+
30
+ balances_on_day = pta.balance format('^%s$', reconciler.from),
31
+ depth: 1,
32
+ end: d.to_s,
33
+ file: RVGP.app.config.project_journal_path
34
+
35
+ balances_found = balances_on_day.accounts.map(&:amounts).flatten.find_all do |amount|
36
+ amount.code == expected_balance.code
37
+ end
38
+
39
+ found = if balances_found.empty?
40
+ # Rather than operate from nil, we'll establish that we're '0' of units
41
+ # of the expected symbol
42
+ RVGP::Journal::Commodity.from_symbol_and_amount expected_balance.code, 0
43
+ else
44
+ balances_found.sum
45
+ end
46
+
47
+ found_as_s = if found
48
+ format('Found: %s', found.to_s)
49
+ else
50
+ found = RVGP::Journal::Commodity.from_symbol_and_amount expected_balance.code, 0
51
+ '(Nil)'
52
+ end
53
+
54
+ is_valid = expected_balance == found
55
+ is_account_valid = false unless is_valid
56
+
57
+ format('(%<day>s) Expected: %<expected>s %<indicator>s',
58
+ day: d.to_s,
59
+ expected: expected_balance.to_s,
60
+ indicator: RVGP.pastel.send(is_valid ? :green : :red, found_as_s))
61
+ end
62
+
63
+ error! 'Failed Checkpoint(s):', cite_balances unless is_account_valid
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RVGP
4
+ module Validations
5
+ # This class implements a journal validation that ensures a given transfer, hasn't
6
+ # been tagged more than once, with the same tag.
7
+ class DuplicateTagsValidation < RVGP::Base::JournalValidation
8
+ # Reviews every transfer, and post, to ensure that there are no tags occurring more than once, in
9
+ # any given entry. Unlike most of the validations in RVGP, this one doesn't use ledger or hledger
10
+ # to validate. This validation parses the file itself, in ruby, and ensures based on the contents.
11
+ def validate
12
+ journal = RVGP::Journal.parse File.read(reconciler.output_file)
13
+ dupe_messages = []
14
+
15
+ journal.postings.each do |posting|
16
+ posting_tag_names = posting.tags.map(&:key)
17
+ found_dupes = posting_tag_names.find_all { |tag| posting_tag_names.count(tag) > 1 }.uniq
18
+
19
+ if found_dupes.empty?
20
+ posting.transfers.each do |transfer|
21
+ transfer_tag_names = transfer.tags.map(&:key) + posting_tag_names
22
+
23
+ found_dupes = transfer_tag_names.find_all { |tag| transfer_tag_names.count(tag) > 1 }.uniq
24
+
25
+ next if found_dupes.empty?
26
+
27
+ dupe_messages << format('Line %<line>d: %<date>s %<desc>s (Transfer: %<tags>s)',
28
+ line: posting.line_number,
29
+ date: posting.date,
30
+ desc: posting.description,
31
+ tags: found_dupes.join(', '))
32
+ end
33
+ else
34
+ dupe_messages << format('Line %<line>d: %<date>s %<desc>s (%<tags>s)',
35
+ line: posting.line_number,
36
+ date: posting.date,
37
+ desc: posting.description,
38
+ tags: found_dupes.join(','))
39
+ end
40
+ end
41
+
42
+ unless dupe_messages.empty?
43
+ error! 'These postings have been tagged with the same tag, more than once', dupe_messages
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RVGP
4
+ # This module provides a number of basic, and common, validations for use in your projects
5
+ module Validations
6
+ # This class implements a journal validation that ensures there are no uncategorized
7
+ # expenses in the journal
8
+ class UncategorizedValidation < RVGP::Base::JournalValidation
9
+ # Ensures that there is no balance for 'Unknown' categories in a journal
10
+ def validate
11
+ validate_no_balance 'Uncategorized Transactions', 'Unknown'
12
+ end
13
+ end
14
+ end
15
+ end
data/lib/rvgp.rb ADDED
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'rvgp/utilities/yaml'
4
+ require_relative 'rvgp/application'
5
+ require_relative 'rvgp/commands'
6
+ require_relative 'rvgp/base/reconciler'
7
+
8
+ # NOTE: Reconcilers & shorthand may want to go into a registry system at
9
+ # some point, akin to grids/validations.
10
+ require_relative 'rvgp/reconcilers/csv_reconciler'
11
+ require_relative 'rvgp/reconcilers/journal_reconciler'
12
+ require_relative 'rvgp/reconcilers/shorthand/mortgage'
13
+ require_relative 'rvgp/reconcilers/shorthand/investment'
14
+ require_relative 'rvgp/reconcilers/shorthand/international_atm'
15
+
16
+ require_relative 'rvgp/base/validation'
17
+
18
+ require_relative 'rvgp/journal'
19
+ require_relative 'rvgp/pta/ledger'
20
+ require_relative 'rvgp/pta/hledger'
21
+
22
+ require_relative 'rvgp/base/grid'
23
+
24
+ # Gem Paths / Resources:
25
+ require_relative 'rvgp/gem'
26
+
27
+ I18n.load_path << Dir[RVGP::Gem.root('resources/i18n/*.yml')]
28
+ RVGP::Journal::Currency.currencies_config = RVGP::Gem.root('resources/iso-4217-currencies.json')
29
+
30
+ # The base module, under which all RVGP code is filed
31
+ module RVGP
32
+ # @param from_path [String] The directory path, to an RVGP project.
33
+ # @return [RVGP::Application] The initialized application, that was stored in RVGP.app
34
+ def self.initialize_app(from_path)
35
+ raise StandardError, 'Application is already initialized' if @app
36
+
37
+ @app = Application.new from_path
38
+ end
39
+
40
+ # @return [RVGP::Application] The currently-initialized RVGP:Application
41
+ def self.app
42
+ @app
43
+ end
44
+
45
+ # @return [Pastel] The global pastel object, used to output to the console.
46
+ def self.pastel
47
+ @pastel ||= Pastel.new enabled: $stdout.tty?
48
+ end
49
+
50
+ # @!attribute [r] self.commands
51
+ # Contains an array of all available objects, with parent of type {RVGP::Base::Command}.
52
+ # @return [Array<RVGP::Base::Command>] the commands that are available in this project
53
+ # @!attribute [r] self.grids
54
+ # Contains an array of all available objects, with parent of type {RVGP::Base::Grid}.<br>
55
+ # **NOTE:** RVGP.app.task_names => Array<String>, will return all grid-building tasks that have been defined in the
56
+ # project.
57
+ # @return [Array<RVGP::Base::Grid>] the grids that are available in this project
58
+ # @!attribute [r] self.journal_validations
59
+ # Contains an array of all available objects, with parent of type {RVGP::Base::JournalValidation}.
60
+ # @return [Array<RVGP::Base::JournalValidation>] the journal validations that are available in this project
61
+ # @!attribute [r] self.system_validations
62
+ # Contains an array of all available objects, with parent of type {RVGP::Base::SystemValidation}.<br>
63
+ # **NOTE:** RVGP.system_validations.task_names => Array<String>, will return all system validation tasks that have
64
+ # been defined in the project.
65
+ # @return [Array<RVGP::Base::SystemValidation>] the system validations that are available in this project
66
+ end
@@ -0,0 +1,80 @@
1
+ colors:
2
+ base:
3
+ # Solarized Light:
4
+ base03: '#002b36'
5
+ base02: '#073642'
6
+ base01: '#586e75'
7
+ base00: '#657b83'
8
+ base0: '#839496'
9
+ base1: '#93a1a1'
10
+ base2: '#eee8d5'
11
+ base3: '#fdf6e3'
12
+ yellow: '#b58900'
13
+ orange: '#cb4b16'
14
+ red: '#dc322f'
15
+ magenta: '#d33682'
16
+ violet: '#6c71c4'
17
+ blue: '#268bd2'
18
+ cyan: '#2aa198'
19
+ green: '#859900'
20
+ font: :base03
21
+ title: :font
22
+ grid: :base02
23
+ axis: :base02
24
+ key_text: :font
25
+ background: :base3
26
+ series:
27
+ # https://www.heavy.ai/blog/12-color-palettes-for-telling-better-stories-with-your-data
28
+ # Spring Pastels
29
+ - "#7eb0d5"
30
+ - "#fd7f6f"
31
+ - "#b2e061"
32
+ - "#bd7ebe"
33
+ - "#ffb55a"
34
+ - "#ffee65"
35
+ - "#beb9db"
36
+ - "#fdcce5"
37
+ - "#8bd3c7"
38
+ # Retro Metro
39
+ - "#ea5545"
40
+ - "#f46a9b"
41
+ - "#ef9b20"
42
+ - "#edbf33"
43
+ - "#ede15b"
44
+ - "#bdcf32"
45
+ - "#87bc45"
46
+ - "#27aeef"
47
+ # http://www.gnuplotting.org/data/dark2.pal
48
+ - '#1B9E77'
49
+ - '#D95F02'
50
+ - '#7570B3'
51
+ - '#E7298A'
52
+ - '#66A61E'
53
+ - '#E6AB02'
54
+ - '#A6761D'
55
+ # Blue to Yellow
56
+ - "#115f9a"
57
+ - "#1984c5"
58
+ - "#22a7f0"
59
+ - "#48b5c4"
60
+ - "#76c68f"
61
+ - "#a6d75b"
62
+ - "#c9e52f"
63
+ - "#d0ee11"
64
+ - "#d0f400"
65
+ header: |+
66
+ set datafile separator ","
67
+ set title "%{title}" font "sans,11" textcolor rgb "%{title_rgb}" enhanced
68
+ set terminal wxt size 1200,400 persist enhanced font 'sans,10' background rgb '%{background_rgb}'
69
+ set style line 102 lc rgb '%{grid_rgb}' lt 0 lw 1
70
+ set grid back ls 102
71
+ set style line 101 lc rgb '%{axis_rgb}' lt 1 lw 1
72
+ set border 3 front ls 101
73
+ set xtics font ",9"
74
+ set ytics font ",11"
75
+ set timefmt "%%m-%%y"
76
+ set format x "%%b-%%y"
77
+ set format y "$ %%'.0f"
78
+ unset colorbox
79
+ set key on under center textcolor rgb "%{key_text_rgb}" enhanced font 'sans,9'
80
+ set decimal locale
@@ -0,0 +1,192 @@
1
+ en:
2
+ help:
3
+ usage: 'Usage: %{program} [OPTION]... [COMMAND] [TARGET]...'
4
+ indent: ' '
5
+ description: >
6
+ A plain text accounting workflow tool for: (r)econciling, (v)alidating, (g)rid-production and (p)lotting your finances with ruby. See the program github for more details.
7
+
8
+ command_introduction: >
9
+ COMMAND - This is a required parameter. Command-specific options are
10
+ further available, once you've specified a command mode. See the 'Available Commands' section for a list of supported commands.
11
+ target_introduction: >
12
+ TARGET(s) - Depending on which command you select, a list of targets can be supplied. Typically,
13
+ supplying the -l option, in lieu of any targets, will output the targets supported by the command.
14
+ Note that any asterisk encountered in a target parameter, will expand to include all targets that
15
+ can be matched given the provided (target) string.
16
+ command_list_introduction: 'Available Commands:'
17
+ global_option_introduction: >
18
+ Global Options:
19
+ The following options are available to all commands:
20
+
21
+ -d --dir[=PATH] Use the supplied PATH as the active rvgp directory.
22
+ If unsupplied, this option defaults to the basedir of
23
+ the LEDGER_FILE environment variable.
24
+ commands:
25
+ cashflow:
26
+ description: 'Output a convenient dashboard with income/expense flows, based on intention'
27
+ options:
28
+ all: 'Output a dashboard for every intention in the system'
29
+ list: 'List all available intention-tag values'
30
+ date: 'Specify a date to use, when calculatingg the "recent months" to display. Defaults to "today".'
31
+ new_project:
32
+ description: 'Create a new plain text accounting project, populated with random data, in the path specified by --dir.'
33
+ plot:
34
+ description: 'Build one or more gnuplots, based on a grid variant.'
35
+ options:
36
+ all: 'Build all available plots'
37
+ list: 'List all available plots'
38
+ stdout: 'Output build to STDOUT instead of output_file'
39
+ publish_gsheets:
40
+ description: 'Publish plots, both as a spreadsheet, as well as a graph, into a google sheets workbook.'
41
+ options:
42
+ all: 'Publish all available plots'
43
+ list: 'List all available plot variants'
44
+ csvdir: 'Output the provided plots, as csvs, with grid hacks applied, into the specified directory. Mostly useful for debugging.'
45
+ title: 'The title of the Google doc being published. Defaults to "RVGP Finance Report %m/%d/%y %H:%M". '
46
+ sleep: 'Seconds to sleep, between each sheet upload. This sleep prevents Google from aborting the publish, under the auspice of api spamming. Defaults to 5.'
47
+ ireconcile:
48
+ description: 'Open an interactive vim session, to edit and build a reconcile'
49
+ options:
50
+ all: 'Process all available reconcilers'
51
+ list: 'List all available reconcilers'
52
+ vsplit: 'Split the input and output panes vertically, instead of horizontally (the default)'
53
+ grid:
54
+ description: "Generate Grid csv's in build/grids"
55
+ options:
56
+ all: 'Process all available grids'
57
+ list: 'List all available grids'
58
+ reconcile:
59
+ description: 'Create/Update the build/*.journal, based on ./reconcilers/*.yml'
60
+ options:
61
+ stdout: 'Output build to STDOUT instead of output_file'
62
+ all: 'Process all available reconcilers'
63
+ list: 'List all available reconcilers'
64
+ concise: "Concise output mode. Strips output that's unrelated to errors and warnings. (Mostly used by the ireconcile command)"
65
+ rotate_year:
66
+ description: "Rotate the given reconcilers, for a new year. And, move the last year's input files into the historical directory under your project feeds."
67
+ options:
68
+ all: 'Rotate all eligible reconcilers'
69
+ list: 'List reconcilers eligible for rotation'
70
+ validate_journal:
71
+ description: 'Validate reconciled journals, using the app/validations'
72
+ options:
73
+ all: 'Process all available journals'
74
+ list: 'List all available journals'
75
+ validate_system:
76
+ description: 'Run validations on the ledger, once the individual journals are valid'
77
+ options:
78
+ all: 'Process all available system validations'
79
+ list: 'List all available system validations'
80
+ commands:
81
+ cashflow:
82
+ list_targets: 'The following cashflow intentions are available:'
83
+ account: 'Account'
84
+ expenses: 'Expenses'
85
+ income: 'Income'
86
+ cash_flow: 'Cash Flow'
87
+ errors:
88
+ screen_too_small: 'Screen width "%{screen_width}" is too small. Minimum width is %{minimum_width}.'
89
+ unrecognized_path: 'Unrecognized path: %{file}'
90
+ new_project:
91
+ directory_exists_prompt: "The directory \"%{dir}\" already exists. Continuing will overwrite some of its contents. Are you sure you wish to continue creating a new project in this directory? (Type \"Yes\" to continue) : "
92
+ project_name_prompt: "Whose project is this? A person's full name or a company name will work: "
93
+ project_name_confirmation: "You entered \"%{project_name}\". Is that correct? (Type \"Yes\" to continue) : "
94
+ confirm_operation: "Yes"
95
+ operation_aborted: "\"new_project\" project operation aborted"
96
+ completed_banner: "\nThe new project has been generated successfully.\nThough you may want to add the following line to your ~/.bashrc:\n export LEDGER_FILE=\"%{journal_path}\"\n\nYou're ready to begin working on this project. Try cd'ing into its directory, and running `rake`."
97
+ initialize:
98
+ project_directory: "Project directory"
99
+ bank_feeds: 'Randomized bank feeds'
100
+ reconcilers: 'Randomized reconcilers'
101
+ errors:
102
+ missing_app_dir: "An application directory was not provided, and is required. Set this parameter via --dir argument, or via the LEDGER_FILE environment variable"
103
+ directory_exists: "The directory \"%{dir}\" could not be created because it already exists."
104
+ plot:
105
+ target_description: 'Build the %{name} plot file, and its variants'
106
+ rotate_year:
107
+ list_targets: 'The following reconcilers are eligible for annual rotation:'
108
+ operations_header: 'The following command(s) will be executed:'
109
+ operation_rotate: 'Rotate "%{name}"'
110
+ operation_mkdir: 'mkdir %{path}'
111
+ confirm_operation: "Yes"
112
+ confirm_operation_prompt: "Would you like to run the above operations? (Type \"Yes\" to continue) "
113
+ operation_aborted: "\"rotate_year\" project operation aborted"
114
+ operation_element: ' 🟢 %{operation}'
115
+ publish_gsheets:
116
+ errors:
117
+ missing_google_secrets: 'Missing a readable config/google-secrets.yml file in your project directory'
118
+ unable_to_write_to_csvdir: 'Unable to write to path "%{csvdir}"'
119
+ grid:
120
+ list_targets: 'The following grids are available:'
121
+ target_description: '%{description} for %{year}'
122
+ rescan_grids:
123
+ target_description: 'Re-scan the journal directory, and register previously unavaible grid targets into the running build'
124
+ rescan_plots:
125
+ target_description: 'Re-scan the grid directory, and register previously unavaible plot targets into the running build'
126
+ reconcile:
127
+ list_targets: 'The following reconcilers are available:'
128
+ target_description: 'Reconcile the "%{input_file}" journal'
129
+ errors:
130
+ journal_missing: 'Journal(s) Missing.'
131
+ either_concise_or_stdout: 'Either -concise or -stdout mode can be specified. But, alas, not both.'
132
+ validate_journal:
133
+ list_targets: 'The following journal validations are available:'
134
+ target_description: 'Validate the "%{input_file}" journal'
135
+ validate_system:
136
+ list_targets: 'The following system validations are available:'
137
+
138
+ error:
139
+ error: 'Error'
140
+ warning: 'Warning'
141
+ missing_entry_in_prices_db: ": The following entry is missing in the prices database:\n P %{time} %{from} %{to}"
142
+ end_of_args: 'Missing an expected parameter value, in the argument list'
143
+ no_targets: "No targets specified"
144
+ missing_target: "One or more targets not found : %{targets}"
145
+ missing_command: 'No command specified. Try running --help to view the available program modes'
146
+ command_unrecognized: '"%{command}" command is unrecognized. Consult the help page.'
147
+ command_errors: "Unable to %{command}:"
148
+ command_error: " 🟢 %{error}"
149
+ no_application_dir: "Unable to determine the directory of your application. Consider setting the LEDGER_FILE environment variable, or providing a directory via the --dir parameter"
150
+ invalid_application_dir: 'Invalid or missing project found at the directory "%{directory}"'
151
+ status:
152
+ indicators:
153
+ complete: '🟢'
154
+ complete_and: '%{left} & %{right} '
155
+ fill: '.'
156
+ indent: ' '
157
+ truncated: '…'
158
+ attention1: '🟢'
159
+ attention2: ' ▩ '
160
+ commands:
161
+ reconcile:
162
+ icon: '🏗️ '
163
+ header: Building Journals from Feeds
164
+ prefix: Expanding
165
+ generate:
166
+ icon: 🏦
167
+ header: Generating Automatic Transactions
168
+ prefix: Making
169
+ validate_journal:
170
+ icon: 📒
171
+ header: Inspecting Individual Journal Files
172
+ prefix: Validating
173
+ validate_system:
174
+ icon: 📚
175
+ header: Inspecting System State
176
+ prefix: Validating
177
+ grid:
178
+ icon: ▦
179
+ header: Generating Grids
180
+ prefix: Calculating
181
+ plot:
182
+ icon: 📈
183
+ header: Generating Plots
184
+ prefix: Plotting
185
+ publish_gsheets:
186
+ icon: 📑
187
+ header: Publishing to Google Sheets
188
+ prefix: Sheeting
189
+ new_project:
190
+ icon: 📖
191
+ header: New Project
192
+ prefix: Initializing