hledger-forecast 0.4.0 → 1.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/ci.yml +1 -1
- data/.gitignore +1 -0
- data/.rubocop.yml +3 -0
- data/README.md +47 -31
- data/hledger-forecast.gemspec +1 -0
- data/lib/hledger_forecast/calculator.rb +21 -0
- data/lib/hledger_forecast/cli.rb +2 -2
- data/lib/hledger_forecast/formatter.rb +24 -0
- data/lib/hledger_forecast/generator.rb +41 -258
- data/lib/hledger_forecast/settings.rb +41 -0
- data/lib/hledger_forecast/{summarize.rb → summarizer.rb} +41 -43
- data/lib/hledger_forecast/transactions/default.rb +88 -0
- data/lib/hledger_forecast/transactions/modifiers.rb +90 -0
- data/lib/hledger_forecast/transactions/trackers.rb +87 -0
- data/lib/hledger_forecast/version.rb +1 -1
- data/lib/hledger_forecast.rb +10 -4
- data/spec/custom_spec.rb +31 -4
- data/spec/modifier_spec.rb +21 -6
- data/spec/monthly_end_date_spec.rb +18 -33
- data/spec/monthly_end_date_transaction_spec.rb +44 -7
- data/spec/track_spec.rb +5 -67
- metadata +12 -7
- data/lib/hledger_forecast/tracker.rb +0 -37
@@ -1,32 +1,69 @@
|
|
1
1
|
require_relative '../lib/hledger_forecast'
|
2
2
|
|
3
|
-
|
3
|
+
base_config = <<~YAML
|
4
4
|
settings:
|
5
5
|
currency: GBP
|
6
6
|
|
7
7
|
monthly:
|
8
8
|
- from: "2023-03-01"
|
9
|
-
to: "2023-06-01"
|
10
9
|
account: "Assets:Bank"
|
11
10
|
transactions:
|
12
11
|
- description: Mortgage
|
12
|
+
to: "2023-06-01"
|
13
13
|
category: "Expenses:Mortgage"
|
14
14
|
amount: 2000.00
|
15
|
+
- description: Mortgage top up
|
16
|
+
to: "2023-06-01"
|
17
|
+
category: "Expenses:Mortgage Top Up"
|
18
|
+
amount: 200.00
|
15
19
|
- description: Food
|
16
20
|
category: "Expenses:Food"
|
17
21
|
amount: 100.00
|
22
|
+
- description: Party time
|
23
|
+
category: "Expenses:Going Out"
|
24
|
+
amount: 50.00
|
18
25
|
YAML
|
19
26
|
|
20
|
-
|
21
|
-
~ monthly from 2023-03-01 to 2023-06-01 * Mortgage,
|
27
|
+
base_output = <<~JOURNAL
|
28
|
+
~ monthly from 2023-03-01 to 2023-06-01 * Mortgage, Mortgage top up
|
29
|
+
Expenses:Mortgage £2,000.00; Mortgage
|
30
|
+
Expenses:Mortgage Top Up £200.00 ; Mortgage top up
|
31
|
+
Assets:Bank
|
32
|
+
|
33
|
+
~ monthly from 2023-03-01 * Food, Party time
|
34
|
+
Expenses:Food £100.00 ; Food
|
35
|
+
Expenses:Going Out £50.00 ; Party time
|
36
|
+
Assets:Bank
|
37
|
+
|
38
|
+
JOURNAL
|
39
|
+
|
40
|
+
computed_config = <<~YAML
|
41
|
+
settings:
|
42
|
+
currency: GBP
|
43
|
+
|
44
|
+
monthly:
|
45
|
+
- from: "2023-03-01"
|
46
|
+
account: "Assets:Bank"
|
47
|
+
transactions:
|
48
|
+
- description: Mortgage
|
49
|
+
category: "Expenses:Mortgage"
|
50
|
+
to: "=12"
|
51
|
+
amount: 2000.00
|
52
|
+
YAML
|
53
|
+
|
54
|
+
computed_output = <<~JOURNAL
|
55
|
+
~ monthly from 2023-03-01 to 2024-02-29 * Mortgage
|
22
56
|
Expenses:Mortgage £2,000.00; Mortgage
|
23
|
-
Expenses:Food £100.00 ; Food
|
24
57
|
Assets:Bank
|
25
58
|
|
26
59
|
JOURNAL
|
27
60
|
|
28
61
|
RSpec.describe 'generate' do
|
29
|
-
it 'generates a forecast with correct MONTHLY transactions that have an end date
|
30
|
-
expect(HledgerForecast::Generator.generate(
|
62
|
+
it 'generates a forecast with correct MONTHLY transactions that have an end date' do
|
63
|
+
expect(HledgerForecast::Generator.generate(base_config)).to eq(base_output)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'generates a forecast with correct MONTHLY transactions that have a COMPUTED end date' do
|
67
|
+
expect(HledgerForecast::Generator.generate(computed_config)).to eq(computed_output)
|
31
68
|
end
|
32
69
|
end
|
data/spec/track_spec.rb
CHANGED
@@ -42,70 +42,13 @@ base_output = <<~JOURNAL
|
|
42
42
|
JOURNAL
|
43
43
|
|
44
44
|
RSpec.describe 'Tracking transactions -' do
|
45
|
-
it 'Determines which transactions should be tracked' do
|
46
|
-
generated = HledgerForecast::Generator
|
47
|
-
generated.generate(base_config)
|
48
|
-
tracked = generated.tracked
|
49
|
-
|
50
|
-
expect(tracked[0]['transaction']).to eq(
|
51
|
-
{ "amount" => "£3,000.00", "category" => "Expenses:Tax", "description" => "Tax owed",
|
52
|
-
"inverse_amount" => "£-3,000.00", "track" => true }
|
53
|
-
)
|
54
|
-
expect(tracked[0]['account']).to eq("Assets:Bank")
|
55
|
-
|
56
|
-
expect(tracked[1]['transaction']).to eq(
|
57
|
-
{ "amount" => "£-1,500.00", "category" => "Income:Salary", "description" => "Salary", "to" => "2023-08-01",
|
58
|
-
"inverse_amount" => "£1,500.00", "track" => true }
|
59
|
-
)
|
60
|
-
expect(tracked[1]['account']).to eq("Assets:Bank")
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'marks a transaction as NOT FOUND if it doesnt exist' do
|
64
|
-
generated = HledgerForecast::Generator
|
65
|
-
generated.tracked = {} # Clear tracked transactions
|
66
|
-
generated.generate(base_config)
|
67
|
-
transactions_to_track = generated.tracked
|
68
|
-
|
69
|
-
track = HledgerForecast::Tracker.track(transactions_to_track, 'spec/stubs/transactions_not_found.journal')
|
70
|
-
|
71
|
-
expect(track[0]['found']).to eq(false)
|
72
|
-
expect(track[1]['found']).to eq(false)
|
73
|
-
end
|
74
|
-
|
75
|
-
it 'marks a transaction as FOUND if it exists' do
|
76
|
-
generated = HledgerForecast::Generator
|
77
|
-
generated.tracked = {} # Clear tracked transactions
|
78
|
-
generated.generate(base_config)
|
79
|
-
transactions_to_track = generated.tracked
|
80
|
-
|
81
|
-
track = HledgerForecast::Tracker.track(transactions_to_track, 'spec/stubs/transactions_found.journal')
|
82
|
-
|
83
|
-
expect(track[0]['found']).to eq(true)
|
84
|
-
expect(track[1]['found']).to eq(true)
|
85
|
-
end
|
86
|
-
|
87
|
-
it 'marks a transaction as FOUND if it exists, even if the category/amount are inversed' do
|
88
|
-
generated = HledgerForecast::Generator
|
89
|
-
generated.tracked = {} # Clear tracked transactions
|
90
|
-
generated.generate(base_config)
|
91
|
-
transactions_to_track = generated.tracked
|
92
|
-
|
93
|
-
track = HledgerForecast::Tracker.track(transactions_to_track, 'spec/stubs/transactions_found_inverse.journal')
|
94
|
-
|
95
|
-
expect(track[0]['found']).to eq(true)
|
96
|
-
end
|
97
|
-
|
98
45
|
it 'writes a NON-FOUND entry into a journal' do
|
99
46
|
options = {}
|
100
47
|
options[:transaction_file] = 'spec/stubs/transactions_not_found.journal'
|
101
48
|
|
102
|
-
|
103
|
-
generated.tracked = {} # Clear tracked transactions
|
104
|
-
|
105
|
-
generated_journal = generated.generate(base_config, options)
|
49
|
+
generated_journal = HledgerForecast::Generator.generate(base_config, options)
|
106
50
|
|
107
|
-
|
108
|
-
expect(generated_journal).to eq(expected_output)
|
51
|
+
expect(generated_journal).to eq(base_output)
|
109
52
|
end
|
110
53
|
|
111
54
|
it 'writes a NON-FOUND entry for dates that are close to the current period' do
|
@@ -147,12 +90,10 @@ RSpec.describe 'Tracking transactions -' do
|
|
147
90
|
options = {}
|
148
91
|
options[:transaction_file] = temp_file.path
|
149
92
|
|
150
|
-
|
151
|
-
generated.tracked = {} # Clear tracked transactions
|
152
|
-
|
153
|
-
generated_journal = generated.generate(forecast_config, options)
|
93
|
+
generated_journal = HledgerForecast::Generator.generate(forecast_config, options)
|
154
94
|
|
155
95
|
expected_output = <<~JOURNAL
|
96
|
+
|
156
97
|
~ #{next_month} * [TRACKED] New kitchen
|
157
98
|
Expenses:House £5,000.00; New kitchen
|
158
99
|
Assets:Bank
|
@@ -180,10 +121,7 @@ RSpec.describe 'Tracking transactions -' do
|
|
180
121
|
options = {}
|
181
122
|
options[:transaction_file] = 'spec/stubs/transactions_not_found.journal'
|
182
123
|
|
183
|
-
|
184
|
-
generated.tracked = {} # Clear tracked transactions
|
185
|
-
|
186
|
-
generated_journal = generated.generate(forecast_config, options)
|
124
|
+
generated_journal = HledgerForecast::Generator.generate(forecast_config, options)
|
187
125
|
|
188
126
|
output = <<~JOURNAL
|
189
127
|
~ monthly from #{next_month} * Food expenses
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hledger-forecast
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oli Morris
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: colorize
|
@@ -113,10 +113,15 @@ files:
|
|
113
113
|
- example.yml
|
114
114
|
- hledger-forecast.gemspec
|
115
115
|
- lib/hledger_forecast.rb
|
116
|
+
- lib/hledger_forecast/calculator.rb
|
116
117
|
- lib/hledger_forecast/cli.rb
|
118
|
+
- lib/hledger_forecast/formatter.rb
|
117
119
|
- lib/hledger_forecast/generator.rb
|
118
|
-
- lib/hledger_forecast/
|
119
|
-
- lib/hledger_forecast/
|
120
|
+
- lib/hledger_forecast/settings.rb
|
121
|
+
- lib/hledger_forecast/summarizer.rb
|
122
|
+
- lib/hledger_forecast/transactions/default.rb
|
123
|
+
- lib/hledger_forecast/transactions/modifiers.rb
|
124
|
+
- lib/hledger_forecast/transactions/trackers.rb
|
120
125
|
- lib/hledger_forecast/version.rb
|
121
126
|
- spec/command_spec.rb
|
122
127
|
- spec/computed_amounts_spec.rb
|
@@ -144,16 +149,16 @@ require_paths:
|
|
144
149
|
- lib
|
145
150
|
required_ruby_version: !ruby/object:Gem::Requirement
|
146
151
|
requirements:
|
147
|
-
- - "
|
152
|
+
- - "~>"
|
148
153
|
- !ruby/object:Gem::Version
|
149
|
-
version: '0'
|
154
|
+
version: '3.0'
|
150
155
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
151
156
|
requirements:
|
152
157
|
- - ">="
|
153
158
|
- !ruby/object:Gem::Version
|
154
159
|
version: '0'
|
155
160
|
requirements: []
|
156
|
-
rubygems_version: 3.
|
161
|
+
rubygems_version: 3.2.3
|
157
162
|
signing_key:
|
158
163
|
specification_version: 4
|
159
164
|
summary: An extended wrapper around hledger's forecasting functionality
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module HledgerForecast
|
2
|
-
# Checks for the existence of a transaction in a journal file and tracks it
|
3
|
-
class Tracker
|
4
|
-
def self.track(transactions, transaction_file)
|
5
|
-
next_month = Date.new(Date.today.year, Date.today.month, 1).next_month
|
6
|
-
|
7
|
-
transactions.each_with_object({}) do |(key, transaction), updated_transactions|
|
8
|
-
found = transaction_exists?(transaction_file, transaction['from'], Date.today, transaction['account'],
|
9
|
-
transaction['transaction'])
|
10
|
-
updated_transactions[key] = transaction.merge('from' => next_month, 'found' => found)
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def self.latest_date(file)
|
15
|
-
command = %(hledger print --file #{file} | grep '^[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}' | awk '{print $1}' | sort -r | head -n 1)
|
16
|
-
|
17
|
-
date_output = `#{command}`
|
18
|
-
date_output.strip
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.transaction_exists?(file, from, to, account, transaction)
|
22
|
-
category = escape_str(transaction['category'])
|
23
|
-
amount = transaction['amount']
|
24
|
-
inverse_amount = transaction['inverse_amount']
|
25
|
-
|
26
|
-
# We run two commands and check to see if category +/- amount or account +/- amount exists
|
27
|
-
command1 = %(hledger print -f #{file} "date:#{from}..#{to}" | tr -s '[:space:]' ' ' | grep -q -Eo "#{category} (#{amount}|#{inverse_amount})")
|
28
|
-
command2 = %(hledger print -f #{file} "date:#{from}..#{to}" | tr -s '[:space:]' ' ' | grep -q -Eo "#{account} (#{amount}|#{inverse_amount})")
|
29
|
-
|
30
|
-
system(command1) || system(command2)
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.escape_str(str)
|
34
|
-
str.gsub('[', '\\[').gsub(']', '\\]').gsub('(', '\\(').gsub(')', '\\)')
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|