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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +22 -0
  3. data/.github/workflows/{ci.yml → test.yml} +5 -5
  4. data/.rubocop.yml +17 -17
  5. data/README.md +8 -172
  6. data/example.journal +14 -14
  7. data/hledger-forecast.gemspec +1 -1
  8. data/lib/hledger_forecast/calculator.rb +5 -1
  9. data/lib/hledger_forecast/cli.rb +5 -18
  10. data/lib/hledger_forecast/generator.rb +52 -35
  11. data/lib/hledger_forecast/settings.rb +42 -27
  12. data/lib/hledger_forecast/summarizer.rb +28 -62
  13. data/lib/hledger_forecast/summarizer_formatter.rb +12 -3
  14. data/lib/hledger_forecast/transactions/default.rb +28 -57
  15. data/lib/hledger_forecast/transactions/trackers.rb +34 -40
  16. data/lib/hledger_forecast/utilities.rb +14 -0
  17. data/lib/hledger_forecast/version.rb +1 -1
  18. data/lib/hledger_forecast.rb +1 -2
  19. data/spec/cli_spec.rb +3 -12
  20. data/spec/computed_amounts_spec.rb +11 -22
  21. data/spec/custom_spec.rb +15 -35
  22. data/spec/half-yearly_spec.rb +6 -13
  23. data/spec/monthly_end_date_spec.rb +8 -18
  24. data/spec/monthly_end_date_transaction_spec.rb +20 -45
  25. data/spec/monthly_spec.rb +11 -28
  26. data/spec/once_spec.rb +6 -13
  27. data/spec/quarterly_spec.rb +5 -12
  28. data/spec/summarizer_spec.rb +11 -42
  29. data/spec/track_spec.rb +19 -49
  30. data/spec/verbose_output_spec.rb +3 -3
  31. data/spec/yearly_spec.rb +5 -12
  32. metadata +10 -18
  33. data/example.yml +0 -98
  34. data/lib/hledger_forecast/csv_parser.rb +0 -106
  35. data/spec/csv_and_yml_comparison_spec.rb +0 -32
  36. data/spec/csv_parser_spec.rb +0 -110
  37. data/spec/modifier_spec.rb +0 -102
  38. 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
@@ -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
@@ -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
@@ -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