Reconciliation Engine

A 5-pass deterministic matching engine that reconciles a company's purchase register against its vendor-filed GSTR-2B. Each pass trades strictness for recall, surviving entries drop into the next pass, never backwards.

← Architecture Overview ← Invoice Matching Logic End-to-End Data Flow → Reconciliation Workbench →
Engine Signature

Everything runs through a single entry point. Inputs are already-parsed, already-validated rows. Output is a full per-entry breakdown plus aggregate counters.

reconcile(books, gstr2b, config) → ReconciliationReport

books
Purchase register rows (parsed by excel_parser) after validation by invoice_validator.
gstr2b
GSTR-2B invoices (parsed by gstr2b_parser) with IRN, GSTIN, normalized invoice #, dedup hashes.
config
Tolerances: value_tolerance=1.0, date_tolerance_days=7, fuzzy_threshold=0.85.
Report
Summary counts + entries[] with match_category, match_confidence, match_pass, mismatch_types[], auto_resolved, and the full books_record + gstr2b_record for audit.
The 5-Pass Ladder

Click any pass to see exactly how it matches, what confidence it emits, and why it sits where it does.

Pass 1
Certain
IRN_EXACT
Match by Invoice Reference Number (cryptographic e-invoice hash)
Used only for e-invoiced B2B supplies · passes/irn_match.py
1.00
confidence
Pass 2
Strict
EXACT_MATCH
GSTIN + normalized invoice # + value (± Rs 1)
The happy path · passes/exact_match.py
0.95
confidence
Pass 3
Fuzzy
FUZZY_INVOICE
GSTIN exact + fuzzy invoice # (similarity ≥ 0.85 via rapidfuzz)
Handles OCR errors and ERP export drift · passes/fuzzy_match.py
0.80
confidence
Pass 4
Review
AMOUNT_DATE
GSTIN + amount (±2%) + date (±7 days). Invoice # unreconcilable.
Always flags requires_review · passes/amount_date_match.py
0.65
confidence
Pass 5
Loose
LOOSE_MATCH
GSTIN + amount (±1%) only. Last resort before declaring missing.
Always requires_review · passes/loose_match.py
0.45
confidence
Click any pass row above for the matching recipe, algorithm, and typical use cases.
Full Engine Pipeline

The 5 passes are sandwiched between preparation and resolution steps, ordering matters.

engine.reconcile(), step by step

Step 1 · Dedup
duplicate_detector.py, exact duplicates are removed, near-duplicates flagged for review.
Step 2 · Pass 1-5
Sequential matching. Each pass consumes a slice of the unmatched pool.
Step 3 · Mismatch detect
For matched pairs, diff each field. Populate mismatch_types[].
Step 4 · Auto-resolve
auto_resolver.py, promote trivial mismatches back to MATCHED.
Step 5 · Cross-period
cross_period.py, residue searches adjacent 2B periods (late filers) with month penalty.
Step 6 · Classify
Unmatched books → MISSING_IN_GSTR2B · Unmatched 2B → MISSING_IN_BOOKS.
Step 7 · Confidence
confidence.py, adjust per-entry score with vendor grade & cross-period penalties.
Mismatch Types

Even inside MATCHED pairs, individual fields can disagree. Each type carries a downstream ITC risk weight.

TAXABLE_VALUE
Net (pre-tax) invoice value differs between books and 2B.
Risk weight: +50
TAX_AMOUNT
CGST/SGST/IGST/cess don't match, direct ITC mismatch.
Risk weight: +60
TAX_RATE
Effective GST rate implied by the numbers differs.
Risk weight: +40
DATE
Invoice dates differ by more than tolerance.
Risk weight: +10
INVOICE_NUMBER_FORMAT
Matched fuzzy but invoice # strings differ, format drift.
Risk weight: +5 (warn)
REVERSE_CHARGE
Books claims reverse charge but 2B does not (or vice versa).
Risk weight: +30
Auto-Resolver Rules

Small, deterministic fixes that take a mismatched pair back to MATCHED without human review.

Rule 1 · Rounding

  • Value difference ≤ Rs 1 (absolute)
  • Each tax component delta within proportional tolerance
  • Dates and GSTINs already exact
  • Marks entry as auto_resolved=true, MATCHED

Rule 2 · Date-only drift

  • All numeric fields exact
  • Date difference ≤ 3 days
  • Invoice # exact (post-normalization)
  • Common cause: book-date vs invoice-date convention

Rule 3 · Invoice # format-only

  • Normalized invoice # identical (prefix strip, zero strip, case fold)
  • All other fields already exact
  • Original strings differ (e.g. INV-001/24 vs inv1/24)

Never auto-resolves

  • Tax amount mismatches beyond rounding
  • Different GSTINs (even if visually similar)
  • Pass 4 / Pass 5 matches (always require review)
  • Reverse-charge flag disagreements
Confidence Adjustment

The pass baseline is just the start, confidence moves up or down based on secondary signals.

Exact value match
+0.05
Exact date match
+0.03
Vendor grade A (high compliance)
+0.03
Vendor grade C (low compliance)
-0.05
Cross-period (per month offset)
-0.05
IRN available but not used
-0.02
ITC Risk Scoring

Once reconciliation completes, every non-MATCHED (or mismatched) entry is scored 0-100 and bucketed. This drives the finance-team action queue.

Critical
71-100
3-day SLA · immediate vendor follow-up
High
51-70
7-day SLA · confirm with vendor this week
Medium
31-50
15-day SLA · batch follow-up
Low
0-30
30-day SLA · monitor at month-end
MISSING_IN_GSTR2B
+80
TAX_AMOUNT_MISMATCH
+60
TAXABLE_VALUE_MISMATCH
+50
CROSS_PERIOD_MATCH
+20
VENDOR_LOW_COMPLIANCE
+15
HIGH_VALUE (>= Rs 1L)
+10
DATE_ONLY_MISMATCH
+10
Sample Report Output

Shape of the final ReconciliationReport for a typical month with 200 vendor invoices.

ReconciliationReport (illustrative)

142
Matched
18
Auto-resolved
14
Mismatched
9
Probable matches
12
Missing in 2B
5
Missing in books
3
Duplicates
{ "total_books_entries": 200, "total_gstr2b_entries": 195, "matched": 142, "auto_resolved": 18, "mismatched": 14, "probable_matches": 9, "missing_in_gstr2b": 12, "missing_in_books": 5, "duplicates_detected": 3, "entries": [ { "supplier_gstin": "29ABCDE1234F1Z5", "invoice_number": "INV-2024/0412", "match_category": "MATCHED", "match_pass": "EXACT_MATCH", "match_confidence": 0.98, "books_value": 125000.00, "gstr2b_value": 125000.00, "mismatch_types": [], "auto_resolved": false }, // ... one entry per reconciled pair or unmatched record ] }