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

Legacy VAT C-Form Interstate Sales Tracking

A pre-GST concession record for interstate sales, retained for historical bookkeeping — not part of the live compliance path

1. Requirements

This mechanism predates the GST regime documented elsewhere in this set and is described here purely as legacy, historical support. It sits on no current statutory filing path, and its own in-source documentation marks it for eventual removal.

1.1 Functional requirements

  • Record receipt of a statutory concession document (a Concession Record): a series-numbered identifier, received date, customer, legal entity, declared quarter and state, and a declared total amount.
  • Attach one or more Sales Invoices to a Concession Record as the interstate sales it covers, keeping each invoice’s own “which record claims me” reference in sync with that list.
  • Recompute and store a running total of the linked invoices’ grand totals, independent of the manually entered declared total.
  • Prevent an invoice being claimed by two Concession Records at once, or claimed while not flagged eligible, or claimed if it does not exist as a real, posted invoice.
  • On cancellation, release every invoice the record had claimed.

1.2 Non-functional requirements

  • Referential correctness over time: the set of invoices claimed and the set of invoices pointing back must never diverge.
  • No silent double-claiming: the already-claimed guard is enforced server-side, not only in the entry-picker’s client-side filter.

1.3 Constraints

  • The record follows the ordinary draft → submit → cancel → amend lifecycle common to submittable documents here; nothing about its status model is bespoke.
  • The linked-invoice table has no special submitted-editability — once submitted, changing the claimed list is not a routine edit.
  • Historical note on currency: the eligibility flag this mechanism reads from an invoice, and the field it writes its own identifier into, are not present anywhere in this codebase’s current core invoice schema, nor in any custom-field definition found in either source tree read for this document. The controller logic below is described exactly as written; whether these two fields still exist in a given deployment is a separate, environment-specific question this document does not resolve.

2. High-Level Design

2.1 Component diagram

2.2 Data flow — submitting a Concession Record

  1. Validate each listed invoice: flagged eligible; not already claimed by a different record (re-claiming by itself is fine); and a lookup restricted to posted, uncancelled invoices must return a row, or the save is rejected.
  2. On submit, run the bidirectional sync (§ 3.2).
  3. On every save, recompute the stored total as the sum of every listed invoice’s grand total, written directly.
  4. On cancel, clear the claim reference from every invoice pointing back at this record, regardless of whether it is still in the child table.

3. Deep Dive

3.1 Data model

Concession Record — the submittable header: identifier, received date, customer, legal entity, quarter (I–IV), a freeform state, a declared total, a separately computed invoiced-amount total, and the standard amended-from reference.

Amending a cancelled record follows the ordinary framework pattern — a new Draft references the cancelled one — and is the only way to change a claimed-invoice list once submitted, since the original’s list is otherwise frozen (§ 3.2).

Concession Invoice Line — a child row: the linked Sales Invoice plus read-only, auto-fetched copies of its date, territory, net total, and grand total, pulled the moment an invoice is picked.

Sales Invoice (plain, reused) — the claimed side. The controller reads one field to decide eligibility and reads/writes a second to record which Concession Record currently claims it, both compared as plain strings — the eligibility flag against a literal value, not a boolean test — and the claim reference is cleared by writing an empty value, not a dedicated “unclaim” flag.

3.2 The bidirectional reference sync (the one non-obvious behavior)

On submit, the mechanism updates in two directions in a single pass: every invoice listed in the child table gets this record’s identifier written into its claim-reference field, and, separately, any invoice not in that list but still carrying this record’s identifier gets that field cleared. The second half is the only place stale claims get cleaned up — without it, an invoice removed from the list pre-submission would keep pointing at a record that no longer claims it.

This sync runs only at submit — never on an ordinary save, and nowhere else in the controller. Since the child table has no submitted-editability, there is no routine way to change the claimed list afterward; the practical path is cancel (§ 3.3) and amend into a new draft, whose eventual submit re-runs the same sync fresh. A submitted record’s claim list is therefore effectively fixed the moment it is submitted.

3.3 Cancellation cleanup

Cancellation clears the claim-reference field from every invoice currently pointing back at this record — unconditional, not filtered by the current child-table contents. This is a strict subset of what the submit-time sync can do: it only ever removes references, never adds one.

3.4 Total recomputation

The stored total is recalculated on every save, draft or submitted, as the sum of every listed invoice’s grand total — a direct field write, not a full re-save. This is distinct from the record’s manually entered declared total; nothing compares the two.

3.5 Error handling

  • Ineligible invoice — rejected outright, naming the offending row.
  • Invoice already claimed elsewhere — rejected, pointing at the other record.
  • Invoice missing, cancelled, or not found — rejected by row number.
  • Empty invoice list — the claim step is rejected outright rather than silently creating a claim-less record.

4. Scale and Reliability

  • Low volume by nature. A concession document arrives per customer per statutory period, not per transaction — a manually-triggered workflow, not a hot path.
  • No concurrency guard beyond the claim check itself. “Not already claimed” is a point-in-time read at save time; two racing submissions are not explicitly locked against each other, though low volume makes this a narrow window.
  • Nothing scheduled touches this mechanism — every transition is a direct user action, so there is nothing to monitor beyond the save-time rejections themselves.

5. Trade-off Analysis

Decision Trade-off
Bidirectional sync runs only at submit, not on every save Keeps the common case simple and cheap — but the claimed list is effectively frozen post-submission, with cancel-and-amend the only path to change it.
Cancellation cleanup is unconditional, not scoped to current child-table contents Guarantees no invoice is left pointing at a cancelled record — redundant with the submit-time sync’s own cleanup half outside genuine edge cases.
Eligibility flag compared as a literal string, not a boolean Consistent with how the field appears to be defined, but a stricter type would catch a typo or case mismatch at the schema level, not only at this call site.
Two independently maintained totals (declared vs. computed), never reconciled Preserves the declared paper-document amount unmodified — but leaves any mismatch entirely to manual review; nothing flags a discrepancy.

6. What to Revisit as the System Grows

  • This mechanism should not gain new investment. It is pre-GST legacy support that its own in-source documentation marks for eventual removal, and the invoice-side fields it depends on may already be absent from a given deployment’s schema.
  • If retained at all, give the eligibility flag a stricter field type so a comparison mistake fails at data-entry time, not only at this one call site.
  • Document the frozen-after-submit claim list explicitly for support staff — cancel-and-amend is not a discoverable way to change it without reading the controller.

Was this page helpful?