I have been using ledger to keep track of my expenses. It is simple, it doesn’t interfere with my workflow, and I can edit data with my favorite text editor Emacs, because ledger only deals with plain text files. I love this workflow.

At some point I ran onto the limitation about how to organize the invoices I receive. Most of the time they are pdfs I receive on my email, but it could also be physical ones that I then scan with my smartphone. I started storing them on a local folder, renaming them into something I can make sense when looking at all the files, and finally writing their location as some extra metadata to the posting on my ledger file.

Naturally, after doing it a few times, my annoyance grew. I needed to automate this process. Emacs is great to edit text and its operating system capabilities should also take care of the renaming of the files. So I dived into practicing some ELisp to implement a function that does exactly what I need.

Emacs already has a mode for ledger files, which provides all the functionalities to edit the files and each posting individually. The next code block automates my previous manual workflow. I’ll comment on each step of this function, as a kind of reminder to myself what everything does.

 1(defun on/ledger-link-invoice ()
 2  "Attach an invoice file to this posting."
 3  (interactive) ; This allow the function to be called with interactively; M-x
 4  ; when-let* is a beatiful macro, every language should have it. What it does
 5  ; is as a let bind variables, if that variable holds a true value it goes into
 6  ; the body of the macro. As soon as any varible evaluates to nil, the process
 7  ; stops and the body is not executed.
 8  (when-let* (; Use ledger-mode function to get the date of the posting
 9              (date (ledger-xact-date))
10              ; Extract the payee of the posting and replace whitespace with underscores
11              ; the trim right is because this extraction includes the new line \n
12              (payee (replace-regexp-in-string " " "_" (string-trim-right (ledger-xact-payee))))
13              ; Select the file I want to link in my file, default the folder of searching
14              ; to ~/Downloads, "Attach: " is for the prompt
15              (src-file (read-file-name "Attach: " "~/Downloads"))
16              ; Create the new relative path "invoices/YYYY-MM-DD_payee.pdf"
17              (file-name (concat "invoices/" date "_" payee "." (file-name-extension src-file))))
18    ; move my selected file to the new location and new name given by file-name.
19    ; The expansion is done for safety reasons to be sure which is the target
20    ; base directory. I my case it does match to whery the ledger file is
21    ; and the relative path is good enough
22    (rename-file src-file (expand-file-name file-name "~/accounting"))
23    ; this is the text editing part. Move to the very-begging of the posting
24    (ledger-navigate-beginning-of-xact)
25    ; move pointer to end
26    (end-of-line)
27    ; This writes a new line on my buffer
28    (newline)
29    ; write the metadata string "    ; Invoice: invoices/YYYY-MM-DD_payee.pdf"
30    (insert "    ; Invoice: " file-name)))

That is all the automation I need for the moment. I’m happy that I can adapt Emacs to help me on these tasks. Now every digital invoice is neatly organized and won’t get lost. There is now enough context, for each posting, because I link to the invoice.