Migrating from ledger to hledger

2022-02-05 - Louis-Philippe Véronneau

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

  account2  expenses:leisure:videogame

  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

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

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

Neat eh? Here is the little bash script I've written to automate all these steps:

# Convert CSV to ledger using hledger

declare -a assets=("credit" "debit")

# Read the array values with space
for i in "${assets[@]}"
  if test -f "$i.csv"
    # 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"
    printf "File $i.csv does not exist\n"

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!

  1. One for my checking+savings account and another one for my credit card.