cadrumo.domain.calculations.registry.withholding_bindings module¶
Withholding row-set binding helpers.
Withholding-source bindings declared on a ModeloRevision are resolved
from per-perceptor withholding observations into scalar values or row outputs.
- resolve_retencion_clave(value, effective_date, *, modelo=None, authority=None)[source]¶
Project one raw clave through the selected registry vocabulary.
Membership, canonical value, and model applicability are all authored in the withholding catalogue. The projection intentionally fails closed when any declaration is missing or inconsistent; it never turns an unknown raw token into a core value.
- Return type:
- Parameters:
value (str)
effective_date (date)
modelo (str | None)
authority (GovernedFactSource | None)
- class WithholdingObservation(**data)[source]¶
Bases:
BaseModelPer-perceptor retencion / ingreso-a-cuenta observation for modelo 190 / 193.
perceptor_tax_idis normalised to its canonical identity token on construction, so the identity the clave/subclave aggregations count is the identity the encrypted percepciones store keys by. Holding the raw declaration here split the two: the repository trimmed and uppercased the tax ID before hashing it into the object key, so two canonically-equal declarations were counted as two distinct percepciones while sharing one stored row, and the later write overwrote the earlier evidence.- Parameters:
data (Any)
- source_id: str¶
- perceptor_tax_id: TaxIdIdentityToken¶
- perceptor_legal_name: str¶
- country_code: CountryCodeAlpha2 | None¶
The party’s country, or
Nonewhen the source stated none.Nullable rather than defaulted to
ES, because these forms carry the NON-RESIDENT population by construction – a perceptor on a withholding form is routinely foreign, and an attribution member can be – so a default silently declares a foreign party Spanish on a filing surface.Absence propagates as an ABSENT KEY in the built row rather than as a value, so a binding that needs the country refuses with the shipped not-produced error naming itself. That is the visible failure the silent default replaced.
- transaction_date: date¶
- clave: RetencionClave¶
- subclave: str¶
- percibido_dinerario: Decimal¶
the design’s campo 11 excludes the incapacidad-laboral prestaciones, which file in their own block (255-281), so the operator records them in the incap facts.
- Type:
The NON-incapacidad part of the row’s dineraria percepciones
- percibido_especie: Decimal¶
- retencion_practicada: Decimal¶
- ingreso_a_cuenta: Decimal¶
- province_code: str | None¶
Perceptor domicilio province code (01-52, 53 La Palma), or 98 for a Spanish IRPF contributor resident abroad, per the Modelo 190 record design’s own list.
Nullable rather than defaulted: the design’s 98 special case makes a default province a fabricated residence. Absence propagates as an ABSENT KEY in the built row, so a binding that needs the province refuses with the shipped not-produced error naming itself.
- territorial_deduction_clave: int | None¶
1 when the payer applied the art. 68.4 deduction for Ceuta/Melilla rentas, 2 for the Isla de La Palma exceptional deduction, 0 otherwise. A retention-rate determination fact the payer’s own data carries; never derived from the province code.
- Type:
Modelo 190 CEUTA O MELILLA clave
- perceptor_birth_year: int | None¶
Perceptor birth year, only declared by the design for claves A, B (subclaves 01, 03, 04, 99) and C.
- perceptor_situacion_familiar: int | None¶
Perceptor family-situation clave (1-3) per the design’s own relation, only declared for claves A, B (subclaves 01, 03, 04, 99) and C.
- representative_tax_id: TaxIdIdentityToken | None¶
NIF of the perceptor’s legal representative, declared by the design only when the perceptor is under 14; every other row writes the design’s own spaces.
- spouse_or_unit_titular_tax_id: TaxIdIdentityToken | None¶
NIF of the perceptor’s spouse (situacion familiar 2, claves A/B/C) or of the unidad de convivencia’s titular (clave L.29 with titular clave 2); spaces elsewhere.
- disability_clave: int | None¶
Perceptor disability degree clave (0 none/<33%, 1 33-65%, 2 33-65% needing third-party help or reduced mobility, 3 >=65%), declared for claves A, B (subclaves 01, 03, 04, 99) and C.
- contract_relation_clave: int | None¶
Contract-or-relation type clave (1 general, 2 under-a-year/artists, 3 other special dependent relations, 4 sporadic peonadas), declared for clave A only.
- unit_convivencia_titular_clave: int | None¶
Whether the perceptor is the unidad de convivencia’s titular (1 yes, 2 no), declared for clave L.29 only.
- geographic_mobility_clave: int | None¶
Art. 19.2.f) geographic-mobility deduction flag (1 entitled, 0 not), declared for clave A only.
- ingreso_a_cuenta_repercutido: Decimal¶
Ingresos a cuenta efectuados on non-incapacidad especie payments that the payer repercutido to the perceptor (design positions 135-147).
- accrual_year: int | None¶
Ejercicio de devengo (design positions 148-151), declared only for atrasos devengados in earlier exercises or reintegros of earlier-exercise amounts; every other row writes the design’s own zeros.
- reducciones_aplicables: Decimal¶
Art. 18.2/18.3, DT 11/12 and art. 32.1 reductions the payer actually considered (design positions 171-183); the design’s own zeros when none.
- gastos_deducibles: Decimal¶
Art. 19.2 a)-c) deductible expenses the payer considered to determine the retention rate (design positions 184-196); the design’s own zeros when none.
- pension_compensatoria: Decimal¶
Annual compensatory pension to the spouse by judicial resolution (design positions 197-209); the design’s own zeros when none.
- anualidades_alimentos: Decimal¶
Annual food annuities to children by judicial decision (design positions 210-222); the design’s own zeros when none.
- descendants_under_3_total: int | None¶
Descendants under 3 (design position 223), counted per art. 58 m�nimo por descendientes rules; the design’s own zero when none.
- descendants_under_3_whole: int | None¶
Of the position-223 descendants, those computed por entero (design 224).
- descendants_rest_total: int | None¶
Remaining descendants (design positions 225-226).
- descendants_rest_whole: int | None¶
Of the position 225-226 descendants, those computed por entero (227-228).
- descendants_disabled_33_65_total: int | None¶
Disabled descendants 33-65% (design positions 229-230, art. 60.2).
- descendants_disabled_33_65_whole: int | None¶
Of the 229-230 descendants, those computed por entero (231-232).
- descendants_disabled_mobility_total: int | None¶
Disabled 33-65% descendants with reduced mobility or third-party help need (design positions 233-234).
- descendants_disabled_mobility_whole: int | None¶
Of the 233-234 descendants, those computed por entero (235-236).
- descendants_disabled_65_plus_total: int | None¶
Disabled descendants at 65% or more (design positions 237-238).
- descendants_disabled_65_plus_whole: int | None¶
Of the 237-238 descendants, those computed por entero (239-240).
- ascendants_under_75_total: int | None¶
Ascendants under 75 (design position 241, art. 59).
- ascendants_under_75_whole: int | None¶
Of the position-241 ascendants, those computed por entero (242).
- ascendants_75_plus_total: int | None¶
Ascendants at 75 or more (design position 243).
- ascendants_75_plus_whole: int | None¶
Of the position-243 ascendants, those computed por entero (244).
- ascendants_disabled_33_65_total: int | None¶
Disabled ascendants 33-65% (design position 245).
- ascendants_disabled_33_65_whole: int | None¶
Of the position-245 ascendants, those computed por entero (246).
- ascendants_disabled_mobility_total: int | None¶
Disabled 33-65% ascendants with reduced mobility or third-party help need (design position 247).
- ascendants_disabled_mobility_whole: int | None¶
Of the position-247 ascendants, those computed por entero (248).
- ascendants_disabled_65_plus_total: int | None¶
Disabled ascendants at 65% or more (design position 249).
- ascendants_disabled_65_plus_whole: int | None¶
Of the position-249 ascendants, those computed por entero (250).
- first_child_compute: int | None¶
How the first child was computed for the retention rate (design position 251): 1 por entero, 2 por mitad.
- second_child_compute: int | None¶
How the second child was computed (design position 252).
- third_child_compute: int | None¶
How the third child was computed (design position 253).
- housing_loan_communication_clave: int | None¶
Whether the perceptor communicated vivienda-habitual loan amounts at some point in the exercise (design position 254, art. 86.1 RIRPF last paragraph): clave 0 never applied, 1 applied – both are recorded facts, never defaults.
- incapacity_cash_perception: Decimal¶
- incapacity_cash_withholding: Decimal¶
- incapacity_kind_value: Decimal¶
- incapacity_kind_ingreso_a_cuenta: Decimal¶
- incapacity_kind_repercutido: Decimal¶
- complemento_infancia_clave: int | None¶
Whether any mensualidad of the L.29 prestacion included the IMV complemento de ayuda para la infancia (design position 322): clave 1 included, 2 not – both are recorded facts, never defaults.
- foral_retention_estatal: Decimal¶
- foral_retention_araba: Decimal¶
- foral_retention_gipuzkoa: Decimal¶
- foral_retention_bizkaia: Decimal¶
- emerging_stock_excess_clave: int | None¶
Whether the row’s in-kind percepciones include emerging-company stock over the art. 42.3.f) exempt amount (design position 388): clave 1 yes, 0 the rest of the in-kind retributions – both recorded facts, declared only for clave A and only when the especie block has content.
- startup_fund_rendimientos_clave: int | None¶
Whether the row’s payments include D.A. 53a fund-management rendimientos del trabajo (2025-edition design position 389): clave 1 yes, 0 the rest. The cumplimentacion trigger is the payer’s own composition judgment, so the clave is recorded-when-applicable: spaces when absent, refused outside clave A.
- pension_prestacion_jubilacion: int | None¶
Whether the clave B.01 prestaciones include jubilacion (2025-edition design position 390): each type’s 0/1 flag is always recorded for B.01.
- pension_prestacion_viudedad: int | None¶
Whether the clave B.01 prestaciones include viudedad (position 391).
- pension_prestacion_incapacidad: int | None¶
Whether the clave B.01 prestaciones include incapacidad permanente total o parcial (position 392).
- pension_prestacion_no_contributiva: int | None¶
Whether the clave B.01 prestaciones include pensiones no contributivas por invalidez o jubilacion (position 393).
- pension_prestacion_resto: int | None¶
Whether the clave B.01 prestaciones include the remaining non-exempt art. 17.2.a).1a prestaciones (position 394).
- perceptor_mediador_flag: str | None¶
Modelo 193 ‘X’ flag (position 76) marking a perceptor that is itself a mediator entity paying by proxy, declared only for claves A, B and D.
- clave_codigo: int | None¶
Modelo 193 clave codigo (position 79) identifying what the codigo emisor and ISIN fields carry, always recorded for claves A, B and D (clave 4 is the general case).
- codigo_emisor: str | None¶
Modelo 193 codigo emisor (positions 80-91), declared only for claves A, B and D: the issuer’s NIF for clave codigo 1/4, empty for 2, ZXX country code for 3.
- naturaleza: str | None¶
the two-digit subclave of the clave de percepcion, always recorded.
- Type:
Modelo 193 naturaleza (positions 93-94)
- pago: int | None¶
who paid, always recorded for claves A, B and D (1 emisor, 2/3/4 mediador, 5 other mediation).
- Type:
Modelo 193 pago (position 95)
- tipo_codigo: str | None¶
what the codigo cuenta field holds, always recorded for claves A, B and D.
- Type:
Modelo 193 tipo codigo (position 96)
- codigo_cuenta: str | None¶
Modelo 193 codigo cuenta valores / numero operacion prestamo (positions 97-116), recorded only when a financial entity manages the valores.
- pendiente_flag: str | None¶
Modelo 193 ‘X’ flag (position 117) marking percepciones devengadas but not yet paid because the holder did not claim them.
- tipo_percepcion: int | None¶
1 dinerarias, 2 en especie, always recorded.
- Type:
Modelo 193 tipo de percepcion (position 122)
- reducciones: Decimal¶
Modelo 193 art. 26.2 reductions (positions 139-151) applied when the perceptor is an IRPF contribuyente; the design’s own zeros when none.
- base_retenciones: Decimal¶
- porcentaje_retencion: Percentage¶
Modelo 193 retention/ingreso-a-cuenta percentage applied (positions 165-168), generally 19 with the design’s clave-naturaleza specific rates; the last percentage applied when several were used.
- penalizaciones: Decimal¶
Modelo 193 penalizaciones (positions 182-192), declared only for claves B and D; the design’s own zeros when none.
- isin_code: str | None¶
Modelo 193 codigo ISIN (positions 193-204, 2025 edition), recorded when clave codigo is 2 or 4.
- naturaleza_declarante: str | None¶
Modelo 193 perceptor-record naturaleza del declarante (position 208): ‘S’ when the declarante is outside the design’s categories, blank otherwise – recorded-when-applicable.
- fecha_inicio_prestamo: str | None¶
Modelo 193 fecha de inicio del prestamo (positions 209-216, AAAAMMDD), declared only when tipo codigo is ‘P’.
- fecha_vencimiento_prestamo: str | None¶
Modelo 193 fecha de vencimiento del prestamo (positions 217-224, AAAAMMDD), declared only when tipo codigo is ‘P’.
- compensaciones: Decimal¶
Modelo 193 compensaciones (positions 225-236), declared only for prestamo de valores rows; the design’s own zeros when none.
- garantias: Decimal¶
Modelo 193 garantias (positions 237-248), declared only for prestamo de valores rows; the design’s own zeros when none.
- nif_pagador_anterior: TaxIdIdentityToken | None¶
Modelo 193 NIF of the immediately previous payer in the payment chain (positions 322-330), obligatory when pago is 2-5 except where the previous payer is foreign without a Spanish NIF – recorded-when-applicable.
- fecha_devengo: str | None¶
Modelo 193 fecha de devengo (positions 331-338, DDMMAAAA), declared only for clave A.
- clave_mercado: str | None¶
Modelo 193 clave de mercado (position 339), always recorded for claves A, B and D.
- numero_orden: int | None¶
- class WithholdingProvider(**data)[source]¶
Bases:
BaseModelThe per-perceptor withholding provider: one retencion fact over a clave set.
- Parameters:
data (Any)
- kind: Literal[BindingSourceKind.WITHHOLDING]¶
- fact: _WithholdingFact¶
- claves: tuple[str, ...]¶
- row_field: _WithholdingRowField | None¶
- grouping: WithholdingGrouping | None¶
- record: str | None¶
- data_type: ExportFieldDataType | None¶
Scalar type of the value this row field contributes to the export.
The same fact
BindingRowExportSelector.data_typecarries; declared here so the selector model admits the key, since a source-family selector is validated whole against its own strict model. Optional while the families adopt it.
- validate_withholding_binding_selector_shape(binding)[source]¶
Validate the withholding fact/op invariants for snapshot build.
The provider shape itself is the union member’s own gate, already enforced when the row was constructed; what is left to lift to build time is the cross-invariant between the declared fact and the aggregation operation.
- Return type:
- Parameters:
binding (BindingDefinition)
- distinct_percepcion_keys(observations)[source]¶
Return the distinct
(perceptor, clave, subclave)percepci�n keys.Modelo 190’s “n�mero total de percepciones” counts DISTINCT type-2 “registro de perceptor” records (AEAT Dise�o de Registros), not distinct NIFs: one perceptor paid under two claves files two percepciones. The key is therefore clave-bearing.
Shared so the bound
percepcion_countfact and the per-clave breakdown count the same thing. Grouping by clave and keying on(perceptor, subclave)yields the same per-clave counts as this key does across a scope, which is why the breakdown can build on it.- Return type:
set[tuple[str,RetencionClave,str]]- Parameters:
observations (Iterable[WithholdingObservation])
- percibido_total(observations)[source]¶
Sum percibido dinerario, en especie, and both incapacidad-laboral parts.
The base amount facts carry the NON-incapacidad part (the design field’s own meaning); the incapacidad blocks are separate parts the design files at 255-321, so the row’s full percibido total is the four magnitudes together. Shared by the bound
percibido_sumfact and the per-clave breakdown so a change to what counts as percibido reaches both.- Return type:
- Parameters:
observations (Iterable[WithholdingObservation])
- retencion_total(observations)[source]¶
Sum retenci�n practicada, ingreso a cuenta, and the incap retentions.
The base amount facts carry the NON-incapacidad part; the retentions on the incapacidad-laboral percepciones file in their own design block, so the row’s full retenido total is the four magnitudes together. Shared by the bound
retencion_sumfact and the per-clave breakdown so a change to what counts as retenido reaches both.- Return type:
- Parameters:
observations (Iterable[WithholdingObservation])
- resolve_withholding_binding_values(revision, observations, *, authority=None)[source]¶
Resolve scalar withholding-source bindings into Decimal aggregates.
The
ModeloRevisioncontributes scalar withholding bindings; row producer bindings are handled byresolve_withholding_binding_row_values.- Return type:
- Parameters:
revision (ModeloRevision)
observations (Iterable[WithholdingObservation])
authority (GovernedFactSource | None)
- resolve_withholding_binding_row_values(revision, observations)[source]¶
Resolve row-producer withholding bindings into per-row indexed values.
One row per observation, ordered by perceptor, clave, subclave and source so the row index is stable across reads. An unset field (
Noneor blank text) is left out of the row rather than filled, so a required value stays visibly absent.- Parameters:
revision (
ModeloRevision) – TheModeloRevisiondeclaring the row-producer withholding bindings to resolve.observations (
Iterable[WithholdingObservation]) – The withholding observations one row is built from.
- Return type:
- class WithholdingClaveBreakdown(**data)[source]¶
Bases:
BaseModelOne per-clave row of the Modelo 190 retenci�n reconciliation breakdown.
Groups the per-perceptor-clave withholding detail (the AEAT Dise�o de Registros type-2 records) by
clave de percepci�nand carries that clave’s distinct percepci�n count and percibido / retenci�n magnitudes. The figures reuse the scalar withholding-fact arithmetic (resolve_withholding_binding_values()):percepcion_countis the distinct(perceptor, clave, subclave)count,percibido_totalispercibido_dinerario + percibido_especie, andretencion_totalisretencion_practicada + ingreso_a_cuenta. It is a projection of the same store the percepciones-count resolver reads, so the operator can reconcile the annual Modelo 190 totals against the individual Modelo 111 quarterly filings clave by clave.- Parameters:
data (Any)
- clave: RetencionClave¶
- percepcion_count: NonNegativeInt¶
- percibido_total: Decimal¶
- retencion_total: Decimal¶
- aggregate_withholding_by_clave(observations)[source]¶
Project withholding observations into
WithholdingClaveBreakdownrows.Pure function: identical observations in any order yield the same tuple, sorted by
clave. No new aggregation is introduced � each magnitude is produced by the samedistinct_percepcion_keys()/percibido_total()/retencion_total()helper thatresolve_withholding_binding_values()uses for the corresponding bound fact, so the breakdown cannot drift from the facts that feed the calculation. The clave is the grouping axis here and part of the key there; grouping first and counting the clave-bearing key within each group gives the same per-clave totals.That sentence used to be a claim rather than a guarantee: this function re-implemented all three formulas inline. An operator reconciles the annual Modelo 190 against the four quarterly Modelo 111 filings from this breakdown, so a formula changed in the resolver alone would have shown up as a reconciliation mismatch with nothing pointing at the cause.
- Return type:
- Parameters:
observations (Iterable[WithholdingObservation])