hledger-forecast 1.5.2 → 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.
@@ -6,67 +6,46 @@ module HledgerForecast
6
6
  end
7
7
 
8
8
  def summarize(config, cli_options = nil)
9
- @forecast = YAML.safe_load(config)
9
+ @forecast = CSV.parse(config, headers: true)
10
10
  @settings = Settings.config(@forecast, cli_options)
11
11
 
12
- return { output: generate(@forecast), settings: @settings }
12
+ { output: generate(@forecast), settings: @settings }
13
13
  end
14
14
 
15
15
  private
16
16
 
17
17
  def generate(forecast)
18
- output = {}
19
- forecast.each do |period, blocks|
20
- next if %w[settings].include?(period)
21
-
22
- blocks.each do |block|
23
- key = if @settings[:roll_up].nil?
24
- period
25
- else
26
- output.length
27
- end
28
-
29
- output[key] ||= []
30
- output[key] << process_block(period, block)
31
- end
32
- end
33
-
34
- output = filter_out(flatten_and_merge(output))
35
- output = calculate_rolled_up_amount(output) unless @settings[:roll_up].nil?
36
-
37
- output
38
- end
39
-
40
- def process_block(period, block)
41
18
  output = []
42
19
 
43
- output << {
44
- account: block['account'],
45
- from: Date.parse(block['from']),
46
- to: block['to'] ? Date.parse(block['to']) : nil,
47
- type: period,
48
- frequency: block['frequency'],
49
- transactions: []
50
- }
20
+ forecast.each do |row|
21
+ next if row['type'] == 'settings'
22
+ next if row['summary_exclude']
51
23
 
52
- process_transactions(period, block, output)
53
- end
24
+ row['amount'] = Utilities.convert_amount(row['amount'])
54
25
 
55
- def process_transactions(period, block, output)
56
- block['transactions'].each do |t|
57
- amount = Calculator.new.evaluate(t['amount'])
26
+ begin
27
+ annualised_amount = row['roll-up'] ? row['amount'] * row['roll-up'].to_f : row['amount'] * annualise(row['type'])
28
+ rescue StandardError
29
+ puts "\nError: ".bold.red + 'Could not create an annualised ammount. Have you set the roll-up for your custom type transactions?'
30
+ exit
31
+ end
58
32
 
59
- output.last[:transactions] << {
60
- amount: amount,
61
- annualised_amount: amount * (t['roll-up'] || annualise(period)),
62
- rolled_up_amount: 0,
63
- category: t['category'],
64
- exclude: t['summary_exclude'],
65
- description: t['description'],
66
- to: t['to'] ? Calculator.new.evaluate_date(Date.parse(block['from']), t['to']) : nil
33
+ output << {
34
+ account: row['account'],
35
+ from: Date.parse(row['from']),
36
+ to: row['to'] ? Calculator.new.evaluate_date(Date.parse(row['from']), row['to']) : nil,
37
+ type: row['type'],
38
+ frequency: row['frequency'],
39
+ category: row['category'],
40
+ description: row['description'],
41
+ amount: row['amount'],
42
+ annualised_amount: annualised_amount.to_f,
43
+ exclude: row['summary_exclude']
67
44
  }
68
45
  end
69
46
 
47
+ output = calculate_rolled_up_amount(output) unless @settings[:roll_up].nil?
48
+
70
49
  output
71
50
  end
72
51
 
@@ -84,22 +63,9 @@ module HledgerForecast
84
63
  annualise[period]
85
64
  end
86
65
 
87
- def filter_out(data)
88
- data.reject { |item| item[:exclude] == true }
89
- end
90
-
91
- def flatten_and_merge(blocks)
92
- blocks.values.flatten.flat_map do |block|
93
- block[:transactions].map do |transaction|
94
- block.slice(:account, :from, :to, :type, :frequency).merge(transaction)
95
- end
96
- end
97
- end
98
-
99
- def calculate_rolled_up_amount(data)
100
- data.map do |item|
101
- item[:rolled_up_amount] = item[:annualised_amount] / annualise(@settings[:roll_up])
102
- item
66
+ def calculate_rolled_up_amount(forecast)
67
+ forecast.each do |row|
68
+ row[:rolled_up_amount] = row[:annualised_amount] / annualise(@settings[:roll_up])
103
69
  end
104
70
  end
105
71
  end
@@ -41,7 +41,7 @@ module HledgerForecast
41
41
  @table.add_row([{ value: type.capitalize.bold, colspan: 3, alignment: :center }])
42
42
  total = 0
43
43
  items.each do |item|
44
- total += item[:amount]
44
+ total += item[:amount].to_f
45
45
 
46
46
  if @settings[:verbose]
47
47
  @table.add_row [{ value: item[:category], alignment: :left },
@@ -63,7 +63,7 @@ module HledgerForecast
63
63
  def sort(data)
64
64
  data.each do |type, items|
65
65
  data[type] = items.sort_by do |item|
66
- value = item[:amount]
66
+ value = item[:amount].to_f
67
67
  [value >= 0 ? 1 : 0, value >= 0 ? -value : value]
68
68
  end
69
69
  end
@@ -114,11 +114,11 @@ module HledgerForecast
114
114
 
115
115
  def add_total_row_to_table(data, row_to_sum)
116
116
  total = data.reduce(0) do |sum, item|
117
- sum + item[row_to_sum]
117
+ sum + item[row_to_sum].to_f
118
118
  end
119
119
 
120
120
  income = data.reduce(0) do |sum, item|
121
- sum += item[row_to_sum] if item[row_to_sum] < 0
121
+ sum += item[row_to_sum].to_f if item[row_to_sum].to_f < 0
122
122
  sum
123
123
  end
124
124
 
@@ -6,15 +6,15 @@ module HledgerForecast
6
6
  # Expenses:Groceries $250.00 ; Food expenses
7
7
  # Assets:Checking
8
8
  class Default
9
- def self.generate(data, options)
10
- new(data, options).generate
9
+ def self.generate(forecast, settings)
10
+ new(forecast, settings).generate
11
11
  end
12
12
 
13
13
  def generate
14
- data.each_value do |blocks|
15
- blocks.each do |block|
16
- process_block(block)
17
- end
14
+ forecast.each do |row|
15
+ next if row[:type] == "settings"
16
+
17
+ process_transactions(row)
18
18
  end
19
19
 
20
20
  output
@@ -22,59 +22,34 @@ module HledgerForecast
22
22
 
23
23
  private
24
24
 
25
- attr_reader :data, :options, :output
25
+ attr_reader :forecast, :settings, :output
26
26
 
27
- def initialize(data, options)
28
- @data = data
29
- @options = options
27
+ def initialize(forecast, settings)
28
+ @forecast = forecast
29
+ @settings = settings
30
30
  @output = []
31
31
  end
32
32
 
33
- def process_block(block)
34
- block[:transactions].each do |to, transactions|
35
- to = get_header(block[:to], to)
36
-
37
- if block[:type] == "custom"
38
- process_custom_transactions(block, to, transactions)
39
- else
40
- process_standard_transactions(block, to, transactions)
41
- end
42
- end
43
- end
44
-
45
- def process_custom_transactions(block, to, transactions)
46
- transactions.each do |t|
47
- frequency = get_periodic_rules(block[:type], t[:frequency])
33
+ def process_transactions(row)
34
+ to = build_to_header(row[:to])
35
+ frequency = get_periodic_rules(row[:type], row[:frequency])
48
36
 
49
- header = build_header(block, to, frequency, t[:description])
50
- footer = build_footer(block)
51
- output << build_transaction(header, [t], footer)
37
+ if @settings[:verbose]
38
+ description = row[:description]
39
+ transactions = [row]
40
+ else
41
+ description = get_descriptions(row[:transactions])
42
+ transactions = row[:transactions]
52
43
  end
53
- end
54
44
 
55
- def process_standard_transactions(block, to, transactions)
56
- if @options[:verbose]
57
- transactions.map do |t|
58
- # Skip transactions that have been marked as tracked
59
- next if t[:track]
60
-
61
- frequency = get_periodic_rules(block[:type], block[:frequency])
62
- header = build_header(block, to, frequency, t[:description])
63
- footer = build_footer(block)
64
- output << build_transaction(header, [t], footer)
65
- end
66
- return
67
- end
45
+ header = build_header(row, frequency, to, description)
46
+ footer = build_footer(row)
68
47
 
69
- block[:descriptions] = get_descriptions(transactions)
70
- frequency = get_periodic_rules(block[:type], block[:frequency])
71
- header = build_header(block, to, frequency, block[:descriptions])
72
- footer = build_footer(block)
73
48
  output << build_transaction(header, transactions, footer)
74
49
  end
75
50
 
76
- def build_header(block, to, frequency, description)
77
- "#{frequency} #{block[:from]}#{to} * #{description}\n"
51
+ def build_header(row, frequency, to, descriptions)
52
+ "#{frequency} #{row[:from]}#{to} * #{descriptions}\n"
78
53
  end
79
54
 
80
55
  def build_footer(block)
@@ -85,11 +60,8 @@ module HledgerForecast
85
60
  { header: header, transactions: write_transactions(transactions), footer: footer }
86
61
  end
87
62
 
88
- def get_header(block, transaction)
89
- return " to #{transaction}" if transaction
90
- return " to #{block}" if block
91
-
92
- return nil
63
+ def build_to_header(to)
64
+ return " to #{to}" if to
93
65
  end
94
66
 
95
67
  def get_descriptions(transactions)
@@ -110,7 +82,6 @@ module HledgerForecast
110
82
  'yearly' => '~ yearly from',
111
83
  'custom' => "~ #{frequency} from"
112
84
  }
113
-
114
85
  map[type]
115
86
  end
116
87
 
@@ -119,11 +90,11 @@ module HledgerForecast
119
90
  # Skip transactions that have been marked as tracked
120
91
  next if t[:track]
121
92
 
122
- t[:amount] = t[:amount].to_s.ljust(options[:max_amount])
123
- t[:category] = t[:category].ljust(options[:max_category])
93
+ t[:amount] = t[:amount].to_s.ljust(@settings[:max_amount] + 5)
94
+ t[:category] = t[:category].to_s.ljust(@settings[:max_category])
124
95
 
125
96
  " #{t[:category]} #{t[:amount]}; #{t[:description]}\n"
126
- end
97
+ end.compact
127
98
  end
128
99
  end
129
100
  end
@@ -9,84 +9,78 @@ module HledgerForecast
9
9
  # Expenses:Groceries $250.00 ; Food expenses
10
10
  # Assets:Checking
11
11
  class Trackers
12
- def self.generate(data, options)
13
- new(data, options).generate
12
+ def self.generate(forecast, options)
13
+ new(forecast, options).generate
14
14
  end
15
15
 
16
16
  def generate
17
- return nil unless tracked?(data)
17
+ return if @options[:no_track]
18
+ return nil unless tracked?(forecast)
18
19
 
19
- data.each_value do |blocks|
20
- blocks.each do |block|
21
- process_tracked(block)
22
- end
20
+ forecast.each do |row|
21
+ process_tracked(row)
23
22
  end
24
23
 
25
24
  output
26
25
  end
27
26
 
28
- def self.track?(transaction, data, options)
27
+ def self.track?(row, options)
29
28
  now = Date.today
30
- transaction['track'] && Date.parse(data['from']) <= now && !exists?(transaction, data['account'],
31
- data['from'], now, options)
29
+ row['track'] && Date.parse(row['from']) <= now && !exists?(row, now, options)
32
30
  end
33
31
 
34
- def self.exists?(transaction, account, from, to, options)
35
-
36
- if !options[:transaction_file]
32
+ def self.exists?(row, now, options)
33
+ unless options[:transaction_file]
37
34
  puts "\nWarning: ".bold.yellow + "For tracked transactions, please specify a file with the `-t` flag"
38
35
  puts "ERROR: ".bold.red + "Tracked transactions ignored for now"
39
36
  return
40
37
  end
41
38
 
42
39
  # Format the money
43
- amount = Formatter.format_money(transaction['amount'], options)
44
- inverse_amount = Formatter.format_money(transaction['amount'] * -1, options)
40
+ amount = Formatter.format_money(row['amount'], options)
41
+ inverse_amount = Formatter.format_money(row['amount'] * -1, options)
45
42
 
46
- category = transaction['category'].gsub('[', '\\[').gsub(']', '\\]').gsub('(', '\\(').gsub(')', '\\)')
43
+ from = Date.parse(row['from'])
44
+ category = row['category'].gsub('[', '\\[').gsub(']', '\\]').gsub('(', '\\(').gsub(')', '\\)')
47
45
 
48
46
  # We run two commands and check to see if category +/- amount or account +/- amount exists
49
- command1 = %(hledger print -f #{options[:transaction_file]} "date:#{from}..#{to}" | tr -s '[:space:]' ' ' | grep -q -Eo "#{category} (#{amount}|#{inverse_amount})")
50
- command2 = %(hledger print -f #{options[:transaction_file]} "date:#{from}..#{to}" | tr -s '[:space:]' ' ' | grep -q -Eo "#{account} (#{amount}|#{inverse_amount})")
47
+ command1 = %(hledger print -f #{options[:transaction_file]} "date:#{from}..#{now}" | tr -s '[:space:]' ' ' | grep -q -Eo "#{category} (#{amount}|#{inverse_amount})")
48
+ command2 = %(hledger print -f #{options[:transaction_file]} "date:#{from}..#{now}" | tr -s '[:space:]' ' ' | grep -q -Eo "#{row['account']} (#{amount}|#{inverse_amount})")
51
49
 
52
50
  system(command1) || system(command2)
53
51
  end
54
52
 
55
53
  private
56
54
 
57
- attr_reader :data, :options, :output
55
+ attr_reader :forecast, :options, :output
58
56
 
59
- def initialize(data, options)
60
- @data = data
57
+ def initialize(forecast, options)
58
+ @forecast = forecast
61
59
  @options = options
62
60
  @output = []
63
61
  end
64
62
 
65
- def tracked?(data)
66
- data.any? do |_, blocks|
67
- blocks.any? do |block|
68
- block[:transactions].any? do |_, transactions|
69
- transactions.any? { |t| t[:track] }
70
- end
71
- end
63
+ def tracked?(forecast)
64
+ forecast.any? do |row|
65
+ return true if row[:track] == true
72
66
  end
67
+
68
+ return false
73
69
  end
74
70
 
75
- def process_tracked(block)
76
- block[:transactions].each do |_to, transactions|
77
- transactions.each do |t|
78
- next unless t[:track]
71
+ def process_tracked(row)
72
+ row[:transactions].each do |t|
73
+ next if t[:track] == false
79
74
 
80
- category = t[:category].ljust(options[:max_category])
81
- amount = t[:amount].to_s.ljust(options[:max_amount])
75
+ category = t[:category].ljust(options[:max_category])
76
+ amount = t[:amount].to_s.ljust(options[:max_amount])
82
77
 
83
- header = "~ #{Date.new(Date.today.year, Date.today.month,
84
- 1).next_month} * [TRACKED] #{t[:description]}\n"
85
- transactions = " #{category} #{amount}; #{t[:description]}\n"
86
- footer = " #{block[:account]}\n\n"
78
+ header = "~ #{Date.new(Date.today.year, Date.today.month,
79
+ 1).next_month} * [TRACKED] #{t[:description]}\n"
80
+ transactions = " #{category} #{amount}; #{t[:description]}\n"
81
+ footer = " #{row[:account]}\n\n"
87
82
 
88
- output << { header: header, transactions: [transactions], footer: footer }
89
- end
83
+ output << { header: header, transactions: [transactions], footer: footer }
90
84
  end
91
85
  end
92
86
  end
@@ -0,0 +1,14 @@
1
+ module HledgerForecast
2
+ class Utilities
3
+ def self.convert_amount(amount)
4
+ case amount
5
+ when /^-?\d+\.\d+$/ # Detects floating-point numbers (including negatives)
6
+ amount.to_f
7
+ when /^-?\d+$/ # Detects integers (including negatives)
8
+ amount.to_i
9
+ else
10
+ amount
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,3 +1,3 @@
1
1
  module HledgerForecast
2
- VERSION = "1.5.2".freeze
2
+ VERSION = "2.0.0".freeze
3
3
  end
@@ -8,7 +8,6 @@ require 'highline'
8
8
  require 'money'
9
9
  require 'optparse'
10
10
  require 'terminal-table'
11
- require 'yaml'
12
11
 
13
12
  Money.locale_backend = nil
14
13
  Money.rounding_mode = BigDecimal::ROUND_HALF_UP
@@ -17,12 +16,12 @@ Money.default_currency = 'USD'
17
16
  require_relative 'hledger_forecast/calculator'
18
17
  require_relative 'hledger_forecast/cli'
19
18
  require_relative 'hledger_forecast/comparator'
20
- require_relative 'hledger_forecast/csv_parser'
21
19
  require_relative 'hledger_forecast/formatter'
22
20
  require_relative 'hledger_forecast/generator'
23
21
  require_relative 'hledger_forecast/settings'
24
22
  require_relative 'hledger_forecast/summarizer'
25
23
  require_relative 'hledger_forecast/summarizer_formatter'
24
+ require_relative 'hledger_forecast/utilities'
26
25
  require_relative 'hledger_forecast/version'
27
26
 
28
27
  require_relative 'hledger_forecast/transactions/default'
data/spec/cli_spec.rb CHANGED
@@ -2,12 +2,12 @@ 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 ; Mortgage
6
+ Expenses:Food £100.00 ; Food
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 ; Savings
11
11
  Assets:Savings
12
12
 
13
13
  JOURNAL
@@ -26,15 +26,6 @@ ensure
26
26
  end
27
27
 
28
28
  RSpec.describe 'command' do
29
- it 'uses the CLI to generate an output' do
30
- generated_journal = './test_output.journal'
31
- File.delete(generated_journal) if File.exist?(generated_journal)
32
-
33
- system("./bin/hledger-forecast generate -f ./spec/stubs/forecast.yml -o ./test_output.journal --force")
34
-
35
- expect(File.read(generated_journal)).to eq(output)
36
- end
37
-
38
29
  it 'uses the CLI to generate an output with a CSV config file' do
39
30
  generated_journal = './test_output.journal'
40
31
  File.delete(generated_journal) if File.exist?(generated_journal)
@@ -1,29 +1,18 @@
1
1
  require_relative '../lib/hledger_forecast'
2
2
 
3
- config = <<~YAML
4
- settings:
5
- currency: GBP
6
-
7
- monthly:
8
- - account: "Liabilities:Amex"
9
- from: "2023-05-01"
10
- transactions:
11
- - amount: "=5000/24"
12
- category: "Expenses:House"
13
- description: New Kitchen
14
- - amount: "=25*4.3"
15
- category: "Expenses:Food"
16
- description: Monthly food shop
17
- - amount: "=(102.50+3.25)/2"
18
- category: "Expenses:Food"
19
- description: Random food
20
- YAML
3
+ config = <<~CSV
4
+ type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
5
+ monthly,,Liabilities:Amex,01/05/2023,,New kitchen,Expenses:House,=5000/24,,,
6
+ monthly,,Liabilities:Amex,01/05/2023,,Monthly food shop,Expenses:Food,=25*4.3,,,
7
+ monthly,,Liabilities:Amex,01/05/2023,,Random food,Expenses:Food,=(102.50+3.25)/2,,,
8
+ settings,currency,GBP,,,,,,,,
9
+ CSV
21
10
 
22
11
  output = <<~JOURNAL
23
- ~ monthly from 2023-05-01 * New Kitchen, Monthly food shop, Random food
24
- Expenses:House £208.33 ; New Kitchen
25
- Expenses:Food £107.50 ; Monthly food shop
26
- Expenses:Food £52.88 ; Random food
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
27
16
  Liabilities:Amex
28
17
 
29
18
  JOURNAL
data/spec/custom_spec.rb CHANGED
@@ -1,52 +1,32 @@
1
1
  require_relative '../lib/hledger_forecast'
2
2
 
3
- base_config = <<~YAML
4
- custom:
5
- - account: "[Assets:Bank]"
6
- from: "2023-05-01"
7
- transactions:
8
- - amount: 80
9
- category: "[Expenses:Personal Care]"
10
- description: Hair and beauty
11
- frequency: "every 2 weeks"
12
- - amount: 50
13
- category: "[Expenses:Groceries]"
14
- description: Gotta feed that stomach
15
- frequency: "every 5 days"
16
-
17
- settings:
18
- currency: GBP
19
- YAML
3
+ base_config = <<~CSV
4
+ type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
5
+ custom,every 2 weeks,[Assets:Bank],01/05/2023,,Hair and beauty,[Expenses:Personal Care],80,,,
6
+ custom,every 5 days,[Assets:Bank],01/05/2023,,Food,[Expenses:Groceries],50,,,
7
+ settings,currency,GBP,,,,,,,,
8
+ CSV
20
9
 
21
10
  base_output = <<~JOURNAL
22
11
  ~ every 2 weeks from 2023-05-01 * Hair and beauty
23
- [Expenses:Personal Care] £80.00; Hair and beauty
12
+ [Expenses:Personal Care] £80.00 ; Hair and beauty
24
13
  [Assets:Bank]
25
14
 
26
- ~ every 5 days from 2023-05-01 * Gotta feed that stomach
27
- [Expenses:Groceries] £50.00; Gotta feed that stomach
15
+ ~ every 5 days from 2023-05-01 * Food
16
+ [Expenses:Groceries] £50.00 ; Food
28
17
  [Assets:Bank]
29
18
 
30
19
  JOURNAL
31
20
 
32
- calculated_config = <<~YAML
33
- custom:
34
- - account: "[Assets:Bank]"
35
- from: "2023-05-01"
36
- transactions:
37
- - amount: 80
38
- category: "[Expenses:Personal Care]"
39
- description: Hair and beauty
40
- frequency: "every 2 weeks"
41
- to: "=6"
42
-
43
- settings:
44
- currency: GBP
45
- YAML
21
+ calculated_config = <<~CSV
22
+ type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
23
+ custom,every 2 weeks,[Assets:Bank],01/05/2023,=6,Hair and beauty,[Expenses:Personal Care],80,,,
24
+ settings,currency,GBP,,,,,,,,
25
+ CSV
46
26
 
47
27
  calculated_output = <<~JOURNAL
48
28
  ~ every 2 weeks from 2023-05-01 to 2023-10-31 * Hair and beauty
49
- [Expenses:Personal Care] £80.00; Hair and beauty
29
+ [Expenses:Personal Care] £80.00 ; Hair and beauty
50
30
  [Assets:Bank]
51
31
 
52
32
  JOURNAL
@@ -1,21 +1,14 @@
1
1
  require_relative '../lib/hledger_forecast'
2
2
 
3
- config = <<~YAML
4
- settings:
5
- currency: GBP
6
-
7
- half-yearly:
8
- - from: "2023-04-01"
9
- account: "Assets:Bank"
10
- transactions:
11
- - description: Holiday
12
- category: "Expenses:Holiday"
13
- amount: 500
14
- YAML
3
+ config = <<~CSV
4
+ type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
5
+ half-yearly,,Assets:Bank,01/04/2023,,Holiday,Expenses:Holiday,500,,,
6
+ settings,currency,GBP,,,,,,,,
7
+ CSV
15
8
 
16
9
  output = <<~JOURNAL
17
10
  ~ every 6 months from 2023-04-01 * Holiday
18
- Expenses:Holiday £500.00; Holiday
11
+ Expenses:Holiday £500.00 ; Holiday
19
12
  Assets:Bank
20
13
 
21
14
  JOURNAL
@@ -1,26 +1,16 @@
1
1
  require_relative '../lib/hledger_forecast'
2
2
 
3
- config = <<~YAML
4
- settings:
5
- currency: GBP
6
-
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
18
- YAML
3
+ config = <<~CSV
4
+ type,frequency,account,from,to,description,category,amount,roll-up,summary_exclude,track
5
+ monthly,,Assets:Bank,01/03/2023,01/06/2023,Mortgage,Expenses:Mortgage,2000.00,,,
6
+ monthly,,Assets:Bank,01/03/2023,01/06/2023,Food,Expenses:Food,100.00,,,
7
+ settings,currency,GBP,,,,,,,,
8
+ CSV
19
9
 
20
10
  output = <<~JOURNAL
21
11
  ~ monthly from 2023-03-01 to 2023-06-01 * Mortgage, Food
22
- Expenses:Mortgage £2,000.00; Mortgage
23
- Expenses:Food £100.00 ; Food
12
+ Expenses:Mortgage £2,000.00 ; Mortgage
13
+ Expenses:Food £100.00 ; Food
24
14
  Assets:Bank
25
15
 
26
16
  JOURNAL