cadrumo.application.modelo.action_errors module

Typed exception vocabulary for modelo application actions.

The classes in this module are the stable application-layer errors raised by modelo work-unit lifecycle, calculation, verification, filing, amendment, external-import, and workflow-gate services. They all inherit from cadrumo.domain.modelos.errors.ModeloError so CLI and API error boundaries can route them through the central error-code registry without depending on the implementation module that raised them.

Most classes are deliberately thin taxonomy markers whose operator-facing code and message key live in cadrumo.core.errors.registry. The richer contracts are kept here when the exception must preserve domain context without leaking it into rendered error payloads, as with ModeloWorkflowGateError and its private WorkflowResult.

See also

cadrumo.application.modelo.action_errors:

Defining module for these action errors; the package initializer is inert.

cadrumo.core.errors.registry:

Maps these exception classes to stable error codes and message keys.

cadrumo.application.modelo.workflow_gate:

Raises ModeloWorkflowGateError after persisting an aborted workflow run.

cadrumo.application.modelo.profile_readiness_gate:

Raises ModeloProfileReadinessError for filing-grade profile preflight failures.

Evidence identifiers attached to workflow-gate refusal observations.

The cross-period clean-state finding keeps these centrally catalogued ids in the workflow-gate provenance payload. They are evidence coordinates, not branching rules or calculation inputs.

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

Bases: ModeloError

Raised when a work-unit lookup or mutation targets a missing id.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

code
class ModeloPreconditionErrorMixin(message=None, *, context=None, translated_message=None, precondition_failure=None)[source]

Bases: object

Attach one locale-neutral application decision to a registered error.

Parameters:
property precondition_failure: ModeloPreconditionFailure | None

Return the failed-precondition carrier for a later transport.

property terminal_precondition_verdict: PreconditionVerdict | None

Expose the one application-owned verdict to the generic CLI boundary.

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

Bases: ModeloPreconditionErrorMixin, ModeloError

Raised when discard is invoked on a work unit already discarded.

Parameters:
Return type:

None

code
exception WorkUnitMutationRefusedError(message=None, *, context=None, translated_message=None, precondition_failure=None)[source]

Bases: ModeloPreconditionErrorMixin, ModeloError

Raised when a lifecycle mutation targets a rejected work-unit state.

Parameters:
Return type:

None

code
exception CalculationRevisionNotFoundError(message=None, *, context=None, translated_message=None, precondition_failure=None)[source]

Bases: ModeloPreconditionErrorMixin, ModeloError, CoreNotFoundError

Raised when a calculation revision lookup fails.

Parameters:
Return type:

None

code
exception CalculationRevisionStateError(message=None, *, context=None, translated_message=None, precondition_failure=None)[source]

Bases: ModeloPreconditionErrorMixin, ModeloError

Raised when a state transition is requested from an incompatible source state.

Parameters:
Return type:

None

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

Bases: ModeloError

Raised when a filing record lookup fails.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when a verification report lookup fails.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

code
exception AmendmentEvidenceMissingError(message=None, *, context=None, translated_message=None, precondition_failure=None)[source]

Bases: ModeloPreconditionErrorMixin, ModeloError

Raised when the modelo-amend path lacks imported official evidence.

Parameters:
Return type:

None

code
amendment_evidence_missing_precondition(*, work_unit_id, filing_record_id)[source]

Return the declared no-action refusal for an unattested amendment baseline.

Return type:

ModeloPreconditionFailure

Parameters:
  • work_unit_id (str)

  • filing_record_id (str)

modelo_work_wizard_retry_exhausted_precondition(*, work_unit_id, retry_limit)[source]

Return the declared no-action outcome after the wizard exhausts its retries.

Return type:

ModeloPreconditionFailure

Parameters:
  • work_unit_id (str)

  • retry_limit (int)

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

Bases: ModeloError

Raised when an amendment omits detail rows that constitute the declaration.

For M184, M232, M347 and M349 the per-counterpart rows ARE the return, so an amendment filed without them declares that there were none. That is a substantive statement about the period, and the two amendment kinds make opposite ones: LGT art. 122.2 para. 2 has a complementaria COMPLETE the original (its rows are the ones being ADDED) while a sustitutiva REPLACES it (its rows are the full corrected set). Nothing in the amendment call distinguishes them, and inheriting the baseline’s rows would silently pick the second reading while the corrected aggregate totals were computed for neither. So the operator states the rows rather than the code guessing.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when the closed M303 rectificativa motive is missing or inapplicable.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when the modelo-amend path targets a non-current filing record.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when the requested amendment kind is not legally available for the period.

AEAT’s amendment mechanism changed over time: the unified autoliquidación rectificativa (LGT art. 120.4, RD 117/2024) replaced the dual complementaria/solicitud-de-rectificación regime (LGT art. 122.2 / art. 120.3) only from the period each modelo’s own orden establishes (see cadrumo.core.amendment_kind_regime). Requesting rectificativa for a pre-adoption period, or complementaria for a modelo/period where rectificativa has replaced it as the ordinary correction mechanism, is refused rather than silently accepted or silently downgraded — the accepted kind set for the resolved period is always named in the refusal.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when a pre-rectificativa complementaria would decrease liability.

Before the autoliquidación rectificativa unification (LGT art. 120.4), a self-filed complementaria (LGT art. 122.2) can only ever RAISE the taxpayer’s own declared tax due (or lower a requested devolución): “los obligados tributarios podrán presentar autoliquidaciones complementarias” when the new autoliquidación yields “un importe a ingresar superior… o una cantidad a devolver inferior”. A correction that LOWERS the declared liability is not a complementaria in law; it requires the separate solicitud de rectificación procedure (LGT art. 120.3, developed by RGAT art. 126-128). Filing a liability-decreasing correction as a complementaria would silently misrepresent which legal procedure the taxpayer used, so it is refused with guidance toward the correct procedure rather than silently accepted.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when a persisted calculation revision has drifted from its content-addressed id.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when a sealed revision’s evidence cannot be re-bundled from the live ledger.

Recapture exists to pick up evidence attached AFTER a revision was sealed — an invoice linked to a row the filing gate refused — without disturbing the facts the filing asserts. That is only sound while the tax facts themselves are unchanged, because the revision’s content address is derived from them and the bundle is pegged to their fingerprint.

A moved row fingerprint therefore means something else happened: the ledger drifted, and the revision no longer describes it. That is staleness, whose remedy is a fresh calculation, not a re-bundle. Recapturing over it would silently swap the fact basis underneath a sealed filing while leaving its id and casilla values asserting the old one.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when the external-filing import path cannot persist an imported baseline.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when an operator-supplied local observation cannot be persisted.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

code
exception ModeloCrossPeriodCleanStateError(message=None, *, context=None, translated_message=None, precondition_failure=None)[source]

Bases: ModeloPreconditionErrorMixin, ModeloError

Raised when a filing-grade workflow lacks clean prior-filing proof.

Parameters:
Return type:

None

code
exception ModeloWorkflowGateError(result)[source]

Bases: ModeloError

Raised when the workflow gate refuses an internal file transition.

The constructor stores the live WorkflowResult on a private attribute and exposes it through result. The rendered error context contains only primitive machine codes (abort_code and stage), which keeps CLI JSON/text payloads stable while allowing telemetry and tests to inspect the full workflow run.

See also

cadrumo.application.modelo.workflow_gate.run_revision_workflow_gate():

Persists the workflow run and raises this error for aborted results.

cadrumo.core.errors.error_codes.render_error_text():

Renders the primitive context without serialising the live result.

Parameters:

result (WorkflowResult)

Return type:

None

property result: WorkflowResult

Return the live WorkflowResult that triggered the abort.

property terminal_precondition_verdict: PreconditionVerdict

Return the persisted terminal verdict without exposing the live run.

The workflow-result model requires every aborted run to end in a failed step carrying this verdict. Keeping the result private avoids a raw persistence-object leak through generic error context, while the CLI boundary can schema-resolve this exact application-owned decision.

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

Bases: ModeloError

Raised when an amendment override targets an undeclared casilla id.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when the corrected casilla map fails verification.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when the registry snapshot for a work unit cannot be resolved.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

code
exception ModeloAggregationBindingError(message=None, *, context=None, translated_message=None, precondition_failure=None)[source]

Bases: ModeloPreconditionErrorMixin, ModeloError

Raised when bucket-derived aggregation bindings conflict with caller input.

Parameters:
Return type:

None

code
exception ModeloRequiredBindingsMissingError(message=None, *, context=None, translated_message=None, precondition_failure=None)[source]

Bases: ModeloPreconditionErrorMixin, ModeloError

Raised when Modelo 202 lifecycle work lacks required calculation bindings.

Parameters:
Return type:

None

code
exception ModeloProfileReadinessError(message=None, *, context=None, translated_message=None, precondition_failure=None)[source]

Bases: ModeloPreconditionErrorMixin, ModeloError

Raised when filing-grade modelo work starts with missing active-profile facts.

Carries the declared precondition failure so the operator surface resolves the recovery from the scenario identity and its machine facts rather than from a rendered explanation.

Parameters:
Return type:

None

code
exception M303FilingEvidenceError(message=None, *, context=None, translated_message=None, precondition_failure=None)[source]

Bases: ModeloPreconditionErrorMixin, ModeloError

Raised when Modelo 303 filing-instance evidence fails its revision-time validation.

Carries the declared precondition failure rather than a rendered explanation, so the operator surface resolves the recovery from the scenario identity and its machine facts.

Parameters:
Return type:

None

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

Bases: ModeloError

Raised when an engine-result casilla has no registry definition.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when an unknown applicability filter name is encountered.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when an operator elects a Modelo 303 refund for an ineligible period.

A non-REDEME taxpayer may request a negative Modelo 303 result back as a refund (devolución, Tipo de declaración D) only in the last filing period of the year (the annual liquidación, Ley 37/1992 art. 116). Electing devolver for any earlier period is refused rather than silently downgraded to compensación — a silent downgrade would hide that the operator’s refund request was discarded, and a silent upgrade would file a refund the law does not permit for the period. The fix is operator-driven: carry the credit forward (compensar), or make the election in the year’s last period.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when a non-default payment election contradicts the result sign.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when a canonical payment election lacks a grounded capability.

CUENTA_CORRIENTE remains typed so its future capability does not fork the contract, but is refused until officially grounded. It never infers or reuses a charge account.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when a prior-direct-debit election lacks legal, registry, or U-proof authority.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when a refund-disposition export has no refund account on file.

When the determined disposition is a refund (devolución, D / V / X) the fichero must carry the cuenta-devolución block AEAT pays into — the IBAN, or the SWIFT-BIC plus foreign-bank block for a non-SEPA account. If the operator’s profile carries no refund account (no iban), the export REFUSES rather than emitting an empty or partial DID block: an empty refund block produces a devolución fichero AEAT cannot pay — a silent, defective filing. The fix is operator-driven: configure a refund account on the profile, or carry the credit forward (compensar) instead of requesting a refund. This is the no-silent-under-declaration sibling of the election’s eligibility refusal.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when a domiciliación export has no charge account on file.

A U declaration instructs AEAT to debit the taxpayer’s account. The DID page therefore needs the separately recorded charge-account IBAN; a refund account is a destination for payments from AEAT and cannot satisfy a debit instruction. The export refuses rather than falling back to that separate account or writing an empty account page.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: ModeloError

Raised when the registry’s law-determined revision diverges from the work unit’s pinned revision.

This can only happen when the registry’s law-mapping was corrected after the work unit was created (the creation gate now enforces resolver-equality), or for work units persisted before the strengthened creation gate landed. The resolution is to re-create the work unit so its identity reflects the corrected law-determined revision.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

code