Tips and tricks
Pre- and post-processing
While ledger2beancount is fairly powerful and configurable, it won't meet all needs. In some cases, it makes more sense to pre-process the input file or post-process the output file rather than to add more features to ledger2beancount.
An example where pre-processing is useful are ledger transactions that were created with ledger's convert command, which allows the generation of transactions from CSV.
Since ledger doesn't distinguish between payee and narration, narration information can be stored in a transaction note. The problem with processing such transactions is that ledger puts the note either on the same line as the payee or the next line depending on the length of the payee and narration information. Furthermore, ledger2beancount doesn't support taking narration information from transaction notes, so we have to transform the transaction and store the information as metadata.
Let's take this CSV file as an example:
date,note,payee,amount
2021-04-21,A very long note that will be printed on the next line,Short Payee,-7.99 EUR
2021-04-21,Short note,Short Payee,-7.99 EUR
2021-04-21,Short note,A Long Payee Name that leaves no more room for the note,-7.99 EUR
The command ledger convert --account Assets:Bank
produces this
output:
2021-04-21 * Short Payee
;A very long note that will be printed on the next line
Expenses:Unknown -7.99 EUR
Assets:Bank
2021-04-21 * Short Payee ;Short note
Expenses:Unknown -7.99 EUR
Assets:Bank
2021-04-21 * A Long Payee Name that leaves no more room for the note
;Short note
Expenses:Unknown -7.99 EUR
Assets:Bank
The following script takes the transaction notes (either the transaction
note on the same line as the payee or the first comment of a transaction)
and stores them as metadata with the key narration
:
#!/usr/bin/perl
use warnings;
use strict;
my $before_posting = 1;
my $seen_note = 0;
while (<>) {
if (/^\d/) { # Transaction header
$before_posting = 1;
if (/ ;/) {
my ($header, $note) = split / ;/;
print "$header\n";
print " ; narration: $note";
$seen_note = 1;
} else {
print;
$seen_note = 0;
}
} elsif (/^\s+[^;\s]/) { # Posting
$before_posting = 0;
print;
} elsif (/^\s+;(.*)/) { # Comment
my $note = $1;
if ($before_posting && !$seen_note) {
print " ; narration: $note\n";
$seen_note = 1;
} else {
print;
}
} else {
print;
}
}
This changes the input file to:
2021-04-21 * Short Payee
; narration: A very long note that will be printed on the next line
Expenses:Unknown -7.99 EUR
Assets:Bank
2021-04-21 * Short Payee
; narration: Short note
Expenses:Unknown -7.99 EUR
Assets:Bank
2021-04-21 * A Long Payee Name that leaves no more room for the note
; narration: Short note
Expenses:Unknown -7.99 EUR
Assets:Bank
Now the comment is metadata and we can use the following configuration:
payee_split:
- (?<payee>.*)
narration_tag: narration
to get the expected output:
2021-04-21 * "Short Payee" "A very long note that will be printed on the next line"
Expenses:Unknown -7.99 EUR
Assets:Bank
2021-04-21 * "Short Payee" "Short note"
Expenses:Unknown -7.99 EUR
Assets:Bank
2021-04-21 * "A Long Payee Name that leaves no more room for the note" "Short note"
Expenses:Unknown -7.99 EUR
Assets:Bank
...
An example of post-processing is to change the variable type of ledger
codes from string to integer. Using code_tag
, the ledger code from
transactions is stored as metadata. Since ledger codes can be
anything, they are stored as strings by default:
2021-04-14 * "Code"
code: "1201"
...
If you know that all your codes are integer, you can remove the quotation marks to turn them from strings into integers:
perl -pi -e 's/^(\s+code: )"(\d+)"$/$1$2/' *.beancount
The code
metadata is now an integer:
2021-04-14 * "Code"
code: 1201
...