--- layout: post title: Automating a Parking Expenses Report date: 2025-03-02 14:38:00 Europe/Amsterdam categories: paperless paperless-ngx nix python python3 --- For the past year, I was consulting at a place where I could request reimbursement of parking expenses. This had to be claimed using a standard Excel template. Doing this manually every month seemed boring, so I automated a significant part of that: - Fetch parking costs for a certain month from my Paperless-ngx instance - Calculate costs after taxes and a grand total - Fill in an Excel sheet with the details - Convert the Excel sheet to PDF and append photos of parking costs Note: I don't expect anybody to have the same requirements as me here, but hopefully pieces can be useful to some. The open-sourced project can be found [here](https://git.kun.is/pim/parking-expenses). # Organizing the Parking Costs Each time I would park, I would accumulate a parking ticket. This ticket contains the following information: - Date and time - Car license plate - Price - VAT In order to automate the report, I had to have this information in a structured way. Therefore, I turned to [Paperless-ngx](https://docs.paperless-ngx.com/). Paperless-ngx is an self-hostable open-source service that helps digitalizing paper documents. Third-party mobile apps help quickly scanning documents and uploading them to the server. This is exactly what I did with the parking tickets as well: each time I received a ticket, I would scan it with the Paperless app. Paperless-ngx uses Optical Character Recognition (OCR) to cleverly extract the date of the ticket automatically. I also created a custom label to indicate a parking ticket, and I added custom fields to indicate a ticket's cost and VAT amount. You can see this information in the screenshot below. ![foo](ticket.png) _Translated from Dutch: aanmaakdatum means creation date, parkeerkaart means parking ticket, BTW means VAT and bedrag means cost_ Paperless-ngx will also automatically learn to label the tickets with the "parking ticket" label, based on the document's content. That just leaves the VAT and price data points. Unfortunately I found the OCR to be too unreliable to extract that text from the documents. Therefore, I had to manually set those two data points for each ticket. Neat, we now have all parking tickets organized on Paperless-ngx! In order to create a report, we can just query the Paperless-ngx API! # Using the Paperless-ngx API The remaining part of this post will be code explanations. You can find the full open-sourced Python code [here](https://git.kun.is/pim/parking-expenses/src/commit/645fd8b4e4e46bc8f3d8ba831ca28261cd3edb39/main.py). Paperless-ngx has [an API](https://docs.paperless-ngx.com/api/) we can now use to query our parking tickets. I found the filtering logic pretty hard to understand. Therefore I simply looked at what API calls the web UI makes with the query I needed. This turned out to be something like this: ```python response = requests.get( f"{paperless_ngx_url}/api/documents/?page_size=50&query=created:[{start_date} TO {end_date}]&tags__id__all={FILTER_TAG_ID}&correspondent__id__in={FILTER_CORRESPONDENT_ID}", headers={"Authorization": f"Token {token}"}, ) ``` In the above code snippet, `FILTER_TAG_ID` is the ID of the "Parking ticket" label and `FILTER_CORRESPONDENT_ID` is the ID of the correspondent (I included this to filter out potential unrelated parking tickets). Perfect, this returns a JSON list of parking tickets we can use to create the report! # Creating the Report All that's left is to fill in an Excel template now. This actually proved to be the most annoying part but also the most boring part. I used the [OpenPyXL](https://openpyxl.readthedocs.io/en/stable/) Python library to manipulate the Excel sheets and basically just add a row for each parking ticket. For code, check [here](https://git.kun.is/pim/parking-expenses/src/commit/645fd8b4e4e46bc8f3d8ba831ca28261cd3edb39/main.py#L104). To convert the Excel sheet to a PDF, I used the (apparently now deprecated) [unoconv](https://github.com/unoconv/unoconv) utility. It uses LibreOffice under the hood for the conversion, which adds a 2GiB dependency for the project 🫠. Finally, to merge the report PDF with photos of the parking tickets, I used the [PyPDF2](https://pypdf2.readthedocs.io/en/3.x/) Python library. I would now show some example output, but it contains quite some sensitive information so unfortunately I can't... But I never had any complaints from the finance department, so it worked great!