hledger-forecast 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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