Blog/GST & ITC
Catch it before it reaches the books

The same invoice was posted twice. One had a different GSTIN.

Duplicate invoices are not always obvious. A scanned photocopy resubmitted with a slightly different GSTIN can slip past a simple uniqueness check. Conditional uniqueness stops the exact duplicate; a different GSTIN still needs a human.

Your supplier sends the same invoice twice. Once as a photocopy, once as a phone photo. The first one has GSTIN 27AAPFU1234A1ZF. The second has 27AAPFU1234A1ZE. One digit is different because the second scan is blurrier. Both have the same invoice number. Both get posted. You pay twice.

This is not a hypothetical. It happens in every AP department that processes scanned invoices. The duplicate is not always identical. Sometimes the GSTIN is slightly different. Sometimes the total is rounded differently. Sometimes the date is typed as 15/01 instead of 01/15. The duplicate is there, but it does not look like a duplicate.

Three kinds of duplicates

TypeWhat it looks likeHow to catch it
Exact duplicateSame invoice number, same GSTIN, same amountSimple uniqueness check
Near-duplicateSame invoice number, different GSTIN (typo in second scan)Manual review; the constraint only fires on the same GSTIN
Amount splitSame supplier, same date, amounts that sum to the originalSupplier + date + amount pattern
The uniqueness keyBoth present, both match
Invoice no.
INV-447
+
Seller GSTIN
27AAPFU1234A1ZF
=
Verdict
Duplicate
Same number, different GSTIN
Not this key. Could be two suppliers, or a blurry second scan. Review, do not auto-kill.
Empty GSTIN
Key does not fire. Unidentified rows can coexist. That is the empty-GSTIN exception.

The exact duplicate is easy. Conditional uniqueness catches it: same invoice number and same seller GSTIN cannot become a second row. On ingest the existing row is updated; the manual create path returns 409.

The near-duplicate is harder. The invoice number is the same, but the GSTIN is different. This happens when the same invoice is scanned twice, and the second scan is blurrier. The OCR reads a different digit in the GSTIN. The result is two rows with the same invoice number but different GSTINs. The conditional constraint allows this because the GSTINs differ. The system does not run a separate invoice-number-only duplicate flag today; a person has to spot it.

The amount split is the rarest. The supplier sends two invoices that together cover the original amount. Maybe the original was for ₹100,000 and the supplier sent two invoices for ₹60,000 and ₹40,000. Or maybe the supplier corrected a mistake and sent a credit note. These are not true duplicates, but they need to be investigated.

How EntryLedger catches them

EntryLedger uses conditional uniqueness. The database constraint is: the same invoice number plus the same seller GSTIN is a duplicate. But if the GSTIN is empty (unidentified invoice), multiple rows can coexist. This handles both the exact duplicate and the near-duplicate where the GSTIN was mistyped in one scan.

The conditional part is important. Not every invoice has a clear GSTIN. Some scans are too blurry to read the GSTIN. Some invoices from small suppliers do not carry a GSTIN at all. If the uniqueness check required a GSTIN, these invoices would be blocked even when they are legitimate. By making the constraint conditional (GSTIN must match only if both are non-empty), EntryLedger handles the real-world messiness of scanned invoices.

When the same invoice number and seller GSTIN already exist for the tenant, the new read is merged into that row on ingest, and the manual create endpoint returns 409. There is no separate duplicate review screen and no side-by-side comparison yet. The same file bytes are skipped before extraction. A near-duplicate with a different GSTIN is not caught by the constraint.

Why this matters

Duplicate invoices cost money. If you pay the same invoice twice, the cost is the full invoice amount. If you post the same invoice twice to Tally, your books are wrong. The reconciliation shows a discrepancy. Someone spends time tracing it. The time costs money.

The cost is not just financial. Duplicate entries distort your AP aging. They inflate your expenses. They make your financial statements inaccurate. At audit time, duplicate entries raise questions. The questions take time to answer. The answers cost money.

Catching duplicates at entry time is cheap. Catching them at audit time is expensive. The difference is a few seconds of validation versus hours of investigation.

What this looks like in practice

An invoice comes in as a photocopy. The invoice number and seller GSTIN are extracted. EntryLedger checks whether both already exist for the tenant. If they do, the database refuses a second row: ingest merges the new read into the existing row, and the manual create endpoint returns 409. No side-by-side duplicate screen exists today, so a near-duplicate with a different GSTIN still needs a person to spot it.

The process takes seconds. The duplicate is caught at entry time, before it reaches Tally. The books stay clean. The reconciliation stays clean. The audit stays clean.

What Tally will and will not save you from

Tally can reject a duplicate voucher number in some setups. It will not help if the second scan got a different invoice number because OCR read O as 0. It will not help if the clerk “fixed” a failed import by changing the number. Detection has to happen on the scan identity (number + seller GSTIN) before export, not after a human has already improvised.

Empty GSTIN is a special case. Unidentified invoices must be allowed to sit in the queue without crashing uniqueness. If you make GSTIN mandatory in the database, you either invent GSTINs (criminal) or you cannot ingest the scan. Conditional uniqueness is the boring engineering that matches paper. Block when both keys are present and equal. Allow when a key is still missing. Review the missing ones.

Payment runs are where duplicates become cash. AP that posts twice and pays twice is a recovery project. AP that refuses the second row is a click. Build the click. Do not wait for the bank statement.

If your volume is small and one person remembers every invoice, you may not need software for this. If two clerks share a pile and email-in is on, you do. Memory does not scale to 40,000 pages. Constraints do.

Sources
  • EntryLedger
    entryledger.shop

    Duplicate handling: same invoice number and seller GSTIN, when both are present. Empty GSTIN rows are not forced unique.

Frequently asked questions
How does duplicate detection work?+

Conditional uniqueness: same invoice number + seller GSTIN = duplicate. Empty GSTINs coexist.

What happens when a duplicate is found?+

An identical re-send is merged into the one row, silently. A re-arrival with different figures is held for review with a duplicate reason naming the field that changed, so the earlier amounts are never quietly replaced. The create endpoint returns 409 for a second row with the same number, supplier and type.

Catch duplicates before they reach Tally

Conditional uniqueness stops a second row for the same number and seller GSTIN, before payment.