hledger-forecast 0.1.4 → 0.1.6
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 +4 -4
- data/Gemfile +1 -0
- data/README.md +12 -2
- data/bin/hledger-forecast +1 -1
- data/example.yml +10 -2
- data/hledger-forecast.gemspec +1 -0
- data/lib/hledger_forecast/{command.rb → cli.rb} +3 -1
- data/lib/hledger_forecast/generator.rb +4 -1
- data/lib/hledger_forecast/options.rb +5 -0
- data/lib/hledger_forecast/summarize.rb +68 -0
- data/lib/hledger_forecast/version.rb +1 -1
- data/lib/hledger_forecast.rb +3 -1
- data/spec/half-yearly_spec.rb +13 -0
- data/spec/stubs/half-yearly/forecast_half-yearly.yml +10 -0
- data/spec/stubs/half-yearly/output_half-yearly.journal +20 -0
- metadata +23 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 60e8bd939c2bb57d5d1fdf01d39d4fd7bc1cd59886a189b8b0d8a8fe7a7b6640
|
4
|
+
data.tar.gz: e752946a04d49b531ca303a8832d11a686d610392ce29d5a7d96c5aae2658dee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7b44e5ceebc727be1a578a1210741fc5bbb9ec5e6d5a13feb36012bd35fb253173594ef0a35fb8448e1da697575ecdc97797473f7d5751db032a5065cce2bf8
|
7
|
+
data.tar.gz: c341817f928c9120662319a6ef9c1434d22b8872e685283b162ad4043efd9a0d1366eeeed4a58196e54708689833fa24af1b6b9e92dc59b8a75a98ad77810390
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -37,6 +37,7 @@ Running `hledger-forecast -h` shows the available options:
|
|
37
37
|
-s, --start-date DATE The date to start generating from (yyyy-mm-dd)
|
38
38
|
-e, --end-date DATE The date to start generating to (yyyy-mm-dd)
|
39
39
|
--force Force an overwrite of the output file
|
40
|
+
--summarize Summarize the forecast file and output to the terminal
|
40
41
|
-h, --help Show this message
|
41
42
|
--version Show version
|
42
43
|
|
@@ -73,16 +74,17 @@ Let's examine what's going on in this config file:
|
|
73
74
|
|
74
75
|
- 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
|
75
76
|
- 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
|
76
|
-
- Notice we're also using [virtual postings](https://hledger.org/1.29/hledger.html#virtual-postings) (designated by the brackets)
|
77
|
+
- Notice we're also using [virtual postings](https://hledger.org/1.29/hledger.html#virtual-postings) (designated by the brackets). This makes it easy to filter them out with the `-R` or `--real` option in Hledger
|
77
78
|
- We also have not specified a currency; the default (`USD`) will be used
|
78
79
|
|
79
80
|
### Extending the config file
|
80
81
|
|
81
82
|
#### Periods
|
82
83
|
|
83
|
-
If you'd like to add quarterly, yearly or one-off transactions, use the following keys:
|
84
|
+
If you'd like to add quarterly, half-yearly, yearly or one-off transactions, use the following keys:
|
84
85
|
|
85
86
|
- `quarterly`
|
87
|
+
- `half-yearly`
|
86
88
|
- `yearly`
|
87
89
|
- `once`
|
88
90
|
|
@@ -112,6 +114,14 @@ settings:
|
|
112
114
|
thousands_separator: true # Separate thousands with a comma?
|
113
115
|
```
|
114
116
|
|
117
|
+
### Summarizing the config file
|
118
|
+
|
119
|
+
As your config file grows, it can be helpful to sum up the total amounts and output them in the CLI. This can be achieved by:
|
120
|
+
|
121
|
+
hledger-forecast -f forecast.yml --summarize
|
122
|
+
|
123
|
+
where `forecast.yml` is the config file to sum up.
|
124
|
+
|
115
125
|
## :brain: Rationale
|
116
126
|
|
117
127
|
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!
|
data/bin/hledger-forecast
CHANGED
data/example.yml
CHANGED
@@ -17,6 +17,14 @@ quarterly:
|
|
17
17
|
category: "[Income:Bonus]"
|
18
18
|
description: Bonus
|
19
19
|
|
20
|
+
half-yearly:
|
21
|
+
- account: "[Assets:Bank]"
|
22
|
+
date: "2023-04-01"
|
23
|
+
transactions:
|
24
|
+
- amount: 500
|
25
|
+
category: "[Expenses:Holiday]"
|
26
|
+
description: Holiday
|
27
|
+
|
20
28
|
yearly:
|
21
29
|
- account: "[Assets:Bank]"
|
22
30
|
date: "2023-04-01"
|
@@ -35,6 +43,6 @@ once:
|
|
35
43
|
|
36
44
|
settings:
|
37
45
|
currency: GBP
|
38
|
-
show_symbol:
|
46
|
+
show_symbol: true
|
39
47
|
sign_before_symbol: true
|
40
|
-
thousands_separator:
|
48
|
+
thousands_separator: true
|
data/hledger-forecast.gemspec
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
module HledgerForecast
|
2
|
-
class
|
2
|
+
class CLI
|
3
3
|
def self.run(args)
|
4
4
|
end_date = args[:end_date]
|
5
5
|
start_date = args[:start_date]
|
6
6
|
forecast = File.read(args[:forecast_file])
|
7
7
|
transactions = args[:transactions_file] ? File.read(args[:transactions_file]) : nil
|
8
8
|
|
9
|
+
return HledgerForecast::Summarize.generate(forecast) if args[:summarize]
|
10
|
+
|
9
11
|
puts "[Using default dates: #{start_date} to #{end_date}]" if args[:default_dates]
|
10
12
|
|
11
13
|
transactions = Generator.create_journal_entries(transactions, forecast, start_date, end_date)
|
@@ -29,7 +29,7 @@ module HledgerForecast
|
|
29
29
|
Money.from_cents(formatted_transaction['amount'].to_i * 100, @settings[:currency]).format(
|
30
30
|
symbol: @settings[:show_symbol],
|
31
31
|
sign_before_symbol: @settings[:sign_before_symbol],
|
32
|
-
thousands_separator: @settings[:thousands_separator] ? ',' : nil
|
32
|
+
thousands_separator: @settings[:thousands_separator] ? ',' : nil,
|
33
33
|
)
|
34
34
|
|
35
35
|
formatted_transaction
|
@@ -48,6 +48,8 @@ module HledgerForecast
|
|
48
48
|
date.day == start_date.day
|
49
49
|
when 'quarterly'
|
50
50
|
date.day == start_date.day && date.month % 3 == start_date.month % 3
|
51
|
+
when 'half-yearly'
|
52
|
+
date.day == start_date.day && (date.month - start_date.month) % 6 == 0
|
51
53
|
when 'yearly'
|
52
54
|
date.day == start_date.day && date.month == start_date.month
|
53
55
|
when 'once'
|
@@ -81,6 +83,7 @@ module HledgerForecast
|
|
81
83
|
while date <= end_date
|
82
84
|
process_forecast(output, forecast_data, 'monthly', date)
|
83
85
|
process_forecast(output, forecast_data, 'quarterly', date)
|
86
|
+
process_forecast(output, forecast_data, 'half-yearly', date)
|
84
87
|
process_forecast(output, forecast_data, 'yearly', date)
|
85
88
|
process_forecast(output, forecast_data, 'once', date)
|
86
89
|
|
@@ -32,6 +32,11 @@ module HledgerForecast
|
|
32
32
|
options[:end_date] = a
|
33
33
|
end
|
34
34
|
|
35
|
+
opts.on("--summarize",
|
36
|
+
"Summarize the forecast file and output to the terminal") do |a|
|
37
|
+
options[:summarize] = a
|
38
|
+
end
|
39
|
+
|
35
40
|
opts.on("--force",
|
36
41
|
"Force an overwrite of the output file") do |a|
|
37
42
|
options[:force] = a
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module HledgerForecast
|
2
|
+
# Summarise a forecast YAML file and output it to the CLI
|
3
|
+
class Summarize
|
4
|
+
def self.sum_transactions(forecast_data, period)
|
5
|
+
category_totals = Hash.new(0)
|
6
|
+
forecast_data[period]&.each do |entry|
|
7
|
+
entry['transactions'].each do |transaction|
|
8
|
+
category_totals[transaction['category']] += transaction['amount']
|
9
|
+
end
|
10
|
+
end
|
11
|
+
category_totals
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.print_category_totals(period, category_totals, generator)
|
15
|
+
puts "#{period.capitalize}:"
|
16
|
+
period_total = 0
|
17
|
+
|
18
|
+
category_totals.each do |category, amount|
|
19
|
+
formatted_amount = generator.format_transaction({ 'amount' => amount })['amount']
|
20
|
+
formatted_amount = amount.to_i < 0 ? formatted_amount.red : formatted_amount.green
|
21
|
+
puts " #{category.ljust(40)}#{formatted_amount}"
|
22
|
+
period_total += amount
|
23
|
+
end
|
24
|
+
|
25
|
+
formatted_period_total = generator.format_transaction({ 'amount' => period_total })['amount']
|
26
|
+
formatted_period_total = period_total.to_i < 0 ? formatted_period_total.red : formatted_period_total.green
|
27
|
+
puts " TOTAL".ljust(42) + formatted_period_total
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.sum_all_periods(forecast_data)
|
31
|
+
periods = %w[monthly quarterly half-yearly yearly once]
|
32
|
+
total = {}
|
33
|
+
grand_total = 0
|
34
|
+
|
35
|
+
periods.each do |period|
|
36
|
+
total[period] = sum_transactions(forecast_data, period)
|
37
|
+
grand_total += total[period]
|
38
|
+
end
|
39
|
+
|
40
|
+
total['total'] = grand_total
|
41
|
+
total
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.generate(forecast)
|
45
|
+
forecast_data = YAML.safe_load(forecast)
|
46
|
+
|
47
|
+
category_totals_by_period = {}
|
48
|
+
%w[monthly quarterly half-yearly yearly once].each do |period|
|
49
|
+
category_totals_by_period[period] = sum_transactions(forecast_data, period)
|
50
|
+
end
|
51
|
+
|
52
|
+
grand_total = category_totals_by_period.values.map(&:values).flatten.sum
|
53
|
+
|
54
|
+
generator = HledgerForecast::Generator
|
55
|
+
generator.configure_settings(forecast_data)
|
56
|
+
|
57
|
+
puts
|
58
|
+
category_totals_by_period.each do |period, category_totals|
|
59
|
+
print_category_totals(period, category_totals, generator)
|
60
|
+
puts
|
61
|
+
end
|
62
|
+
|
63
|
+
formatted_grand_total = generator.format_transaction({ 'amount' => grand_total })['amount']
|
64
|
+
formatted_grand_total = grand_total.to_i < 0 ? formatted_grand_total.red : formatted_grand_total.green
|
65
|
+
puts "TOTAL:".ljust(42) + formatted_grand_total
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/hledger_forecast.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require 'colorize'
|
3
4
|
require 'date'
|
4
5
|
require 'highline'
|
5
6
|
require 'money'
|
@@ -12,4 +13,5 @@ Money.rounding_mode = BigDecimal::ROUND_HALF_UP
|
|
12
13
|
require_relative 'hledger_forecast/version'
|
13
14
|
require_relative 'hledger_forecast/options'
|
14
15
|
require_relative 'hledger_forecast/generator'
|
15
|
-
require_relative 'hledger_forecast/
|
16
|
+
require_relative 'hledger_forecast/summarize'
|
17
|
+
require_relative 'hledger_forecast/CLI'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require_relative '../lib/hledger_forecast'
|
2
|
+
|
3
|
+
RSpec.describe 'generate' do
|
4
|
+
it 'generates a forecast with correct HALF-YEARLY transactions' do
|
5
|
+
transactions = File.read('spec/stubs/transactions.journal')
|
6
|
+
forecast = File.read('spec/stubs/half-yearly/forecast_half-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/half-yearly/output_half-yearly.journal')
|
11
|
+
expect(generated_journal).to eq(expected_output)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,20 @@
|
|
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
|
+
|
9
|
+
2023-04-01 * Holiday
|
10
|
+
[Expenses:Holiday] £500.00
|
11
|
+
[Assets:Bank]
|
12
|
+
|
13
|
+
2023-10-01 * Holiday
|
14
|
+
[Expenses:Holiday] £500.00
|
15
|
+
[Assets:Bank]
|
16
|
+
|
17
|
+
2024-04-01 * Holiday
|
18
|
+
[Expenses:Holiday] £500.00
|
19
|
+
[Assets:Bank]
|
20
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hledger-forecast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oli Morris
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: 6.16.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: colorize
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.8.1
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.8.1
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rspec
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -70,14 +84,18 @@ files:
|
|
70
84
|
- example.yml
|
71
85
|
- hledger-forecast.gemspec
|
72
86
|
- lib/hledger_forecast.rb
|
73
|
-
- lib/hledger_forecast/
|
87
|
+
- lib/hledger_forecast/cli.rb
|
74
88
|
- lib/hledger_forecast/generator.rb
|
75
89
|
- lib/hledger_forecast/options.rb
|
90
|
+
- lib/hledger_forecast/summarize.rb
|
76
91
|
- lib/hledger_forecast/version.rb
|
77
92
|
- spec/command_spec.rb
|
93
|
+
- spec/half-yearly_spec.rb
|
78
94
|
- spec/monthly_spec.rb
|
79
95
|
- spec/once_spec.rb
|
80
96
|
- spec/quarterly_spec.rb
|
97
|
+
- spec/stubs/half-yearly/forecast_half-yearly.yml
|
98
|
+
- spec/stubs/half-yearly/output_half-yearly.journal
|
81
99
|
- spec/stubs/monthly/forecast_monthly.yml
|
82
100
|
- spec/stubs/monthly/forecast_monthly_enddate.yml
|
83
101
|
- spec/stubs/monthly/forecast_monthly_enddate_top.yml
|
@@ -119,9 +137,12 @@ specification_version: 4
|
|
119
137
|
summary: Utility to generate forecasts in Hledger
|
120
138
|
test_files:
|
121
139
|
- spec/command_spec.rb
|
140
|
+
- spec/half-yearly_spec.rb
|
122
141
|
- spec/monthly_spec.rb
|
123
142
|
- spec/once_spec.rb
|
124
143
|
- spec/quarterly_spec.rb
|
144
|
+
- spec/stubs/half-yearly/forecast_half-yearly.yml
|
145
|
+
- spec/stubs/half-yearly/output_half-yearly.journal
|
125
146
|
- spec/stubs/monthly/forecast_monthly.yml
|
126
147
|
- spec/stubs/monthly/forecast_monthly_enddate.yml
|
127
148
|
- spec/stubs/monthly/forecast_monthly_enddate_top.yml
|