I first started using ledger — the original plain-text accounting software — in 2017. Since then, I had been pretty happy with my accounting routine, but grew a little annoyed by the repetitive manual work I had to do to assign recurring transactions to the right account.
To make things easier, I had a collection of bash scripts to parse and convert the CSV files from my bank's website1 into ledger entries. They were of course ugly, unreadable piles of sed-grep-regex and did not let met achieve the automation complexity I longed for.
Dissatisfied with ledger's features, I decided to migrate to hledger. Contrary to ledger, hledger comes with a CSV parser one can use to import and classify recurring transactions automagically.
Having a proper DSL for this makes all the difference: I can easily add new rules and understand the old ones. In the end, I get a very consistent result, something my old bash scripts weren't great at.
Here is what my debit.csv.rules
file looks like. It is used to tell hledger
how CSV data from my debit card should be parsed:
# skip the headings line: skip 1 # assign fields fields , , , date, , description, , amount2-in, amount2-out # assign account where the money comes from # 99 to get it at the bottom of the transaction account99 assets:checking include matchers.rules
As you can see, the rules matching transactions to specific accounts are
imported from another file, matchers.rules
. I'm doing this since I want to be
able to use the same set of rules for debit and credit and use the cards
interchangeably.
Here's a snippet of my matchers.rules
file:
# house if Loyer/bail account2 expenses:rent # leisure if PAYPAL .*STEAM GAMES account2 expenses:leisure:videogame if PAYPAL .*BANDCAMP account2 expenses:leisure:music
Using this ruleset, a transaction looking like this:
"SOME ACCOUNT DESC","111111","EOP","2022/01/03",00002,"Loyer/bail","",521.00,"","","","",""
Would be transformed into:
2022-01-03 Loyer/bail expenses:rent 521.00 assets:checking
Sadly, hledger's CSV rules won't let you do arithmetics. This can be useful when you know a certain transaction needs to be split between accounts.
This is where auto postings come in. They are a way to specify arbitrary rules when an account is encountered.
Going back to my previous rent example, I split it 50/50 with my SO using this rule:
= expenses:rent assets:receivable:rent *0.5 assets:checking
After it is applied, the final transaction looks like this:
2022-01-03 Loyer/bail ; modified: expenses:rent 521.00 assets:receivable:rent 260.50 ; generated-posting: = expenses:rent assets:checking
Neat eh? Here is the little bash script I've written to automate all these steps:
#!/bin/bash
#
# Convert CSV to ledger using hledger
declare -a assets=("credit" "debit")
# Read the array values with space
for i in "${assets[@]}"
do
if test -f "$i.csv"
then
# convert to ledger
LEDGER=$(hledger -f "$i.csv" --rules-file rules/"$i.csv".rules print)
# add auto_postings
LEDGER=$(printf "include rules/auto_postings.ledger\n\n$LEDGER\n" | hledger print -f- --auto)
# remove superfluous assets:checking lines
LEDGER=$(printf "$LEDGER" | sed '/assets:checking \+; generated.\+/d')
printf "$LEDGER" > "$i.ledger"
else
printf "File $i.csv does not exist\n"
fi
done
Migrating to hledger, I've cut down the time I spend on accounting from 1 hour per month to about 10 minutes, all while making my workflow much cleaner.
Many thanks to the kind folks on #hledger @irc.libera.chat
for the help!
-
One for my checking+savings account and another one for my credit card. ↩