cadrumo.application.modelo.reconciliation_records module

What a modelo reconciliation IS, and where it is stored.

The nouns of reconciliation, separated from the verb. This module owns the persisted ModeloReconciliationRecord, the encrypted store it lives in, the key that admits N records per work unit, and the typed read-back the operator sees. reconciliation owns the act of reconciling — parsing the evidence, comparing it against the work unit, and producing a report — and persists its outcome through the store here.

The split is what keeps either half nameable. The reconcile service is a comparison; this is a persisted format, with its own key grammar, its own durability enrolment, and its own roundtrip obligations.

See also

reconciliation:

The reconcile service that writes these records, co-emitting each with its bucket event in one unit of work.

class ModeloReconciliationEvidenceKind(*values)[source]

Bases: StrEnum

Closed external-evidence labels accepted by reconciliation commands.

DECLARATION performs casilla-level reconciliation for modelos in _DECLARATION_CASILLA_RECONCILE_MODELOS; other modelos raise ReconciliationDeclaracionSourceUnsupportedError.

JUSTIFICANTE
DECLARATION
class ModeloReconciliationVerdict(*values)[source]

Bases: StrEnum

Closed verdict catalogue for ModeloReconciliationReport.

Closed set: matches / mismatches. A reconcile that reaches a report has already parsed its evidence; an unparseable justificante is surfaced as the typed ReconciliationEvidenceInvalidError refusal (REFUSED_RECONCILIATION_EVIDENCE_INVALID) before any report is built, so there is no evidence_invalid verdict shell. Any expansion requires a design decision and must not add shells.

MATCHES
MISMATCHES
class ModeloReconciliationDiffKind(*values)[source]

Bases: StrEnum

Closed category for a ModeloReconciliationDiff.

header_field — a receipt-identity disagreement (modelo, ejercicio, period, tax id). total — a filed-amount disagreement between the receipt total and the canonical computed result casilla. casilla — a per-casilla value disagreement between the persisted computed revision and a filed declaración, emitted for modelos enrolled in _DECLARATION_CASILLA_RECONCILE_MODELOS (application.modelo.reconcile_casilla.detect_casilla_divergences()).

HEADER_FIELD
TOTAL
CASILLA
class ModeloReconciliationDiff(**data)[source]

Bases: BaseModel

One disagreement between work unit / profile / computed state and evidence.

diff_kind is the closed category (header field, filed total, or per-casilla). kind remains the specific mismatch token (modelo_mismatch, total_ingresar_mismatch, casilla_value_mismatch, casilla_missing_in_filed, casilla_extra_in_filed, …). A total or casilla diff carries the reconciling verification expectation’s / casilla’s legal_refs / source_refs so the divergence surfaces with its legal grounding (aeat-calculation-grounding); header diffs carry empty grounding. For a casilla diff, field_name is the casilla id and work_unit_value / evidence_value carry the computed / filed decimal strings (empty when the corresponding side carried no value, per CasillaDivergenceKind).

Parameters:

data (Any)

field_name: str
work_unit_value: str
evidence_value: str
kind: str
diff_kind: ModeloReconciliationDiffKind
legal_refs: tuple[LegalRefId, ...]
source_refs: tuple[SourceRefId, ...]
class ModeloReconciliationAdvisory(**data)[source]

Bases: BaseModel

One non-blocking reconciliation advisory (surfaced as a CLI Notice).

Carries a stable code (totals_not_reconciled / identity_anchor_unverified), an operator-facing message, and structured context (the reason, the anchor, the modelo). The CLI folds each advisory into a typed Notice on the envelope’s notices channel per aeat-cli-contract — an advisory is never a bespoke result field. Advisories never flip the verdict: they disclose that a comparison could not be performed (so identity-only matches is never a silent false green), not that a value diverged.

Parameters:

data (Any)

code: str
message: str
context: Mapping[str, str]
class ModeloReconciliationRecord(**data)[source]

Bases: BaseModel

One persisted reconciliation outcome, in full.

The durable, auditable copy of a reconciliation: verdict, evidence kind and reference, work unit, the grounded diffs, the advisories, the instant and the actor. It is written to the encrypted profile-scoped reconciliation store selected by ModeloReconciliationPersistencePort in the same unit of work as the MODELO_RECONCILED BucketEvent whose id it carries, so the event log and the detail store cannot disagree about what was reconciled.

Grounding is stored, not re-derived. Each ModeloReconciliationDiff keeps the legal_refs / source_refs that were in force when the reconciliation ran. Re-deriving them at read time would resolve the snapshot from modelo, filing year and period per aeat-registry-authority-flow, so a routine re-grounding sweep that moved a casilla’s legal_refs without moving the revision id would silently rewrite the legal basis of a historical reconciliation — and one that did move the revision id would make the history unreadable. Historical evidence stays self-describing.

source_ref is the evidence reference as supplied: an operator filesystem path, or the secure-storage handle on the bytes path. It is stored here at full length; only the bucket-event copy is shortened to fit the payload cap.

Parameters:

data (Any)

bucket_event_id: Hex64Str
bucket_id: BucketId
work_unit_id: WorkUnitId
registry_snapshot_ref: RegistrySnapshotRef
source_kind: ModeloReconciliationEvidenceKind
source_ref: str
verdict: ModeloReconciliationVerdict
diffs: tuple[ModeloReconciliationDiff, ...]
advisories: tuple[ModeloReconciliationAdvisory, ...]
actor: ModeloActorLabel
reconciled_at: datetime
class ModeloReconciliationHistoryEntry(**data)[source]

Bases: BaseModel

One past reconciliation read back from the reconciliation record store.

modelo_reconcile DOES persist a stored record. Each run writes a ModeloReconciliationRecord into the encrypted profile-scoped reconciliation store selected by ModeloReconciliationPersistencePort, in the same unit of work as the slim MODELO_RECONCILED BucketEvent it emits. This typed entry projects one such record so the operator can enumerate past reconciliation verdicts, and the grounded divergences behind them, without re-parsing any evidence.

The dedicated store exists because a bucket-event payload value is capped at 500 characters while a single grounded Modelo 100 casilla diff encodes to a median 303, so serialising the detail into the event made a genuinely divergent Modelo 100 reconciliation unpersistable — the write raised before anything was saved. The event keeps the verdict and the divergence count; the record keeps the detail. event_id is the id of that co-written event, which is also the record’s own storage key, so the two surfaces are joined by identity rather than by a field that could drift.

diffs carries the grounding that was in force when the reconciliation ran, read straight from the record and never re-derived from the current registry — see ModeloReconciliationRecord.

Parameters:

data (Any)

event_id: Hex64Str
bucket_id: BucketId
work_unit_id: WorkUnitId
source_kind: ModeloReconciliationEvidenceKind
source_path: str
verdict: ModeloReconciliationVerdict
diff_count: NonNegativeInt
diffs: tuple[ModeloReconciliationDiff, ...]
actor: ModeloActorLabel
reconciled_at: datetime
class ModeloReconciliationPersistencePort(*args, **kwargs)[source]

Bases: Protocol

Persistence boundary for history reads and atomic event co-commit.

persist_with_event(record, event)[source]

Atomically persist record and its matching bucket event.

Return type:

None

Parameters:
iter_records()[source]

Iterate authenticated records for the active profile.

Return type:

Iterator[ModeloReconciliationRecord]

class ModeloReconciliationPersistenceFactory(*args, **kwargs)[source]

Bases: Protocol

Construct reconciliation persistence for one host context.

bind_modelo_reconciliation_persistence_factory(factory)[source]

Bind the outward persistence factory for one host lifetime.

Return type:

Generator[ModeloReconciliationPersistenceFactory]

Parameters:

factory (ModeloReconciliationPersistenceFactory)

modelo_reconciliation_persistence()[source]

Resolve the explicitly composed reconciliation persistence adapter.

Return type:

ModeloReconciliationPersistencePort

list_modelo_reconciliations(*, bucket_id, operation, work_unit_id=None)[source]

Return every recorded reconciliation in bucket_id as typed entries.

Reads the encrypted store the bound ModeloReconciliationPersistencePort co-writes with each MODELO_RECONCILED BucketEvent, filtered to bucket_id and ordered oldest-first by the reconciliation instant. Each record is projected onto a typed ModeloReconciliationHistoryEntry — verdict, source kind, diff count, the grounded diffs themselves, actor, and reconciliation instant are preserved, never collapsed to a flat dict[str, Any].

The entry’s event_id is the id of the bucket event written in the same unit of work, so an operator reading this history can join straight back to the event log without either side carrying a cross-reference field that could drift.

An optional work_unit_id narrows the result to one work unit’s reconciliation history. An empty result (no reconciliations recorded, or none for the requested work unit) returns an empty tuple — the clean “no reconciliations recorded yet” signal, not an error.

Return type:

tuple[ModeloReconciliationHistoryEntry, ...]

Parameters:
  • bucket_id (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=128, pattern=None, ascii_only=None)])

  • operation (PinnedAuthorityOperation)

  • work_unit_id (WorkUnitId | None)