hledger-forecast 0.3.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.
@@ -1,47 +1,32 @@
1
1
  require_relative '../lib/hledger_forecast'
2
2
 
3
3
  config = <<~YAML
4
- settings:
5
- currency: GBP
4
+ settings:
5
+ currency: GBP
6
6
 
7
- monthly:
8
- - from: "2023-03-01"
9
- account: "Assets:Bank"
10
- transactions:
11
- - description: Mortgage
12
- to: "2023-06-01"
13
- category: "Expenses:Mortgage"
14
- amount: 2000.00
15
- - description: Mortgage top up
16
- to: "2023-06-01"
17
- category: "Expenses:Mortgage Top Up"
18
- amount: 200.00
19
- - description: Food
20
- category: "Expenses:Food"
21
- amount: 100.00
22
- - description: Party time
23
- category: "Expenses:Going Out"
24
- amount: 50.00
7
+ monthly:
8
+ - from: "2023-03-01"
9
+ to: "2023-06-01"
10
+ account: "Assets:Bank"
11
+ transactions:
12
+ - description: Mortgage
13
+ category: "Expenses:Mortgage"
14
+ amount: 2000.00
15
+ - description: Food
16
+ category: "Expenses:Food"
17
+ amount: 100.00
25
18
  YAML
26
19
 
27
20
  output = <<~JOURNAL
28
- ~ monthly from 2023-03-01 * Food, Party time
29
- Expenses:Food £100.00 ; Food
30
- Expenses:Going Out £50.00 ; Party time
31
- Assets:Bank
32
-
33
- ~ monthly from 2023-03-01 to 2023-06-01 * Mortgage
34
- Expenses:Mortgage £2,000.00; Mortgage
35
- Assets:Bank
36
-
37
- ~ monthly from 2023-03-01 to 2023-06-01 * Mortgage top up
38
- Expenses:Mortgage Top Up £200.00 ; Mortgage top up
39
- Assets:Bank
21
+ ~ monthly from 2023-03-01 to 2023-06-01 * Mortgage, Food
22
+ Expenses:Mortgage £2,000.00; Mortgage
23
+ Expenses:Food £100.00 ; Food
24
+ Assets:Bank
40
25
 
41
26
  JOURNAL
42
27
 
43
28
  RSpec.describe 'generate' do
44
- it 'generates a forecast with correct MONTHLY transactions that have an end date' do
29
+ it 'generates a forecast with correct MONTHLY transactions that have an end date, at the top level' do
45
30
  expect(HledgerForecast::Generator.generate(config)).to eq(output)
46
31
  end
47
32
  end
@@ -1,32 +1,69 @@
1
1
  require_relative '../lib/hledger_forecast'
2
2
 
3
- config = <<~YAML
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
- output = <<~JOURNAL
21
- ~ monthly from 2023-03-01 to 2023-06-01 * Mortgage, Food
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, at the top level' do
30
- expect(HledgerForecast::Generator.generate(config)).to eq(output)
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
- generated = HledgerForecast::Generator
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
- expected_output = base_output
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
- generated = HledgerForecast::Generator
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
- generated = HledgerForecast::Generator
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.3.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-08 00:00:00.000000000 Z
11
+ date: 2023-05-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.8.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: dentaku
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.5.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.5.1
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: highline
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -99,12 +113,18 @@ files:
99
113
  - example.yml
100
114
  - hledger-forecast.gemspec
101
115
  - lib/hledger_forecast.rb
116
+ - lib/hledger_forecast/calculator.rb
102
117
  - lib/hledger_forecast/cli.rb
118
+ - lib/hledger_forecast/formatter.rb
103
119
  - lib/hledger_forecast/generator.rb
104
- - lib/hledger_forecast/summarize.rb
105
- - lib/hledger_forecast/tracker.rb
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
106
125
  - lib/hledger_forecast/version.rb
107
126
  - spec/command_spec.rb
127
+ - spec/computed_amounts_spec.rb
108
128
  - spec/custom_spec.rb
109
129
  - spec/half-yearly_spec.rb
110
130
  - spec/modifier_spec.rb
@@ -129,21 +149,22 @@ require_paths:
129
149
  - lib
130
150
  required_ruby_version: !ruby/object:Gem::Requirement
131
151
  requirements:
132
- - - ">="
152
+ - - "~>"
133
153
  - !ruby/object:Gem::Version
134
- version: '0'
154
+ version: '3.0'
135
155
  required_rubygems_version: !ruby/object:Gem::Requirement
136
156
  requirements:
137
157
  - - ">="
138
158
  - !ruby/object:Gem::Version
139
159
  version: '0'
140
160
  requirements: []
141
- rubygems_version: 3.4.12
161
+ rubygems_version: 3.2.3
142
162
  signing_key:
143
163
  specification_version: 4
144
164
  summary: An extended wrapper around hledger's forecasting functionality
145
165
  test_files:
146
166
  - spec/command_spec.rb
167
+ - spec/computed_amounts_spec.rb
147
168
  - spec/custom_spec.rb
148
169
  - spec/half-yearly_spec.rb
149
170
  - spec/modifier_spec.rb
@@ -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