Client work; the client is named with its public site linked below.
Gemini invoice OCR and automated Tally invoicing for a printer-fleet business
For CD Infoware I built invoice OCR on Gemini through Vertex AI and a nightly meter scrape that drafts Tally-format GST invoices; the platform is live at cd-infoware.web.app with 111 commits, all mine, the OCR function is capped at 10 instances, and every invoice carries its own model cost line.
10 instanceshard cap on Vertex OCR; cost logged per invoice
Context
CD Infoware rents, sells and services Konica Minolta multifunction printers, CCTV and servers across India: buy, stock, place, service, read the meter, bill monthly. I built its platform alone from May 2026 (111 commits, all mine): a React web app, a React Native field app, and four isolated Firebase Functions codebases in a pnpm and Turborepo monorepo.
Constraint: a bulk upload of supplier invoices must never spin up unbounded paid model calls, and nothing experimental may touch the client's real Tally invoice sequence or its production portal account.
Problem
The client keyed supplier invoices and monthly meter readings by hand across every placed device, and monthly GST billing depended on it.
What I built
I built invoice OCR on Gemini through Vertex AI, triggered on file upload, that extracts vendor, GSTIN, PAN, line items, serial numbers and CGST/SGST/IGST against a JSON schema, with callable agents that resolve the customer and vendor and generate a product template. I capped the function at 10 instances so a bulk upload cannot run away, and I record the token cost of every invoice in USD and INR.
For billing, a Puppeteer job scrapes Konica Minolta's remote-care portal at 02:00 IST, diffs about 60 meter counters per device, and drafts one GST invoice per device per month in Tally format in the client's real numbering sequence, with a frozen baseline and PDF re-render on movement; experiments run against a separate sandbox account.
Architecture
Firebase (Firestore in asia-south1, Realtime Database, Storage) with security rules mirrored into a permissions module and claims-first roles; Vertex AI Gemini; Cloud Functions; Puppeteer; Tally-format export. CI runs lint, build, coverage-gated unit tests, emulator rules tests, Playwright end-to-end tests, an Android release-build guard, and a job that fails if the 12-chapter docs book’s code citations drift.
- Invoice PDF uploaded to Storage from the web or field app
- Storage trigger, Cloud Function capped at 10 instances
- Gemini on Vertex AI with a JSON schema (vendor, GSTIN, PAN, lines, serials, tax splits)
- Token cost recorded per invoice in USD and INR; customer and vendor resolved
- Firestore record for review; draft, never final
Integrating where no API exists: the Konica Minolta portal
The meter counters live in a vendor portal with no API. The scrape runs nightly against a sandbox account for experiments and the real account for production, so a broken experiment cannot lock the client out of its own portal.
Stack TypeScript, React 19, Vite, Tailwind, React Native, Firebase, Vertex AI, Gemini, Puppeteer, Playwright, pnpm, Turborepo
Results
Live at cd-infoware.web.app with a sister municipal deployment; every invoice carries its own model cost line; monthly draft invoices are generated per device without manual meter entry.
The most recent month of commits is a hardening sweep: rules validation, role-escalation coverage, a manifest race, GST checks, invoice-numbering rollback safety, customer-data protection, alerting and a runbook.
| Measure | Value | Source |
|---|---|---|
| Commits, May to September 2026 | 111, all mine | git author count |
| Vertex OCR concurrency cap | maxInstances: 10 | function config |
| Cost recorded per invoice | USD and INR | Firestore record |
| Meter counters diffed per device per night | about 60 | scrape job |
| Draft invoices | 1 per device per month, Tally format | billing job |
| Sister deployment (Thane Municipal Corporation) | about 10 real accounts in the MVP | feasibility study, 21 August 2026 |
What I'd do differently
The hardening sweep came last. Next time I would schedule it as a phase in the plan with the client, because invoice-numbering rollback safety and role-escalation tests are the parts a finance team will ask about first, and doing them as a sweep meant re-opening code I had already handed over.
Links, labelled honestly
- cd-infoware.web.applive client application, login required for most screens
- cdinfoware-tmc.web.appsister deployment for municipal field support, live