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.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/{test.yml → ci.yml} +18 -10
  3. data/.github/workflows/publish_ruby_gem.yml +24 -0
  4. data/.github/workflows/release.yml +12 -13
  5. data/.mise.toml +2 -0
  6. data/CHANGELOG.md +17 -0
  7. data/Gemfile +1 -0
  8. data/README.md +149 -119
  9. data/example.csv +15 -15
  10. data/example.journal +17 -18
  11. data/hledger-forecast.gemspec +20 -18
  12. data/lib/hledger_forecast/calculator.rb +7 -15
  13. data/lib/hledger_forecast/cli.rb +98 -71
  14. data/lib/hledger_forecast/comparator.rb +12 -11
  15. data/lib/hledger_forecast/forecast.rb +29 -0
  16. data/lib/hledger_forecast/formatter.rb +13 -15
  17. data/lib/hledger_forecast/generator.rb +32 -72
  18. data/lib/hledger_forecast/settings.rb +34 -47
  19. data/lib/hledger_forecast/summarizer.rb +34 -55
  20. data/lib/hledger_forecast/summarizer_formatter.rb +75 -78
  21. data/lib/hledger_forecast/transaction.rb +63 -0
  22. data/lib/hledger_forecast/transactions/default.rb +45 -72
  23. data/lib/hledger_forecast/version.rb +1 -1
  24. data/lib/hledger_forecast.rb +21 -22
  25. data/spec/calculator_spec.rb +45 -0
  26. data/spec/cli_spec.rb +19 -17
  27. data/spec/compare_spec.rb +16 -14
  28. data/spec/computed_amounts_spec.rb +7 -7
  29. data/spec/custom_spec.rb +9 -9
  30. data/spec/formatter_spec.rb +51 -0
  31. data/spec/half-yearly_spec.rb +5 -5
  32. data/spec/monthly_end_date_spec.rb +6 -6
  33. data/spec/monthly_end_date_transaction_spec.rb +10 -10
  34. data/spec/monthly_spec.rb +7 -7
  35. data/spec/once_spec.rb +5 -5
  36. data/spec/quarterly_spec.rb +5 -5
  37. data/spec/settings_spec.rb +101 -0
  38. data/spec/stubs/forecast.csv +4 -4
  39. data/spec/summarizer_spec.rb +28 -33
  40. data/spec/tags_spec.rb +92 -0
  41. data/spec/verbose_output_spec.rb +8 -8
  42. data/spec/yearly_spec.rb +5 -5
  43. metadata +49 -13
  44. data/lib/hledger_forecast/transactions/modifiers.rb +0 -90
  45. data/lib/hledger_forecast/transactions/trackers.rb +0 -88
  46. data/lib/hledger_forecast/utilities.rb +0 -14
  47. data/spec/track_spec.rb +0 -105
@@ -1,29 +1,28 @@
1
1
  #!/usr/bin/env ruby
2
2
 
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'
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 = 'USD'
14
+ Money.default_currency = "USD"
15
15
 
16
- require_relative 'hledger_forecast/calculator'
17
- require_relative 'hledger_forecast/cli'
18
- require_relative 'hledger_forecast/comparator'
19
- require_relative 'hledger_forecast/formatter'
20
- require_relative 'hledger_forecast/generator'
21
- require_relative 'hledger_forecast/settings'
22
- require_relative 'hledger_forecast/summarizer'
23
- require_relative 'hledger_forecast/summarizer_formatter'
24
- require_relative 'hledger_forecast/utilities'
25
- require_relative 'hledger_forecast/version'
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 'hledger_forecast/transactions/default'
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 '../lib/hledger_forecast'
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 ; Mortgage
6
- Expenses:Food £100.00 ; Food
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 ; Savings
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 'command' do
29
- it 'uses the CLI to generate an output with a CSV config file' do
30
- generated_journal = './test_output.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 eq(output)
35
+ expect(File.read(generated_journal)).to(eq(output))
36
36
  end
37
37
 
38
- it 'uses the CLI to compare two CSV files' do
39
- expected_output = strip_ansi_codes(<<~OUTPUT)
40
- +---------+---------+---------+
41
- | account | 2023-07 | 2023-08 |
42
- +---------+---------+---------+
43
- | total | £10.00 | €-10.00 |
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
- OUTPUT
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 eq(expected_output)
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 '../lib/hledger_forecast'
2
- require 'stringio'
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 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))
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(<<~OUTPUT)
44
- +---------+---------+----------+---------+
45
- | account | 2023-07 | 2023-08 | 2023-09 |
46
- +---------+---------+----------+---------+
47
- | total | £10.00 | €-400.00 | 1144.00 |
48
- +---------+---------+----------+---------+
49
- OUTPUT
50
-
51
- actual_output = capture_stdout { comparator.compare('file1.csv', 'file2.csv') }
52
- expect(strip_ansi_codes(actual_output)).to eq(expected_output)
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 '../lib/hledger_forecast'
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 ; New kitchen
14
- Expenses:Food £107.50 ; Monthly food shop
15
- Expenses:Food £52.88 ; Random food
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 'generate' do
21
- it 'generates a forecast with correct CALCULATED transactions' do
22
- expect(HledgerForecast::Generator.generate(config)).to eq(output)
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 '../lib/hledger_forecast'
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 ; Hair and beauty
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 ; Food
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 ; Hair and beauty
29
+ [Expenses:Personal Care] £80.00
30
30
  [Assets:Bank]
31
31
 
32
32
  JOURNAL
33
33
 
34
- RSpec.describe 'generate' do
35
- it 'generates a forecast with correct CUSTOM transactions' do
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 eq(base_output)
37
+ expect(generated_journal).to(eq(base_output))
38
38
  end
39
39
 
40
- it 'generates a forecast with correct CUSTOM transactions and CALCULATED to dates' do
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 eq(calculated_output)
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
@@ -1,4 +1,4 @@
1
- require_relative '../lib/hledger_forecast'
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 ; Holiday
11
+ Expenses:Holiday £500.00
12
12
  Assets:Bank
13
13
 
14
14
  JOURNAL
15
15
 
16
- RSpec.describe 'generate' do
17
- it 'generates a forecast with correct HALF-YEARLY transactions' do
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 eq(output)
20
+ expect(generated_journal).to(eq(output))
21
21
  end
22
22
  end
@@ -1,4 +1,4 @@
1
- require_relative '../lib/hledger_forecast'
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 ; Mortgage
13
- Expenses:Food £100.00 ; Food
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 '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)
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 '../lib/hledger_forecast'
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 ; Mortgage
14
- Expenses:Mortgage £200.00 ; Mortgage top up
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 ; Food
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 ; Mortgage
31
+ Expenses:Mortgage £2,000.00
32
32
  Assets:Bank
33
33
 
34
34
  JOURNAL
35
35
 
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)
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 '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)
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 '../lib/hledger_forecast'
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 ; Bills
14
- Expenses:Food £500.00 ; Food
15
- Assets:Savings £-1,000.00; Savings
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 'generate' do
21
- it 'generates a forecast with correct MONTHLY transactions' do
22
- expect(HledgerForecast::Generator.generate(config)).to eq(output)
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 '../lib/hledger_forecast'
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; New Kitchen
11
+ Expenses:House £5,000.00
12
12
  Assets:Bank
13
13
 
14
14
  JOURNAL
15
15
 
16
- RSpec.describe 'generate' do
17
- it 'generates a forecast with correct ONCE transactions' do
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 eq(output)
19
+ expect(generated_journal).to(eq(output))
20
20
  end
21
21
  end
@@ -1,4 +1,4 @@
1
- require_relative '../lib/hledger_forecast'
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; Bonus
11
+ Income:Bonus £-1,000.00
12
12
  Assets:Bank
13
13
 
14
14
  JOURNAL
15
15
 
16
- RSpec.describe 'generate' do
17
- it 'generates a forecast with correct QUARTERLY transactions' do
18
- expect(HledgerForecast::Generator.generate(config)).to eq(output)
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
@@ -1,5 +1,5 @@
1
- type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
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,,,,,,,,