Skip to content
Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

Timetracking and billing with (h)ledger

This set of sample files shows how to organize timetracking and billing with tools like ledgercli and hledger.

The ledger file file includes a timelog. The ledger file also includes transactions to issue a bill with the timelog entries and receive a payment against the bill.

The timelog is separate to make interaction with time tracking tools. For example, timeclock.el can generate such a file, usually in ~/.timelog. In my case, I use org-mode's clocking work time system to "punch" in and out of different tasks. I then use John Wiegley's org2tc script to export the clock entry to the timeclock format. (Also note that I have made a few improvements to org2tc that you might be interested in.)

Looking at past entries

The resulting ledger balance looks like this:

$ ledger -f ledger.lgr bal
                 10€  assets:cash
               1.00h  income:client

You see we have 10€ in cash, which is about right. However, the ledger is unbalanced: there's a 10€ and a 1h that are not matched. That is because the time entries are "virtual" and so entered only against one account. To ignore time entries, use the --real (-R) flag:

$ ledger -f ledger.lgr bal -B -R
                 10€  assets:cash
                -10€  income:client

Now our ledger is properly balanced. The downside is that we don't see the 1h that still has to be billed. Also notice how we need to convert the units with --basis (-B) otherwise it will show up like this, which is expected given the different units:

$ ledger -f ledger.lgr bal -R
                 10€  assets:cash
             -10.00h  income:client

Billing new entries

To issue new bills, we will look at those unbalanced virtual entries. They allow you to notice there is another hour of client work to bill. You can issue a bill against that extra hour to complete your billing with an entry like:

2016/01/28 bill
    income:client  -1h @ 1€

Then you see something like this:

$ ledger -f ledger.lgr bal
                 11€  assets
                 10€    cash
                  1€    receivables

Congratulations, you billed all your hours! It is still unbalanced, but you can fix that with -R, as we did before:

$ ledger -f ledger.lgr bal -B -R
                 11€  assets
                 10€    cash
                  1€    receivables
                -11€  income:client

Notice how this shows a 1€ unpaid bill that we are waiting a payment for, which is expected because we only issued the bill and not the payment.

Generating actual invoices

The is the part I am stuck at. I have tried to make a shell script to generate a bill, but I can't figure out how to extract the hourly rate. I'd also love to get the details of the affected punches, but I don't think there's a way to map transactions like this in ledger. I could extract only the uncleared punches, set the invoice then clear them, but this would be overwritten at the next run of org2tc.

I have even tried to set a commodity price, inspired by adams rate example:

P 2016/01/28 00:00:00 h 1€

But that didn't seem to actually work: the price doesn't apply. But I am not very familiar with this part of ledger.

I have also tried using the Python API but failed to go very far: i could not extract cost there either.


Printing invoices

I want to print invoices. More todos above and in the shell script.

HLedger bug

The ledger file fails to load in hledger:

$ hledger -f ledger.lgr bal
creating default conversion rules file /home/anarcat/src/ledger-timetracking/ledger.lgr.rules, edit this file for better results
hledger: "/home/anarcat/src/ledger-timetracking/ledger.lgr" (line 1, column 1):
unexpected 'a'
expecting journal transaction or directive or end of input

It first chokes on the apply account directive from ledger (this was fixed in commit a2b989d). But even using the regular hledger include fails:

$ hledger -f hledger.lgr bal
using conversion rules file /home/anarcat/src/ledger-timetracking/hledger.lgr.rules
hledger: "/home/anarcat/src/ledger-timetracking/hledger.lgr" (line 2, column 1) in included file "timelog":
"/home/anarcat/src/ledger-timetracking/timelog" (line 1, column 1):
unexpected 'i'
expecting journal transaction or directive or end of input

This was filed as issue #320 in hledger.

I may be able to feed the timetracking data directly in the hledger.lgr file, but that's rather painful: i'd like to keep that messy file separate from the main accounting.

Unbalanced entries

The way transactions are imported, above, makes time entries show up as unbalanced. It's confusing and annoying - there should be a way to balance them with the right account, maybe the income account.

But then that would mean the income account would empty when we bill, which is not correct: it should be debited with the size of the bill, so that we have the running expenses showing up in balance reports.

So in fact, unbalanced entries may be exactly the point of this.

Equity and reporting

There's this strange error that happens when doing a monthly registry report:

$ ledger -f ledger.lgr reg -p monthly
Error: 'equity' cannot accept virtual and non-virtual postings to the same account

This is not a problem when doing a daily report, as the invoice is (deliberately) sent the day after the time entries:

$ ledger -f ledger.lgr reg -p daily
70-Jan-01 - 70-Jan-01           (income:client)               9.99h        9.99h
70-Jan-02 - 70-Jan-02           assets:cash                     10€        9.99h
                                equity:volunteer              59.5m       10.98h
                                income:client               -10.99h         -30s

How can I work around that issue?

Balance assertions

If I understand this right, such an assertion should check that, at the given time, the balance on the account is a given amount:

1970/01/02 assert we have billed everything
    [income:client]  = 0s

In the above case, there should be zero seconds to be billed to the client on January 2nd. Unfortunately, this doesn't work: if there are punches after that date, they still count towards that balance:

$ ledger -f ledger.lgr bal
While parsing file "/home/anarcat/src/ledger-timetracking/ledger.lgr", line 19:
While balancing transaction from "/home/anarcat/src/ledger-timetracking/ledger.lgr", lines 18-19:
> 1970/01/02 assert we have billed everything
>     [income:client]  = 0s
Unbalanced remainder is:
Amount to balance against:
Error: Transaction does not balance

Whereas the punch was after the given date:

i 1970-02-01 12:00:00 client  client:test4 1h
o 1970-02-01 13:00:00

A month later!

Sources and inspiration


sample timetracker data for ledger



No releases published


No packages published