Work / Chitragupt / Wiki / Concepts
receipt-to-transaction-linkage
Conceptcanonicalverified 2026-08-18
CONCEPT.RECEIPT-TO-TRANSACTION-LINKAGESummary
Evidence-only documents (receipts, bills, invoices) never create their own ledger entries — instead, the backend matches each one to the bank transaction that already represents the same cash movement, so the user sees one thing, not two.
Why it matters
A user who uploads both a flight ticket and the bank statement carrying the same debit would otherwise see that spend counted twice — once from the receipt, once from the bank row. Evidence-only forms (travel-receipt, utility-bill, motor-insurance-policy, generic-receipt, professional-receipt) deliberately emit no ledger mapper of their own; the bank statement is the single source of truth for cash movement. linkEvidenceToLedger (apps/functions/src/inbox/link-evidence-to-ledger.ts) closes the gap between "the document exists" and "the user can see it's the same ₹5,000" by matching on amount + a ±7-day date window and stamping the link both ways.
Implications
- Matching runs once, automatically, at document-confirm time — not user-initiated.
- Only forms in
EVIDENCE_FORMS_FOR_LINKING(packages/shared/src/config/evidence-linking.ts) are attempted.hr-letteris deliberately excluded — offer/relieving letters carry no matching debit. Any new evidence-only form type must be added there explicitly; it is not automatic. - A match requires an exact paise amount and a candidate within 7 days — no tolerance. A partial payment or a paise-rounding difference produces
no_candidate_found, not a fuzzy match. - More than 5 exact-amount, same-day candidates (e.g. bulk EMI batch) is treated as
ambiguous_multiple_matchesand deliberately left unlinked rather than guessed. - The outcome of every attempt (
linked,no_extractable_amount_or_date,no_candidate_found,ambiguous_multiple_matches) is stamped on the document aslink_reasonand surfaced in the Inbox (LinkedTransactionNote) — a receipt that didn't link tells the user why instead of silently showing nothing. - The confirm-time attempt is one-shot: if the matching bank statement is uploaded after the receipt, only
hourlyRetryEvidenceLinking(apps/functions/src/triggers/scheduled/hourly-retry-evidence-linking.ts) — which re-queries rows still atlink_reason: no_candidate_found, capped atEVIDENCE_LINK_RETRY.max_retrieshourly passes — can still resolve it.no_extractable_amount_or_dateandambiguous_multiple_matchesare not retried; re-running doesn't change either outcome. - N-to-1 is supported: an invoice and its payment receipt can both stack onto the same ledger row via
evidence_doc_idsrather than fighting over it.
Related
- inbox-document-types — defines which form types are evidence-only vs. ledger-mapped
- 2026-08-10-inbox-wireframe-alignment — the decision that first surfaced this linkage in the UI
Sources
- apps/functions/src/inbox/link-evidence-to-ledger.ts
- apps/functions/src/triggers/scheduled/hourly-retry-evidence-linking.ts
- apps/functions/src/triggers/firestore/handlers/persist-confirmed-document.ts
- packages/shared/src/config/evidence-linking.ts
- packages/shared/src/config/evidence-link-retry.ts
- apps/website/src/components/inbox/LinkedTransactionNote.tsx
Every project of mine is written down like this.
Read the résumé