aeat.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 at sede or in person, 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 LIVE_M036_DECLARATION_NAMESPACE rows, emits the matching modelo.036.declaration.* bucket event, and exposes the same SecureSnapshotRepository path 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

aeat.domain.calculations.registry._censo_modelos

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

aeat.entrypoints.cli._modelo_m036_cli

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

aeat.domain.buckets.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 filed at sede.

The operator files the 036 with AEAT through the sede portal (or in person at an oficina). This command records that the declaration happened locally so the downstream stale-cascade + audit-trail logic can react. 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:
  • 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)

  • note (str | None)

profile_id: ProfileId
event_kind: CensoModeloEventKind
declared_on: date
sede_justificante: str | None
note: str | None
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 storage cross-check SecureSnapshotRepository performs when loading and saving records (it refuses payloads whose bucket disagrees with the repository binding), per the M036-declaration-service Path A ADR decision. Downstream consumers (stale-cascade engine, profile-state re-derivation) read these fields to decide what to recompute.

The record is the payload model for SecureSnapshotRepository rows stored under LIVE_M036_DECLARATION_NAMESPACE.

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

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

  • note (str | None)

  • recorded_at (datetime)

declaration_id: str
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
m036_declaration_object_key(bucket_id, declaration_id)[source]
Return type:

str

Parameters:
  • bucket_id (str)

  • declaration_id (str)

list_m036_declarations(*, bucket_id)[source]

Return every recorded M036 declaration in the active bucket.

Reads through the same SecureSnapshotRepository the write path persists into (no parallel read path), enumerating the encrypted LIVE_M036_DECLARATION_NAMESPACE 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)])

read_m036_declaration(declaration_id, *, bucket_id)[source]

Return one recorded M036 declaration by id or unambiguous prefix.

Reads through the owning SecureSnapshotRepository, 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)])

record_m036_declaration(command, *, bucket_id)[source]

Persist an M036 declaration record and emit its BucketEvent.

Records that the operator filed an M036 declaration at sede. 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. 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 BucketEventHistoryRepository.

The persisted M036DeclarationResult is encrypted into the bucket-local LIVE_M036_DECLARATION_NAMESPACE row keyed by m036-declaration:<bucket_id>:<declaration_id> via the standard SecureSnapshotRepository machinery (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.

See also

CensoModeloEventKind aeat.domain.buckets.BucketEventType LIVE_M036_DECLARATION_NAMESPACE

Return type:

M036DeclarationResult

Parameters:
  • command (M036DeclarationCommand)

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