cadrumo.domain.modelos.row_models module

Typed CLI row models for multi-row informational modelos.

Provides strictly-validated pydantic row shapes for operator-supplied detail rows on modelos whose filing content is a list of repeating records rather than a set of scalar casilla values.

Supported row types:

  • Modelo184MemberRow — atribución member for modelo 184 (--row miembro nif=X share=Y importe=Z)

  • Modelo232VinculadaRow — operación vinculada for modelo 232 (--row vinculada nif=X tipo_vinculacion=Y importe=Z metodo=M pais=P)

  • Modelo349OperadorRow — operador row for modelo 349 (--row operador codigo_pais=DE nif_comunitario=DE123456789 razon_social=X clave_operacion=E importe=Y) Used when no collectible-invoice ledger exists; maps directly to the operator record layout.

  • Modelo349RectificacionRow — rectificación intracomunitaria for modelo 349 (--row rectificacion codigo_pais=DE nif_comunitario=DE123456789 razon_social=X clave_operacion=E ejercicio=2025 periodo=2T base_rectificada=Y base_anterior=Z) Used when the operator declares Tipo-2 rectification records directly.

  • Modelo347ContraparteRow — contraparte declarada for modelo 347 (--row contraparte nif=X nombre=Y importe_Q1=Z clave_operacion=A) One row per counterparty. Annual importe threshold check (> €3,005.06) is performed by the CLI validator, not the model, so partial row sets accumulate correctly before final validation.

  • Modelo210AgrupacionRentaRow — one component renta in an annual Modelo 210 agrupación (period 0A). The row retains the official two-digit renta code and the statutory grouping keys; it is evidence only and never an alternate arithmetic input.

These models are the CLI boundary layer. They validate operator input before being carried into detail_rows on the CalculationRevision.

resolve_detail_row_owning_modelos(*, effective_date)[source]

Project registry-declared ownership for every typed detail-row token.

Return type:

Mapping[str, str]

Parameters:

effective_date (date)

resolve_detail_bearing_modelos(*, effective_date)[source]

Project the registry’s repeated-detail modelo membership declaration.

Return type:

frozenset[str]

Parameters:

effective_date (date)

class Modelo184MemberRow(**data)[source]

Bases: BaseModel

One (member, clave, subclave) atribución row for Modelo 184.

The socio record’s own diseño repeats per clave or subclave with an importe declared, not per member alone (see the accepted row-shape ADR), so this row’s identity is (nif, clave, subclave) rather than nif alone – the same shape 349’s operador row already carries via its clave_operacion axis.

Fields mirror the per-record Tipo-2 layout declared in the M184 bindings/0001-bindings.toml atribucion_member source block.

Parity assertions:

  • nif → member_tax_id (binding: modelo-184-member-row-nif)

  • nombre → member_legal_name (binding: modelo-184-member-row-name)

  • porcentaje → share_percentage (binding: modelo-184-member-row-share)

  • importe → base_imponible_assigned (binding: modelo-184-member-row-base-assigned)

  • pais → country_code (required; never inferred)

  • clave → clave (binding: modelo-184-member-row-clave)

  • subclave → subclave (binding: modelo-184-member-row-subclave)

  • every clave/subclave-conditional field below mirrors its own modelo-184-member-row-<field> binding.

Deliberately excluded (see the accepted row-shape ADR): the clave-A reducción (its governing article is unresolved), provisiones-gastos- dificil-justificacion (computed from the entity’s own régimen fact, not an operator-declared value), and any clave-E eligibility fact (a tracked gap, no representation in this tree yet).

Parameters:

data (Any)

row_type: Literal['miembro']
nif: _NifStr
nombre: _NameStr
pais: _IsoCountryCode | None
porcentaje: Decimal
importe: Decimal
clave: M184Clave
subclave: M184Subclave | None
codigo_provincia: Annotated[str, StringConstraints(strip_whitespace=True, max_length=2)] | None
miembro_a_31_diciembre: bool | None
dias_miembro: int | None
domicilio_fiscal: Annotated[str, StringConstraints(strip_whitespace=True, max_length=40)] | None
naturaleza_inmueble: M184NaturalezaInmueble | None
situacion_inmueble: M184SituacionInmueble | None
referencia_catastral: Annotated[str, StringConstraints(strip_whitespace=True, max_length=20)] | None
clave_declarado: M184ClaveDeclarado | None
porcentaje_titularidad_inmueble: Decimal | None
dias_arrendamiento: int | None
reduccion: Decimal | None
rendimiento_neto_previo_eo: Decimal | None
rendimiento_neto_minorado_agricola_eo: Decimal | None
class Modelo232VinculadaRow(**data)[source]

Bases: BaseModel

One operación vinculada row for Modelo 232.

Fields mirror the related_party_operation binding source declared in 232/revisions/2018-y-siguientes/bindings/0218…0223-*.toml.

The three coded fields carry the closed catalogues AEAT’s diseño de registro DR23200 publishes as Tablas A, C and B — off-catalogue codes are refused here rather than travelling into a fichero field that cannot hold them.

Parity assertions:

  • nif → counterparty_tax_id (binding: modelo-232-related-party-row-nif)

  • nombre → counterparty_legal_name (binding: modelo-232-related-party-row-name)

  • pais → country_code (binding: modelo-232-related-party-row-country)

  • tipo_operacion → operation_kind_code (binding: modelo-232-related-party-row-operation-kind)

  • metodo → transfer_pricing_method_code (binding: modelo-232-related-party-row-tpr-method)

  • importe → amount (binding: modelo-232-related-party-row-amount)

Parameters:

data (Any)

row_type: Literal['vinculada']
nif: _NifStr
nombre: _NameStr
pais: _IsoCountryCode
tipo_vinculacion: Annotated[TipoVinculacion | str, BeforeValidator(lambda v: _hydrate_m232_codigo(field_name='tipo_vinculacion', value=v, code_set=TipoVinculacion))]
tipo_operacion: Annotated[TipoOperacionVinculada | str, BeforeValidator(lambda v: _hydrate_m232_codigo(field_name='tipo_operacion', value=v, code_set=TipoOperacionVinculada))]
metodo: Annotated[MetodoValoracion | str, BeforeValidator(lambda v: _hydrate_m232_codigo(field_name='metodo', value=v, code_set=MetodoValoracion))]
importe: Decimal
exception Modelo349CountryPrefixContextError(*, country_code, clave_operacion, filing_year, period, reason)[source]

Bases: CadrumoError

A Modelo 349 country prefix is invalid for the filing context.

Parameters:
  • country_code (str)

  • clave_operacion (str)

  • filing_year (int)

  • period (str)

  • reason (str)

Return type:

None

code
class Modelo349OperadorRow(**data)[source]

Bases: BaseModel

One typed operator row for Modelo 349 manual-entry input.

Parameters:

data (Any)

row_type: Literal['operador']
codigo_pais: _IsoCountryCode
nif_comunitario: _NifStr
razon_social: _RequiredNameStr
clave_operacion: Modelo349ClaveOperacionValue
importe: Decimal
class Modelo349RectificacionRow(**data)[source]

Bases: BaseModel

One typed rectification row for Modelo 349 manual-entry input.

Parameters:

data (Any)

row_type: Literal['rectificacion']
codigo_pais: _IsoCountryCode
nif_comunitario: _NifStr
razon_social: _RequiredNameStr
clave_operacion: Modelo349ClaveOperacionValue
ejercicio: Annotated[str, StringConstraints(strip_whitespace=True, min_length=4, max_length=4)]
periodo: str
base_rectificada: Decimal
base_anterior: Decimal
validate_m349_nif_format(nif, pais)[source]

Return whether nif has the NIF-IVA structure Modelo 349 accepts for pais.

Return type:

bool

Parameters:
validate_m349_country_prefix_context(*, country_code, clave_operacion, filing_year, period, is_rectification=False, rectified_year=None, rectified_period=None)[source]

Validate the post-Brexit GB / XI prefix rules for one Modelo 349 row.

AEAT’s Modelo 349 instructions keep XI for Northern Ireland goods operations from the transition year onward and exclude the service keys from it. GB stays valid only for rectifications of pre-transition periods and for the transition year’s first periods, never for service keys. The transition year, first periods and service keys are registry-declared.

Return type:

None

Parameters:
  • country_code (str)

  • clave_operacion (str)

  • filing_year (int)

  • period (str)

  • is_rectification (bool)

  • rectified_year (int | None)

  • rectified_period (str | None)

m349_nif_number_for_export(nif, pais)[source]

Return the BOE NIF subfield without the separate country-code prefix.

Modelo 349 operator records split the IVA identifier into codigo_pais and nif_comunitario fields. The CLI accepts and validates the full prefixed IVA identifier for operator ergonomics, but the fixed-width export must write only the number part into positions 78-92.

Return type:

str

Parameters:
class Modelo347ClaveOperacion(*values)[source]

Bases: StrEnum

Clave de operación declarable on a Modelo 347 counterparty row.

This operation-key type is specific to the M347 row family and is not shared with the registry-owned M349 operation-key shell.

A
B
C
D
E
F
G
Modelo347ClaveOperacionValue

The same code set for a wire or operation payload field.

alias of Literal[A, B, C, D, E, F, G]

class Modelo347ContraparteRow(**data)[source]

Bases: BaseModel

One contraparte declarada row for Modelo 347.

Fields mirror the per-counterparty Tipo-2 record layout declared in 347/revisions/2011-2024.

One row per counterparty. The annual total importe (sum of Q1-Q4) must exceed €3,005.06 per RD 1065/2007 art. 33.1.

Parity assertions:

  • nif → contraparte.nif (counterparty tax id)

  • nombre → contraparte.nombre (legal name)

  • importe_Q1/Q2/Q3/Q4 → quarterly importe slots

  • clave_operacion → operation type code

  • pais_codigo → contraparte.pais (ISO 3166-1; None = domestic)

Parameters:

data (Any)

row_type: Literal['contraparte']
nif: _NifStr
nombre: _NameStr
importe_Q1: Decimal
importe_Q2: Decimal
importe_Q3: Decimal
importe_Q4: Decimal
clave_operacion: Modelo347ClaveOperacionValue
pais_codigo: _IsoCountryCode | None
property importe_total: Decimal

Sum of quarterly importes — used for M347 threshold check.

class Modelo210AgrupacionRentaRow(**data)[source]

Bases: BaseModel

One typed component row in an annual Modelo 210 grouping.

Parameters:

data (Any)

row_type: Literal['agrupacion_renta']
source_id: _RequiredNameStr
tipo_renta_code: _M210OfficialTipoRentaCode
importe: Decimal
tipo_gravamen: UnitProportion
pagador_mode: M210PayerMode
pagador_id: _RequiredNameStr | None
deriva_de_bien_derecho: bool
bien_derecho_id: _RequiredNameStr | None
exception Modelo210AgrupacionRentaRowsError(*, reason, detail)[source]

Bases: CadrumoError

A Modelo 210 annual grouped-renta set violates row compatibility.

Parameters:
Return type:

None

code
validate_m210_agrupacion_renta_rows(rows, *, effective_date)[source]

Validate a grouped-renta row set against one dated registry catalogue.

Return type:

None

Parameters:
exception Modelo347ThresholdError(*, nif, total, threshold)[source]

Bases: CadrumoError

A Modelo 347 contraparte row falls at or below the declarability threshold.

Parameters:
Return type:

None

code
exception Modelo184ShareSumError(*, total, count)[source]

Bases: CadrumoError

Modelo 184 member share percentages do not sum to exactly 100%.

Parameters:
  • total (Decimal)

  • count (int)

Return type:

None

code
validate_m347_threshold(rows, *, effective_date)[source]

Enforce the Modelo 347 per-counterparty declarability threshold.

RD 1065/2007 art. 33.1: only counterparties whose annual operations exceed EUR 3,005.06 are declarable. The threshold applies to the SUM of every operation with the same person (same NIF), aggregated across all contraparte rows — not to each row in isolation. A counterparty’s operations may be split across several rows (e.g. entregas and adquisiciones), so a per-row check would wrongly reject a counterparty whose individual rows are each at/below the threshold while their annual aggregate exceeds it (a missed declaration), and would never apply the “same person” threshold the regulation defines.

Raises:

Modelo347ThresholdError – for the first counterparty (in NIF first-appearance order) whose AGGREGATED annual total is at or below the threshold.

Return type:

None

Parameters:
validate_m184_member_share_sum(rows)[source]

Enforce that Modelo 184 member share percentages sum to exactly 100%.

Only checked when at least one miembro row is present (partial sets are skipped).

Raises:

Modelo184ShareSumError – when the share percentages do not total exactly 100.

Return type:

None

Parameters:

rows (Sequence[Modelo184MemberRow])