TimeWarrior PDF invoice generator
Doc clean-up, and restore the wayward inv script.
Adds an invoice number based on a client.id and the current YEARMONTH


browse log



#TimeWarrior Invoice Generator


Generates a PDF invoice from TimeWarrior logs.


Both dependencies are probably in your package manager. vim comes with a Lout syntax.

This software does not require any non-core Go libraries to build, so there's no go.mod file.


Build the executable:

go build -o invoice .

Copy invoice and inv to your extensions directory:

chmod u+x inv
cp invoice inv ~/.timewarrior/extensions


Configure it first. It needs to know your information, your client's information, and other stuff, and it'll be unhappy if you don't configure it first.


The template requires some configuration that uses TimeWarrior's configuration utility. Rates and addresses are pulled from the timew config, and this requires configuration before running the tool. Configurations can be set with the timew config command, e.g.

timew config rate 60.0

With the default template, the following are expected:


: float, the hourly rate used to calculate subtotals and totals


: string, your name formatted for display


: string, the first line of your address


: string, the second line of your address


: string, your phone number


: string, your email address

For example:

timew config myname "Sean E. Russell"

Set your client's contact info with the following configurations:


: string, the name of your client formatted for display


: string, the name of a contact at your client, e.g. "Bill Manager"


: string, the first line of your client's address


: string, the second line of your client's address


: string, an ID used in generating a unique invoice number


timew config megacorp.company "MegaCorp, Inc"

You may enter as many clients as you wish.

Note: TimeWarrior uses # as a comment character, and don't document how to escape it. I haven't yet figured it out, so you can't have it in any of the configuration fields.

Next, you have to tag all of your records for that client with client:, e.g.

timew tag @1 client:megacorp

Run your reports with the client:megacorp tag; the program uses that to determine which client configuration information to use, which allows you to have multiple clients.


In the normal course of things, you'd run this with timew, e.g.

timew report inv 2019-12-01 - 2019-12-31 client:megacorp

The inv script does some set-up, calls invoice to generate the Lout source, runs lout to create the PDF, and then cleans up. It's a pretty simple script, and isn't necessary if you want to run some commands yourself.


#Raw Lout Source

You can get the raw Lout source by using the invoice report:

timew report invoice 2019-12-01 - 2019-12-31 client:megacorp

The invoice command itself reads from STDIN and expects output in the format of timew report.

invoice [args]

One way to do that is to have an echo report in the extensions directory:

cat > ~/.timewarrior/extensions/echo <<EOL

while read x; do
    echo $x

and then:

timew report echo 2019-12-01 - 2019-12-31 client:megacorp | ./invoice

#Modify the report template

There is limited ability to override the template. To do so, have a file invoice.tmpl in the current directory. You can use the one in the source repository as a starting point. These are Go templates; Go replaces the stuff in double-braces ({{ ... }}), and if you hard-code those you don't need the addresses in the configuration, or the client tags on your TimeWarrior records. You do still need the rate configuration, and the {{range .}} ... {{end}} markup. However, you can mess around with the table formats, fonts, and etcetera. Change the $ to . Move the columns around. Weighing in at 78 lines long, it's not too complex. One thing to keep in mind: double-braces are Go, single-braces are Lout. Make sure there are spaces between Lout braces to prevent Go from trying to mess with them.

IMPORTANT: Any time you change invoice.tmpl, you must run go generate before compiling the tool. This requires the bundling tool esc, which you can get with go get github.com/mjibson/esc.

#Q & A

Why not LaTeX?

: Lout is a more simple syntax, with a much smaller learning curve. I can (and do) go for years between using Lout and it's easy to pick up each time; I can't say the same for TeX.

Lout is a single 727KB executable. LaTeX (tetex-live) is 445MB, distributed as a bunch of packages (on Arch), and that's considered a small implementation of LaTex. As I use these infrequently, I'd rather clutter up my hard drive with a smaller tool.

Why not Markdown?

: It's not the right tool. An invoice is tabular data on (usually) letterhead; Markdown has never claimed to be, and is not particularly good at, desktop publishing, and it discourages tabular data. The original Markdown format required escaping into HTML to do tables.

Why not X?

: I don't know X. I'm sure it's great, and I'd be thrilled if any part of this project helps you implement a solution based around X. If you do, please do let me know; I'd love to hear about it.

Lout sounds awesome! Where can I learn more?

: Right here.

Why esc, and not Y?

: It was installed from an earlier project, and I haven't yet taken the time to see if any of the other asset bundling tools are any better.