Feature Spotlight: GSTR‑2B Reconciliation Engine
The Problem
Indian SMBs spend dozens of hours each month reconciling the purchase register against the GSTR‑2B download. The traditional approach relies on exact string matching of vendor name, GSTIN, invoice number, amount, tax rate, and HSN code. Even a single typo or a minor rounding difference can cause a match to fail, forcing manual investigation and increasing the risk of ITC mis‑claiming.
Solution Architecture
GSTBot replaces the manual process with an automated reconciliation engine built on a proven stack:
-
FastAPI – a lightweight ASGI framework that exposes a
/reconcileendpoint. - Python – the core language for business logic.
- PostgreSQL – stores the purchase register, GSTR‑2B records, and reconciliation results.
- Redis – provides a distributed lock to prevent concurrent reconciliation runs for the same period.
- Celery – schedules the heavy lifting as an asynchronous task.
The engine works in three phases:
- Data Ingestion – PDFs, photos, or Excel files are parsed by an AI extractor into a structured JSON payload.
- Reconciliation – a tolerance‑aware matcher compares each register line to the GSTR‑2B set.
- Exception Classification – unmatched or partially matched lines are tagged with a recommended action.
Tolerance Engine
The heart of the matcher is a set of configurable tolerances that replace strict equality checks. A typical configuration looks like:
# config/tolerances.py
TOLERANCES = {
"amount_pct": 0.02, # 2% difference allowed
"tax_rate_pct": 0.01, # 1% difference allowed
"hsn_variation": 1, # one HSN code difference tolerated
"date_window_days": 7, # invoices within 7 days are considered
}
During reconciliation, each register line is queried against the GSTR‑2B table using a single, indexed SQL statement. Recent commits (GB029 M8 performance audit) removed redundant double‑scan functions and narrowed the /rule37 query to credit‑eligible purchases, improving runtime from ~45 s to <10 s on a 10 k‑row dataset.
SELECT g.*
FROM gstr2b g
JOIN purchase_register p
ON p.vendor_gstin = g.vendor_gstin
AND p.invoice_number = g.invoice_number
WHERE ABS(p.amount - g.amount) / g.amount <= :amount_pct
AND ABS(p.tax_rate - g.tax_rate) / g.tax_rate <= :tax_rate_pct
AND ABS(EXTRACT(DAY FROM p.invoice_date - g.invoice_date)) <= :date_window_days
AND ABS(p.hsn - g.hsn) <= :hsn_variation;
The query uses a composite index on (vendor_gstin, invoice_number, amount, tax_rate, hsn, invoice_date) to keep lookups sub‑millisecond.
Concurrency Guard
To avoid double‑processing, the Celery task acquires a Redis lock keyed by the fiscal month and year. The lock has a short TTL (30 s) to prevent accidental deadlocks.
# tasks/reconcile.py
from redis import Redis
from celery import shared_task
redis = Redis()
@shared_task
def run_reconciliation(month, year):
lock_key = f"reconcile:{year}-{month:02d}"
if not redis.set(lock_key, "1", nx=True, ex=30):
raise RuntimeError("Reconciliation already running for this period")
try:
# core logic
pass
finally:
redis.delete(lock_key)
Example API Flow
# api/main.py
from fastapi import FastAPI, Depends
from tasks.reconcile import run_reconciliation
app = FastAPI()
@app.post("/reconcile")
async def start_reconciliation(month: int, year: int):
task = run_reconciliation.delay(month, year)
return {"task_id": task.id, "status": "queued"}
The UI (React + Vite) polls /tasks/{id} for status and displays a table of reconciled rows, color‑coded by exception type. Clicking a row reveals the underlying GSTR‑2B and register data, along with a suggested action such as "Adjust amount", "Verify GSTIN", or "Re‑file".
Real‑World Usage
A typical SMB uploads a 5 MB batch of invoices in a single operation. The AI extractor runs in the background, pushing JSON blobs to a Celery queue. Within 2 minutes, the /reconcile task completes, and the finance team can review the 1 % exception rate in a matter of minutes, rather than hours of manual checks.
Performance & Observability
Recent commits (GB032 M11) added structured logging for silent operations, while GB033 M5 surfaced swallowed errors in OAuth and payment flows. The observability stack now logs every reconciliation run, including query execution times and exception counts, making it straightforward to spot regressions.
Conclusion
GSTBot’s GSTR‑2B reconciliation engine turns a labor‑intensive, error‑prone task into a repeatable, auditable process. By leveraging tolerances, a robust query plan, and a distributed lock, the system delivers reliable ITC matching for Indian SMBs without the high cost of enterprise‑grade tools.
Key takeaways
- Configurable tolerances replace brittle exact matching.
- A single indexed query keeps latency low.
- Redis locks and Celery ensure idempotent, concurrent‑safe runs.
- The UI presents actionable exceptions, streamlining compliance.
GSTBot exemplifies how focused automation can elevate GST compliance for small businesses in India.
Top comments (0)