hledger-forecast 1.5.1 → 2.0.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 +4 -4
- data/.github/workflows/release.yml +22 -0
- data/.github/workflows/{ci.yml → test.yml} +5 -5
- data/.rubocop.yml +17 -17
- data/README.md +8 -172
- data/example.journal +14 -14
- data/hledger-forecast.gemspec +1 -1
- data/lib/hledger_forecast/calculator.rb +5 -1
- data/lib/hledger_forecast/cli.rb +5 -18
- data/lib/hledger_forecast/generator.rb +52 -35
- data/lib/hledger_forecast/settings.rb +42 -27
- data/lib/hledger_forecast/summarizer.rb +28 -62
- data/lib/hledger_forecast/summarizer_formatter.rb +12 -3
- data/lib/hledger_forecast/transactions/default.rb +28 -57
- data/lib/hledger_forecast/transactions/trackers.rb +34 -40
- data/lib/hledger_forecast/utilities.rb +14 -0
- data/lib/hledger_forecast/version.rb +1 -1
- data/lib/hledger_forecast.rb +1 -2
- data/spec/cli_spec.rb +3 -12
- data/spec/computed_amounts_spec.rb +11 -22
- data/spec/custom_spec.rb +15 -35
- data/spec/half-yearly_spec.rb +6 -13
- data/spec/monthly_end_date_spec.rb +8 -18
- data/spec/monthly_end_date_transaction_spec.rb +20 -45
- data/spec/monthly_spec.rb +11 -28
- data/spec/once_spec.rb +6 -13
- data/spec/quarterly_spec.rb +5 -12
- data/spec/summarizer_spec.rb +11 -42
- data/spec/track_spec.rb +19 -49
- data/spec/verbose_output_spec.rb +3 -3
- data/spec/yearly_spec.rb +5 -12
- metadata +10 -18
- data/example.yml +0 -98
- data/lib/hledger_forecast/csv_parser.rb +0 -106
- data/spec/csv_and_yml_comparison_spec.rb +0 -32
- data/spec/csv_parser_spec.rb +0 -110
- data/spec/modifier_spec.rb +0 -102
- data/spec/stubs/forecast.yml +0 -19
data/example.yml
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
monthly:
|
2
|
-
- account: "Assets:Bank"
|
3
|
-
from: "2023-03-01"
|
4
|
-
transactions:
|
5
|
-
- amount: -3500
|
6
|
-
category: "Income:Salary"
|
7
|
-
description: Salary
|
8
|
-
- amount: 2000
|
9
|
-
category: "Expenses:Mortgage"
|
10
|
-
description: Mortgage
|
11
|
-
to: "2025-01-01"
|
12
|
-
- amount: 175
|
13
|
-
category: "Expenses:Bills"
|
14
|
-
description: Bills
|
15
|
-
- amount: 500
|
16
|
-
category: "Expenses:Food"
|
17
|
-
description: Food
|
18
|
-
modifiers:
|
19
|
-
- amount: 0.02
|
20
|
-
description: "Inflation"
|
21
|
-
from: "2024-01-01"
|
22
|
-
to: "2024-12-31"
|
23
|
-
- amount: 0.05
|
24
|
-
description: "Inflation"
|
25
|
-
from: "2025-01-01"
|
26
|
-
to: "2025-12-31"
|
27
|
-
- amount: "=5000/24"
|
28
|
-
category: "Expenses:House"
|
29
|
-
description: New Kitchen
|
30
|
-
- amount: 125
|
31
|
-
category: "Expenses:Holiday"
|
32
|
-
description: Holiday
|
33
|
-
to: "=12"
|
34
|
-
- account: "Assets:Bank"
|
35
|
-
from: "2023-03-01"
|
36
|
-
to: "2025-01-01"
|
37
|
-
transactions:
|
38
|
-
- amount: 300
|
39
|
-
category: "Assets:Savings"
|
40
|
-
description: "Rainy day fund"
|
41
|
-
- account: "Assets:Pension"
|
42
|
-
from: "2024-01-01"
|
43
|
-
transactions:
|
44
|
-
- amount: -500
|
45
|
-
category: "Income:Pension"
|
46
|
-
description: Pension draw down
|
47
|
-
|
48
|
-
quarterly:
|
49
|
-
- account: "Assets:Bank"
|
50
|
-
from: "2023-04-01"
|
51
|
-
transactions:
|
52
|
-
- amount: -1000.00
|
53
|
-
category: "Income:Bonus"
|
54
|
-
description: Quarterly bonus
|
55
|
-
|
56
|
-
half-yearly:
|
57
|
-
- account: "Assets:Bank"
|
58
|
-
from: "2023-04-01"
|
59
|
-
transactions:
|
60
|
-
- amount: 500
|
61
|
-
category: "Expenses:Holiday"
|
62
|
-
description: Top up holiday funds
|
63
|
-
|
64
|
-
yearly:
|
65
|
-
- account: "Assets:Bank"
|
66
|
-
from: "2023-04-01"
|
67
|
-
transactions:
|
68
|
-
- amount: -2000.00
|
69
|
-
category: "Income:Bonus"
|
70
|
-
description: Annual Bonus
|
71
|
-
|
72
|
-
once:
|
73
|
-
- account: "Assets:Bank"
|
74
|
-
from: "2023-03-05"
|
75
|
-
transactions:
|
76
|
-
- amount: -3000
|
77
|
-
category: "Expenses:Shopping"
|
78
|
-
description: Refund for that damn laptop
|
79
|
-
summary_exclude: true
|
80
|
-
track: true
|
81
|
-
|
82
|
-
custom:
|
83
|
-
- account: "Assets:Bank"
|
84
|
-
from: "2023-03-01"
|
85
|
-
transactions:
|
86
|
-
- amount: 80
|
87
|
-
category: "Expenses:Personal Care"
|
88
|
-
description: Hair and beauty
|
89
|
-
frequency: "every 2 weeks"
|
90
|
-
roll-up: 26
|
91
|
-
- amount: 30
|
92
|
-
category: "Expenses:General Expenses"
|
93
|
-
description: Misc expenses
|
94
|
-
frequency: "every 5 weeks"
|
95
|
-
roll-up: 10.4
|
96
|
-
|
97
|
-
settings:
|
98
|
-
currency: USD
|
@@ -1,106 +0,0 @@
|
|
1
|
-
module HledgerForecast
|
2
|
-
# Formats various items used throughout the application
|
3
|
-
class CSVParser
|
4
|
-
def self.parse(csv_data, cli_options = nil)
|
5
|
-
new.parse(csv_data, cli_options)
|
6
|
-
end
|
7
|
-
|
8
|
-
def parse(csv_data, _cli_options)
|
9
|
-
csv_data = CSV.parse(csv_data, headers: true)
|
10
|
-
yaml_data = {}
|
11
|
-
group_by_type(csv_data, yaml_data)
|
12
|
-
yaml_data.to_yaml
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def group_by_type(csv_data, yaml_data)
|
18
|
-
csv_data.group_by { |row| row['type'] }.each do |type, rows|
|
19
|
-
if type == 'settings'
|
20
|
-
handle_settings(rows, yaml_data)
|
21
|
-
else
|
22
|
-
yaml_data[type] ||= []
|
23
|
-
group_by_account_and_from(rows, yaml_data[type], type)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def handle_settings(rows, yaml_data)
|
29
|
-
yaml_data['settings'] ||= {}
|
30
|
-
rows.each do |row|
|
31
|
-
yaml_data['settings'][row['frequency']] = cast_to_proper_type(row['account'])
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def group_by_account_and_from(rows, yaml_rows, type)
|
36
|
-
rows.group_by { |row| [row['account'], row['from']] }.each do |(account, from), transactions|
|
37
|
-
yaml_rows << if type == 'custom'
|
38
|
-
build_custom_transaction(account, from, transactions)
|
39
|
-
else
|
40
|
-
build_transaction(account, from, transactions)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def build_transaction(account, from, transactions)
|
46
|
-
transaction = {
|
47
|
-
'account' => account,
|
48
|
-
'from' => Date.parse(from).strftime('%Y-%m-%d'),
|
49
|
-
'transactions' => []
|
50
|
-
}
|
51
|
-
|
52
|
-
transactions.each do |row|
|
53
|
-
transaction['transactions'] << build_transaction_data(row)
|
54
|
-
end
|
55
|
-
|
56
|
-
transaction
|
57
|
-
end
|
58
|
-
|
59
|
-
def build_custom_transaction(account, from, transactions)
|
60
|
-
transaction = {
|
61
|
-
'account' => account,
|
62
|
-
'from' => Date.parse(from).strftime('%Y-%m-%d'),
|
63
|
-
'transactions' => []
|
64
|
-
}
|
65
|
-
|
66
|
-
transactions.each do |row|
|
67
|
-
transaction_data = build_transaction_data(row)
|
68
|
-
transaction_data['frequency'] = row['frequency']
|
69
|
-
transaction_data['roll-up'] = row['roll-up'].to_f if row['roll-up']
|
70
|
-
transaction['transactions'] << transaction_data
|
71
|
-
end
|
72
|
-
|
73
|
-
transaction
|
74
|
-
end
|
75
|
-
|
76
|
-
def build_transaction_data(row)
|
77
|
-
transaction_data = {
|
78
|
-
'amount' => row['amount'].start_with?("=") ? row['amount'].to_s : row['amount'].to_f,
|
79
|
-
'category' => row['category'],
|
80
|
-
'description' => row['description']
|
81
|
-
}
|
82
|
-
|
83
|
-
if row['to']
|
84
|
-
transaction_data['to'] = if row['to'].start_with?("=")
|
85
|
-
row['to']
|
86
|
-
else
|
87
|
-
Date.parse(row['to']).strftime('%Y-%m-%d')
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
transaction_data['summary_exclude'] = true if row['summary_exclude'] && row['summary_exclude'].downcase == "true"
|
92
|
-
transaction_data['track'] = true if row['track'] && row['track'].downcase == "true"
|
93
|
-
|
94
|
-
transaction_data
|
95
|
-
end
|
96
|
-
|
97
|
-
def cast_to_proper_type(str)
|
98
|
-
case str.downcase
|
99
|
-
when 'true', 'false'
|
100
|
-
str.downcase == 'true'
|
101
|
-
else
|
102
|
-
str
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
require_relative '../lib/hledger_forecast'
|
2
|
-
|
3
|
-
RSpec.describe 'CSV and yml outputs' do
|
4
|
-
it 'should return the same value when ran through hledger' do
|
5
|
-
generated_journal = './test_output.journal'
|
6
|
-
File.delete(generated_journal) if File.exist?(generated_journal)
|
7
|
-
|
8
|
-
system("./bin/hledger-forecast generate -f example.csv -o ./test_output.journal -t ./spec/stubs/transactions_not_found.journal --force")
|
9
|
-
csv_output = `hledger -f ./test_output.journal --forecast bal -b=2023-01 -e=2023-06`
|
10
|
-
|
11
|
-
system("./bin/hledger-forecast generate -f example.yml -o ./test_output.journal -t ./spec/stubs/transactions_not_found.journal --force")
|
12
|
-
yml_output = `hledger -f ./test_output.journal --forecast bal -b=2023-01 -e=2023-06`
|
13
|
-
|
14
|
-
expect(csv_output).to eq(yml_output)
|
15
|
-
end
|
16
|
-
|
17
|
-
# it 'check that it can fail!' do
|
18
|
-
# generated_journal = './test_output.journal'
|
19
|
-
# File.delete(generated_journal) if File.exist?(generated_journal)
|
20
|
-
#
|
21
|
-
# system("./bin/hledger-forecast generate -f ./spec/stubs/csv_and_yml/forecast.csv -o ./test_output.journal --force > /dev/null 2>&1")
|
22
|
-
#
|
23
|
-
# ### CHANGE DATE!!!!!!!!!!!!!!!!
|
24
|
-
# csv_output = `hledger -f ./test_output.journal --forecast bal -b=2023-01 -e=2023-03`
|
25
|
-
# ### CHANGE DATE!!!!!!!!!!!!!!!!
|
26
|
-
#
|
27
|
-
# system("./bin/hledger-forecast generate -f ./spec/stubs/csv_and_yml/forecast.yml -o ./test_output.journal --force > /dev/null 2>&1")
|
28
|
-
# yml_output = `hledger -f ./test_output.journal --forecast bal -b=2023-01 -e=2023-06`
|
29
|
-
#
|
30
|
-
# expect(csv_output).not_to eq(yml_output)
|
31
|
-
# end
|
32
|
-
end
|
data/spec/csv_parser_spec.rb
DELETED
@@ -1,110 +0,0 @@
|
|
1
|
-
require_relative '../lib/hledger_forecast'
|
2
|
-
|
3
|
-
input = <<~CSV
|
4
|
-
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
|
5
|
-
monthly,,Assets:Bank,01/03/2023,,Salary,Income:Salary,-3500,,,
|
6
|
-
monthly,,Assets:Bank,01/03/2023,01/01/2025,Mortgage,Expenses:Mortgage,2000,,,
|
7
|
-
monthly,,Assets:Bank,01/03/2023,,Bills,Expenses:Bills,175,,,
|
8
|
-
monthly,,Assets:Bank,01/03/2023,,Food,Expenses:Food,500,,,
|
9
|
-
monthly,,Assets:Bank,01/03/2023,,New Kitchen,Expenses:House,=5000/24,,,
|
10
|
-
monthly,,Assets:Bank,01/03/2023,=12,Holiday,Expenses:Holiday,125,,,
|
11
|
-
monthly,,Assets:Bank,01/03/2023,01/01/2025,Rainy day fund,Assets:Savings,300,,,
|
12
|
-
monthly,,Assets:Pension,01/01/2024,,Pension draw down,Income:Pension,-500,,,
|
13
|
-
quarterly,,Assets:Bank,01/04/2023,,Quarterly bonus,Income:Bonus,-1000,,,
|
14
|
-
half-yearly,,Assets:Bank,01/04/2023,,Top up holiday funds,Expenses:Holiday,500,,,
|
15
|
-
yearly,,Assets:Bank,01/04/2023,,Annual bonus,Income:Bonus,-2000,,,
|
16
|
-
once,,Assets:Bank,05/03/2023,,Refund for that damn laptop,Expenses:Shopping,-3000,,TRUE,TRUE
|
17
|
-
custom,every 2 weeks,Assets:Bank,01/03/2023,,Hair and beauty,Expenses:Personal Care,80,26,,
|
18
|
-
settings,currency,USD,,,,,,,,
|
19
|
-
settings,show_symbol,TRUE,,,,,,,,
|
20
|
-
settings,thousands_separator,TRUE,,,,,,,,
|
21
|
-
CSV
|
22
|
-
|
23
|
-
output = <<~YAML
|
24
|
-
---
|
25
|
-
monthly:
|
26
|
-
- account: Assets:Bank
|
27
|
-
from: '2023-03-01'
|
28
|
-
transactions:
|
29
|
-
- amount: -3500.0
|
30
|
-
category: Income:Salary
|
31
|
-
description: Salary
|
32
|
-
- amount: 2000.0
|
33
|
-
category: Expenses:Mortgage
|
34
|
-
description: Mortgage
|
35
|
-
to: '2025-01-01'
|
36
|
-
- amount: 175.0
|
37
|
-
category: Expenses:Bills
|
38
|
-
description: Bills
|
39
|
-
- amount: 500.0
|
40
|
-
category: Expenses:Food
|
41
|
-
description: Food
|
42
|
-
- amount: "=5000/24"
|
43
|
-
category: Expenses:House
|
44
|
-
description: New Kitchen
|
45
|
-
- amount: 125.0
|
46
|
-
category: Expenses:Holiday
|
47
|
-
description: Holiday
|
48
|
-
to: "=12"
|
49
|
-
- amount: 300.0
|
50
|
-
category: Assets:Savings
|
51
|
-
description: Rainy day fund
|
52
|
-
to: '2025-01-01'
|
53
|
-
- account: Assets:Pension
|
54
|
-
from: '2024-01-01'
|
55
|
-
transactions:
|
56
|
-
- amount: -500.0
|
57
|
-
category: Income:Pension
|
58
|
-
description: Pension draw down
|
59
|
-
quarterly:
|
60
|
-
- account: Assets:Bank
|
61
|
-
from: '2023-04-01'
|
62
|
-
transactions:
|
63
|
-
- amount: -1000.0
|
64
|
-
category: Income:Bonus
|
65
|
-
description: Quarterly bonus
|
66
|
-
half-yearly:
|
67
|
-
- account: Assets:Bank
|
68
|
-
from: '2023-04-01'
|
69
|
-
transactions:
|
70
|
-
- amount: 500.0
|
71
|
-
category: Expenses:Holiday
|
72
|
-
description: Top up holiday funds
|
73
|
-
yearly:
|
74
|
-
- account: Assets:Bank
|
75
|
-
from: '2023-04-01'
|
76
|
-
transactions:
|
77
|
-
- amount: -2000.0
|
78
|
-
category: Income:Bonus
|
79
|
-
description: Annual bonus
|
80
|
-
once:
|
81
|
-
- account: Assets:Bank
|
82
|
-
from: '2023-03-05'
|
83
|
-
transactions:
|
84
|
-
- amount: -3000.0
|
85
|
-
category: Expenses:Shopping
|
86
|
-
description: Refund for that damn laptop
|
87
|
-
summary_exclude: true
|
88
|
-
track: true
|
89
|
-
custom:
|
90
|
-
- account: Assets:Bank
|
91
|
-
from: '2023-03-01'
|
92
|
-
transactions:
|
93
|
-
- amount: 80.0
|
94
|
-
category: Expenses:Personal Care
|
95
|
-
description: Hair and beauty
|
96
|
-
frequency: every 2 weeks
|
97
|
-
roll-up: 26.0
|
98
|
-
settings:
|
99
|
-
currency: USD
|
100
|
-
show_symbol: true
|
101
|
-
thousands_separator: true
|
102
|
-
YAML
|
103
|
-
|
104
|
-
RSpec.describe 'CSV parser' do
|
105
|
-
it 'converts a CSV file to the YML output needed for the plugin' do
|
106
|
-
computed_yaml = HledgerForecast::CSVParser.parse(input)
|
107
|
-
|
108
|
-
expect(computed_yaml).to eq(output)
|
109
|
-
end
|
110
|
-
end
|
data/spec/modifier_spec.rb
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
require_relative '../lib/hledger_forecast'
|
2
|
-
|
3
|
-
base_config = <<~YAML
|
4
|
-
monthly:
|
5
|
-
- account: "Assets:Bank"
|
6
|
-
from: "2023-01-01"
|
7
|
-
transactions:
|
8
|
-
- amount: 300
|
9
|
-
category: "Expenses:Groceries"
|
10
|
-
description: Food shopping
|
11
|
-
modifiers:
|
12
|
-
- amount: 0.02
|
13
|
-
description: "Y1 inflation"
|
14
|
-
from: "2024-01-01"
|
15
|
-
to: "2024-12-31"
|
16
|
-
- amount: 0.05
|
17
|
-
description: "Y2 inflation"
|
18
|
-
from: "2025-01-01"
|
19
|
-
to: "2025-12-31"
|
20
|
-
- account: "Assets:Savings"
|
21
|
-
from: "2023-05-01"
|
22
|
-
transactions:
|
23
|
-
- amount: 500
|
24
|
-
category: "Assets:Bank"
|
25
|
-
description: Savings
|
26
|
-
modifiers:
|
27
|
-
- amount: 0.1
|
28
|
-
description: "Savings uplift"
|
29
|
-
from: "2024-05-01"
|
30
|
-
to: "2025-04-30"
|
31
|
-
|
32
|
-
settings:
|
33
|
-
currency: USD
|
34
|
-
YAML
|
35
|
-
|
36
|
-
base_journal = <<~JOURNAL
|
37
|
-
~ monthly from 2023-01-01 * Food shopping
|
38
|
-
Expenses:Groceries $300.00; Food shopping
|
39
|
-
Assets:Bank
|
40
|
-
|
41
|
-
~ monthly from 2023-05-01 * Savings
|
42
|
-
Assets:Bank $500.00; Savings
|
43
|
-
Assets:Savings
|
44
|
-
|
45
|
-
= Expenses:Groceries date:2024-01-01..2024-12-31
|
46
|
-
Expenses:Groceries *0.02 ; Food shopping - Y1 inflation
|
47
|
-
Assets:Bank *-0.02
|
48
|
-
|
49
|
-
= Expenses:Groceries date:2025-01-01..2025-12-31
|
50
|
-
Expenses:Groceries *0.05 ; Food shopping - Y2 inflation
|
51
|
-
Assets:Bank *-0.05
|
52
|
-
|
53
|
-
= Assets:Bank date:2024-05-01..2025-04-30
|
54
|
-
Assets:Bank *0.1 ; Savings - Savings uplift
|
55
|
-
Assets:Savings *-0.1
|
56
|
-
|
57
|
-
JOURNAL
|
58
|
-
|
59
|
-
no_date_config = <<~YAML
|
60
|
-
monthly:
|
61
|
-
- account: "Assets:Bank"
|
62
|
-
from: "2023-01-01"
|
63
|
-
transactions:
|
64
|
-
- amount: 500
|
65
|
-
category: "Expenses:Groceries"
|
66
|
-
description: Food shopping
|
67
|
-
modifiers:
|
68
|
-
- amount: 0.1
|
69
|
-
description: "Inflation"
|
70
|
-
|
71
|
-
settings:
|
72
|
-
currency: USD
|
73
|
-
YAML
|
74
|
-
|
75
|
-
no_date_journal = <<~JOURNAL
|
76
|
-
~ monthly from 2023-01-01 * Food shopping
|
77
|
-
Expenses:Groceries $500.00; Food shopping
|
78
|
-
Assets:Bank
|
79
|
-
|
80
|
-
= Expenses:Groceries date:2023-01-01
|
81
|
-
Expenses:Groceries *0.1 ; Food shopping - Inflation
|
82
|
-
Assets:Bank *-0.1
|
83
|
-
|
84
|
-
JOURNAL
|
85
|
-
|
86
|
-
RSpec.describe 'Applying modifiers to transactions -' do
|
87
|
-
it 'Auto-postings should be created correctly' do
|
88
|
-
generated = HledgerForecast::Generator
|
89
|
-
|
90
|
-
generated_journal = generated.generate(base_config)
|
91
|
-
|
92
|
-
expect(generated_journal).to eq(base_journal)
|
93
|
-
end
|
94
|
-
|
95
|
-
it 'Auto-postings should be created correctly if no dates are set' do
|
96
|
-
generated = HledgerForecast::Generator
|
97
|
-
|
98
|
-
generated_journal = generated.generate(no_date_config)
|
99
|
-
|
100
|
-
expect(generated_journal).to eq(no_date_journal)
|
101
|
-
end
|
102
|
-
end
|
data/spec/stubs/forecast.yml
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
monthly:
|
2
|
-
- account: "Assets:Bank"
|
3
|
-
from: "2023-03-01"
|
4
|
-
transactions:
|
5
|
-
- amount: 2000.55
|
6
|
-
category: "Expenses:Mortgage"
|
7
|
-
description: Mortgage
|
8
|
-
- amount: 100
|
9
|
-
category: "Expenses:Food"
|
10
|
-
description: Food
|
11
|
-
- account: "Assets:Savings"
|
12
|
-
from: "2023-03-01"
|
13
|
-
transactions:
|
14
|
-
- amount: -1000
|
15
|
-
category: "Assets:Bank"
|
16
|
-
description: Savings
|
17
|
-
|
18
|
-
settings:
|
19
|
-
currency: GBP
|