A config once, inter-month-idempotent, calendar aware, capable and maintenance-free invoice solution written in LaTeX + Lua.
- Config once: Set your company, client and project information using ENV vars in the
.envrc.secretfile (git ignored). No LaTeX or Lua skills needed! - Inter-month-idempotent: You build the invoice any number of times, it always results in the same invoice number when run within the same month. The proceeding month the next invoice number will be used.
- Calendar aware: Using your machines system time to determine the month, it calculates the number of working days for the target month. Invoice date is set to last day of the target month and due date is set dependent on the payment terms set in your ENV config.
- Capable: Supports setting number of days you were off, to be extracted from the automatically calculated number of working days. Supports expenses using "',,';',,'" CSV inspired string.
- Maintenance free: The invoice number automatically set based on the current month. When you build the invoice the next month, the next number is used.
With minimal modification this ought to work on Linux too, but I've only verified it on macOS.
Install direnv
brew install direnvDirenv allows your system to automatically to load the environment variables standing in this projects.
There are two different ways of installing LaTeX on macOS. Either install MacTeX which is a complete and very big distribution, which will eat up ~10 gb of your space. Use that if you have all that space to spare and want the easiest installation. Alternatively you can install TinyTex which is a much (typically 90%) smaller distribution. I recommend TinyTex since it is small and fast and it only has one extra required installation step which is fast and easy.
Install LaTeX (TinyTex) here
curl -sL "https://yihui.org/tinytex/install-bin-unix.sh" | shYou will be prompted for your password.
A package manager called tlmgr is now installed, we will use it in the next section to install some packages.
Install the following packages:
tlmgr install babel-english fancyhdr arydshln lastpage datetime2 fp ragged2e xstring fancybox luacode threeparttableLaTeX should now be setup!
If you have previously installed MacTex and want to switch over to TinyTex, uninstall MacTex like so:
Run this command in Terminal to remove the main MacTeX installation (typically 4-9 GB):
sudo rm -rf /usr/local/texliveThese links point to the binaries inside the MacTeX install.
sudo rm -rf /Library/TeXYou may also want to remove any profile entries from:
sudo rm /etc/paths.d/TeXThese may include:
- TeX Live Utility (/Applications/TeX)
- TeXShop (/Applications/TeXShop.app)
You can delete them manually from the /Applications folder.
Clone this repo, then cd to it.
Make copy of .envrc.example and call it .envrc.secret
cp .envrc.example .envrc.secretReplace the placeholder values from .envrc.example with your company, client and project values.
Source the updated environment variables by calling:
direnv allow .And now call make:
makeWhich will call out to lualatex.
Note
All environment variables references to in document are all set in .envrc.secret (which you should have copied over from .envrc.example).
They will all be exported when you call direnv allow . (needed once and then after each change to .envrc.secret, which you typically only upon initial setup)
Build the invoice (make standing in the root of this project) on the first day month after the completed month. So if you have finished working in May, wait until June and build it. The invoice date will be set to 2025-05-31 and the due date will be set to invoice day + $INVOICE_DAYS_DUE_AFTER_END_OF_MONTH.
Set INVOICE_NUMBER_BASE_OFFSET = 42 if you have sent 42 invoices already and want the next to invoice to be 43, in order for this to work you need also set INVOICE_NUMBER_BASE_OFFSET_DATE_MONTH and INVOICE_NUMBER_BASE_OFFSET_DATE_YEAR to the previous month (and relevant year).
Note
The invoice will be named "<INVOICEMONTH>_invoice<INVOICE_NUMBER>.pdf" and places in a folder next to the makefile called invoices.
This name is inter-month-idempotent, running several times same month will use same name.
Next month the next invoice will be generated with the subsequent month using the subsequent invoice number.
If you were out of office (OOO) for an entire month, maybe due to parental leave or long vacation or long term sickness, you can set increment INVOICE_NUMBER_MONTHS_FREE by how many numbers of months you where OOO. This allows you to not have to "hacky" decrement your INVOICE_NUMBER_BASE_OFFSET to get the correct invoice number for the next month.
If you were out of office for some days for the period you are invoicing you can call:
make DAYS_OFF=5make EXPENSES="'Sandwich,8.67,1';'Coffee,4.20,2'"Or if you had days of and expenses:
make DAYS_OFF=2 EXPENSES="'Headphones,79.99,1'"Which will subtract 5 from the number of working days that month (as calculated by the Lua script for you).
If you build the invoice without changing any of the example values from .envrc.example, built with make DAYS_OFF=3 EXPENSES="'Sandwich,8.67,1';'Coffee,4.20,2'" it will look like this:
direnv will source .envrc which in turn will source .envrc.example and then if .envrc.secret exists, it will source that, effectively overriding the values of .envrc.example. If you haven't created .envrc.secret yet the values of .envrc.example are used instead, or if you were to comment out or remove any variable from .envrc.secret, that variable won't be overridden.
The LaTeX main file invoice.tex will include other helper files, such as logic.tex which in turn wraps the lua functions inside logic.lua as LaTeX commands.
We use pre-commit to run some soundness checks when you try to commit, such as checking for typos.
Install pre-commit CLI tool:
brew install pre-commitpre-commit install