cadrumo.application.modelo.m036_lifecycle module

Modelo 036 declarative-recording contracts, storage, and event emission.

Per the accepted Modelo 036/037 foundation decision, the local app never files a 036. AEAT is the authority; the operator files the declaration through AEAT Sede or in person at a competent AEAT office, then records that fact locally through aeat app modelo m036 {alta,modificacion,baja}. This module owns the typed application service behind that surface: it persists encrypted declaration rows through the required application-owned lifecycle ports, emits the matching modelo.036.declaration.* bucket event, and exposes the same application capability for list/view read-back.

The closed event-kind axis comes from CensoModeloEventKind, whose values are derived from the registry-owned censo foundation. Modelo 037 remains historical metadata and is intentionally outside this recording surface.

See also

cadrumo.domain.calculations.registry.censo_modelos

Registry-owned Modelo 036 active-foundation and Modelo 037 historical routing.

cadrumo.entrypoints.cli._modelo_m036_cli

Thin Typer boundary that turns CLI verbs into these application commands.

cadrumo.domain.buckets.event.BucketEventType

Declares the CENSO_DECLARATION_ALTA, CENSO_DECLARATION_MODIFICACION, and CENSO_DECLARATION_BAJA audit events emitted here.

derive_m036_declaration_id(*, profile_id, event_kind, declared_on, sede_justificante)[source]

Content-address the declaration tuple as 64-char lowercase SHA-256 hex.

The address makes a replay of the same operator-declared filing idempotent: a second invocation with identical inputs hashes to the same declaration_id and the secure-object write becomes a no-op overwrite of the same row. sede_justificante is folded in unmangled ("-" when omitted) so a same-day same-kind re-declaration that acquires the acuse is recorded as a distinct record, not silently coalesced with the pre-acuse draft.

The event_kind member is a CensoModeloEventKind, so the digest can only describe one of the registry-backed alta, modificacion, or baja lifecycle events.

Return type:

str

Parameters:
  • profile_id (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=36, pattern=^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$, ascii_only=None)])

  • event_kind (CensoModeloEventKind)

  • declared_on (date)

  • sede_justificante (str | None)

class M036DeclarationCommand(**data)[source]

Bases: BaseModel

Operator request to record an M036 declaration already filed with AEAT.

The operator files the 036 through AEAT Sede or in person at a competent AEAT office. This command records that declaration locally so the downstream stale-cascade + audit-trail logic can react. The optional sede_justificante records an electronic AEAT receipt when available; its absence does not prevent recording an office filing. The command MUST NOT trigger any local filing action.

event_kind is typed as CensoModeloEventKind, preserving the registry foundation’s closed event set at the application boundary.

Parameters:

data (Any)

profile_id: ProfileId
event_kind: CensoModeloEventKind
declared_on: date
sede_justificante: str | None
note: str | None
exception M036DeclarationNotFoundError(message=None, *, context=None, translated_message=None)[source]

Bases: CadrumoError

Raised when a requested M036 declaration does not exist.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

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

Bases: CadrumoError

Raised when an M036 declaration prefix addresses multiple records.

Parameters:
  • message (str | None)

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

  • translated_message (str | None)

Return type:

None

code
class M036DeclarationResult(**data)[source]

Bases: BaseModel

Outcome of a successful declaration-recording call.

Carries the content-addressed declaration id (SHA-256 over the derived tuple), the canonical event-kind, the declared date, the bucket scope of the record, and the timestamp at which the local record was written. The bucket_id field bridges the persistence capability’s bucket cross-check when loading and saving records (it refuses payloads whose bucket disagrees with the repository binding). Downstream consumers (stale-cascade engine, profile-state re-derivation) read these fields to decide what to recompute.

The record is the payload model for the application-owned declaration repository capability.

Parameters:

data (Any)

declaration_id: ContentDigest
bucket_id: BucketId
profile_id: ProfileId
event_kind: CensoModeloEventKind
declared_on: date
sede_justificante: str | None
note: str | None
recorded_at: datetime
property snapshot_id: str

Return the declaration id under the generic name the snapshot repository expects.

m036_declaration_object_key(bucket_id, declaration_id)[source]
Return type:

str

Parameters:
  • bucket_id (str)

  • declaration_id (str)

list_m036_declarations(*, bucket_id, ports)[source]

Return every recorded M036 declaration in the active bucket.

Reads through the same declaration capability the write path persists into (no parallel read path), enumerating the encrypted rows scoped to bucket_id and returning the typed M036DeclarationResult records verbatim — every persisted field (declaration_id, event_kind, declared_on, recorded_at, sede_justificante, note) is preserved, never collapsed to a flat mapping. An empty bucket returns an empty tuple, the clean “no declarations recorded yet” signal, not an error.

Return type:

tuple[M036DeclarationResult, ...]

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)])

  • ports (M036LifecyclePorts)

read_m036_declaration(declaration_id, *, bucket_id, ports)[source]

Return one recorded M036 declaration by id or unambiguous prefix.

Reads through the owning declaration capability, resolving the full content-addressed declaration_id or an unambiguous prefix of it to a single typed M036DeclarationResult. Raises the repository’s not-found error for an unknown id and the ambiguous-prefix error when a prefix matches more than one record, mirroring the established secure-object id-or-prefix resolution.

Return type:

M036DeclarationResult

Parameters:
  • declaration_id (str)

  • 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)])

  • ports (M036LifecyclePorts)

record_m036_declaration(command, *, bucket_id, ports)[source]

Persist an M036 declaration record and emit its BucketEvent.

Records an M036 declaration already filed through AEAT Sede or in person at a competent AEAT office. The local app NEVER files; this verb only records the operator’s declaration so downstream profile-state re-derivation and stale-cascade reasoning can react. An optional sede_justificante records electronic receipt evidence, so its absence does not prevent recording an office filing. The content-addressed declaration_id keeps a re-declaration with the identical tuple idempotent; the parallel BucketEvent (one of BucketEventType.CENSO_DECLARATION_ALTA / CENSO_DECLARATION_MODIFICACION / CENSO_DECLARATION_BAJA) carries the audit-trail entry the composition-service rule requires alongside the data write, saved via the bucket-event capability supplied in ports.

The persisted M036DeclarationResult is encrypted into the bucket-local declaration row keyed by m036-declaration:<bucket_id>:<declaration_id> via the declaration repository capability (shared with the list_m036_declarations() / read_m036_declaration() read-back surface through _m036_declaration_repository()). bucket_id is checked against the repository binding at save time, so a cross-bucket payload cannot land silently.

Parameters:
  • command (M036DeclarationCommand) – The declaration command naming the profile, event kind, declared-on date, and optional electronic justificante.

  • bucket_id (str) – The bucket to persist into; checked against command.profile_id before anything is derived or stored.

  • ports (M036LifecyclePorts) – Bucket-bound persistence and event capabilities used by the declaration lifecycle.

Raises:
  • LiveApplicationInputError – If command.profile_id names a different profile than bucket_id. A bucket identity IS the profile UUID that owns it, so the two must agree before anything is derived, stored, or emitted.

  • Modelo036PriorAltaRequiredError – If modificacion or baja is requested with no declaration on record yet. Checked, and refused, before anything is derived, stored or emitted.

  • Modelo036TerminalStateError – If the latest recorded declaration is already a baja — a terminal state no further declaration (alta included) may follow. Checked, and refused, before anything is derived, stored or emitted.

Return type:

M036DeclarationResult

Return type:

M036DeclarationResult

Parameters: