---
title: "Gemini invoice OCR and Tally GST invoicing | Manas Nikam"
canonical: https://manasnikam.dev/case-studies/cd-infoware-gemini-ocr-tally-invoicing/
updated: 2026-09-23
---


# Gemini invoice OCR and automated Tally invoicing for a printer-fleet business

By Manas Nikam, May to September 2026 (ongoing). Client work.

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.

## 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.

> Scoping artifact. A feasibility study dated 21 August 2026, approved by the client, defined the sister deployment for Thane Municipal Corporation field support: about 10 real accounts in the MVP and account-less QR ticket reporting guarded by App Check. It is live at cdinfoware-tmc.web.app. The fleet telematics spec (16 September, revised 17 September 2026) for the same client records a completed Go design that I reviewed and discarded for Rust.

## 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.

1. Invoice PDF uploaded to Storage from the web or field app
2. Storage trigger, Cloud Function capped at 10 instances
3. Gemini on Vertex AI with a JSON schema (vendor, GSTIN, PAN, lines, serials, tax splits)
4. Token cost recorded per invoice in USD and INR; customer and vendor resolved
5. Firestore record for review; draft, never final

OCR path: upload, capped function, schema-constrained Gemini call, cost line, reviewable draft. The billing path runs separately: a 02:00 IST Puppeteer scrape of the Konica Minolta portal, a counter diff, and a Tally-format draft per device per month.

### 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.

CD Infoware platform, counts from the repository

| 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

- [cd-infoware.web.app](https://cd-infoware.web.app): live client application, login required for most screens
- [cdinfoware-tmc.web.app](https://cdinfoware-tmc.web.app): sister deployment for municipal field support, live
