Skip to content
ERPNext Data Model
Esc
navigateopen⌘Jpreview
On this page

GST Inward Supply Reconciliation & Invoice Management System

A design reference for matching government-reported purchase data against local purchase records to determine input-tax-credit eligibility

1. Requirements

1.1 Functional requirements

  • The system must ingest inward-supply data from three distinct government-hosted sources — a rolling, supplier-authored feed; a static, once-generated monthly statement; and a near-real-time actionable feed — into one shared local record store, so that downstream matching, reporting and credit-eligibility logic operates on a single record shape regardless of which source produced it.
  • Every downloaded inward-supply record must be automatically matched against the legal entity’s own purchase records (ordinary purchase invoices and import-duty documents) without manual intervention for the common case, using a cascade of increasingly permissive rules.
  • Where automatic matching cannot find a confident match, the system must expose the specific numeric and field-level differences so an operator can decide how to treat the record: accept it, ignore it, mark it pending, or manually link it to a specific purchase record.
  • Every match outcome — automatic or manual — must translate into a concrete input-tax-credit (ITC) claim-period decision written onto the linked purchase record, not merely a descriptive label.
  • For the actionable feed specifically, an operator’s accept/reject/pending decision on a document must be capable of being pushed back to the government portal as a save or reset request, and the outcome of that request must be tracked back to completion.
  • Re-running a download for a period that was already downloaded must not create duplicate inward-supply records; it must reconcile the existing set against what the portal currently reports, including records that have since disappeared from the feed.
  • Operators must be able to view a purchase register and an HSN-wise summary of inward supplies computed from the company’s own bookkeeping, independent of whatever the portal currently reports, as a cross-check against the reconciliation view.

1.2 Non-functional requirements

  • Idempotent ingestion: downloading the same period twice, or recovering from a partially completed download, must never produce duplicate inward-supply records for the same document.
  • Bounded matching cost: the reconciliation pass must not compare every purchase record against every inward-supply record; candidates must be bucketed by supplier registration number (and, on a second pass, by the broader entity-level grouping) before any field-by-field comparison runs.
  • Resilience to asynchronous portal processing: a “queued, retry later” response from the portal must be absorbed by a background retry path rather than surfaced as a failure, and must not block the rest of a multi-period or multi-category download.
  • Safety around a non-idempotent write-back: the actionable feed’s save/reset request is a real, portal-side, non-repeatable action tied to a transaction token; the system must track that token to a terminal outcome rather than re-issuing the request blindly.

1.3 Constraints

  • All three feeds are pulled through the shared, credential-scoped transport layer described separately; this document assumes a working, authenticated session and does not re-derive session handling, OTP verification, or egress-IP binding.
  • The government portal’s own processing (how it decides to generate a statement, how it decides a document is “filed”) is not observable and is treated as an opaque request/response contract.
  • Matching itself is entirely local and batch-oriented — no portal call happens during the comparison pass; the portal is consulted only to fetch data and, for the actionable feed, to push a decision back.
  • Generic tax computation and landed-cost apportionment are out of scope here and are covered by the tax engine and the landed-cost allocation subsystem documented separately.

2. High-Level Design

2.1 Component diagram

The three feeds converge on one store precisely so that the reconciliation engine, the ITC-eligibility logic, and the two purchase-side reports never need to know which feed produced a given record. The one asymmetry is the actionable feed’s return path: it is the only one of the three that a local decision can push back into.

2.2 Data flow — download and idempotent upsert

This is a per-source annotated procedure rather than a branching graph:

  1. Resolve the periods to fetch. For the static statement, a period already marked “don’t re-download” (because a summary return was filed after the data was last pulled) is skipped; for the rolling feed there is no such marker, since it is expected to keep changing until the return closes.
  2. Call the portal once per period per category (the rolling feed is split into several categories — ordinary invoices, credit/debit notes, input-service-distributor documents, and import-related categories; the static statement and the actionable feed call once per period or per section instead).
  3. Classify the response before touching any local record:
    • no data found → record the outcome in a download log and move on; nothing is written to the inward-supply store.
    • queued → record the returned token and a server-suggested wait in the download log; a background sweep later re-issues the same request using that token until it resolves.
    • statement not yet generated → stop processing any later period in the same batch, since a later period cannot be ready before an earlier one.
    • data present → proceed to step 4.
  4. Upsert, never insert blindly. Each document from the response is matched against an existing inward-supply record by registration number, document number, document date, and category classification. If a row already exists for that key, it is updated in place; otherwise a new row is created. This is what makes re-running a download for an already-downloaded period safe — the second run overwrites the same rows rather than duplicating them.
  5. Reconcile what fell out of the feed. Before the upsert loop runs, the existing rows for that period are loaded into a working set; every record touched by the current response is removed from that set as it is processed. Whatever remains afterward is a record the portal no longer reports for that period. For the rolling feed, such a record is deleted outright — it typically means the supplier’s filing changed. For the static statement, such a record instead has its period tag cleared rather than being deleted outright, since the same document can still exist in the actionable feed or reappear against a different, corrected period; separately, any document explicitly reported as rejected by the actionable side within that same static-statement batch is deleted.

2.3 Data flow — the two-pass matching cascade

Both passes group candidates by supplier before any field comparison, so the cost of the cascade scales with the number of documents per supplier, not with the full cross-product of every purchase record against every inward-supply record.


3. Deep Dive

3.1 Data model

Inward Supply Record — one row per document as reported by any of the three sources, keyed for upsert purposes by supplier registration number, document number, document date, and a classification code (ordinary invoice, credit/debit note, input-service-distributor document, or an import-related category). Carries the supplier’s declared amounts (taxable value and each tax head), a link back to the matched purchase record once one exists, and source-provenance flags recording which of the three feeds populated or last touched it. A document can be re-classified as “amended” against an earlier filing under the same registration and document number; when that happens the original record’s own match status and link are transferred onto the amendment, so an accepted match survives a supplier’s correction rather than being lost.

Match Status — the field written onto an inward-supply record once it has an opinion about a purchase-side counterpart. Only four values are ever actually persisted by the matching engine or by manual action: Exact Match, Suggested Match, Mismatch, and Manual Match (set only by an operator’s explicit link action). Two further values exist on the field’s own option list but are not produced by the engine: Unlinked is the value an operator’s unlink action writes back, and Amended marks a record superseded by a later filing. Two more labels — Missing in Purchase Record and Missing in Inward Supply — appear only in the reconciliation view built for the operator, computed at read time from which side of a pairing is absent; neither is ever written onto a stored record, since by definition one side of that pairing does not exist to write onto. A seventh, internal-only category the engine uses while cascading — a permissive “amounts agree, document number ignored” rule — is never itself persisted: by the time a match assigned by that rule is saved, it has already been rewritten to Suggested Match (if the two documents share a registration number) or Mismatch (if they were matched only at the broader entity-grouping level).

Reconciliation Status — a companion field on the purchase-side record (ordinary purchase invoice or import-duty document), independent of Match Status. Values are Unreconciled, Reconciled, Ignored, Match Found, and Not Applicable. The engine and the manual-link path both set it to Match Found; an operator’s bulk accept/ignore/pending action maps onto Reconciled/Ignored/Unreconciled respectively on the purchase side, while the matching inward-supply record’s own action field is set directly to the same word.

ITC Claim Period — a field on the purchase-side record recording which filing period a claim should land in, or a sentinel value meaning “deferred to a later period.” It is not merely descriptive: a validation rule blocks changing it once the target period’s summary return has already been filed, and a submitted document cannot silently drift its claim period after the fact without that same check re-running.

Reconciliation Workspace and IMS Workspace — both are single, per-installation control surfaces (there is exactly one live instance of each, not one per document) that carry the current filter scope (company, registration, date ranges) and expose the operator actions described in §3.3. Neither stores reconciliation data itself; both compute their views on demand from the Inward Supply Record Store and the purchase-side records.

3.2 The matching algorithm

Matching runs as two passes, each a fixed, ordered list of rules evaluated top to bottom; the first rule that matches a given pair consumes both records so no later rule or later pair can reuse them.

  • Registration-level pass — seven rules, all requiring the same registration number on both sides plus the same fiscal year, place of supply, and reverse-charge flag. They differ in how strictly the document number and the amounts must agree:
    • an exact rule requiring every tax field to match to the unit;
    • two variants of a permissive rule accepting either a fuzzy document-number match or amounts within a rounding tolerance (or both at once);
    • two mismatch rules that fire when the document number agrees (exactly or fuzzily) but nothing else does;
    • one residual rule that ignores the document number entirely provided the amounts agree within tolerance, restricted to pairs within a ten-day date window (this restriction is dropped for the credit/debit-note category, since a note’s own date is often later than the purchase document it corrects).
  • Entity-level (PAN) pass — run only over what the first pass left unmatched, and skipped entirely for the import category (the rolling feed carries no supplier registration information for imports, so there is nothing to fold). The registration number’s ten-character core is used to group candidates that may sit under different branch registrations of the same legal entity. Four rules apply, all evaluating the combined tax total rather than the individual tax heads (since IGST versus CGST/SGST splits can legitimately differ across branches for the same underlying transaction).

The “fuzzy” document-number comparison strips financial-year fragments and separators, strips leading zeros, and requires the two documents’ dates to fall within the same ten-day window before comparing at all; it accepts a 100% partial-string match immediately, otherwise falls back to a best-match score and requires at least 90%. The “rounding tolerance” comparison is a flat absolute difference of at most one currency unit per field (or on the combined total, at the entity-level pass) — not a percentage, and not scaled to invoice size.

3.3 ITC eligibility decision

A match by itself is not an eligibility decision; a separate calculation turns it into a specific filing period (or a deferral). The calculation consults, in order:

  1. A direct actionable-feed decision on the document itself. A Reject or Pending decision always defers the claim to a later period. An Accept decision claims it in whichever period the operator was working in.
  2. A prior actionable-feed decision on the linked inward-supply record, when the trigger was an ordinary match rather than a fresh action — a document already marked Reject/Pending upstream still defers the claim even if the local match just succeeded.
  3. A default period, computed as the later of the purchase document’s own posting period and the period the inward-supply record was reported against — never earlier than the transaction itself, and never a period whose summary return the registration has already filed. If the computed period turns out to already be filed, the calculation steps forward one period at a time until it finds one that is not, up to a statutory cutoff.

Every period this calculation assigns is recorded with a comment explaining what triggered it (an automatic match, a manual link, or a named actionable-feed decision), so a claim period is always traceable to the event that set it.

3.4 Write-back: the actionable feed’s save/reset request

The actionable feed is the only one of the three sources the system pushes decisions back into, and that write-back is asynchronous and tracked to completion rather than fire-and-forget.

Only documents whose local decision differs from what was last confirmed as uploaded are included in a given save or reset request, so repeating the workspace action after a successful upload sends nothing. A request that is still in progress is not re-issued; the workspace instead polls the same token. A terminal, per-document error from the portal (returned alongside an otherwise-successful batch) excludes only that document from the local reconciliation step, rather than failing the whole batch.

3.5 Error handling

  • Partial download. Each category/period combination is logged independently as it completes; a failure partway through a multi-period, multi-category download leaves earlier combinations recorded and only the untried remainder pending on the next run — nothing already saved is redone or duplicated.
  • Queued outcome. Handled identically across all three feeds: the response’s token and suggested wait are recorded, and a background job re-issues the exact same request later using that token, deleting the pending record on an eventual “no data” or terminal error, and completing the original save on eventual success.
  • A session that dies mid-download. The download pipeline makes no attempt to recover a lost session itself — that is the shared transport layer’s responsibility (documented separately) and this pipeline simply lets the resulting error surface. Because the upsert step is keyed by document identity rather than by position in a response, a download that is retried after re-authenticating picks up cleanly: periods and categories already saved are not re-fetched by the caller’s own “what’s still missing” check, so no duplicate rows result from resuming.
  • Manual mismatch resolution never reaches the portal. Accepting, ignoring, marking pending, or manually linking/unlinking a record in the reconciliation workspace is a purely local write; unlinking explicitly leaves any already-assigned ITC claim period untouched rather than reverting it, on the reasoning that an operator can always set it again deliberately.

4. Scale and Reliability

  • Download load pattern: bursty around filing-window deadlines, when many registrations are pulled in succession; the pipeline reports progress per period via a push channel so a long multi-period pull does not look stalled to an operator watching it.
  • Matching cost: bounded by supplier-registration bucketing before any field comparison, and further bounded by removing matched pairs from the working set as soon as a rule fires, so later rules in the same cascade see a strictly shrinking candidate pool rather than re-scanning everything.
  • No proactive backlog control for queued downloads: the retry sweep re-checks every pending token on its own schedule regardless of how many have accumulated; a portal-side slowdown that queues many periods at once produces a correspondingly larger sweep, not a smaller one.
  • Write-back concurrency: the actionable feed’s “only upload what changed since the last confirmed upload” comparison is a plain field comparison at request-build time, with no lock preventing a second workspace action from being queued while an earlier save/reset request is still outstanding for the same registration; the portal’s own token model is what ultimately prevents that from corrupting anything, not anything in this layer.
  • Monitoring: a pending download-log entry with a token and no resolution after several sweep cycles signals a portal-side stall worth escalating; a purchase record stuck at Unreconciled well past a filing deadline signals either a genuine data gap or a rule the cascade does not cover.

5. Trade-off Analysis

Decision Trade-off
One shared inward-supply record store for three feeds with different period semantics Downstream matching, ITC-eligibility, and reporting logic is written once, but the store must carry three independent sets of provenance and period fields (one static, one rolling, one per-document) rather than one clean period column, and the “what fell out of the feed” reconciliation logic differs by source rather than being generic.
Ordered rule cascade, first hit wins, per pass Cheap to reason about and to extend (append a rule), but rule order is load-bearing: a looser rule placed earlier would silently starve a stricter rule of candidates it should have matched first.
A distinct entity-level (PAN) pass rather than one combined rule set Lets branch-to-branch supplier filings match even when the registration number differs, but doubles the rule surface to maintain and means “why did this match” sometimes requires knowing which pass fired, not just which rule.
Flat absolute rounding tolerance rather than a percentage Simple and predictable for small invoices, but the same one-unit tolerance is proportionally far more forgiving on a small invoice than on a large one.
Asynchronous, token-tracked write-back for the actionable feed only Correctly respects that a save/reset request is a real, non-repeatable government-side action, but means the workspace’s own “pending” state can lag the portal’s actual state until the next status poll — an operator can see a decision as unresolved slightly after the portal has already settled it.
Manual link/unlink never contacts the portal Keeps operator corrections cheap and instantaneous, but a manually corrected match is invisible to the portal — there is no equivalent “tell the portal we disagree” path for the two feeds that are pull-only.

6. What to Revisit as the System Grows

  • Surface the entity-level pass’s rule provenance in the workspace, not just the final Match Status, so an operator reviewing a Suggested or Mismatch result can see whether it came from the stricter registration-level cascade or the looser entity-level one — today that distinction is computed and then discarded.
  • Scale the rounding tolerance to invoice size once the flat one-unit tolerance starts producing visibly wrong classifications at the high and low ends of the invoice-value distribution.
  • Add a lock around the actionable feed’s save/reset request-building step, once workspace usage is concurrent enough that two overlapping “upload what changed” comparisons for the same registration become a realistic race rather than a theoretical one.
  • Give the download-log retry sweep backlog awareness — a fixed-schedule sweep that treats one queued period the same as fifty will not scale gracefully through a portal-wide slowdown at a filing deadline.
  • Reconsider silently dropping the entity-level pass for the import category if import volume grows enough that cross-branch import matching becomes a real gap rather than a rare edge case — today it is skipped unconditionally rather than degraded gracefully.

Was this page helpful?