aeat.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 intracomunitario 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 Tipo-2 operador record layout (Orden HAC/174/2020 Anexo II).

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

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

class Modelo184MemberRow(**data)[source]

Bases: BaseModel

One atribución member row for Modelo 184.

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

Parity assertions: * nifmember_tax_id (binding: modelo-184-member-row-nif) * nombremember_legal_name (binding: modelo-184-member-row-name) * porcentajeshare_percentage (binding: modelo-184-member-row-share) * importebase_imponible_assigned (binding: modelo-184-member-row-base-assigned) * paiscountry_code (ES default)

Parameters:
  • row_type (Literal['miembro'])

  • nif (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=20, pattern=None, ascii_only=None)])

  • nombre (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=None, max_length=200, pattern=None, ascii_only=None)])

  • pais (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=2, max_length=2, pattern=None, ascii_only=None)])

  • porcentaje (Decimal)

  • importe (Decimal)

row_type: Literal['miembro']
nif: _NifStr
nombre: _NameStr
pais: _IsoCountryCode
porcentaje: Decimal
importe: Decimal
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.

Parity assertions: * nifcounterparty_tax_id (binding: modelo-232-related-party-row-nif) * nombrecounterparty_legal_name (binding: modelo-232-related-party-row-name) * paiscountry_code (binding: modelo-232-related-party-row-country) * tipo_operacionoperation_kind_code (binding: modelo-232-related-party-row-operation-kind) * metodotransfer_pricing_method_code (binding: modelo-232-related-party-row-tpr-method) * importeamount (binding: modelo-232-related-party-row-amount)

Parameters:
  • row_type (Literal['vinculada'])

  • nif (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=20, pattern=None, ascii_only=None)])

  • nombre (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=None, max_length=200, pattern=None, ascii_only=None)])

  • pais (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=2, max_length=2, pattern=None, ascii_only=None)])

  • tipo_vinculacion (str)

  • tipo_operacion (str)

  • metodo (str)

  • importe (Decimal)

row_type: Literal['vinculada']
nif: _NifStr
nombre: _NameStr
pais: _IsoCountryCode
tipo_vinculacion: str
tipo_operacion: str
metodo: str
importe: Decimal
exception Modelo349CountryPrefixContextError(*, country_code, clave_operacion, filing_year, period, reason)[source]

Bases: AeatError, ValueError

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: ClassVar[ErrorCode]
class Modelo349OperadorRow(**data)[source]

Bases: BaseModel

One operador intracomunitario row for Modelo 349 (manual-entry path).

Fields mirror the Tipo-2 operador record layout declared in 349/revisions/2020-y-siguientes/bindings/0007-bindings.toml.

This row is used when the collectible-invoice ledger is absent and the operator declares intracom counterparties directly via the CLI.

Parity assertions: * codigo_pais -> op.codigo-pais (record positions 76-77) * nif_comunitario -> op.nif-comunitario (record positions 78-92) * razon_social -> op.apellidos-razon-social (record positions 93-132) * clave_operacion -> op.clave-operacion (record position 133) * importe -> op.base-imponible (record positions 134-146)

Parameters:
  • row_type (Literal['operador'])

  • codigo_pais (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=2, max_length=2, pattern=None, ascii_only=None)])

  • nif_comunitario (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=20, pattern=None, ascii_only=None)])

  • razon_social (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=200, pattern=None, ascii_only=None)])

  • clave_operacion (Literal['E', 'M', 'H', 'A', 'T', 'S', 'I', 'R', 'D', 'C'])

  • importe (Decimal)

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

Bases: BaseModel

One rectificación row for Modelo 349 (manual-entry path).

Fields mirror the Tipo-2 rectificación record layout declared in 349/revisions/2020-y-siguientes/bindings/0007-bindings.toml.

Parity assertions: * codigo_pais -> op.codigo-pais (record positions 76-77) * nif_comunitario -> op.nif-comunitario (record positions 78-92) * razon_social -> op.apellidos-razon-social (record positions 93-132) * clave_operacion -> op.clave-operacion (record position 133) * ejercicio -> rect.ejercicio-rectificado (record positions 147-150) * periodo -> rect.periodo-rectificado (record positions 151-152) * base_rectificada -> rect.base-rectificada (record positions 153-165) * base_anterior -> rect.base-anterior (record positions 166-178)

Parameters:
  • row_type (Literal['rectificacion'])

  • codigo_pais (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=2, max_length=2, pattern=None, ascii_only=None)])

  • nif_comunitario (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=20, pattern=None, ascii_only=None)])

  • razon_social (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=200, pattern=None, ascii_only=None)])

  • clave_operacion (Literal['E', 'M', 'H', 'A', 'T', 'S', 'I', 'R', 'D', 'C'])

  • ejercicio (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=4, max_length=4, pattern=None, ascii_only=None)])

  • periodo (Literal['01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12', '1M', '1T', '2T', '3T', '4T'])

  • base_rectificada (Decimal)

  • base_anterior (Decimal)

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

Return True when nif matches the expected NIF-IVA format for pais.

Every current EU Member State (plus XI) resolves its structural pattern from the canonical aeat.core.identity.nif_iva_format_for_country() authority. GB is validated against Modelo 349’s own Brexit-transition pattern (see _M349_GB_NIF_PATTERN), since post-Brexit UK carries no entry in the general EU NIF-IVA authority. Unsupported country prefixes fail closed. The NIF string must include the same two-letter country prefix.

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 post-Brexit GB / XI rules for Modelo 349.

AEAT’s Brexit IVA instructions keep XI for Northern Ireland goods operations after 2021 and exclude S / I service keys from XI. Ordinary GB rows are not valid for post-transition periods, except for the limited 2021 first-period and pre-2021 rectification cases named by the official instructions.

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 VAT identifier into codigo_pais and nif_comunitario fields. The CLI accepts and validates the full prefixed VAT identifier for operator ergonomics, but the fixed-width export must write only the number part into positions 78-92.

Return type:

str

Parameters:
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/2008-y-siguientes.

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: * nifcontraparte.nif (counterparty tax id) * nombrecontraparte.nombre (legal name) * importe_Q1/Q2/Q3/Q4 → quarterly importe slots * clave_operacion → operation type code * pais_codigocontraparte.pais (ISO 3166-1; None = domestic)

Parameters:
  • row_type (Literal['contraparte'])

  • nif (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=1, max_length=20, pattern=None, ascii_only=None)])

  • nombre (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=None, max_length=200, pattern=None, ascii_only=None)])

  • importe_Q1 (Decimal)

  • importe_Q2 (Decimal)

  • importe_Q3 (Decimal)

  • importe_Q4 (Decimal)

  • clave_operacion (Literal['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'])

  • pais_codigo (Annotated[str, StringConstraints(strip_whitespace=True, to_upper=None, to_lower=None, strict=None, min_length=2, max_length=2, pattern=None, ascii_only=None)] | None)

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

Sum of quarterly importes — used for M347 threshold check.

exception Modelo347ThresholdError(*, nif, total)[source]

Bases: AeatError, ValueError

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

Parameters:
  • nif (str)

  • total (Decimal)

Return type:

None

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

Bases: AeatError, ValueError

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

Parameters:
  • total (Decimal)

  • count (int)

Return type:

None

code: ClassVar[ErrorCode]
validate_m347_threshold(rows)[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:

rows (Sequence[Modelo347ContraparteRow])

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