Overview
The v4 reconciliation engine (lib/invoice_v4.ts) transforms raw OCR output into mathematically consistent invoice data. It handles ambiguous pricing modes, applies sequential discounts, normalizes GST rates to standard slabs, and reconciles computed totals against printed anchors.
Core principle: Trust printed anchors (HSN table, taxable subtotal, grand total) as ground truth, then work backwards to adjust line items and discounts.
Reconciliation Pipeline
Phase 1: Item Line Recomputation
Location:lib/invoice_v4.ts:136-241
Choosing Line Ex-Tax
The problem: OCR may provide multiple sources for the same value:- Computed from
qty × rate_ex_tax × (1 - discounts) - Printed line amount (may include or exclude tax)
- Model-provided
amount_ex_tax_after_discount
lib/invoice_v4.ts:149-205):
GST Normalization
Location:lib/standards.ts:15-38
GST slabs: [0, 0.25, 3, 5, 12, 18, 28]
- Input:
17.8→ Snap to18 - Input:
0.28→ Scale to28, snap to28 - Input:
19.5→ No slab match, return19.5
CGST/SGST vs IGST Split
Location:lib/invoice_v4.ts:140-148
Intra-state (supplier state == place of supply):
lib/standards.ts:165-170).
Phase 2: Header Discounts
Location:lib/invoice_v4.ts:245-332
Sequential Application
Rule: Apply inorder field, multiplicatively for percents.
- Items ex-tax: ₹1000 (18% bucket: ₹600, 12% bucket: ₹400)
- Discount 1: 10% → ₹900 (18%: ₹540, 12%: ₹360)
- Discount 2: ₹50 absolute → ₹850 (split 540/900 and 360/900: ~₹33.33 and ~₹16.67)
Smart Allocation to GST Buckets
Location:lib/invoice_v4.ts:270-327
When a printed GST total exists, absolute discounts are allocated greedily to buckets matching the target effective tax rate.
Phase 3: Anchor to Printed Values
Location:lib/invoice_v4.ts:334-450
HSN Tax Table Scaling
Priority 1: If printed HSN table exists, scale items to match exactly.- Printed HSN table: 18% bucket = ₹10,000
- Computed 18% items = ₹10,050
- Scale factor = 10000 / 10050 = 0.9950
- All 18% items scaled down by 0.5%
Printed Taxable Subtotal Fallback
Location:lib/invoice_v4.ts:419-449
If no HSN table, use printed taxable_subtotal:
taxable_subtotal, subtract them first.
Phase 4: Charges Processing
Location:lib/invoice_v4.ts:452-477
GST Rate Inference
- Items: ₹10,000 ex-tax, ₹1,800 GST → weighted rate = 18%
- Freight charge: ₹500, taxable=true, no rate hint → infer 18%
Non-Taxable Charges Decision
Location:lib/invoice_v4.ts:479-521
Some invoices exclude non-taxable charges (e.g., packing materials) from the grand total. The engine tries both:
Phase 5: Totals Computation
Location:lib/invoice_v4.ts:479-566
Calculation Order
Round-Off Philosophy
Location:lib/invoice_v4.ts:543-544
Phase 6: Multiple Hypotheses
Location:lib/invoice_v4.ts:587-641
Alternate Scenarios
ThereconcileV4 function tries 4 hypotheses:
- as_is: Use model’s rate interpretation
- items_only_when_no_hsn: Exclude charges from taxable subtotal anchor
- from_printed_with_tax: Reinterpret printed rate as tax-inclusive
- from_printed_without_tax: Reinterpret as tax-exclusive
Scoring
Location:lib/invoice_v4.ts:598-607
Round-Off Adoption
Location:lib/invoice_v4.ts:624-632
Edge Cases
Case 1: Pre-Discount Amount Printed
Location:lib/invoice_v4.ts:184-200
Problem: Invoice prints Qty × Rate in the Amount column even when discount exists.
Solution:
Case 2: Charges in Taxable Subtotal
Location:lib/invoice_v4.ts:421-432
Problem: Some layouts include freight/packing in “Total Taxable Value”, others don’t.
Solution: Try both interpretations, pick lower error:
Case 3: Mixed Intra/Inter-State
Location:lib/invoice_v4.ts:140-148
Problem: Supplier and buyer in different states but place of supply ambiguous.
Solution: Fall back to buyer GSTIN if place_of_supply_state_code missing:
Case 4: HSN Table Missing Rates
Location:lib/invoice_v4.ts:349-361
Problem: HSN table has taxable_value but no explicit cgst_rate/sgst_rate/igst_rate.
Solution: Infer from tax amounts:
Case 5: TCS on Which Base?
Location:lib/invoice_v4.ts:535-539
Problem: TCS may apply to subtotal before or after non-taxable charges.
Solution: The decideIncludeNonTaxable logic already accounts for this by trying both.
Debugging Output
Location:lib/invoice_v4.ts:634-638
components/invoice-viewer-v4.tsx:215-219
Performance Characteristics
- Time complexity: O(n × k) where n = items, k = candidates (fixed at 4)
- Space complexity: O(n) for item cloning per candidate
- Typical runtime: under 10ms for 50-item invoices on modern hardware
Testing
Location:lib/__tests__/invoice_v4.test.ts
Coverage:
- Basic reconciliation (matched totals)
- Price mode detection (WITH_TAX vs WITHOUT_TAX)
- Sequential discounts
- HSN table scaling
- CGST/SGST vs IGST split
- Non-taxable charges inclusion/exclusion
Next Steps
- OCR Processing Flow - End-to-end pipeline
- OpenRouter Integration - API setup
- Testing Guide - Writing reconciliation tests
