cadrumo.application.ledger.closure_findings module¶
Arithmetic identities a real invoice must satisfy, checked at read time.
Three families of identity, all deterministic, all computed from figures the document itself states:
The total closes. total = base + cuota + recargo + suplido. Every term is
a component the document prints; when they do not reach the printed total,
either a component was misread or the document carries one the draft cannot
represent. Both are silent under-declarations when the printed figure is
discarded unexamined, which is why the printed total is never normalised toward
the computed one – the disagreement IS the finding.
Cash closes. cash = total - retencion. IRPF withheld at source is
subtracted from the invoice total to reach the money that actually moves, so a
draft that drops the retención reconciles the ledger against the wrong figure –
and reconciles successfully, against a bank movement that is short by exactly
the withholding.
The per-rate breakdown closes. The per-rate subtotals must sum to the flat base and cuota beside them. These are two independent readings of one document, so a disagreement means at least one is wrong and the draft cannot say which. Modelo 303 declares cuota devengada per tier, so a breakdown that sums correctly in total while misattributing base between tiers still declares into the wrong tier – checked per tier as well as in aggregate.
On tolerance, deliberately. Real invoices round per line, so a few cents of
drift across several terms is arithmetic rather than error. The allowance is one
cent per contributing term and no more (ROUNDING_ALLOWANCE_PER_TERM).
That is wide enough to absorb genuine per-line rounding and far too narrow to
absorb a misread component. A tolerance loose enough to let a real discrepancy
pass is not a more forgiving version of this check – it is the check switched
off, which is why the gate mutation-proves the boundary rather than trusting it.
This module is distinct from printed_total_discrepancy(),
and the distinction is the point: that function compares the document against the
invoice that was actually WRITTEN, so it can only run at confirm. These identities
are internal to the document and run the moment it is read, which is where the
operator is still deciding.
See also
DraftDiscrepancyFindingThe finding record produced here.
DraftDiscrepancyKindThe closed kind axis; this module never widens it.
printed_total_discrepancy()The confirm-time, document-versus-record counterpart.
- ROUNDING_ALLOWANCE_PER_TERM¶
Cents of drift allowed per contributing term, and not one more.
Per-line rounding is real: a document stating four components can legitimately land a cent away from their sum. Scaling the allowance by the number of terms admits exactly that and nothing else. It is deliberately NOT a percentage – a percentage grows with the invoice, so the largest invoices, where a misread component costs the most, would get the widest licence to be wrong.
- within_rounding_allowance(difference, *, term_count)[source]¶
Return whether difference is inside the per-term rounding allowance.
- closure_findings(draft)[source]¶
Return every arithmetic-closure finding the draft’s own figures raise.
Deterministic and total: the same draft always yields the same findings, and an empty result means every identity that COULD be checked held – never that the document is correct, because an identity with a missing term is not checked at all.
- Parameters:
draft (
InvoiceDraft) – The draft to check, carrying the figures the document stated.- Return type:
- Returns:
Findings in a stable order – total closure, cash closure, flat rate consistency, per-tier rate consistency, then breakdown sums – so an operator surface and a test both read them the same way.