aeat.application.modelo._reconcile module¶
Modelo reconciliation: compare work-unit state and computed result against evidence.
modelo_reconcile accepts a modelo work unit and either an AEAT justificante
PDF or a filed declaración PDF, then produces a ModeloReconciliationReport.
For a justificante, the report records whether the work unit’s modelo, period,
ejercicio, and active-profile tax id match the receipt, AND — where the
revision declares reconciliation_total_casilla_ids and a persisted
calculation revision exists — whether the receipt’s printed total equals the
canonical computed result casilla
(one-aggregation-path-pull-equals-calculate). A filed-amount divergence
surfaces as a typed total diff carrying the reconciling expectation’s legal
grounding; where the total could not be reconciled (no map, no revision, no
printed total) a totals_not_reconciled advisory discloses it so an
identity-only matches is never a silent false green.
For a filed declaración, the same header comparison runs, and — for the
modelos enrolled in _DECLARATION_CASILLA_RECONCILE_MODELOS — every
casilla the registry’s verification policy reconciles is compared, one by one,
against the persisted revision’s casilla_values via
application.modelo._reconcile_casilla.detect_casilla_divergences().
A divergence surfaces as a typed casilla diff
(ModeloReconciliationDiffKind.CASILLA). A modelo not yet enrolled in
casilla-level declaration reconcile is refused with
ReconciliationDeclaracionSourceUnsupportedError rather than silently
degrading to header-only comparison.
The path-based service is local-only: it never contacts AEAT and never invokes
require_live_read — the computed result is read from the already-persisted
CalculationRevision, never a fresh calculation.
Authenticated live pulls use modelo_reconcile_bytes after storing captured
justificante bytes in secure storage. Both paths append a MODELO_RECONCILED
BucketEvent through
BucketEventHistoryRepository, persisting the
structured diffs so reconcile history reports which fields diverged.
- class ModeloReconciliationEvidenceKind(*values)[source]¶
Bases:
StrEnumClosed external-evidence labels accepted by reconciliation commands.
DECLARATIONperforms casilla-level reconciliation for modelos in_DECLARATION_CASILLA_RECONCILE_MODELOS; other modelos raiseReconciliationDeclaracionSourceUnsupportedError.- JUSTIFICANTE¶
- DECLARATION¶
- class ModeloReconciliationVerdict(*values)[source]¶
Bases:
StrEnumClosed 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 typedReconciliationEvidenceInvalidErrorrefusal (REFUSED_RECONCILIATION_EVIDENCE_INVALID) before any report is built, so there is noevidence_invalidverdict shell. Any expansion requires a design decision and must not add shells.- MATCHES¶
- MISMATCHES¶
- class ModeloReconciliationDiffKind(*values)[source]¶
Bases:
StrEnumClosed 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 ModeloReconciliationHistoryEntry(**data)[source]¶
Bases:
BaseModelOne past reconciliation read back from the bucket event history.
modelo_reconcilepersists no stored record: a reconciliation is repeatable on demand from the justificante, so the durable trace is the append-onlyMODELO_RECONCILEDBucketEventit emits. This typed entry projects one such event so the operator can enumerate past reconciliation verdicts without re-parsing any evidence. The read path is the same bucket-event catalogue the write path appends into — there is no parallel reconciliation store.- Parameters:
data (Any)
- event_id: str¶
- bucket_id: BucketId¶
- work_unit_id: WorkUnitId¶
- source_kind: ModeloReconciliationEvidenceKind¶
- source_path: str¶
- verdict: ModeloReconciliationVerdict¶
- diff_count: int¶
- diffs: tuple[ModeloReconciliationDiff, ...]¶
- actor: str¶
- reconciled_at: datetime¶
- class ModeloReconciliationDiff(**data)[source]¶
Bases:
BaseModelOne disagreement between work unit / profile / computed state and evidence.
diff_kindis the closed category (header field, filed total, or per-casilla).kindremains the specific mismatch token (modelo_mismatch,total_ingresar_mismatch,casilla_value_mismatch,casilla_missing_in_filed,casilla_extra_in_filed, …). Atotalorcasilladiff carries the reconciling verification expectation’s / casilla’slegal_refs/source_refsso the divergence surfaces with its legal grounding (aeat-calculation-grounding); header diffs carry empty grounding. For acasilladiff,field_nameis the casilla id andwork_unit_value/evidence_valuecarry the computed / filed decimal strings (empty when the corresponding side carried no value, perCasillaDivergenceKind).- Parameters:
- field_name: str¶
- work_unit_value: str¶
- evidence_value: str¶
- kind: str¶
- diff_kind: ModeloReconciliationDiffKind¶
- legal_refs: tuple[str, ...]¶
- source_refs: tuple[str, ...]¶
- class ModeloReconciliationAdvisory(**data)[source]¶
Bases:
BaseModelOne non-blocking reconciliation advisory (surfaced as a CLI
Notice).Carries a stable
code(totals_not_reconciled/identity_anchor_unverified), an operator-facingmessage, and structuredcontext(the reason, the anchor, the modelo). The CLI folds each advisory into a typedNoticeon the envelope’snoticeschannel percli-notices-are-the-only-diagnostic-channel— an advisory is never a bespoke result field. Advisories never flip the verdict: they disclose that a comparison could not be performed (so identity-onlymatchesis never a silent false green), not that a value diverged.- code: str¶
- message: str¶
- context: Mapping[str, str]¶
- class ModeloReconciliationCommand(**data)[source]¶
Bases:
BaseModelStrict input contract for
modelo_reconcile.source_pathpoints to the operator-supplied evidence file andsource_kindrecords how that file must be parsed. Justificante PDFs are supported for every modelo; declaration PDFs are supported (casilla-level) only for modelos in_DECLARATION_CASILLA_RECONCILE_MODELOS— an unenrolled modelo is refused before parsing.- Parameters:
work_unit_id (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=64, max_length=64, pattern=^[0-9a-f]{64}$, ascii_only=None)])
source_kind (ModeloReconciliationEvidenceKind)
source_path (Path)
actor (str)
- work_unit_id: WorkUnitId¶
- source_kind: ModeloReconciliationEvidenceKind¶
- source_path: Path¶
- actor: str¶
- class ModeloReconciliationBytesCommand(**data)[source]¶
Bases:
BaseModelStrict input contract for reconciling secure-storage justificante bytes.
Used by authenticated live pulls after the captured justificante has already been persisted in secure storage. The raw bytes remain in memory;
source_refis the non-file secure-storage reference recorded in the reconciliation event.- Parameters:
- work_unit_id: WorkUnitId¶
- source_kind: ModeloReconciliationEvidenceKind¶
- source_bytes: bytes¶
- source_ref: str¶
- actor: str¶
- class ModeloReconciliationReport(**data)[source]¶
Bases:
BaseModelOutcome of
modelo_reconcile.The verdict summarises the comparison at the work-unit level. The diff list enumerates the disagreements — header-field (modelo, period,
ejercicio, tax id), the filed justificantetotalagainst the computed result casilla where reconciled, and (for a declaración source on an enrolled modelo) each per-casilladivergence; empty onmatches. The advisory list carries non-blocking disclosures (a total or casilla set that could not be reconciled, an identity anchor that could not be verified); advisories never flip the verdict.- Parameters:
work_unit_id (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=64, max_length=64, pattern=^[0-9a-f]{64}$, ascii_only=None)])
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)])
source_kind (ModeloReconciliationEvidenceKind)
source_path (str)
verdict (ModeloReconciliationVerdict)
diffs (tuple[ModeloReconciliationDiff, ...])
advisories (tuple[ModeloReconciliationAdvisory, ...])
reconciled_at (datetime)
narrative (str)
- work_unit_id: WorkUnitId¶
- bucket_id: BucketId¶
- source_kind: ModeloReconciliationEvidenceKind¶
- source_path: str¶
- verdict: ModeloReconciliationVerdict¶
- diffs: tuple[ModeloReconciliationDiff, ...]¶
- advisories: tuple[ModeloReconciliationAdvisory, ...]¶
- reconciled_at: datetime¶
- narrative: str¶
- exception ReconciliationEvidenceInvalidError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
AeatErrorRaised when the supplied external evidence cannot be parsed.
Raised for malformed justificantes. The CLI surfaces it as a refusal with the canonical recovery hint; downstream consumers branch on it without string-matching the message.
- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- exception ReconciliationDeclaracionSourceUnsupportedError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
AeatErrorRaised when a declaración reconcile targets a modelo not yet enrolled.
Casilla-level declaración reconciliation is enrolled one modelo at a time in
_DECLARATION_CASILLA_RECONCILE_MODELOS; a modelo outside that set refuses cleanly rather than silently degrading to a header-only compare.- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- exception ReconciliationCrossBucketRefusedError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
AeatErrorRaised when the addressed work unit belongs to a different bucket than the active profile bucket.
Every event is scoped to a bucket id. Allowing the service to emit into a non-active bucket would let any caller write into other operators’ history. The check is enforced at the application service so neither the CLI nor any future caller can bypass it.
- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- modelo_reconcile(command)[source]¶
Reconcile a modelo work unit against a justificante or declaración PDF file.
Local-only: never contacts AEAT and never invokes
require_live_read.For a justificante, reimplements the metadata comparison inline against the justificante parser at
adapters.inbound.justificante. The receipt totals ARE reconciled against the persisted revision’s computed result where the revision declaresreconciliation_total_casilla_ids.For a declaración, parses via
adapters.inbound.declaracion.parse_declaracion()and — for modelos enrolled in_DECLARATION_CASILLA_RECONCILE_MODELOS— compares every registry-reconciled casilla against the persisted revision’scasilla_values, surfacing each divergence as a typedcasilladiff. A modelo outside that set raisesReconciliationDeclaracionSourceUnsupportedError.Emits
MODELO_RECONCILEDinto the bucket-event-history catalogue. The verdict is included in the event payload so downstream auditors can replay the reconciliation timeline without re-parsing the evidence.- Return type:
- Returns:
- Parameters:
command (ModeloReconciliationCommand)
- modelo_reconcile_bytes(command)[source]¶
Reconcile secure-storage evidence bytes without materialising a plaintext file.
Declaración reconciliation is not offered on the bytes path: the only authenticated live-capture flow today captures justificante snapshots (
application.live.capture_justificante_snapshot()), never a filed declaración. Usemodelo_reconcile()with a local declaración PDF file for casilla-level reconcile.- Return type:
- Returns:
The
ModeloReconciliationReportcomparing the parsed justificante metadata to the work unit and active profile.- Parameters:
command (ModeloReconciliationBytesCommand)
- list_modelo_reconciliations(*, bucket_id, work_unit_id=None)[source]¶
Return every recorded reconciliation in
bucket_idas typed entries.modelo_reconcilestores no record; its durable trace is theMODELO_RECONCILEDBucketEventit appends. This read-back enumerates those events from the sameBucketEventHistoryRepositorycatalogue the write path appends into (no parallel read path), filtered to the activebucket_idand ordered oldest-first byoccurred_at. Each event is projected onto a typedModeloReconciliationHistoryEntry— the verdict, source kind, diff count, actor, and reconciliation instant are preserved, never collapsed to a flatdict[str, Any].An optional
work_unit_idnarrows 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:
- 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)])
work_unit_id (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=64, max_length=64, pattern=^[0-9a-f]{64}$, ascii_only=None)] | None)