DEV Community

Cover image for Shopkeepers won't retype their udhaar khata. KhataLens reads it with Gemma 4, locally
Rahul Tapase
Rahul Tapase

Posted on AI-assisted

Shopkeepers won't retype their udhaar khata. KhataLens reads it with Gemma 4, locally

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

Walk into almost any kirana shop and you'll find a notebook next to the till: the udhaar khata. A regular customer takes five kilo atta today and pays on Saturday, so the shopkeeper writes it down: date, name, item, amount. When money comes back, he writes "jama".

The notebook works until he needs a total. Knowing who owes what means re-adding pages of handwriting, with the same customer scattered across many pages. Then comes the harder part: asking a neighbour for money without it getting awkward. So dues slip.

Free ledger apps like Khatabook and OkCredit exist, but they ask the shopkeeper to type every entry into a phone, on top of the notebook he already keeps. KhataLens starts from the notebook instead:

  1. Photograph the pages. One page or a whole book. Gemma 4 reads every handwritten line into an editable ledger that sits next to the photo.
  2. Check it. Fix any misread in place. Corrected rows are tagged, and balances update as you type.
  3. Get the hisaab. The same customer across pages adds up into one balance (udhaar minus jama), sorted by who owes most, with the total to collect always visible.
  4. Send a polite reminder. Gemma drafts a short, respectful Hinglish message with the customer's name, the exact ₹ amount, the shop name and, optionally, the shop's UPI ID. The shopkeeper copies it or opens WhatsApp, picks the contact and taps send himself.
  5. Export. Download the ledger and the balances as a CSV for Excel.

Everything runs on the shop's own computer. Gemma 4 E2B runs locally in Ollama, so the photos, names and amounts never leave it.

Who would pay, and for what. The customer is the shopkeeper. He wouldn't be paying for a ledger, because those are free. He'd be paying to not change his habit: he keeps writing in the notebook, and once a week he photographs the new pages and gets every balance plus a ready-to-send reminder for each customer. If the reminders bring back one forgotten ₹500 a month, a ₹99 monthly fee pays for itself. That price only works because reading a page costs nothing: there's no per-image API bill that grows with every page he photographs.

To be upfront: no shopkeeper has used it yet. Everything below was tested on three khata pages I handwrote myself, with made-up names.

Demo

KhataLens needs Ollama on the same machine, by design, so there's no hosted demo. Setup is a model pull and two npm commands, all in the README.

1. Three handwritten pages, one ledger. The photo stays next to the table, and every row is tagged with the page it came from (P1, P2, P3), so the shopkeeper can always go back to the paper:

KhataLens: three khata pages read into one ledger, each row tagged with its page

2. The hisaab, and a reminder drafted by Gemma. After all three pages, Ramesh owes ₹240, Pooja ₹150, Imran ₹110 and Arjun ₹40, while Kavita and Sunita are settled: ₹540 to collect (₹1,700 udhaar minus ₹1,160 jama). Pick a customer and a tone, and Gemma writes the message:

Hisaab across three pages and a Gemma-drafted Hinglish reminder

Code

KhataLens

Photograph a page of a handwritten udhaar khata. Gemma 4 reads it on your laptop, turns it into an editable ledger, totals what each customer owes, and drafts a polite Hinglish payment reminder you can copy or open in WhatsApp. Nothing is sent automatically and nothing leaves the laptop.

Built for Best Use of Gemma 4 at Hacktoberfest 2026 Mumbai.

KhataLens: three khata pages read into one ledger, each row tagged with its page

The problem

Small shops across India run on credit. A regular customer takes atta today and pays at the end of the week, and the shopkeeper writes it in a paper udhaar notebook: date, name, item, amount and "jama" when money comes back. Working out who owes how much means re-adding pages of handwriting, and asking for money is awkward, so dues slip.

Existing ledger apps expect the shopkeeper to retype everything. KhataLens starts from the notebook he already keeps.

What it does

  1. Photos in. Take or drop one…

Vite and TypeScript with no runtime dependencies, plain HTML and CSS, and Gemma 4 E2B through Ollama. There is no backend.

How I Built It

One small open model, two jobs. KhataLens uses gemma4:e2b (about 4.6B parameters, Q4_K_M) through Ollama's /api/chat. The browser reaches Ollama through Vite's dev proxy, so there's no server, no API key and no CORS setup. On my laptop's 4 GB RTX 2050, a page takes about 6 to 9 seconds once the model is loaded.

1. Reading the page: vision with a JSON schema. The photo is downscaled in the browser to a 1280 px JPEG and sent with a JSON schema in Ollama's format field, so Gemma has to answer as structured data. Thinking is off and the temperature is 0.1. The system prompt explains khata conventions: a name with an amount is udhaar unless the line says jama, paid, diya or received; read numbers literally and never calculate; one entry per written line, never invent rows; set unclear fields to null; count the lines you can't read.

2. The bug that shaped the schema. My first schema asked for customer, amount, kind, date and item directly. On my handwritten pages, Gemma read two short payment lines like "Pooja jama 100" as udhaar. That's the worst kind of error for a khata: it adds ₹100 to Pooja's balance instead of taking it off.

The fix was to make Gemma copy the whole line before splitting it:

properties: {
  // Exact transcription first: the model reads the whole line before splitting it,
  // and the app can check it for payment words itself.
  line: { type: "string" },
  customer: { type: "string" },
  amount: { type: "number" },
  kind: { type: "string", enum: ["udhaar", "jama"] },
  date: { type: ["string", "null"] },
  item: { type: ["string", "null"] },
},
Enter fullscreen mode Exit fullscreen mode

Then code applies the khata rule to each line instead of trusting the model's label: a payment word (jama, paid, diya, received...) in the copied line means jama, and a real item of goods means udhaar.

Version Result on 3 handwritten pages (24 lines)
First schema 2 short jama lines read as udhaar
Copy-the-line-first schema + code rule every name, amount, date and udhaar/jama correct, in each of 3 runs

3. Guardrails around the model. Invalid JSON gets one retry; after that, Gemma's raw answer is shown instead of a blank error. The model is warmed up as soon as the app opens, and a status pill shows "Gemma loading" or "Gemma 4 ready". The editable table is the core of the design, not a fallback: handwriting is messy, so the photo and the rows are always side by side. If the same page is added twice, the app asks before counting it again. For that it stores only a SHA-256 fingerprint of each resized photo, never the photo.

4. Reminders: Gemma writes the words, code checks the facts. Gemma gets the customer, the amount, the shop name and a tone (polite, friendly, or firm but respectful), and writes at most 60 words of Roman-script Hinglish. The prompt rules out threats, shaming, legal words and any mention of other customers. Before a draft is shown, code checks that it contains the name, the exact amount and the shop name, and no Devanagari. If not, it retries once, then falls back to a fixed template. The UPI line is added by code, never by the model, so it's always exact. If a later page or an edit changes the balance, the draft is marked outdated, so an old amount isn't sent by mistake.

5. Small security details. Model output goes into the page as text, never as HTML. The CSV export writes any cell starting with =, +, - or @ as text, so a customer name can't turn into a spreadsheet formula. Ollama stays on localhost.

How the work was split. I built it solo with an AI coding agent (Kiro), phase by phase from a written plan: the Gemma core first, tested on a handwritten photo before any UI existed, then the ledger, reminders, a design pass and multi-page reading. I made the calls on scope, the khata rules and the tests, and the agent did much of the typing.

Why Does Open Innovation Matter?

A khata is other people's financial data. It records which neighbour bought what and who hasn't paid. That isn't the shopkeeper's data to upload to a server he doesn't control, and "we send your customers' dues to an AI company" is not a pitch a kirana owner will accept. With an open-weight model running in Ollama, the photo, the names and the amounts stay on his computer.

Zero cost per page is what makes the price work. A hosted vision API charges for every image, so the bill would grow with every page he photographs and every reminder he drafts. Local inference makes that cost zero. A flat monthly price doesn't get eaten by usage, and the shopkeeper never sees a surprise bill.

It doesn't depend on the shop's connection. After ollama pull gemma4:e2b, reading pages and writing reminders work offline. Only the final WhatsApp step needs the internet.

I could shape the whole pipeline, not just the prompt. JSON-schema output, thinking off for speed, low temperature, and the copy-the-line-first schema that fixed the jama bug. Because the model is small, one model covers both jobs, reading handwriting and writing Hinglish, on a laptop with a 4 GB GPU.

Where closed would be better, to be honest. A large hosted model would probably read messier handwriting better. KhataLens makes up for that by keeping the photo and the editable row side by side, so a misread costs a two-second fix instead of a wrong balance.

Limitations

  • It was tested on three pages (24 lines) in my own handwriting, with made-up names. Real khatas are messier: cramped lines, crossed-out entries, running totals.
  • It handles Roman-script Hinglish with English numerals only. Devanagari and Marathi notebooks aren't supported yet.
  • Customers are matched by name (trimmed, case-insensitive), so "Ramesh" and "Rameshbhai" become two customers.
  • It needs a computer with Ollama, and many shopkeepers only have a phone.
  • No shopkeeper has used it yet.

What's next

Put it in front of a real shopkeeper with a real khata, and ask whether he'd pay. After that: Devanagari pages, a phone version (E2B is the smallest Gemma 4 size, so it's worth testing on a phone), voice entry ("Ramesh, atta paanch kilo, do sau pachaas"), and sending through the WhatsApp Business API with the shopkeeper's approval for every message.

Prize Categories

  • Best Use of Gemma: Gemma 4 E2B does both jobs locally: vision with structured output to read handwritten khata pages, and writing Hinglish payment reminders.

Top comments (0)