hledger-forecast 2.0.0 → 3.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/{test.yml → ci.yml} +18 -10
- data/.github/workflows/publish_ruby_gem.yml +24 -0
- data/.github/workflows/release.yml +12 -13
- data/.mise.toml +2 -0
- data/CHANGELOG.md +17 -0
- data/Gemfile +1 -0
- data/README.md +149 -119
- data/example.csv +15 -15
- data/example.journal +17 -18
- data/hledger-forecast.gemspec +20 -18
- data/lib/hledger_forecast/calculator.rb +7 -15
- data/lib/hledger_forecast/cli.rb +98 -71
- data/lib/hledger_forecast/comparator.rb +12 -11
- data/lib/hledger_forecast/forecast.rb +29 -0
- data/lib/hledger_forecast/formatter.rb +13 -15
- data/lib/hledger_forecast/generator.rb +32 -72
- data/lib/hledger_forecast/settings.rb +34 -47
- data/lib/hledger_forecast/summarizer.rb +34 -55
- data/lib/hledger_forecast/summarizer_formatter.rb +75 -78
- data/lib/hledger_forecast/transaction.rb +63 -0
- data/lib/hledger_forecast/transactions/default.rb +45 -72
- data/lib/hledger_forecast/version.rb +1 -1
- data/lib/hledger_forecast.rb +21 -22
- data/spec/calculator_spec.rb +45 -0
- data/spec/cli_spec.rb +19 -17
- data/spec/compare_spec.rb +16 -14
- data/spec/computed_amounts_spec.rb +7 -7
- data/spec/custom_spec.rb +9 -9
- data/spec/formatter_spec.rb +51 -0
- data/spec/half-yearly_spec.rb +5 -5
- data/spec/monthly_end_date_spec.rb +6 -6
- data/spec/monthly_end_date_transaction_spec.rb +10 -10
- data/spec/monthly_spec.rb +7 -7
- data/spec/once_spec.rb +5 -5
- data/spec/quarterly_spec.rb +5 -5
- data/spec/settings_spec.rb +101 -0
- data/spec/stubs/forecast.csv +4 -4
- data/spec/summarizer_spec.rb +28 -33
- data/spec/tags_spec.rb +92 -0
- data/spec/verbose_output_spec.rb +8 -8
- data/spec/yearly_spec.rb +5 -5
- metadata +49 -13
- data/lib/hledger_forecast/transactions/modifiers.rb +0 -90
- data/lib/hledger_forecast/transactions/trackers.rb +0 -88
- data/lib/hledger_forecast/utilities.rb +0 -14
- data/spec/track_spec.rb +0 -105
data/lib/hledger_forecast.rb
CHANGED
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
9
|
-
require
|
|
10
|
-
require
|
|
3
|
+
require "colorize"
|
|
4
|
+
require "csv"
|
|
5
|
+
require "date"
|
|
6
|
+
require "dentaku"
|
|
7
|
+
require "highline"
|
|
8
|
+
require "money"
|
|
9
|
+
require "optparse"
|
|
10
|
+
require "terminal-table"
|
|
11
11
|
|
|
12
12
|
Money.locale_backend = nil
|
|
13
13
|
Money.rounding_mode = BigDecimal::ROUND_HALF_UP
|
|
14
|
-
Money.default_currency =
|
|
14
|
+
Money.default_currency = "USD"
|
|
15
15
|
|
|
16
|
-
require_relative
|
|
17
|
-
require_relative
|
|
18
|
-
require_relative
|
|
19
|
-
require_relative
|
|
20
|
-
require_relative
|
|
21
|
-
require_relative
|
|
22
|
-
require_relative
|
|
23
|
-
require_relative
|
|
24
|
-
require_relative
|
|
25
|
-
require_relative
|
|
16
|
+
require_relative "hledger_forecast/calculator"
|
|
17
|
+
require_relative "hledger_forecast/settings"
|
|
18
|
+
require_relative "hledger_forecast/transaction"
|
|
19
|
+
require_relative "hledger_forecast/forecast"
|
|
20
|
+
require_relative "hledger_forecast/formatter"
|
|
21
|
+
require_relative "hledger_forecast/generator"
|
|
22
|
+
require_relative "hledger_forecast/summarizer"
|
|
23
|
+
require_relative "hledger_forecast/summarizer_formatter"
|
|
24
|
+
require_relative "hledger_forecast/comparator"
|
|
25
|
+
require_relative "hledger_forecast/cli"
|
|
26
|
+
require_relative "hledger_forecast/version"
|
|
26
27
|
|
|
27
|
-
require_relative
|
|
28
|
-
require_relative 'hledger_forecast/transactions/modifiers'
|
|
29
|
-
require_relative 'hledger_forecast/transactions/trackers'
|
|
28
|
+
require_relative "hledger_forecast/transactions/default"
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
|
+
|
|
3
|
+
RSpec.describe HledgerForecast::Calculator do
|
|
4
|
+
describe ".evaluate" do
|
|
5
|
+
it "returns a float for an integer" do
|
|
6
|
+
expect(described_class.evaluate(250)).to(eq(250.0))
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "returns a float for a float" do
|
|
10
|
+
expect(described_class.evaluate(99.5)).to(eq(99.5))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "evaluates a division formula" do
|
|
14
|
+
expect(described_class.evaluate("=5000/24")).to(be_within(0.01).of(208.33))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it "evaluates a multiplication formula" do
|
|
18
|
+
expect(described_class.evaluate("=25*4.3")).to(be_within(0.01).of(107.5))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it "evaluates a compound expression" do
|
|
22
|
+
expect(described_class.evaluate("=(102.50+3.25)/2")).to(be_within(0.01).of(52.875))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
describe ".evaluate_date" do
|
|
27
|
+
let(:from) { Date.parse("2023-03-01") }
|
|
28
|
+
|
|
29
|
+
it "parses a plain date string" do
|
|
30
|
+
expect(described_class.evaluate_date(from, "2023-06-01")).to(eq(Date.parse("2023-06-01")))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "calculates N months forward and subtracts one day" do
|
|
34
|
+
expect(described_class.evaluate_date(from, "=12")).to(eq(Date.parse("2024-02-29")))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "handles a 6-month offset" do
|
|
38
|
+
expect(described_class.evaluate_date(from, "=6")).to(eq(Date.parse("2023-08-31")))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
it "handles a 24-month offset" do
|
|
42
|
+
expect(described_class.evaluate_date(from, "=24")).to(eq(Date.parse("2025-02-28")))
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/spec/cli_spec.rb
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
2
|
|
|
3
3
|
output = <<~JOURNAL
|
|
4
4
|
~ monthly from 2023-03-01 * Mortgage, Food
|
|
5
|
-
Expenses:Mortgage £2,000.55
|
|
6
|
-
Expenses:Food £100.00
|
|
5
|
+
Expenses:Mortgage £2,000.55
|
|
6
|
+
Expenses:Food £100.00
|
|
7
7
|
Assets:Bank
|
|
8
8
|
|
|
9
9
|
~ monthly from 2023-03-01 * Savings
|
|
10
|
-
Assets:Bank £-1,000.00
|
|
10
|
+
Assets:Bank £-1,000.00
|
|
11
11
|
Assets:Savings
|
|
12
12
|
|
|
13
13
|
JOURNAL
|
|
@@ -25,28 +25,30 @@ ensure
|
|
|
25
25
|
$stdout = old_stdout
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
RSpec.describe
|
|
29
|
-
it
|
|
30
|
-
generated_journal =
|
|
28
|
+
RSpec.describe "command" do
|
|
29
|
+
it "uses the CLI to generate an output with a CSV config file" do
|
|
30
|
+
generated_journal = "./test_output.journal"
|
|
31
31
|
File.delete(generated_journal) if File.exist?(generated_journal)
|
|
32
32
|
|
|
33
33
|
system("./bin/hledger-forecast generate -f ./spec/stubs/forecast.csv -o ./test_output.journal --force")
|
|
34
34
|
|
|
35
|
-
expect(File.read(generated_journal)).to
|
|
35
|
+
expect(File.read(generated_journal)).to(eq(output))
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
it
|
|
39
|
-
expected_output = strip_ansi_codes(
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
38
|
+
it "uses the CLI to compare two CSV files" do
|
|
39
|
+
expected_output = strip_ansi_codes(
|
|
40
|
+
<<~OUTPUT
|
|
41
|
+
+---------+---------+---------+
|
|
42
|
+
| account | 2023-07 | 2023-08 |
|
|
43
|
+
+---------+---------+---------+
|
|
44
|
+
| total | £10.00 | €-10.00 |
|
|
45
|
+
+---------+---------+---------+
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
OUTPUT
|
|
48
|
+
)
|
|
47
49
|
|
|
48
50
|
actual_output = `./bin/hledger-forecast compare ./spec/stubs/output1.csv ./spec/stubs/output2.csv`
|
|
49
51
|
|
|
50
|
-
expect(strip_ansi_codes(actual_output)).to
|
|
52
|
+
expect(strip_ansi_codes(actual_output)).to(eq(expected_output))
|
|
51
53
|
end
|
|
52
54
|
end
|
data/spec/compare_spec.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
require_relative
|
|
2
|
-
require
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
|
+
require "stringio"
|
|
3
3
|
|
|
4
4
|
def strip_ansi_codes(str)
|
|
5
5
|
str.gsub(/\e\[([;\d]+)?m/, "")
|
|
@@ -33,22 +33,24 @@ RSpec.describe HledgerForecast::Comparator do
|
|
|
33
33
|
let(:file2) { StringIO.new(file2_content) }
|
|
34
34
|
|
|
35
35
|
before do
|
|
36
|
-
allow(CSV).to
|
|
37
|
-
allow(CSV).to
|
|
36
|
+
allow(CSV).to(receive(:read).with("file1.csv").and_return(CSV.parse(file1.read)))
|
|
37
|
+
allow(CSV).to(receive(:read).with("file2.csv").and_return(CSV.parse(file2.read)))
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
it "compares the contents of two CSV files and outputs the difference" do
|
|
41
41
|
comparator = described_class.new
|
|
42
42
|
|
|
43
|
-
expected_output = strip_ansi_codes(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
43
|
+
expected_output = strip_ansi_codes(
|
|
44
|
+
<<~OUTPUT
|
|
45
|
+
+---------+---------+----------+---------+
|
|
46
|
+
| account | 2023-07 | 2023-08 | 2023-09 |
|
|
47
|
+
+---------+---------+----------+---------+
|
|
48
|
+
| total | £10.00 | €-400.00 | 1144.00 |
|
|
49
|
+
+---------+---------+----------+---------+
|
|
50
|
+
OUTPUT
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
actual_output = capture_stdout { comparator.compare("file1.csv", "file2.csv") }
|
|
54
|
+
expect(strip_ansi_codes(actual_output)).to(eq(expected_output))
|
|
53
55
|
end
|
|
54
56
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
2
|
|
|
3
3
|
config = <<~CSV
|
|
4
4
|
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
|
|
@@ -10,15 +10,15 @@ CSV
|
|
|
10
10
|
|
|
11
11
|
output = <<~JOURNAL
|
|
12
12
|
~ monthly from 2023-05-01 * New kitchen, Monthly food shop, Random food
|
|
13
|
-
Expenses:House £208.33
|
|
14
|
-
Expenses:Food £107.50
|
|
15
|
-
Expenses:Food £52.88
|
|
13
|
+
Expenses:House £208.33
|
|
14
|
+
Expenses:Food £107.50
|
|
15
|
+
Expenses:Food £52.88
|
|
16
16
|
Liabilities:Amex
|
|
17
17
|
|
|
18
18
|
JOURNAL
|
|
19
19
|
|
|
20
|
-
RSpec.describe
|
|
21
|
-
it
|
|
22
|
-
expect(HledgerForecast::Generator.generate(config)).to
|
|
20
|
+
RSpec.describe "generate" do
|
|
21
|
+
it "generates a forecast with correct CALCULATED transactions" do
|
|
22
|
+
expect(HledgerForecast::Generator.generate(config)).to(eq(output))
|
|
23
23
|
end
|
|
24
24
|
end
|
data/spec/custom_spec.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
2
|
|
|
3
3
|
base_config = <<~CSV
|
|
4
4
|
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
|
|
@@ -9,11 +9,11 @@ CSV
|
|
|
9
9
|
|
|
10
10
|
base_output = <<~JOURNAL
|
|
11
11
|
~ every 2 weeks from 2023-05-01 * Hair and beauty
|
|
12
|
-
[Expenses:Personal Care] £80.00
|
|
12
|
+
[Expenses:Personal Care] £80.00
|
|
13
13
|
[Assets:Bank]
|
|
14
14
|
|
|
15
15
|
~ every 5 days from 2023-05-01 * Food
|
|
16
|
-
[Expenses:Groceries] £50.00
|
|
16
|
+
[Expenses:Groceries] £50.00
|
|
17
17
|
[Assets:Bank]
|
|
18
18
|
|
|
19
19
|
JOURNAL
|
|
@@ -26,19 +26,19 @@ CSV
|
|
|
26
26
|
|
|
27
27
|
calculated_output = <<~JOURNAL
|
|
28
28
|
~ every 2 weeks from 2023-05-01 to 2023-10-31 * Hair and beauty
|
|
29
|
-
[Expenses:Personal Care] £80.00
|
|
29
|
+
[Expenses:Personal Care] £80.00
|
|
30
30
|
[Assets:Bank]
|
|
31
31
|
|
|
32
32
|
JOURNAL
|
|
33
33
|
|
|
34
|
-
RSpec.describe
|
|
35
|
-
it
|
|
34
|
+
RSpec.describe "generate" do
|
|
35
|
+
it "generates a forecast with correct CUSTOM transactions" do
|
|
36
36
|
generated_journal = HledgerForecast::Generator.generate(base_config)
|
|
37
|
-
expect(generated_journal).to
|
|
37
|
+
expect(generated_journal).to(eq(base_output))
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
it
|
|
40
|
+
it "generates a forecast with correct CUSTOM transactions and CALCULATED to dates" do
|
|
41
41
|
generated_journal = HledgerForecast::Generator.generate(calculated_config)
|
|
42
|
-
expect(generated_journal).to
|
|
42
|
+
expect(generated_journal).to(eq(calculated_output))
|
|
43
43
|
end
|
|
44
44
|
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
|
+
|
|
3
|
+
RSpec.describe HledgerForecast::Formatter do
|
|
4
|
+
def make_settings(overrides = {})
|
|
5
|
+
rows = []
|
|
6
|
+
overrides.each do |key, value|
|
|
7
|
+
rows << {type: "settings", frequency: key.to_s, account: value.to_s}
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
csv_rows = rows.map { |r| "settings,#{r[:frequency]},#{r[:account]},,,,,,,," }.join("\n")
|
|
11
|
+
full_csv = "type,frequency,account,from,to,description,category,amount,roll_up,summary_exclude\n#{csv_rows}\n"
|
|
12
|
+
|
|
13
|
+
parsed = CSV.parse(
|
|
14
|
+
full_csv,
|
|
15
|
+
headers: true,
|
|
16
|
+
header_converters: -> (h) { h.to_s.tr("-", "_").to_sym },
|
|
17
|
+
converters: :numeric
|
|
18
|
+
)
|
|
19
|
+
HledgerForecast::Settings.parse(parsed.select { |r| r[:type] == "settings" })
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe ".format_money" do
|
|
23
|
+
context("GBP with defaults") do
|
|
24
|
+
let(:settings) { make_settings(currency: "GBP") }
|
|
25
|
+
|
|
26
|
+
it "formats a positive amount" do
|
|
27
|
+
expect(described_class.format_money(1000, settings)).to(eq("£1,000.00"))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
it "formats a negative amount" do
|
|
31
|
+
expect(described_class.format_money(-500, settings)).to(eq("£-500.00"))
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
context("with thousands_separator disabled") do
|
|
36
|
+
let(:settings) { make_settings(currency: "USD", thousands_separator: "false") }
|
|
37
|
+
|
|
38
|
+
it "omits the thousands separator" do
|
|
39
|
+
expect(described_class.format_money(1000, settings)).to(eq("$1000.00"))
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context("with no settings (all defaults)") do
|
|
44
|
+
let(:settings) { make_settings }
|
|
45
|
+
|
|
46
|
+
it "defaults to USD" do
|
|
47
|
+
expect(described_class.format_money(100, settings)).to(eq("$100.00"))
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
data/spec/half-yearly_spec.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
2
|
|
|
3
3
|
config = <<~CSV
|
|
4
4
|
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
|
|
@@ -8,15 +8,15 @@ CSV
|
|
|
8
8
|
|
|
9
9
|
output = <<~JOURNAL
|
|
10
10
|
~ every 6 months from 2023-04-01 * Holiday
|
|
11
|
-
Expenses:Holiday £500.00
|
|
11
|
+
Expenses:Holiday £500.00
|
|
12
12
|
Assets:Bank
|
|
13
13
|
|
|
14
14
|
JOURNAL
|
|
15
15
|
|
|
16
|
-
RSpec.describe
|
|
17
|
-
it
|
|
16
|
+
RSpec.describe "generate" do
|
|
17
|
+
it "generates a forecast with correct HALF-YEARLY transactions" do
|
|
18
18
|
generated_journal = HledgerForecast::Generator.generate(config)
|
|
19
19
|
|
|
20
|
-
expect(generated_journal).to
|
|
20
|
+
expect(generated_journal).to(eq(output))
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
2
|
|
|
3
3
|
config = <<~CSV
|
|
4
4
|
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
|
|
@@ -9,14 +9,14 @@ CSV
|
|
|
9
9
|
|
|
10
10
|
output = <<~JOURNAL
|
|
11
11
|
~ monthly from 2023-03-01 to 2023-06-01 * Mortgage, Food
|
|
12
|
-
Expenses:Mortgage £2,000.00
|
|
13
|
-
Expenses:Food £100.00
|
|
12
|
+
Expenses:Mortgage £2,000.00
|
|
13
|
+
Expenses:Food £100.00
|
|
14
14
|
Assets:Bank
|
|
15
15
|
|
|
16
16
|
JOURNAL
|
|
17
17
|
|
|
18
|
-
RSpec.describe
|
|
19
|
-
it
|
|
20
|
-
expect(HledgerForecast::Generator.generate(config)).to
|
|
18
|
+
RSpec.describe "generate" do
|
|
19
|
+
it "generates a forecast with correct MONTHLY transactions that have an end date, at the top level" do
|
|
20
|
+
expect(HledgerForecast::Generator.generate(config)).to(eq(output))
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
2
|
|
|
3
3
|
config = <<~CSV
|
|
4
4
|
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
|
|
@@ -10,12 +10,12 @@ CSV
|
|
|
10
10
|
|
|
11
11
|
output = <<~JOURNAL
|
|
12
12
|
~ monthly from 2023-03-01 to 2023-06-01 * Mortgage, Mortgage top up
|
|
13
|
-
Expenses:Mortgage £2,000.00
|
|
14
|
-
Expenses:Mortgage £200.00
|
|
13
|
+
Expenses:Mortgage £2,000.00
|
|
14
|
+
Expenses:Mortgage £200.00
|
|
15
15
|
Assets:Bank
|
|
16
16
|
|
|
17
17
|
~ monthly from 2023-03-01 * Food
|
|
18
|
-
Expenses:Food £100.00
|
|
18
|
+
Expenses:Food £100.00
|
|
19
19
|
Assets:Bank
|
|
20
20
|
|
|
21
21
|
JOURNAL
|
|
@@ -28,17 +28,17 @@ CSV
|
|
|
28
28
|
|
|
29
29
|
computed_output = <<~JOURNAL
|
|
30
30
|
~ monthly from 2023-03-01 to 2024-02-29 * Mortgage
|
|
31
|
-
Expenses:Mortgage £2,000.00
|
|
31
|
+
Expenses:Mortgage £2,000.00
|
|
32
32
|
Assets:Bank
|
|
33
33
|
|
|
34
34
|
JOURNAL
|
|
35
35
|
|
|
36
|
-
RSpec.describe
|
|
37
|
-
it
|
|
38
|
-
expect(HledgerForecast::Generator.generate(config)).to
|
|
36
|
+
RSpec.describe "generate" do
|
|
37
|
+
it "generates a forecast with correct MONTHLY transactions that have an end date" do
|
|
38
|
+
expect(HledgerForecast::Generator.generate(config)).to(eq(output))
|
|
39
39
|
end
|
|
40
40
|
|
|
41
|
-
it
|
|
42
|
-
expect(HledgerForecast::Generator.generate(computed_config)).to
|
|
41
|
+
it "generates a forecast with correct MONTHLY transactions that have a COMPUTED end date" do
|
|
42
|
+
expect(HledgerForecast::Generator.generate(computed_config)).to(eq(computed_output))
|
|
43
43
|
end
|
|
44
44
|
end
|
data/spec/monthly_spec.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
2
|
|
|
3
3
|
config = <<~CSV
|
|
4
4
|
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
|
|
@@ -10,15 +10,15 @@ CSV
|
|
|
10
10
|
|
|
11
11
|
output = <<~JOURNAL
|
|
12
12
|
~ monthly from 2023-03-01 * Bills, Food, Savings
|
|
13
|
-
Expenses:Bills £175.00
|
|
14
|
-
Expenses:Food £500.00
|
|
15
|
-
Assets:Savings £-1,000.00
|
|
13
|
+
Expenses:Bills £175.00
|
|
14
|
+
Expenses:Food £500.00
|
|
15
|
+
Assets:Savings £-1,000.00
|
|
16
16
|
Assets:Bank
|
|
17
17
|
|
|
18
18
|
JOURNAL
|
|
19
19
|
|
|
20
|
-
RSpec.describe
|
|
21
|
-
it
|
|
22
|
-
expect(HledgerForecast::Generator.generate(config)).to
|
|
20
|
+
RSpec.describe "generate" do
|
|
21
|
+
it "generates a forecast with correct MONTHLY transactions" do
|
|
22
|
+
expect(HledgerForecast::Generator.generate(config)).to(eq(output))
|
|
23
23
|
end
|
|
24
24
|
end
|
data/spec/once_spec.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
2
|
|
|
3
3
|
config = <<~CSV
|
|
4
4
|
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
|
|
@@ -8,14 +8,14 @@ CSV
|
|
|
8
8
|
|
|
9
9
|
output = <<~JOURNAL
|
|
10
10
|
~ 2023-07-01 * New Kitchen
|
|
11
|
-
Expenses:House £5,000.00
|
|
11
|
+
Expenses:House £5,000.00
|
|
12
12
|
Assets:Bank
|
|
13
13
|
|
|
14
14
|
JOURNAL
|
|
15
15
|
|
|
16
|
-
RSpec.describe
|
|
17
|
-
it
|
|
16
|
+
RSpec.describe "generate" do
|
|
17
|
+
it "generates a forecast with correct ONCE transactions" do
|
|
18
18
|
generated_journal = HledgerForecast::Generator.generate(config)
|
|
19
|
-
expect(generated_journal).to
|
|
19
|
+
expect(generated_journal).to(eq(output))
|
|
20
20
|
end
|
|
21
21
|
end
|
data/spec/quarterly_spec.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
require_relative
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
2
|
|
|
3
3
|
config = <<~CSV
|
|
4
4
|
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
|
|
@@ -8,13 +8,13 @@ CSV
|
|
|
8
8
|
|
|
9
9
|
output = <<~JOURNAL
|
|
10
10
|
~ every 3 months from 2023-04-01 * Bonus
|
|
11
|
-
Income:Bonus £-1,000.00
|
|
11
|
+
Income:Bonus £-1,000.00
|
|
12
12
|
Assets:Bank
|
|
13
13
|
|
|
14
14
|
JOURNAL
|
|
15
15
|
|
|
16
|
-
RSpec.describe
|
|
17
|
-
it
|
|
18
|
-
expect(HledgerForecast::Generator.generate(config)).to
|
|
16
|
+
RSpec.describe "generate" do
|
|
17
|
+
it "generates a forecast with correct QUARTERLY transactions" do
|
|
18
|
+
expect(HledgerForecast::Generator.generate(config)).to(eq(output))
|
|
19
19
|
end
|
|
20
20
|
end
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
require_relative "../lib/hledger_forecast"
|
|
2
|
+
|
|
3
|
+
RSpec.describe HledgerForecast::Settings do
|
|
4
|
+
def settings_rows_from(csv)
|
|
5
|
+
CSV
|
|
6
|
+
.parse(csv, headers: true, header_converters: -> (h) { h.to_s.tr("-", "_").to_sym }, converters: :numeric)
|
|
7
|
+
.select { |r| r[:type] == "settings" }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
describe "defaults when no settings rows are present" do
|
|
11
|
+
let(:rows) {
|
|
12
|
+
settings_rows_from("type,frequency,account,from,to,description,category,amount,roll_up,summary_exclude\n")
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
it "defaults to USD" do
|
|
16
|
+
expect(described_class.parse(rows).currency).to(eq("USD"))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "shows the currency symbol" do
|
|
20
|
+
expect(described_class.parse(rows).show_symbol).to(eq(true))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "uses a thousands separator" do
|
|
24
|
+
expect(described_class.parse(rows).thousands_separator).to(eq(","))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "is not verbose" do
|
|
28
|
+
expect(described_class.parse(rows).verbose?).to(eq(false))
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
describe "single settings row" do
|
|
33
|
+
let(:rows) do
|
|
34
|
+
settings_rows_from(
|
|
35
|
+
<<~CSV
|
|
36
|
+
type,frequency,account,from,to,description,category,amount,roll_up,summary_exclude
|
|
37
|
+
settings,currency,GBP,,,,,,,,
|
|
38
|
+
CSV
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "applies the currency" do
|
|
43
|
+
expect(described_class.parse(rows).currency).to(eq("GBP"))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "retains other defaults" do
|
|
47
|
+
settings = described_class.parse(rows)
|
|
48
|
+
expect(settings.show_symbol).to(eq(true))
|
|
49
|
+
expect(settings.thousands_separator).to(eq(","))
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe "multiple settings rows do not clobber each other" do
|
|
54
|
+
let(:rows) do
|
|
55
|
+
settings_rows_from(
|
|
56
|
+
<<~CSV
|
|
57
|
+
type,frequency,account,from,to,description,category,amount,roll_up,summary_exclude
|
|
58
|
+
settings,currency,GBP,,,,,,,,
|
|
59
|
+
settings,show_symbol,false,,,,,,,,
|
|
60
|
+
settings,thousands_separator,false,,,,,,,,
|
|
61
|
+
CSV
|
|
62
|
+
)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
it "preserves all three settings independently" do
|
|
66
|
+
settings = described_class.parse(rows)
|
|
67
|
+
expect(settings.currency).to(eq("GBP"))
|
|
68
|
+
expect(settings.show_symbol).to(eq("false"))
|
|
69
|
+
expect(settings.thousands_separator).to(eq("false"))
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe "cli_options take precedence over csv settings" do
|
|
74
|
+
let(:rows) do
|
|
75
|
+
settings_rows_from(
|
|
76
|
+
<<~CSV
|
|
77
|
+
type,frequency,account,from,to,description,category,amount,roll_up,summary_exclude
|
|
78
|
+
settings,currency,GBP,,,,,,,,
|
|
79
|
+
CSV
|
|
80
|
+
)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "overrides currency from cli" do
|
|
84
|
+
expect(described_class.parse(rows, {currency: "EUR"}).currency).to(eq("EUR"))
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
describe "cli-only options" do
|
|
89
|
+
let(:rows) {
|
|
90
|
+
settings_rows_from("type,frequency,account,from,to,description,category,amount,roll_up,summary_exclude\n")
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
it "stores verbose from cli_options" do
|
|
94
|
+
expect(described_class.parse(rows, {verbose: true}).verbose?).to(eq(true))
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it "stores roll_up from cli_options" do
|
|
98
|
+
expect(described_class.parse(rows, {roll_up: "monthly"}).roll_up).to(eq("monthly"))
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
data/spec/stubs/forecast.csv
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude
|
|
2
|
-
monthly,,Assets:Bank,01/03/2023,,Mortgage,Expenses:Mortgage,2000.55
|
|
3
|
-
monthly,,Assets:Bank,01/03/2023,,Food,Expenses:Food,100
|
|
4
|
-
monthly,,Assets:Savings,01/03/2023,,Savings,Assets:Bank,-1000
|
|
1
|
+
type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude
|
|
2
|
+
monthly,,Assets:Bank,01/03/2023,,Mortgage,Expenses:Mortgage,2000.55,,
|
|
3
|
+
monthly,,Assets:Bank,01/03/2023,,Food,Expenses:Food,100,,
|
|
4
|
+
monthly,,Assets:Savings,01/03/2023,,Savings,Assets:Bank,-1000,,
|
|
5
5
|
settings,currency,GBP,,,,,,,,
|