DEV Community

Cover image for What sends a bill to review? Routing rules for Hindi + English OCR in a fleet ledger
JC TechLabs
JC TechLabs

Posted on

What sends a bill to review? Routing rules for Hindi + English OCR in a fleet ledger

By JC TechLabs · Lessons from building JC Fleet

In Indian trucking, the "document management system" is usually WhatsApp. Diesel slips, toll receipts, weighbridge tickets and repair bills arrive as photos from drivers, often at night and often in Hindi, English or both.

When we built WhatsApp bill OCR into JC Fleet, the hard part wasn't reading the bills. OCR does that reasonably well. The hard part was deciding what the system is allowed to believe.

Our starting rule: OCR output is a draft, not a fact. Everything below follows from it.

Why not just post it?

A fleet ledger is a chain. A diesel entry feeds trip settlement, which feeds driver pay, which feeds the truck's P&L and eventually the CSV that goes to Tally or Busy. A wrong amount at the start quietly corrupts everything after it, and nobody notices until month-end, when it's hard to trace.

Wrong numbers in a ledger are far more expensive than slow numbers. So the design question becomes: which drafts can move along with light checks, and which must stop for a person?

The WhatsApp bill OCR pipeline, briefly

WhatsApp / upload ──> intake ──> OCR (hi + en) ──> extract fields ──> match ──> route
                                                                         │
                                    ┌────────────────────────────────────┴───────┐
                                review queue                               ready for entry
                                    │                                            │
                      person checks against original               entry after review / approval
                                    └──────────────> ledger, trip, driver pay <──┘
                                                           │
                                                 CSV for Tally / Busy / Excel
Enter fullscreen mode Exit fullscreen mode

The router is where the trust lives.

Hindi + English OCR routing rules that send a bill to review

We group the triggers into three families: incomplete, unclear and conflicting.

1. Incomplete: a required field is missing.
Every record type has a minimum set of fields. A fuel slip needs at least a vehicle, a quantity or amount, and a date. If any of them is missing, the draft can't become an entry by itself.

REQUIRED = {
    "fuel_receipt": ["vehicle", "total", "date"],
    "toll_receipt": ["vehicle", "total", "date", "plaza"],
    "weighbridge":  ["vehicle", "gross_weight", "date"],
}

def missing_fields(doc_type, fields):
    return [f for f in REQUIRED[doc_type] if not fields.get(f)]
Enter fullscreen mode Exit fullscreen mode

2. Unclear: the OCR isn't sure.
Low confidence on a field that matters (amounts, plates, dates) sends the draft to review. We care about field-level confidence, not page-level. A slip can be 95% readable and still have the one number that matters smudged.

Hindi and mixed-script bills deserve their own attention here: handwritten totals, Devanagari numerals and stamps over printed text are common. We don't try to be clever with them. Uncertain means review.

3. Conflicting: the fields disagree with each other or with what we know.
This is the family people forget, and the most useful one.

def conflicts(f, ctx):
    issues = []
    if f.get("qty") and f.get("rate") and f.get("total"):
        if abs(f["qty"] * f["rate"] - f["total"]) > ctx.tolerance:
            issues.append("qty x rate != total")
    if f.get("vehicle") and f["vehicle"] not in ctx.fleet_plates:
        issues.append("unknown or conflicting plate")
    if ctx.is_possible_duplicate(f):
        issues.append("possible duplicate bill")
    return issues
Enter fullscreen mode Exit fullscreen mode

Quantity × rate not matching the total, a plate that isn't in the fleet registry, the same bill sent twice by two people: each one is a cheap check, and each one catches a class of error that confidence scores miss completely. OCR can be very confident about a number that is simply wrong.

What the reviewer sees

A review queue only works if checking is fast. Each item shows:

  • the original image next to the extracted fields
  • the reason it was routed ("qty × rate ≠ total", "plate not in registry")
  • controls to correct a field, retry extraction on a better photo, or reject

Supervisors can also pause automatic posting rules entirely, for example while onboarding a new route or a new kind of bill, so everything goes through review until they're comfortable.

What we deliberately don't do

  • No silent "best guess" amounts. If the system isn't sure, it says so.
  • No P&L from unreviewed data. P&L follows the recorded entries, not an unverified automatic profit figure.
  • No pretending the record is complete. Reports are only as complete as the entries behind them, and the UI says that plainly.

The takeaway

If you're building OCR into anything that touches money, design the distrust first. Define what's required, what counts as unclear and what counts as a conflict, and give a person a fast way to check. Your users will forgive a bill that waited an hour for review. They won't forgive a ledger they can't trust.


JC Fleet is fleet operations and accounting software for Indian transporters, a product of JC TechLabs. If you run a fleet and want to see the review queue on your own bills, **book a 15-minute walkthrough* at jcfleet.com/demo or ask for a WhatsApp demo on +91 95408 38669. Bring 2 or 3 sample bills. More: document upload & OCR · accounting & Tally / Busy / Excel export · truck diesel slip review guide.*

Top comments (0)