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

What I Built

I kept hearing the same complaint from shop owners: bills. Piles of paper purchase bills from distributors, typed into a notebook or handed to the CA at month end, and nobody noticing that the atta got ₹30 more expensive three bills ago.

Munim (named after the traditional Indian bookkeeper) is a bill diary that runs on the shop's own computer. The owner photographs a supplier bill with their phone, a local vision model reads it, and Munim gives them three things:

  • Price-change alerts. "Aashirvaad Chakki Atta 10kg is 7.9% higher than your last bill from Bharat Trading Co (₹380 → ₹410)", with both invoices linked as evidence. It also catches the opposite case: the same Dettol soap costs 13.6% less from a different supplier.
  • Supplier dues. Who the shop owes, how much, what is overdue, with partial payments recorded by date and mode.
  • A month-end pack for the CA. A CSV export plus a Tally-style layout, so nobody retypes bills.

The person who would pay is a shop owner with a handful of regular suppliers and a monthly stack of paper. For them the price alerts are the hook: spotting one quiet 8% increase on a fast-moving item can cover a monthly subscription by itself.

Munim dashboard in a dark theme showing October spend of ₹36,400.25 from 8 bills, 6 open price alerts, ₹73,941.68 in supplier dues across 5 overdue bills, and 3 bills needing review, above a photo upload area

List of six price-change alerts: five price increases on atta, Parle-G biscuits, sunflower oil, toothpaste and detergent, plus one saving on Dettol soap from a cheaper supplier, each with linked invoice numbers as evidence

Code

मुनीम (Munim) — Local-First Kirana Bill Diary & Price-Alert Engine

Powered by Gemma, running on this computer.

Named after the traditional Indian bookkeeper, Munim is a local-first bill diary and price-alert engine tailored for small Indian shop owners (kirana, pharmacy, hardware, clothing).

The shopkeeper photographs supplier bills when goods arrive. Munim reads them using Google's open-weight Gemma 3 vision model served locally via Ollama, validates the GST and arithmetic deterministically, and provides three critical business safeguards:

  1. Price-Change Alerts: "Parle-G 12-pack is 4.2% higher than on your last bill from Sharma Distributors (₹118 -> ₹123)."
  2. Supplier Dues Tracker: Running balance, credit cycles, and overdue payment alerts.
  3. Month-End CA & Tally Pack: Clean CSVs ready to hand over to Chartered Accountants or import directly into Tally Prime.

Core Promise: Supplier pricing and trade terms never leave the owner's laptop/PC. Zero per-bill API cost.

🚀 60-Second Quickstart

Prerequisites

  1. Go…

The README has a 60-second quickstart. You need Go, Ollama and one ollama pull gemma3:12b.

How I Built It

  • Model: Gemma 3 (12B by default) running locally through Ollama. The model size is a dropdown in the UI, so a shop with an older laptop can pick a smaller one.
  • Backend: Go and SQLite, shipped as a single program. There is no cloud API anywhere in the runtime path, and it keeps working with the internet off once the model is downloaded.
  • Extraction: each bill photo goes to Gemma along with a JSON schema, so the output is always parseable. Then plain Go code checks it: GSTIN format and check digit, whether line items add up to the subtotal, and whether subtotal plus tax equals the total. Anything that fails lands on a review screen with the original bill image next to the editable fields.
  • Price engine: items are normalized (name, pack size, unit) and matched per supplier. Each new bill is compared with the last bill and with the trailing 3-bill median. An alert only appears when there are two matched data points to show as evidence.
  • Phone capture: the app serves a local page and prints a QR code, so a phone on the same Wi-Fi can upload bills without anything touching a third-party server.
  • Hindi UI toggle, because many shop owners move between Hindi and English.

Review screen with the scanned Bharat Trading Co bill on the left and editable fields on the right for supplier, GSTIN, invoice number, date, line items, taxes and a grand total of ₹5,371.35

The design rule I followed: the model reads, the code decides. Gemma turns a photo into fields. Everything that touches someone's money, such as validation, matching and alerts, is ordinary code I can test.

Supplier dues table for Kailash Agro Foods, Bharat Trading Co and Sharma Distributors showing total billed, total paid, balance due, due date and days overdue, with a Pay button on each row

The bill that nearly broke it

My first demo dashboard flagged 29 of 30 bills as "Needs Review." I assumed Gemma was struggling. It wasn't. The reason was "GSTIN checksum mismatch", and the checker was right: my synthetic bill generator was producing fake GST numbers with invalid check digits, so my own validator was rejecting my own test data. The validator was doing its job. I was testing it with bad inputs.

After fixing the generator I left three deliberately broken bills in the dataset (a wrong GSTIN, a total that does not add up, a hard-to-read handwritten total) so the review queue has real things to catch. The dashboard went from 29 flagged bills to 3.

The next bug was quieter: the spend card showed exactly double what the ledger added up to, because my seeder had inserted every bill twice. A "that number is suspiciously round" moment is now a unique constraint and a test.

Results

On the synthetic dataset (30 bills across three fictional distributors over roughly two months), I planted five price hikes and one case where another supplier sells the same item cheaper. Munim surfaced all six, one alert each, with both invoices as evidence.

That tests the matching logic, not how well Gemma reads a crumpled bill. I'm not claiming extraction accuracy beyond what I measured, and these bills are generated, not photographed in a real shop.

Why Does Open Innovation Matter?

Three reasons, specific to this product:

  1. A shop's supplier prices are a business secret. What a kirana pays for atta, and from whom, is exactly what a competitor would like to see. With an open-weight model, the bill photos never leave the owner's machine. The privacy comes from where the model runs, not from a policy page.
  2. The economics need a bill to cost nothing to read. A shop may photograph hundreds of bills a month, and a subscription priced for a small shop cannot carry a per-page API fee. Locally, reading a bill costs only electricity.
  3. The model is a part I can swap. Gemma is selectable in the UI, and nothing else in the app depends on it. When a better small vision model arrives, or a shop's distributor uses an odd bill format, I can change or tune the model without renegotiating a price or rewriting the product.

Open models also mean the thing I'd want to promise a shop owner, "your data stays with you", is true by construction rather than by trust.

What's Next

The honest gap is real bills. Everything here ran on synthetic data, so the next step is putting Munim in front of an actual shop owner, with their suppliers' messy formats, and seeing what breaks.