This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle
What I Built
LocalLedger AI is a bookkeeping assistant for freelancers and small businesses. It imports receipts, reads the text on the computer where it runs, and waits for a person to confirm every amount before that amount becomes part of the ledger. It also writes invoices, tracks payments already received, and exports CSV.
The person who would pay for this is a freelancer who keeps a folder of receipt photos and a spreadsheet, and does not want those files sent to a hosted model. A bookkeeper charging for monthly cleanup would pay for the same reason: the client's names, addresses, and prices stay on the laptop. The app does not connect to a bank and does not collect payments. It is a record, not a payment processor or a tax adviser.
Demo
The ledger is local. These screenshots are a fictional Northwind Supplies receipt, not a customer's documents.
Demo Video
The landing page:
A new ledger starts empty. The figures are not sample data until you ask for them:
- Upload a text PDF receipt. PyMuPDF reads the vendor, date, currency, subtotal, tax, total, and receipt number. The fields stay editable until you approve them.
- Change the total from 1080.00 to 1075.00. The app shows that subtotal plus tax no longer matches, and it does not rewrite the numbers.
- Approve the expense. Only then does it appear in the expense list and in the default CSV.
- Upload the same file again. The matching file hash is explained, and a second expense is not created.
- Issue an invoice with two lines and different tax rates. The server calculates LKR 3,600.00 in minor units and downloads a PDF.
- Open the category report and the dashboard. Amounts in different currencies are listed separately. This receipt is dated March 2026, so "all dates" is selected.
The same path is scripted in scripts/run_demo.py. It uses a temporary data directory and does not call a commercial AI API. On this machine the script completed with the local model switched off. The screenshots are in docs/screenshots.
Code
LocalLedger AI
Your receipts. Your records. Your computer.
LocalLedger AI is a local bookkeeping assistant for freelancers and small businesses. It imports receipts, reads what it can on your machine, and waits for you to confirm every amount before that amount becomes part of the ledger. It also creates invoices, reports, and CSV exports. It does not connect to a bank, collect payments, or call a paid AI API.
What it does
- You upload a JPEG, PNG, WebP, or PDF.
- The app stores the original file, hashes it, and reads embedded PDF text. Images and scanned PDFs use Tesseract when it is installed.
- A deterministic parser pulls vendor, date, currency, amounts, and reference from that text. Missing or ambiguous fields stay empty.
- You correct the fields and approve the expense. Until then it is excluded from totals, reports, and the default CSV export.
- You can create invoices, record payments you…
The license is MIT. Setup, OCR, and Ollama instructions are in the README.
How I Built It
The app is a local FastAPI service with SQLite, and a React interface. Money is stored as integer minor units. Invoice totals are calculated on the server. A client cannot edit a stored total independently of the line items.
Text PDFs are read with PyMuPDF. Photos and scans use Tesseract when it is installed. A deterministic parser then looks for vendor, date, currency, amounts, and a reference. If a field is missing or ambiguous, it stays empty. The parser does not fill a missing date with today, and it does not guess a currency from the computer's location.
Ollama is the open-weight piece. It is optional, loopback-only unless that guard is deliberately turned off, and it has no hosted fallback. A suggestion is kept only when the model quotes text that is actually on the receipt, and only for a field the parser left blank. The suggestion is still marked for review. Approving it is what makes it a ledger record.
I verified the core flow with pytest: 71 passed, 1 skipped. The skip is image OCR, because Tesseract is not installed on this Windows machine. The running demo used Python 3.12 and did not have Ollama started.
Why Does Open Innovation Matter?
A receipt is a worse thing to send to a metered API than a chat message. It has a name, an address, a tax amount, and sometimes an account fragment. A closed API would price that upload per document and keep a copy I do not control. LocalLedger is useful at zero document fees, including when the laptop has no model installed: PDF text and manual review still work.
When a model is installed, I can swap it in Settings. The ledger does not change. A closed provider's price change or model retirement would not take the bookkeeping data with it.
The part a closed API is worse at is refusal. If the receipt does not say the date, the field stays blank. If subtotal, tax, and total disagree, the app shows the disagreement. The model is not allowed to invent the missing number from general knowledge.
My Agent Session
Session with DevRelay
{% agent_session SESSION_SLUG %}









Top comments (0)