AI Document Intelligence, KRA eTIMS Receipt OCR & Automated Three-Way AP Matching: Eliminating Manual Supplier Invoice Data Entry and Overbillings

How CFOs and shared-service finance teams eliminate manual supplier invoice typing and catch price/quantity overbillings using Vision-LLM OCR, real-time KRA eTIMS tax invoice verification, and deterministic 3-Way Matching (PO vs. GRN vs. Invoice).

In mid-to-large retail chains, hospitality groups, hospitals, construction firms, and manufacturing enterprises processing 1,500 to 30,000+ supplier invoices every month, Accounts Payable (AP) clerks spend 70% of their week typing PDF and paper supplier invoices line-by-line into the ERP. Worse still, manual AP processing fails to catch three expensive leaks: suppliers billing KES 1,480 per unit when the approved Purchase Order (LPO) agreed KES 1,420; suppliers invoicing for 200 cases when the warehouse Goods Received Note (GRN) only signed for 185 cases; and non-compliant tax invoices that lacks a valid KRA eTIMS Control Unit Invoice Number (CUIN)—exposing the company to a 30% Corporate Income Tax disallowance on non-eTIMS expenses.

We engineer custom AI Accounts Payable (AP) Automation, Tax Compliance & Deterministic 3-Way Matching Engines in Python, Django, Celery, and PostgreSQL that ingest supplier invoices via email, WhatsApp, or vendor portals, extract every header and line item with 99.4%+ field accuracy, verify the eTIMS QR/Control Unit signature live before booking the liability, and reconcile Purchase Order (LPO) ↔ Warehouse GRN ↔ Supplier Invoice down to the individual SKU and cent.

1. Why Legacy Template OCR & Manual ERP Checks Fail Accounts Payable

Traditional coordinate-based OCR tools (which break whenever a supplier shifts a column by 5 millimeters) and basic ERP invoice entry screens fail modern finance teams in four ways:

  • Supplier SKU Naming Mismatches: Your internal ERP calls an item SKU-40912: Cooking Oil 20L Jerrycan, while Supplier A's invoice prints 20LTR GOLDEN FRY VEG OIL and Supplier B prints G/FRY 20L J-CAN. Without semantic embedding + historical vendor-SKU mapping, automation stalls on every line item.
  • Multi-GRN & Partial Delivery Complexity: A single Blanket LPO for 1,000 bags of cement is delivered across 4 separate truckloads (4 separate GRNs over 2 weeks) and billed across 3 partial supplier invoices. Matching M invoices against N GRNs and 1 LPO requires a cumulative line-balance state machine, not a simple 1-to-1 document check.
  • Invalid or Cancelled KRA eTIMS Tax Invoices: Under strict tax authority electronic invoicing mandates, if a supplier issues a commercial invoice without a valid eTIMS Control Unit serial and QR payload—or issues an eTIMS invoice that fails to declare your company's Buyer PIN—your finance team cannot deduct that expense for corporate tax purposes.
  • Duplicate Invoice Submissions (Email + Physical Copy): A supplier emails a PDF invoice on Tuesday (INV-2026-884) and their driver hands a stamped physical copy of the exact same invoice to the receiving clerk on Friday (INV-2026/884). Without fuzzy invoice-number normalization and SHA-256 fingerprint checks, AP teams accidentally pay the same invoice twice.

2. End-to-End AI AP Ingestion, eTIMS Verification & 3-Way Match Architecture

Our pipeline combines multimodal Vision-LLM structured extraction with strict, deterministic PostgreSQL accounting tolerance gates—ensuring the AI never invents numbers and never posts an unbalanced journal entry:

Stage 01
Omnichannel Capture
AP Email Inbox, Vendor Portal & Mobile Scan Ingestion
→
Stage 02
Vision-LLM + QR Parser
Structured Line Extraction + KRA eTIMS QR/PIN Validation
→
Stage 03
Vendor SKU Resolver
Hybrid Semantic + Historical Alias Mapping to Internal SKU
→
Stage 04
Deterministic 3-Way Match
Line-by-Line PO Rate & Cumulative GRN Quantity Audit
→
Stage 05
ERP Voucher & Debit Note
Auto-Post Clean Invoices; Auto-Draft Discrepancy Debit Note

A. Deterministic Three-Way Matching Engine (PO vs. Cumulative GRNs vs. Invoice)

While Vision LLMs excel at reading messy multi-page scans and mapping supplier item descriptions to your internal SKU catalog, all financial arithmetic and tolerance checks run in deterministic Python Decimal code. For every extracted invoice line, our engine verifies:

  1. Cross-Foot Arithmetic Integrity: Does Qty × Unit_Price - Discount + VAT == Line_Total, and does the sum of all lines equal the invoice header total?
  2. Buyer Tax PIN & eTIMS Control Number Check: Does the decoded eTIMS QR payload match the printed invoice total, Supplier PIN, and your corporate Buyer PIN?
  3. Unit Price Tolerance Gate: Is Invoice_Unit_Price <= PO_Agreed_Unit_Price (within a configurable penny-rounding tolerance, e.g., ±0.5% or KES 2.00)?
  4. Cumulative GRN Quantity Ceiling: Is Previously_Invoiced_Qty + Current_Invoice_Qty <= Total_Accepted_GRN_Qty?
# Python / Django: Deterministic 3-Way AP Matching & Automatic Debit-Note Discrepancy Engine
from decimal import Decimal, ROUND_HALF_UP
from django.db import transaction

class ThreeWayAPMatchEngine:
    UNIT_PRICE_TOLERANCE_PCT = Decimal("0.002")  # 0.2% rounding tolerance

    @classmethod
    @transaction.atomic
    def evaluate_supplier_invoice_match(cls, extracted_invoice_id: int) -> dict:
        inv = ExtractedSupplierInvoice.objects.select_for_update().get(id=extracted_invoice_id)

        # 1. Hard Gate: Verify KRA eTIMS Control Unit Invoice Number (CUIN) & Buyer PIN
        if not inv.etims_verified or inv.extracted_buyer_pin != inv.company.kra_pin:
            inv.match_status = "BLOCKED_ETIMS_NON_COMPLIANT"
            inv.save(update_fields=["match_status"])
            return {"status": inv.match_status, "reason": "Missing/invalid eTIMS CUIN or Buyer PIN mismatch."}

        discrepancies = []
        total_overcharge_kes = Decimal("0.00")

        for line in inv.lines.select_related("po_line").all():
            po_line = line.po_line
            # 2. Check cumulative accepted GRN quantity vs. cumulative invoiced quantity
            unbilled_grn_qty = po_line.total_grn_accepted_qty - po_line.total_qty_already_invoiced
            if line.invoiced_qty > unbilled_grn_qty:
                qty_excess = line.invoiced_qty - unbilled_grn_qty
                overcharge = (qty_excess * po_line.agreed_unit_price).quantize(
                    Decimal("0.01"), rounding=ROUND_HALF_UP
                )
                total_overcharge_kes += overcharge
                discrepancies.append({
                    "sku": po_line.sku.code,
                    "type": "QUANTITY_EXCEEDS_GRN",
                    "invoiced_qty": str(line.invoiced_qty),
                    "available_grn_qty": str(unbilled_grn_qty),
                    "overcharge_kes": str(overcharge),
                })

            # 3. Check Unit Price against Approved PO Unit Price
            max_allowed_price = po_line.agreed_unit_price * (Decimal("1.0") + cls.UNIT_PRICE_TOLERANCE_PCT)
            if line.invoiced_unit_price > max_allowed_price:
                unit_diff = line.invoiced_unit_price - po_line.agreed_unit_price
                billable_qty = min(line.invoiced_qty, max(Decimal("0.0"), unbilled_grn_qty))
                price_overcharge = (unit_diff * billable_qty).quantize(Decimal("0.01"), rounding=ROUND_HALF_UP)
                total_overcharge_kes += price_overcharge
                discrepancies.append({
                    "sku": po_line.sku.code,
                    "type": "UNIT_PRICE_EXCEEDS_PO",
                    "invoiced_price": str(line.invoiced_unit_price),
                    "po_agreed_price": str(po_line.agreed_unit_price),
                    "overcharge_kes": str(price_overcharge),
                })

        if not discrepancies:
            inv.match_status = "THREE_WAY_MATCHED_READY_FOR_POSTING"
        else:
            inv.match_status = "EXCEPTION_DEBIT_NOTE_DRAFTED"
            inv.discrepancy_overcharge_kes = total_overcharge_kes
        inv.save()

        return {
            "invoice_number": inv.invoice_number,
            "match_status": inv.match_status,
            "total_overcharge_caught_kes": str(total_overcharge_kes),
            "discrepancies": discrepancies,
        }

B. Automated Supplier Discrepancy Debit Notes & Monthly Statement Reconciliation

When the 3-Way Match engine catches a price or quantity overcharge (e.g., 10 damaged units rejected at the receiving bay on the GRN that the supplier still billed on the invoice), your AP clerk does not need to spend 25 minutes writing an email. With one click, the system generates an itemized Supplier Discrepancy Claim / Request for Credit Note attaching the signed GRN shortage report and PO price excerpt, holds the disputed delta, and clears the undisputed portion for payment upon receipt of the supplier's eTIMS Credit Note.

3. Build vs. Manual AP Entry & Per-Page OCR SaaS Comparison

Capability Manual AP Clerk Data Entry Generic Cloud OCR SaaS (Per-Page Fee) Custom AI AP, eTIMS & 3-Way Match Engine
Processing Speed per Invoice 6–12 minutes per multi-line invoice Extracts text; manual SKU mapping < 8 seconds end-to-end with auto SKU resolution
KRA eTIMS / Tax QR Verification Manual spot-checks; high audit risk No local tax registry awareness 100% automated eTIMS QR, CUIN & Buyer PIN gate
Multi-GRN & Partial Delivery Match Tracked on paper stapled to POs Simple 1-to-1 PO header match only Cumulative line-level M-to-N PO/GRN/Invoice state machine
Monthly Supplier Statement Recon Days of Excel VLOOKUPs at month-end Not supported Automated PDF Supplier Statement vs. AP Ledger diff

4. Development Timeline & Cost Guide

  • Phase 1: Omnichannel Ingestion, Vision-LLM Line Extraction & eTIMS QR Validator (Weeks 1–4): Dedicated AP email/portal ingestion, structured line extraction, cross-foot math check, and KRA eTIMS compliance gate.
  • Phase 2: Semantic Vendor-SKU Alias Resolver & 3-Way Match Workbench (Weeks 5–8): Self-learning vendor SKU catalog mapping, cumulative PO/GRN/Invoice 3-way matching, and side-by-side exception review UI.
  • Phase 3: Supplier Statement Auto-Reconciliation & ERP/Bank Payment Sync (Weeks 9–11): Automated monthly supplier statement reconciliation, withholding VAT/WHT calculation, and bidirectional sync with SAP, Oracle, Dynamics Business Central, Odoo, or custom ERPs.

5. Frequently Asked Questions (FAQ)

Can the system reconcile a supplier's end-of-month Statement of Account against our internal AP ledger automatically?

Yes. When a supplier emails their monthly PDF or Excel Statement of Account listing 80 invoices and credit notes, our Automated Statement Reconciliation Engine parses every row and immediately categorizes each reference into: Matched & Paid, Matched & Open in ERP, Amount Variance, or Missing Invoice (On Supplier Statement but Never Received by AP).

How does the system handle utility bills, rent, and service invoices that do not have a warehouse GRN?

For non-inventory overhead invoices (electricity, water, legal fees, telecoms, software subscriptions), the engine routes the invoice through a 2-Way Contract/Budget Match and department-head digital approval workflow while still enforcing the eTIMS tax validation and duplicate-invoice guard.

Does this replace our existing ERP or plug into it?

It can operate either as a native module inside your custom Django/PostgreSQL ERP or as an intelligent pre-accounting middleware layer that pushes verified, 3-way-matched AP vouchers directly into SAP, Microsoft Dynamics 365 Business Central, Oracle NetSuite, Sage, or QuickBooks Enterprise.

Architectural Continuity & Deep Dives

For related production architectures and system implementations, explore these companion guides:

Real-Time Voice Agent Latency Budget Estimator

// Full-Duplex WebRTC Pipeline Waterfall
WebRTC / SFU Telemetry

Calculate voice round-trip latency from client audio input to synthesized agent audio output. Target conversational turnaround is <700ms.

160 ms
Turn detection window
110 ms
Deepgram / Whisper chunking
210 ms
vLLM / Groq / OpenAI stream
130 ms
Cartesia / ElevenLabs / Melo
40 ms
Edge SFU Gateway
Estimated Total Turnaround Latency
650 ms
✓ Natural Conversational Flow (<700ms)
VAD STT LLM TTFT TTS 1st Packet WebRTC RTT
Engineering a low-latency voice pipeline? We build full-duplex WebRTC SFU systems with sub-700ms round trips.
// Real-Time Audio Telemetry • Voice AI Architecture Review

Engineering Real-Time Voice Agents or Low-Latency LLM Serving?

Achieving sub-700ms full-duplex conversational latency requires careful orchestration between WebRTC media gateways, continuous batching (vLLM), and neural TTS streaming. Let's inspect your pipeline waterfall together.

All Insights
Chat on WhatsApp