cadrumo.application.aggregation.retencion_observations_repository module

Application contracts for per-perceptor retención observations (Modelo 180/193).

The DEDICATED store the retenciones-summary family reads to count perceptors DISTINCTLY. Modelo 180 casilla decl.total-perceptores (“Número total de perceptores … Número de registros de tipo 2”, AEAT Diseño de Registro) is the count of distinct perceptor NIFs on the annual declaration, NOT the sum of the quarterly Modelo 115 aggregate counts. The validated distinct-count primitive aggregate_retenciones_180 already exists; what it lacked was a persisted, calc-mesh-readable per-perceptor source so the calculate path could compute the distinct count instead of falling back to the wrong quarterly sum. This module is that source: it persists each RetencionObservation (perceptor NIF + scheme + taxable base + retención) keyed by (modelo, filing_year, period) plus the per-perceptor identity, so the pull and calculate surfaces read ONE store.

The encrypted persistence implementation is an outer adapter. This module owns the application repository capability, key grammar, and shared producer helper; the calc-mesh resolver consumes the same required capability and calls the distinct-count primitive.

retencion_observation_key(modelo, filing_year, period, perceptor_nif, scheme)[source]

Opaque per-perceptor object key — the NIF is hashed, never cleartext.

Secure-object payloads are encrypted, but object keys are storage metadata, so the perceptor NIF is sha256-hashed (the iva-wallet-decision key convention). Distinct (perceptor NIF, scheme) pairs persist as distinct rows so a perceptor paid under more than one scheme is preserved while the distinct-NIF count stays correct.

Return type:

str

Parameters:
exception RetencionObservationPersistenceError(operation)[source]

Bases: CadrumoError

Translated failure from the retención-observation persistence port.

Parameters:

operation (str)

Return type:

None

code
class RetencionObservationRepository(*args, **kwargs)[source]

Bases: Protocol

Application persistence capability for per-perceptor observations.

replace_observations(*, modelo, filing_year, period, observations, source_kind, captured_at=None, source_metadata=None)[source]

Atomically replace the complete observation window.

Return type:

None

Parameters:
load_observations(modelo, period)[source]

Return observations for one modelo and filing period.

Return type:

tuple[RetencionObservation, ...]

Parameters:
class RetencionObservationPorts(repository)[source]

Bases: object

Required retención observation capabilities for one profile session.

Parameters:

repository (RetencionObservationRepository)

repository: RetencionObservationRepository
class RetencionObservationPortsFactory(*args, **kwargs)[source]

Bases: Protocol

Construct the retención observation capabilities for one bucket.

persist_retencion_observations(*, ports, modelo, filing_year, period, observations, source_kind=AggregationCaptureKind.AGGREGATE_PULL)[source]

The ONE shared write path every per-perceptor producer calls.

Factoring the persist behind a single application helper makes store completeness STRUCTURAL rather than per-entrypoint discipline a future producer could forget (an unwritten producer -> an incomplete store -> a pull≠calculate divergence). Writes to the active bucket’s encrypted store with SET-REPLACE semantics so pull and calculate read one source. aggregate_per_modelo stays pure — persistence is the entrypoint’s job, not the aggregator’s (aeat-architecture-boundaries).

Return type:

None

Parameters: