cadrumo.domain.modelos.calculation_revision module

One calculation attempt under a modelo work unit.

A work unit owns many calculation revisions. Each calculate invocation produces a fresh, content-addressed CalculationRevision. The work unit carries pointer fields that disambiguate which revision is the most recent (current) and which one is the filed answer (filed). Without those pointers, multiple drafts under the same work unit have no canonical selection — every consumer (year-aggregation, amendment delta, forward-period carry-forward) needs to know which one is THE revision. Formula provenance for every computed casilla is carried through CasillaObservation entries in the typed observations envelope.

Lifecycle states:

  • BORRADOR — newly calculated; mutable in the sense that re-running calculate creates a new revision rather than editing this one. Multiple borradores can coexist.

  • VERIFICADO_COMPLETO — verify ran cleanly: all required casillas resolved, zero blocking findings, source trace persisted. The revision is immutable from this point on; any recalculation produces a fresh borrador instead.

  • PRESENTADO — paired with a ModeloRecord. The revision is the currently-effective filed answer for its (bucket, modelo, year, period) tuple. Exactly one presentado revision per tuple at any time.

  • PRESENTADO_SUPERSEDIDO — a later verified revision was filed against the same tuple. The revision and its filing record remain in the audit trail.

  • DESCARTADO — operator abandoned the revision before filing.

Two CalculationRevisions can never share a calculation_revision_id; the id is the SHA-256 of the inputs + binding overrides + computed casilla values (plus the parent work_unit_id), so structurally identical re-runs produce the same id and re-running calculate with the same data is naturally idempotent.

class CalculationRevisionState(*values)[source]

Bases: StrEnum

Closed enumeration of calculation-revision lifecycle states.

BORRADOR
VERIFICADO_COMPLETO
PRESENTADO
PRESENTADO_SUPERSEDIDO
DESCARTADO
SEALED_REVISION_STATES: frozenset[CalculationRevisionState]

States in which a revision is no longer a mutable draft, because something was verified or filed. CalculationRevisionState.BORRADOR and CalculationRevisionState.DESCARTADO are excluded. CalculationRevisionState.PRESENTADO_SUPERSEDIDO IS included – a superseded filing still occurred.

This is NOT the same as the narrower “currently authoritative” set, which additionally excludes CalculationRevisionState.PRESENTADO_SUPERSEDIDO.

CURRENT_SEALED_REVISION_STATES: frozenset[CalculationRevisionState]

A revision an operator-facing action may select as the CURRENT result – sealed AND not superseded.

This is SEALED_REVISION_STATES minus CalculationRevisionState.PRESENTADO_SUPERSEDIDO. The two must never be substituted for one another: this narrower set is for surfaces that hand a revision to an operator or a third party as the current state of a filing, while the broader SEALED_REVISION_STATES remains correct for surfaces that need every sealed revision including superseded ones.

The operations replay path (application.modelo.operation_definitions’s export operation) deliberately uses the broader SEALED_REVISION_STATES instead of this set, because a journalled export replay must reproduce its own history even after the revision it names has been superseded. This narrower set exists for the operator-facing surfaces that must not do that – as one of them puts it, “a superseded revision is stale and must not be handed to an accountant as current” (see application.modelo.review_package).

empty_row_source_identities()[source]
Return type:

dict[TypeAliasType, RowSourceIdentity]

empty_row_casilla_values()[source]
Return type:

dict[TypeAliasType, Decimal]

empty_row_casilla_provenance()[source]
Return type:

dict[TypeAliasType, DirectRowMaterializationProvenance]

class CalculationRevisionIdentityInputs[source]

Bases: TypedDict

The complete, target-id-free input set for a calculation revision hash.

work_unit_id: str
input_values_by_casilla_id: Mapping[TypeAliasType, str]
binding_overrides: Mapping[TypeAliasType, str]
row_binding_values: Mapping[TypeAliasType, Mapping[str, str]] | None
row_source_identities: Mapping[TypeAliasType, RowSourceIdentity]
row_casilla_values: Mapping[TypeAliasType, Decimal]
row_casilla_provenance: Mapping[TypeAliasType, DirectRowMaterializationProvenance]
casilla_values: Mapping[TypeAliasType, Decimal]
relation_overrides: Mapping[TypeAliasType, str] | None
source_transaction_ids: Sequence[str]
m210_official_tipo_renta_code: str | None
m210_gross_income_source_mode: M210GrossIncomeSourceMode | None
borrador_snapshot_id: str | None
bindings_sourced_from_borrador: Sequence[TypeAliasType]
detail_rows: Sequence[Modelo184MemberRow | Modelo232VinculadaRow | Modelo349OperadorRow | Modelo349RectificacionRow | Modelo347ContraparteRow | Modelo210AgrupacionRentaRow]
source_issues: Sequence[CalculationSourceIssue]
source_provenance: Sequence[CalculationSourceRef]
filing_instance_evidence: FilingInstanceEvidence | None
m303_regimen_simplificado_annual_summary_handoff: M303RegimenSimplificadoAnnualSummaryHandoff | None
amendment_identity: CalculationRevisionAmendmentIdentity | None
cleared_casilla_ids: Sequence[TypeAliasType]
calculation_revision_identity_inputs(*, work_unit_id, input_values_by_casilla_id, binding_overrides, row_binding_values=None, row_source_identities=None, row_casilla_values=None, row_casilla_provenance=None, casilla_values, relation_overrides=None, source_transaction_ids=(), m210_official_tipo_renta_code=None, m210_gross_income_source_mode=None, borrador_snapshot_id=None, bindings_sourced_from_borrador=(), detail_rows=(), source_issues=(), source_provenance, filing_instance_evidence, m303_regimen_simplificado_annual_summary_handoff=None, amendment_identity=None, cleared_casilla_ids=())[source]

Build the one complete target-id-free calculation-revision identity input.

Every revision-id derivation flows through this builder. The annual-summary handoff is deliberately represented here, rather than selectively added by its producer or read-side verifier, so a future identity consumer cannot silently omit the cross-model calculation input.

Return type:

CalculationRevisionIdentityInputs

Parameters:
derive_calculation_revision_id(*, work_unit_id, input_values_by_casilla_id, binding_overrides, row_binding_values=None, row_source_identities=None, row_casilla_values=None, row_casilla_provenance=None, casilla_values, relation_overrides=None, source_transaction_ids=(), m210_official_tipo_renta_code=None, m210_gross_income_source_mode=None, borrador_snapshot_id=None, bindings_sourced_from_borrador=(), detail_rows=(), source_issues=(), source_provenance, filing_instance_evidence, m303_regimen_simplificado_annual_summary_handoff=None, amendment_identity=None, cleared_casilla_ids=())[source]

Return the deterministic SHA-256 id for a calculation attempt.

Return type:

str

Parameters:
class CalculationSourceRef(**data)[source]

Bases: BaseModel

One resolver-level source-object trace persisted on a calculation revision.

The calculation source mesh (cadrumo.application.aggregation) resolves each registry binding source through an enrolled resolver and produces a typed CalculationSourceProvenance row per contributing source object. This is the DOMAIN-side, persistence-shaped projection of that provenance: it carries exactly the resolver→source-object→fingerprint trace that lets an audit reader reconstruct which resolver mesh and which upstream source objects produced a revision, and whether those objects have since drifted.

It deliberately does NOT carry legal_refs / source_refs — those are the per-casilla regulatory grounding already carried by CasillaObservation on the same revision; duplicating them here would fragment the grounding across two surfaces.

source_casilla_ids is NOT grounding and the rationale above does not extend to it. It is a subject IDENTITY – which casilla(s), if any, this source object’s resolution feeds – and nothing else on the revision carries it for the general (non-row-materialized) case; dropping it here was an omission the application-side CalculationSourceProvenance did not itself make (it already carries the field). An empty tuple is honest when the originating resolver call site did not associate this row with a casilla — it is not fabricated as a claim of “no subject”, only carried as “not linked at resolution time”.

Variables:
  • resolver_id – Exact canonical resolver identity that produced this row.

  • resolved_binding_source – Canonical binding source owned by the resolver.

  • contributor_source_kind – Upstream taxonomy token for this source node.

  • contributor_binding_source – Canonical BindingSourceKind when contributor_source_kind belongs to the binding taxonomy; None for an external or non-binding contributor.

  • lineage_role – Whether this row is the resolver-owned primary or supporting contributor.

  • source_ref – Stable reference to the contributing source object (e.g. collectible_invoice:{invoice_id}).

  • fingerprint – Data-dependent digest of the contributing source object when the resolver produced one; None when the resolver emits a reference without a content digest.

  • source_casilla_ids – Casilla identities this source object’s resolution feeds, when the originating resolver associated one; empty when it did not. Carried straight from

Parameters:

data (Any)

source_casilla_ids.
dependency_treatment: The registry’s declared dependency treatment for

this carry, empty when the revision declares none. Unlike legal_refs / source_refs this carries no grounding duplicated elsewhere on the revision — it is the sole persisted trace of whether a carry is a factual_evidence fact to reconcile against or a direct_annual_settlement figure that settles the return, and an audit reader has no other way to recover that distinction after the fact. Carried here rather than gated here: the value is NOT withheld on the basis of its treatment.

resolver_id: str
resolved_binding_source: BindingSourceKind
contributor_source_kind: str
contributor_binding_source: BindingSourceKind | None
lineage_role: CalculationSourceLineageRole
source_ref: str
parent_source_ref: str | None
fingerprint: str | None
source_casilla_ids: tuple[CasillaId, ...]
dependency_treatment: str
class CalculationSourceIssue(**data)[source]

Bases: BaseModel

One durable unresolved source condition found while calculating a revision.

Resolver diagnostics are useful calculate-time operator feedback, but a filing-grade verification runs later against the persisted calculation revision. This narrow envelope carries a source condition that reached no binding, without misrepresenting it as source provenance for a computed output.

Two conditions qualify, and both must survive to the persisted revision because both describe a value absent from the filing. unrouted_observation is a row no binding consumes at all. unrouted_declarable_quantity is an independent quantity that consumed rows carry and no binding drawing that quantity reaches — the row-keyed screens are silent on it by construction, so a verify or export gate that saw only the row condition would read their silence as confirmation.

Parameters:

data (Any)

reason: Literal['unrouted_observation', 'unrouted_declarable_quantity']
binding_source: BindingSourceKind
message: str
resolver_id: str | None
source_ref: str | None
class CalculationRevision(**data)[source]

Bases: BaseModel

One calculation attempt attached to a work unit.

Variables:
  • calculation_revision_id – Lowercase 64-char SHA-256 derived from the parent work_unit_id plus the inputs, overrides, and casilla outputs. Content-addressed: structurally identical re-runs produce the same id.

  • work_unit_id – Parent work unit id (also content-addressed).

  • registry_snapshot_ref – Complete registry coordinate selected for the parent work unit and used to materialize this revision.

  • state – Lifecycle state from CalculationRevisionState.

  • input_values_by_casilla_id – Mapping of canonical input casilla values captured at calculation time. The string values are decimal strings or short literals from the registry input contract.

  • binding_overrides – Mapping of operator-supplied binding overrides applied during this calculation. Empty when no binding overrides were used.

  • row_binding_values – Mapping of row-indexed binding values produced by source meshes for repeating export records. Kept separate from binding_overrides so the row coordinate remains structured.

  • row_source_identities – Opaque source identities and content fingerprints keyed by the exact binding and row coordinate. Registry row-set identities additionally retain their exact selector grouping.

  • relation_overrides – Mapping of relation values applied during this calculation. Kept separate from binding_overrides so BindingId-keyed snapshots never carry RelationId keys.

  • source_transaction_ids – Stable ledger transaction ids that contributed to this revision through bucket-local aggregation. Empty for calculations that did not consume ledger transactions.

  • m210_official_tipo_renta_code – The raw, official two-digit M210 tipo-de-renta code selected for this revision’s ledger projection. It remains distinct from the conceptual tipo_renta token used by the formula rate path, so an audit can distinguish official codes that share one rate concept.

  • cleared_casilla_ids – Casillas the caller explicitly withdrew a previously declared MANUAL value from. Structurally distinct from a casilla that was simply never supplied, even though both are absent from input_values_by_casilla_id.

  • casilla_values – Mapping of computed casilla values (decimal output). The values that would be exported to AEAT if this revision were filed.

  • created_at – UTC timestamp of revision creation.

  • updated_at – UTC timestamp of the most recent state transition. Equals created_at on a fresh draft.

  • verified_at – UTC timestamp at which the revision transitioned to VERIFICADO_COMPLETO. None for non-verified revisions.

  • verified_by – Actor label captured at verification time. None for non-verified revisions.

  • filed_at – UTC timestamp at which the revision was filed. None for non-filed revisions.

  • filed_by – Actor label captured at filing time. None for non-filed revisions.

  • superseded_at – UTC timestamp at which a later filed revision superseded this one. None unless state is PRESENTADO_SUPERSEDIDO.

  • discarded_at – UTC timestamp captured when the revision is moved to DESCARTADO. None otherwise.

  • discarded_by – Actor label captured when the revision is moved to DESCARTADO. None otherwise.

  • discard_reason – Audit reason captured when the revision is moved to DESCARTADO. None otherwise.

Parameters:

data (Any)

calculation_revision_id: CalculationRevisionId
work_unit_id: WorkUnitId
registry_snapshot_ref: RegistrySnapshotRef
state: CalculationRevisionState
input_values_by_casilla_id: Mapping[CasillaId, str]
binding_overrides: Mapping[BindingId, str]
row_binding_values: Mapping[BindingId, Mapping[str, str]]
row_source_identities: Mapping[RowBindingKey, RowSourceIdentity]
row_casilla_values: Mapping[RowCasillaKey, Decimal]
row_casilla_provenance: Mapping[RowCasillaKey, DirectRowMaterializationProvenance]
relation_overrides: Mapping[RelationId, str]
source_transaction_ids: tuple[CalculationRevisionId, ...]
m210_official_tipo_renta_code: str | None
m210_gross_income_source_mode: M210GrossIncomeSourceMode | None
borrador_snapshot_id: SnapshotId | None
bindings_sourced_from_borrador: tuple[BindingId, ...]
cleared_casilla_ids: tuple[CasillaId, ...]
casilla_values: Mapping[CasillaId, Decimal]
observations: tuple[CasillaObservation, ...]
unresolved_outcomes: tuple[RegistryCalculationUnresolvedOutcome, ...]
ledger_filing_snapshot: LedgerFilingSnapshot | None
ledger_filing_evidence: LedgerFilingEvidence | None
filing_instance_evidence: FilingInstanceEvidence | None
m303_regimen_simplificado_annual_summary_handoff: M303RegimenSimplificadoAnnualSummaryHandoff | None
source_provenance: tuple[CalculationSourceRef, ...]
source_issues: tuple[CalculationSourceIssue, ...]
detail_rows: tuple[ModeloDetailRow, ...]
created_at: datetime
updated_at: datetime
verified_at: datetime | None
verified_by: ModeloActorLabel | None
filed_at: datetime | None
filed_by: ModeloActorLabel | None
superseded_at: datetime | None
discarded_at: datetime | None
discarded_by: ModeloActorLabel | None
discard_reason: OperatorReason | None
amendment_identity: CalculationRevisionAmendmentIdentity | None
amendment_reason: OperatorReason | None
calculation_revision_identity_inputs_from_revision(revision)[source]

Project one persisted CalculationRevision through the sole hash-input builder.

Read-side integrity checks and the model’s own invariant both use this function. It prevents those consumers from independently enumerating the identity fields and thereby omitting a newly added calculation input.

Return type:

CalculationRevisionIdentityInputs

Parameters:

revision (CalculationRevision)

derive_calculation_revision_id_from_revision(revision)[source]

Derive a CalculationRevision id from its complete canonical input set.

Return type:

str

Parameters:

revision (CalculationRevision)

class CalculationRevisionCatalogue(**data)[source]

Bases: BaseModel

Immutable catalogue of every calculation revision in storage.

Parameters:

data (Any)

revisions: Mapping[str, CalculationRevision]
get(calculation_revision_id)[source]

Return the revision under this id, or None when the catalogue holds none.

Return type:

CalculationRevision | None

Returns:

The CalculationRevision for calculation_revision_id, or None if the catalogue does not carry it.

Parameters:

calculation_revision_id (CalculationRevisionId)

values()[source]

Return a view of every revision in the catalogue.

Returns:

A view over the catalogue’s CalculationRevision records.

for_work_unit(work_unit_id)[source]

Return every revision attached to one work unit.

Return type:

tuple[CalculationRevision, ...]

Returns:

Tuple of CalculationRevision records for the given work unit.

Parameters:

work_unit_id (str)

exception LedgerFilingCoverageError(message=None, *, context=None, translated_message=None)[source]

Bases: ModeloError

Raised when a persisted revision’s snapshot and evidence contributor sets diverge.

A ledger-derived revision bundles a ledger_filing_snapshot (the fingerprinted contributor set) and a ledger_filing_evidence (the typed fact basis). The two are projected from the same source_transaction_ids and MUST cover the same contributors. A divergence on read-back means a contributor row was silently dropped after persistence; this gate surfaces it on load rather than letting the filing artefact ship an unexplainable casilla.

Parameters:
  • message (str | None)

  • context (Mapping[str, object] | None)

  • translated_message (str | None)

Return type:

None

code
assert_revision_snapshot_evidence_coverage(revision)[source]

Cross-check a loaded revision’s snapshot and evidence contributor coverage.

Parameters:

revision (CalculationRevision) – The CalculationRevision loaded from persistence.

Return type:

None

Post-roundtrip validator (per the modelo-export-evidence-parity discipline): when both ledger_filing_snapshot and ledger_filing_evidence are present, their rows contributor (transaction_id) sets MUST be equal. A revision with neither (a non-ledger or borrador revision) passes trivially. Raises LedgerFilingCoverageError naming the divergent contributors.