cadrumo.application.aggregation.retenciones module

Pure aggregation primitives for the retenciones modelo family.

This module groups typed RetencionObservation rows into stable per-perceptor rollups and totals for Modelos 111, 115, 123, 180, 190, and 193. Observations must carry canonical source kinds from BindingSourceKind; bare invoice provenance is rejected in favour of payable_invoice or collectible_invoice.

The live calculation mesh uses these primitives through RetencionesAggregationSourceResolver for the Modelo 180/193 distinct-NIF perceptor count. Modelo 190’s distinct perceptor/clave/subclave percepciones count is intentionally handled by WithholdingSourceResolver, not by this rollup family.

class RetencionObservation(**data)[source]

Bases: BaseModel

One typed observation feeding a retenciones aggregator.

The source ledger transaction (source_object_id) is referenced by its canonical source kind ledger_transaction. Bare invoice source bindings are forbidden at the registry domain layer; observations originating from invoice records carry one of payable_invoice / collectible_invoice instead.

perceptor_nif is normalised to its canonical identity token on construction, so the identity this aggregator groups by is the identity the encrypted per-perceptor store keys by. Holding the raw declaration here split the two: the repository trimmed and uppercased the NIF before hashing it into the object key, so two canonically-equal declarations produced two rollups and two perceptors in the count while sharing one stored row – the later write overwriting the earlier evidence for a perceptor the declaration still counts twice.

accrued_on is admitted by the canonical date authority. A ten-character length bound is not a date check: 2026-99-99 and 2026-02-30 both satisfy it, and the aggregators then summed and counted a row whose accrual date does not exist, with the encrypted store persisting it as declared evidence.

Parameters:

data (Any)

source_kind: BindingSourceKind
source_object_id: str
perceptor_nif: TaxIdIdentityToken
perceptor_name: str
scheme: RetencionScheme
taxable_base: Decimal
retencion_amount: Decimal
accrued_on: IsoDateString
class RetencionPerceptorRollup(**data)[source]

Bases: BaseModel

One row in the aggregation: a perceptor’s totals across schemes.

Parameters:

data (Any)

source_kind: BindingSourceKind
perceptor_nif: TaxIdIdentityToken
perceptor_name: str
scheme: RetencionScheme
observations_count: NonNegativeInt
total_taxable_base: Decimal
total_retencion: Decimal
class RetencionesAggregation(**data)[source]

Bases: BaseModel

Aggregate output for a retenciones modelo + period.

The output is content-addressable: identical input observations in any order produce the same rollup tuple (sorted by perceptor + scheme) and the same totals.

Parameters:

data (Any)

modelo: str
period: InstanceOf[Period]
rollups: tuple[RetencionPerceptorRollup, ...]
total_perceptors: NonNegativeInt
total_taxable_base: Decimal
total_retencion: Decimal
registry_work_income_retencion_treatments(effective_date, *, authority=None)[source]

Resolve work-income treatment declarations from the dated fact mapping.

Return type:

Mapping[RetencionScheme, WorkIncomeRetencionTreatment]

Parameters:
aggregate_retenciones_111(observations, *, period, operation=None)[source]

Aggregate per (perceptor_nif, scheme) into a Modelo 111 payload.

Pure function: identical observation input + period yields identical output. Rollups are sorted by (perceptor_nif, scheme.value) so two equal aggregations serialise to identical bytes.

Returns a RetencionesAggregation.

Return type:

RetencionesAggregation

Parameters:
aggregate_retenciones_115(observations, *, period, operation=None)[source]

Aggregate Modelo 115 (retenciones sobre arrendamiento urbano).

Only the schemes declared for this modelo by the registry are in scope.

Returns a RetencionesAggregation with per-perceptor rollups and grand totals for Modelo 115.

Return type:

RetencionesAggregation

Parameters:
aggregate_retenciones_123(observations, *, period, operation=None)[source]

Aggregate Modelo 123 retenciones into a RetencionesAggregation.

Covers the capital-income schemes selected by the registry.

Return type:

RetencionesAggregation

Parameters:
aggregate_retenciones_180(observations, *, period, operation=None)[source]

Aggregate Modelo 180 (resumen anual de retenciones sobre arrendamiento urbano).

Shares the selected scheme catalogue with the corresponding quarterly model; the difference is the period scope (full year vs quarter) which the caller supplies. Callers should pass an annual period string (e.g. "2025") and feed in the union of the year’s 115 observations.

Returns a RetencionesAggregation.

Return type:

RetencionesAggregation

Parameters:
aggregate_retenciones_190(observations, *, period, operation=None)[source]

Aggregate Modelo 190 (resumen anual de retenciones IRPF de Modelo 111).

Shares the selected quarterly scheme catalogue widened over the annual period.

Returns a RetencionesAggregation with per-perceptor rollups and grand totals for the annual summary.

Return type:

RetencionesAggregation

Parameters:
aggregate_retenciones_193(observations, *, period, operation=None)[source]

Aggregate Modelo 193 retenciones into a RetencionesAggregation.

Resumen anual de retenciones sobre capital mobiliario. Shares the 123 scheme catalogue.

Return type:

RetencionesAggregation

Parameters: