DEV Community

Cover image for Invoice Sorter: a private expense tracker for freelancers that runs on a laptop
Kavyansh Kumar Baghel
Kavyansh Kumar Baghel

Posted on

Invoice Sorter: a private expense tracker for freelancers that runs on a laptop

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

Invoice Sorter is a small command-line tool for freelancers. You point it at a folder of PDF invoices and receipts, and it writes one clean expenses.csv with vendor, date, total, currency and category.

The people who would pay for it are freelancers, consultants and small studios who lose a weekend at tax time retyping receipts into a spreadsheet. Their invoices contain client names and amounts, so many of them don't want to upload those to a cloud service. Invoice Sorter does the whole job on their own laptop, so the data never leaves it.

Demo

Terminal recording (8 sample invoices in, expenses.csv out, about 45 seconds, all local): https://asciinema.org/a/CWEnTFK4dQxtn78a

Code

GitHub logo krish007code / invoice-sorter

Local invoice/receipt sorter for freelancers: PDFs in, expenses.csv out, powered by Ollama

Local Invoice Sorter

Freelancers and solo consultants who would pay for a lightweight bookkeeping helper can use this local invoice sorter to turn text-based PDF invoices and receipts into a tidy expense CSV, without hand-copying vendors, dates, totals, currencies, and categories. It saves time on repetitive expense entry while keeping client and financial documents on their own laptop.

Why local and open

Documents are read locally and sent only to your local Ollama instance, so client data never leaves your laptop. After downloading a model, processing works offline and has zero per-document API cost. The model is swappable through an environment variable, so you can use the open-weight model that works best for your machine without vendor lock-in. Extraction uses an LLM for extraction, rules as a safety net for common category keywords.

Requirements

Setup and usage

uv sync
ollama pull qwen2.5:3b
uv run make_samples.py
uv
…
Enter fullscreen mode Exit fullscreen mode

How I Built It

  • Model: qwen2.5:3b, an open-weight model, served locally by Ollama. It runs on my 16 GB laptop with no GPU.
  • Pipeline: pdfplumber pulls the text out of each PDF. The model returns structured JSON (vendor, date, total, currency, category) in JSON mode at temperature 0. Polars writes the CSV.
  • LLM for extraction, rules as a safety net: The small model handled vendors, dates, totals and currencies well, including summing an invoice with two line items. Categories were its weak spot. On the first run it labelled almost everything "services", and after I rewrote the prompt with definitions and examples it still invented a category called "hosting" for a DigitalOcean invoice. I added a short keyword-rules fallback (under 25 lines) that corrects invalid or implausible categories. All 8 sample invoices now come out right.
  • Speed: on a CPU-only laptop it processes about one invoice every 5-6 seconds (8 invoices in about 45 seconds, measured from the demo recording). That's plenty for a freelancer's monthly batch.
  • Configurable: the model comes from an OLLAMA_MODEL environment variable and the output file from a --out flag, so trying Llama or Phi is a one-line change.

Why Does Open Innovation Matter?

  • Privacy: client invoices never leave the laptop. There is no upload and no third-party API to trust.
  • Cost: there is no per-document fee. Running 500 receipts at tax time costs nothing beyond electricity, which makes a low price viable for a side hustle.
  • Offline: the pipeline makes no cloud calls, so once the model is pulled it works without internet.
  • No lock-in: I can swap Qwen, Llama or Phi without rewriting anything, and no vendor can change the pricing under me.
  • Tinkering: because I run the model myself, I could see exactly where it failed (categories) and fix it with prompts plus rules. With a closed API I'd be stuck with whatever it returned.
  • Honest limits: it only reads text-based PDFs (scans need OCR), and a 3B model can still mislabel edge cases. Next up: OCR with Tesseract, Excel export, GST-ready columns and a watch-folder mode.

Top comments (0)