hledger-forecast 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e605f8a4436c2b52e4b37e06aec308ef70d0fb4a7572d0791820c73ddafeaa1f
4
+ data.tar.gz: f62dc41d637ea7107ec282b41d61ed0040c3fc3f4b84ec139a1711cf09f94af5
5
+ SHA512:
6
+ metadata.gz: b44389e7cc8ee05d8f6d35a0a649e11d96c2bcc3149f5b2ccf92c563db206dae54ee65a39eb9982f9c9e0dc16688ca42ef012e95aa38b99c9ef378f0ab57d3f9
7
+ data.tar.gz: 5d64a7679916fa88216dc7daf3a4fc3607002114b025925d4e65e84c1c0a6b4243c1f389d7cb8ca0ba28209c6736ed9cf1ec5c8bcf3ddd264949d6be387f321a
@@ -0,0 +1,33 @@
1
+ name: Continuous Integration
2
+
3
+ on:
4
+ pull_request:
5
+ branches: [main]
6
+ push:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby-version:
15
+ - 3.1.2
16
+ - 3.0
17
+
18
+ steps:
19
+ - uses: actions/checkout@v3
20
+ - name: Update package
21
+ run: sudo apt-get update
22
+ - name: Install packages
23
+ run: sudo apt-get -y install ledger hledger
24
+ - name: Set up Ruby
25
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
26
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
27
+ uses: ruby/setup-ruby@v1
28
+ # uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
29
+ with:
30
+ bundler-cache: true # runs 'bundle install' and caches installed gems
31
+ ruby-version: ${{ matrix.ruby-version }}
32
+ - name: Run tests
33
+ run: rspec
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ *.journal
3
+ forecast.yml
4
+ todo.md
data/.rubocop.yml ADDED
@@ -0,0 +1,20 @@
1
+ Layout/LineLength:
2
+ Max: 120
3
+
4
+ Style/StringLiterals:
5
+ Enabled: false
6
+
7
+ Style/RedundantReturn:
8
+ Enabled: false
9
+
10
+ Metrics/ClassLength:
11
+ Enabled: False
12
+
13
+ Metrics/MethodLength:
14
+ Enabled: False
15
+
16
+ Metrics/AbcSize:
17
+ Enabled: False
18
+
19
+ Style/NumericPredicate:
20
+ Enabled: False
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.0
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem 'money'
6
+
7
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,41 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ hledger-forecast (0.1.0)
5
+ highline (~> 2.1.0)
6
+ money (~> 6.16.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ concurrent-ruby (1.2.2)
12
+ diff-lcs (1.5.0)
13
+ highline (2.1.0)
14
+ i18n (1.12.0)
15
+ concurrent-ruby (~> 1.0)
16
+ money (6.16.0)
17
+ i18n (>= 0.6.4, <= 2)
18
+ rspec (3.12.0)
19
+ rspec-core (~> 3.12.0)
20
+ rspec-expectations (~> 3.12.0)
21
+ rspec-mocks (~> 3.12.0)
22
+ rspec-core (3.12.1)
23
+ rspec-support (~> 3.12.0)
24
+ rspec-expectations (3.12.2)
25
+ diff-lcs (>= 1.2.0, < 2.0)
26
+ rspec-support (~> 3.12.0)
27
+ rspec-mocks (3.12.5)
28
+ diff-lcs (>= 1.2.0, < 2.0)
29
+ rspec-support (~> 3.12.0)
30
+ rspec-support (3.12.0)
31
+
32
+ PLATFORMS
33
+ arm64-darwin-21
34
+
35
+ DEPENDENCIES
36
+ hledger-forecast!
37
+ money
38
+ rspec (~> 3.12)
39
+
40
+ BUNDLED WITH
41
+ 2.4.10
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Oli Morris
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,119 @@
1
+ # Hledger-Forecast
2
+
3
+ ![Tests](https://github.com/olimorris/hledger-forecast/workflows/ci/badge.svg)
4
+
5
+ > **Warning**: This is still in the early stages of development and the API is likely to change
6
+
7
+ Uses a YAML file to generate monthly, quarterly, yearly and one-off transactions for better forecasting in [Hledger](https://github.com/simonmichael/hledger).
8
+
9
+ See the [rationale](#brain-rationale) section for why this gem may be useful to you.
10
+
11
+ ## :sparkles: Features
12
+
13
+ - :book: Uses a simple YAML config file to generate periodic transactions
14
+ - :date: Specify start and end dates for forecasts
15
+ - :heavy_dollar_sign: Full currency support (uses the [RubyMoney](https://github.com/RubyMoney/money) gem)
16
+ - :computer: Simple and easy to use CLI
17
+
18
+ ## :package: Installation
19
+
20
+ Assuming you have Ruby and [Rubygems](http://rubygems.org/pages/download) installed on your system, simply run:
21
+
22
+ gem install --user hledger-forecast
23
+
24
+ ## :rocket: Usage
25
+
26
+ Simply run:
27
+
28
+ hledger-forecast
29
+
30
+ Running `hledger-forecast -h` shows the available options:
31
+
32
+ Usage: Hledger-Forecast [options]
33
+
34
+ -f, --forecast FILE The FORECAST yaml file to generate from
35
+ -t, --transaction FILE The base TRANSACTIONS file to extend from
36
+ -o, --output-file FILE The OUTPUT file to create
37
+ -s, --start-date DATE The date to start generating from (yyyy-mm-dd)
38
+ -e, --end-date DATE The date to start generating to (yyyy-mm-dd)
39
+ -h, --help Show this message
40
+ --version Show version
41
+
42
+ To then include in Hledger:
43
+
44
+ hledger -f transactions.journal -f forecast.journal
45
+
46
+ where `transactions.journal` might be your bank transactions (your "actuals") and `forecast.journal` is the file generated with the `-o` option from above (your "forecast").
47
+
48
+ ### A simple config file
49
+
50
+ Firstly, create a `yml` file which will contain the transactions you'd like to forecast:
51
+
52
+ ```yaml
53
+ # forecast.yml
54
+ monthly:
55
+ - account: "[Assets:Bank]"
56
+ start: "2023-03-01"
57
+ transactions:
58
+ - amount: 2000
59
+ category: "[Expenses:Mortgage]"
60
+ description: Mortgage
61
+ - amount: 500
62
+ category: "[Expenses:Food]"
63
+ description: Food
64
+ ```
65
+
66
+ Let's examine what's going on in this config file:
67
+
68
+ - Firstly, we're telling the app to create two monthly transactions and repeat them, forever, starting from March 2023. In this case, forever will be the `end_date` specified when running the app
69
+ - If you ran the app with `hledger-forecast -s 2023-04-01` then no transactions would be generated for March as the start date is greater than the periodic start date
70
+ - Notice we're also using [virtual postings](https://hledger.org/1.29/hledger.html#virtual-postings) (designated by the brackets) to be explicit to Hledger. This also makes it easy to filter them out with the `-R` or `--real` option in Hledger
71
+ - We also have not specified a currency; the default (`USD`) will be used
72
+
73
+ ### Extending the config file
74
+
75
+ #### Periods
76
+
77
+ If you'd like to add quarterly, yearly or one-off transactions, use the following keys:
78
+
79
+ - `quarterly`
80
+ - `yearly`
81
+ - `once`
82
+
83
+ The structure of the config file remains exactly the same.
84
+
85
+ > **Note**: A quarterly transaction will repeat for every 3 months from the start date
86
+
87
+ #### Currency
88
+
89
+ To specify a currency:
90
+
91
+ ```yaml
92
+ # forecast.yml
93
+ settings:
94
+ currency: GBP
95
+ ```
96
+
97
+ #### Additional settings
98
+
99
+ Additional settings in the config file:
100
+
101
+ ```yaml
102
+ # forecast.yml
103
+ settings:
104
+ show_symbol: true # Show the currency symbol?
105
+ sign_before_symbol: true # Show the negative sign before the symbol?
106
+ thousands_separator: true # Separate thousands with a comma?
107
+ ```
108
+
109
+ ## :brain: Rationale
110
+
111
+ Firstly, I've come to realise from reading countless blog and Reddit posts on [plain text accounting](https://plaintextaccounting.org), that everyone does it __completely__ differently!
112
+
113
+ My days working in financial modelling have meant that a big macro-enabled spreadsheet was my go-to tool. Growing tired with the manual approach of importing transactions, heavily manipulating them, watching Excel become increasingly slower lead me to PTA. It's been a wonderful discovery.
114
+
115
+ One of the aspects of my previous approach to personal finance that I liked was the monthly recap of my performance and the looking ahead to the future. Am I still on track to hit my year-end savings goal? Am I still on track to hit my savings goal for 12, 24 months time? It was at this point in my shift to PTA that I hit a wall.
116
+
117
+ While Hledger provides support for [forecasting](https://hledger.org/1.29/hledger.html#forecasting) using periodic transactions, these are computed virtually at runtime. If I notice a big difference in my forecasted year-end balance compared to what I'm expecting, I want to investigate and start reconcilling. Computed transactions make this difficult and tiresome.
118
+
119
+ With this gem, my aim was to make it easy for users to change a config file, re-run a CLI command and be able to open a text file and see the changes. No guesswork. No surprises.
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/hledger_forecast'
4
+ # require 'hledger_forecast'
5
+
6
+ begin
7
+ options = HledgerForecast::Options.parse_command_line_options
8
+ rescue RuntimeError => e
9
+ puts("ERROR: #{e}")
10
+ exit(1)
11
+ end
12
+
13
+ HledgerForecast::Command.run(options)
data/example.yaml ADDED
@@ -0,0 +1,26 @@
1
+ monthly:
2
+ - date: "2023-03-01"
3
+ account: "[Assets:Bank]"
4
+ transactions:
5
+ - description: Mortgage
6
+ category: "[Expenses:Mortgage]"
7
+ amount: £1,000.00
8
+ - description: Food
9
+ category: "[Expenses:Food]"
10
+ amount: £500.00
11
+
12
+ quarterly:
13
+ - date: "2023-04-01"
14
+ account: "[Assets:Bank]"
15
+ transactions:
16
+ - description: Bonus
17
+ category: "[Income:Bonus]"
18
+ amount: -£1,000.00
19
+
20
+ yearly:
21
+ - date: "2023-04-01"
22
+ account: "[Assets:Bank]"
23
+ transactions:
24
+ - description: Annual Bonus
25
+ category: "[Income:Bonus]"
26
+ amount: -£2,000.00
@@ -0,0 +1,25 @@
1
+ require 'hledger_forecast/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'hledger-forecast'
5
+ s.version = HledgerForecast::VERSION
6
+ s.authors = ['Oli Morris']
7
+ s.summary = 'Utility to generate forecasts in Hledger'
8
+ s.description = 'Uses a YAML file to generate monthly, quarterly, yearly and one-off transactions for better forecasting in Hledger'
9
+ s.email = 'olimorris@users.noreply.github.com'
10
+ s.homepage = 'https://github.com/olimorris/hledger-forecast'
11
+ s.license = 'MIT'
12
+
13
+ s.required_ruby_version = '>= 3.0.0'
14
+
15
+ s.add_dependency "highline", "~> 2.1.0"
16
+ s.add_dependency "money", "~> 6.16.0"
17
+ s.add_development_dependency 'rspec', '~> 3.12'
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ s.test_files = `git ls-files -- spec/*`.split("\n")
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map do |f|
22
+ File.basename(f)
23
+ end
24
+ s.require_paths = ['lib']
25
+ end
@@ -0,0 +1,30 @@
1
+ module HledgerForecast
2
+ class Command
3
+ def self.run(args)
4
+ end_date = args[:end_date]
5
+ start_date = args[:start_date]
6
+ forecast = File.read(args[:forecast_file])
7
+ transactions = args[:transactions_file] ? File.read(args[:transactions_file]) : nil
8
+
9
+ puts "[Using default dates: #{start_date} to #{end_date}]" if args[:default_dates]
10
+
11
+ transactions = Generator.create_journal_entries(transactions, forecast, start_date, end_date)
12
+
13
+ output_file = args[:output_file]
14
+ if File.exist?(output_file)
15
+ print "File '#{output_file}' already exists. Overwrite? (y/n): "
16
+ overwrite = gets.chomp.downcase
17
+
18
+ if overwrite == 'y'
19
+ File.write(output_file, transactions)
20
+ puts "File '#{output_file}' has been overwritten."
21
+ else
22
+ puts "Operation aborted. File '#{output_file}' was not overwritten."
23
+ end
24
+ else
25
+ File.write(output_file, transactions)
26
+ puts "File '#{output_file}' has been created."
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,93 @@
1
+ module HledgerForecast
2
+ # Generates journal entries based on a YAML forecast file.
3
+ # on forecast data and optional existing transactions.
4
+ #
5
+ class Generator
6
+ class << self
7
+ attr_accessor :settings
8
+ end
9
+
10
+ self.settings = {}
11
+
12
+ def self.configure_settings(forecast_data)
13
+ @settings[:currency] = Money::Currency.new(forecast_data.fetch('settings', {}).fetch('currency', 'USD'))
14
+ @settings[:show_symbol] = forecast_data.fetch('settings', {}).fetch('show_symbol', true)
15
+ @settings[:sign_before_symbol] = forecast_data.fetch('settings', {}).fetch('sign_before_symbol', true)
16
+ @settings[:thousands_separator] = forecast_data.fetch('settings', {}).fetch('thousands_separator', true)
17
+ end
18
+
19
+ def self.write_transactions(output, date, account, transaction)
20
+ output.concat("#{date} * #{transaction['description']}\n")
21
+ output.concat(" #{transaction['category']} #{transaction['amount']}\n")
22
+ output.concat(" #{account}\n\n")
23
+ end
24
+
25
+ def self.format_transaction(transaction)
26
+ formatted_transaction = transaction.clone
27
+
28
+ formatted_transaction['amount'] =
29
+ Money.from_cents(formatted_transaction['amount'].to_i * 100, @settings[:currency]).format(
30
+ symbol: @settings[:show_symbol],
31
+ sign_before_symbol: @settings[:sign_before_symbol],
32
+ thousands_separator: @settings[:thousands_separator] ? ',' : nil
33
+ )
34
+
35
+ formatted_transaction
36
+ end
37
+
38
+ def self.process_forecast(output_file, forecast_data, type, date)
39
+ forecast_data[type]&.each do |forecast|
40
+ start_date = Date.parse(forecast['start'])
41
+ end_date = forecast['end'] ? Date.parse(forecast['end']) : nil
42
+ account = forecast['account']
43
+
44
+ next if end_date && date > end_date
45
+
46
+ date_matches = case type
47
+ when 'monthly'
48
+ date.day == start_date.day
49
+ when 'quarterly'
50
+ date.day == start_date.day && date.month % 3 == start_date.month % 3
51
+ when 'yearly'
52
+ date.day == start_date.day && date.month == start_date.month
53
+ when 'once'
54
+ date == start_date
55
+ end
56
+
57
+ if date_matches
58
+ forecast['transactions'].each do |transaction|
59
+ end_date = transaction['end'] ? Date.parse(transaction['end']) : nil
60
+
61
+ next unless end_date.nil? || date <= end_date
62
+
63
+ write_transactions(output_file, date, account, format_transaction(transaction))
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def self.create_journal_entries(transactions, forecast, start_date, end_date)
70
+ start_date = Date.parse(start_date)
71
+ end_date = Date.parse(end_date)
72
+ forecast_data = YAML.safe_load(forecast)
73
+
74
+ configure_settings(forecast_data)
75
+
76
+ output = ''
77
+ output.concat(transactions) if transactions
78
+
79
+ date = start_date
80
+
81
+ while date <= end_date
82
+ process_forecast(output, forecast_data, 'monthly', date)
83
+ process_forecast(output, forecast_data, 'quarterly', date)
84
+ process_forecast(output, forecast_data, 'yearly', date)
85
+ process_forecast(output, forecast_data, 'once', date)
86
+
87
+ date = date.next_day
88
+ end
89
+
90
+ output
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,67 @@
1
+ module HledgerForecast
2
+ class Options
3
+ def self.parse_command_line_options(args = ARGV, _stdin = $stdin)
4
+ cli = HighLine.new
5
+ options = {}
6
+
7
+ OptionParser.new do |opts|
8
+ opts.banner = "Usage: Hledger-Forecast [options]"
9
+ opts.separator ""
10
+
11
+ opts.on("-f", "--forecast FILE",
12
+ "The FORECAST yaml file to generate from") do |file|
13
+ options[:forecast_file] = file
14
+ end
15
+
16
+ opts.on("-t", "--transaction FILE",
17
+ "The base TRANSACTIONS file to extend from") do |file|
18
+ options[:transactions_file] = file if file && !file.empty?
19
+ end
20
+
21
+ opts.on("-o", "--output-file FILE",
22
+ "The OUTPUT file to create") do |file|
23
+ options[:output_file] = file
24
+ end
25
+
26
+ opts.on("-s", "--start-date DATE",
27
+ "The date to start generating from (yyyy-mm-dd)") do |a|
28
+ options[:start_date] = a
29
+ end
30
+
31
+ opts.on("-e", "--end-date DATE",
32
+ "The date to start generating to (yyyy-mm-dd)") do |a|
33
+ options[:end_date] = a
34
+ end
35
+
36
+ opts.on_tail("-h", "--help", "Show this message") do
37
+ puts opts
38
+ exit
39
+ end
40
+
41
+ opts.on_tail("--version", "Show version") do
42
+ puts VERSION
43
+ exit
44
+ end
45
+
46
+ opts.parse!(args)
47
+ end
48
+
49
+ options[:forecast_file] = "forecast.yml" unless options[:forecast_file]
50
+ options[:output_file] = "forecast.journal" unless options[:output_file]
51
+
52
+ today = Date.today
53
+
54
+ unless options[:start_date]
55
+ options[:default_dates] = true
56
+ options[:start_date] =
57
+ Date.new(today.year, today.month, 1).next_month.to_s
58
+ end
59
+ unless options[:end_date]
60
+ options[:default_dates] = true
61
+ options[:end_date] = Date.new(today.year + 3, 12, 31).to_s
62
+ end
63
+
64
+ return options
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,3 @@
1
+ module HledgerForecast
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'date'
4
+ require 'highline'
5
+ require 'money'
6
+ require 'optparse'
7
+ require 'yaml'
8
+
9
+ Money.locale_backend = nil
10
+ Money.rounding_mode = BigDecimal::ROUND_HALF_UP
11
+
12
+ require_relative 'hledger_forecast/version'
13
+ require_relative 'hledger_forecast/options'
14
+ require_relative 'hledger_forecast/generator'
15
+ require_relative 'hledger_forecast/command'
@@ -0,0 +1,33 @@
1
+ require_relative '../lib/hledger_forecast'
2
+
3
+ RSpec.describe 'generate' do
4
+ it 'generates a forecast with correct MONTHLY transactions' do
5
+ transactions = File.read('spec/stubs/transactions.journal')
6
+ forecast = File.read('spec/stubs/monthly/forecast_monthly.yml')
7
+
8
+ generated_journal = HledgerForecast::Generator.create_journal_entries(transactions, forecast, '2023-03-01', '2023-05-30')
9
+
10
+ expected_output = File.read('spec/stubs/monthly/output_monthly.journal')
11
+ expect(generated_journal).to eq(expected_output)
12
+ end
13
+
14
+ it 'generates a forecast with correct MONTHLY transactions that have an end date' do
15
+ transactions = File.read('spec/stubs/transactions.journal')
16
+ forecast = File.read('spec/stubs/monthly/forecast_monthly_enddate.yml')
17
+
18
+ generated_journal = HledgerForecast::Generator.create_journal_entries(transactions, forecast, '2023-03-01', '2023-08-30')
19
+
20
+ expected_output = File.read('spec/stubs/monthly/output_monthly_enddate.journal')
21
+ expect(generated_journal).to eq(expected_output)
22
+ end
23
+
24
+ it 'generates a forecast with correct MONTHLY transactions that have an end date, at the top level' do
25
+ transactions = File.read('spec/stubs/transactions.journal')
26
+ forecast = File.read('spec/stubs/monthly/forecast_monthly_enddate_top.yml')
27
+
28
+ generated_journal = HledgerForecast::Generator.create_journal_entries(transactions, forecast, '2023-03-01', '2023-08-30')
29
+
30
+ expected_output = File.read('spec/stubs/monthly/output_monthly_enddate_top.journal')
31
+ expect(generated_journal).to eq(expected_output)
32
+ end
33
+ end
data/spec/once_spec.rb ADDED
@@ -0,0 +1,13 @@
1
+ require_relative '../lib/hledger_forecast'
2
+
3
+ RSpec.describe 'generate' do
4
+ it 'generates a forecast with correct ONCE transactions' do
5
+ transactions = File.read('spec/stubs/transactions.journal')
6
+ forecast = File.read('spec/stubs/once/forecast_once.yml')
7
+
8
+ generated_journal = HledgerForecast::Generator.create_journal_entries(transactions, forecast, '2023-03-01', '2024-04-30')
9
+
10
+ expected_output = File.read('spec/stubs/once/output_once.journal')
11
+ expect(generated_journal).to eq(expected_output)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require_relative '../lib/hledger_forecast'
2
+
3
+ RSpec.describe 'generate' do
4
+ it 'generates a forecast with correct QUARTERLY transactions' do
5
+ transactions = File.read('spec/stubs/transactions.journal')
6
+ forecast = File.read('spec/stubs/quarterly/forecast_quarterly.yml')
7
+
8
+ generated_journal = HledgerForecast::Generator.create_journal_entries(transactions, forecast, '2023-03-01', '2023-10-30')
9
+
10
+ expected_output = File.read('spec/stubs/quarterly/output_quarterly.journal')
11
+ expect(generated_journal).to eq(expected_output)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ monthly:
2
+ - account: "[Assets:Bank]"
3
+ start: "2023-03-01"
4
+ transactions:
5
+ - amount: 2000
6
+ category: "[Expenses:Mortgage]"
7
+ description: Mortgage
8
+ - amount: 100
9
+ category: "[Expenses:Food]"
10
+ description: Food
11
+
12
+ settings:
13
+ currency: GBP
@@ -0,0 +1,14 @@
1
+ settings:
2
+ currency: GBP
3
+
4
+ monthly:
5
+ - start: "2023-03-01"
6
+ account: "[Assets:Bank]"
7
+ transactions:
8
+ - description: Mortgage
9
+ end: "2023-06-01"
10
+ category: "[Expenses:Mortgage]"
11
+ amount: 2,000.00
12
+ - description: Food
13
+ category: "[Expenses:Food]"
14
+ amount: 100.00
@@ -0,0 +1,14 @@
1
+ settings:
2
+ currency: GBP
3
+
4
+ monthly:
5
+ - start: "2023-03-01"
6
+ end: "2023-06-01"
7
+ account: "[Assets:Bank]"
8
+ transactions:
9
+ - description: Mortgage
10
+ category: "[Expenses:Mortgage]"
11
+ amount: 2,000.00
12
+ - description: Food
13
+ category: "[Expenses:Food]"
14
+ amount: 100.00
@@ -0,0 +1,11 @@
1
+ settings:
2
+ currency: GBP
3
+
4
+ monthly:
5
+ - start: "2023-03-01"
6
+ account: "[Assets:Bank]"
7
+ transactions:
8
+ - description: Mortgage
9
+ category: "[Expenses:Mortgage]"
10
+ amount: 2,000.00
11
+ modifier: 1.1x
@@ -0,0 +1,10 @@
1
+ settings:
2
+ currency: GBP
3
+
4
+ once:
5
+ - start: "2023-07-01"
6
+ account: "[Assets:Bank]"
7
+ transactions:
8
+ - description: New Kitchen
9
+ category: "[Expense:House]"
10
+ amount: 5,000.00
@@ -0,0 +1,10 @@
1
+ settings:
2
+ currency: GBP
3
+
4
+ quarterly:
5
+ - start: "2023-04-01"
6
+ account: "[Assets:Bank]"
7
+ transactions:
8
+ - description: Bonus
9
+ category: "[Income:Bonus]"
10
+ amount: -1,000.00
@@ -0,0 +1,8 @@
1
+ 2023-02-01 * Opening balance
2
+ Assets:Bank £1,000.00
3
+ Equity:Opening balance
4
+
5
+ 2023-02-05 * Mortgage payment
6
+ Expenses:Mortgage £1,500.00
7
+ Assets:Bank
8
+
@@ -0,0 +1,10 @@
1
+ settings:
2
+ currency: GBP
3
+
4
+ yearly:
5
+ - start: "2023-04-01"
6
+ account: "[Assets:Bank]"
7
+ transactions:
8
+ - description: Bonus
9
+ category: "[Income:Bonus]"
10
+ amount: -3,000.00
@@ -0,0 +1,13 @@
1
+ require_relative '../lib/hledger_forecast'
2
+
3
+ RSpec.describe 'generate' do
4
+ it 'generates a forecast with correct YEARLY transactions' do
5
+ transactions = File.read('spec/stubs/transactions.journal')
6
+ forecast = File.read('spec/stubs/yearly/forecast_yearly.yml')
7
+
8
+ generated_journal = HledgerForecast::Generator.create_journal_entries(transactions, forecast, '2023-03-01', '2024-04-30')
9
+
10
+ expected_output = File.read('spec/stubs/yearly/output_yearly.journal')
11
+ expect(generated_journal).to eq(expected_output)
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hledger-forecast
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Oli Morris
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-04-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: highline
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.1.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: money
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 6.16.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 6.16.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.12'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.12'
55
+ description: Uses a YAML file to generate monthly, quarterly, yearly and one-off transactions
56
+ for better forecasting in Hledger
57
+ email: olimorris@users.noreply.github.com
58
+ executables:
59
+ - hledger-forecast
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".github/workflows/ci.yml"
64
+ - ".gitignore"
65
+ - ".rubocop.yml"
66
+ - ".ruby-version"
67
+ - Gemfile
68
+ - Gemfile.lock
69
+ - LICENSE
70
+ - README.md
71
+ - bin/hledger-forecast
72
+ - example.yaml
73
+ - hledger-forecast.gemspec
74
+ - lib/hledger_forecast.rb
75
+ - lib/hledger_forecast/command.rb
76
+ - lib/hledger_forecast/generator.rb
77
+ - lib/hledger_forecast/options.rb
78
+ - lib/hledger_forecast/version.rb
79
+ - spec/monthly_spec.rb
80
+ - spec/once_spec.rb
81
+ - spec/quarterly_spec.rb
82
+ - spec/stubs/monthly/forecast_monthly.yml
83
+ - spec/stubs/monthly/forecast_monthly_enddate.yml
84
+ - spec/stubs/monthly/forecast_monthly_enddate_top.yml
85
+ - spec/stubs/monthly/forecast_monthly_modifier.yml
86
+ - spec/stubs/once/forecast_once.yml
87
+ - spec/stubs/quarterly/forecast_quarterly.yml
88
+ - spec/stubs/transactions.journal
89
+ - spec/stubs/yearly/forecast_yearly.yml
90
+ - spec/yearly_spec.rb
91
+ homepage: https://github.com/olimorris/hledger-forecast
92
+ licenses:
93
+ - MIT
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 3.0.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.4.10
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Utility to generate forecasts in Hledger
114
+ test_files:
115
+ - spec/monthly_spec.rb
116
+ - spec/once_spec.rb
117
+ - spec/quarterly_spec.rb
118
+ - spec/stubs/monthly/forecast_monthly.yml
119
+ - spec/stubs/monthly/forecast_monthly_enddate.yml
120
+ - spec/stubs/monthly/forecast_monthly_enddate_top.yml
121
+ - spec/stubs/monthly/forecast_monthly_modifier.yml
122
+ - spec/stubs/once/forecast_once.yml
123
+ - spec/stubs/quarterly/forecast_quarterly.yml
124
+ - spec/stubs/transactions.journal
125
+ - spec/stubs/yearly/forecast_yearly.yml
126
+ - spec/yearly_spec.rb