aeat.domain.iva._invoice_classification module

Reusable IVA classification record for ledger-side categorization.

Bridges the substrate’s three IVA classification axes (IvaCategory, IvaRateKind, IvaFlowDirection) into one frozen pydantic record that the ledger and downstream filing surfaces can pass around without re-deriving the mapping.

Every IVA-bearing ledger line — invoice line, payment, ledger entry — carries one IvaInvoiceClassification. The record captures:

  • The operation kind (domestic / intra-community / recargo / OSS / etc.).

  • The applicable rate tier (general / reduced / super-reduced / zero / exempt).

  • The flow direction (output / input / self-assessed reverse charge).

  • The derived settlement-side classification (devengada and / or deducible) — pre-computed at construction time so consumers don’t have to call settlement_sides_for_flow() repeatedly.

Why a separate record instead of fields on aeat.domain.invoices.Invoice?

Multiple ledger surfaces need the same triple — invoice lines, payment-record IVA splits, expense-report ledger lines, OSS / IOSS observations, recargo de equivalencia entries — and embedding the fields directly on each model would scatter the substrate-bridge logic across the codebase. The record is the canonical reusable construct: build one from substrate primitives once, pass it down the ledger pipeline.

The classify_invoice_line_for_iva() helper accepts an IvaRate plus the invoice direction (issued / received) and returns a IvaInvoiceClassification for the standard domestic-IVA case (the most common autónomo operation). For reverse-charge, intra-community, OSS / IOSS, and other non-domestic cases, callers construct the record directly with the appropriate IvaCategory from the substrate’s classifier output (classify_iva()).

class IvaInvoiceClassification(**data)[source]

Bases: BaseModel

Frozen pydantic record bundling the IVA classification triple and derived settlement sides.

Variables:
  • category – Substrate IvaCategory classifying the operation kind.

  • rate_kind – Substrate IvaRateKind rate tier; None for operations outside the scope of IVA (NOT_SUBJECT, ERRONEOUS_INVOICE, UNKNOWN).

  • flow_direction – Substrate IvaFlowDirection — REPERCUTIDO (output), SOPORTADO (input), or INVERSION_SUJETO_PASIVO (self-assessed reverse charge).

  • settlement_sides – Pre-computed frozen set of the IvaSettlementSide cornerstones the line contributes to. Derived from flow_direction at construction time via settlement_sides_for_flow() so consumers don’t recompute it.

Parameters:
category: IvaCategory
rate_kind: IvaRateKind | None
flow_direction: IvaFlowDirection
settlement_sides: frozenset[IvaSettlementSide]
property contributes_to_devengada: bool

Return True iff this line owes IVA to the Treasury.

property contributes_to_deducible: bool

Return True iff this line reclaims IVA from the Treasury.

property is_reverse_charge: bool

Return True iff the line is self-assessed reverse charge.

INVERSION_SUJETO_PASIVO is the only flow that contributes to BOTH settlement sides on the same operation (LIVA art. 84.Uno.2).

classify_invoice_line_for_iva(*, iva_rate, invoice_kind)[source]

Build a classification record for the standard domestic-IVA case.

Covers the most common autónomo operation: a domestic invoice issued to a Spanish customer or received from a Spanish supplier at one of the four IVA rate slots (or EXEMPT). The record’s flow direction is derived from invoice_kind:

For reverse-charge, intra-community, export, import, recargo de equivalencia, and OSS / IOSS cases, callers construct IvaInvoiceClassification directly with the appropriate IvaCategory from the substrate classifier (aeat.domain.iva.classify_iva()).

Parameters:
  • iva_rate (IvaRate) – One of the closed IvaRate slots. IvaRate.NOT_SUBJECT is rejected — see module docstring for the rationale.

  • invoice_kind (InvoiceKind) – Whether the invoice was issued (sale) or received (purchase).

Return type:

IvaInvoiceClassification

Returns:

A frozen IvaInvoiceClassification with the derived substrate triple and pre-computed settlement-side set.

Raises:

InvoiceValidationError – If iva_rate is IvaRate.NOT_SUBJECT, which has no rate-tier classification and cannot be handled by the standard-case helper.

invoice_line_to_iva_observation(*, invoice_id, issued_at, invoice_kind, iva_rate, base_amount, iva_amount)[source]

Build an IvaLedgerObservation from invoice line metadata.

The runtime resolver for the substrate’s ledger_iva_aggregation binding source kind consumes aeat.domain.calculations.registry.IvaLedgerObservation records. This helper turns invoice-line metadata into the observation shape the modelo registry expects, applying the standard-case classification (domestic IVA, REPERCUTIDO for issued invoices, SOPORTADO for received). Reverse-charge and intra-community lines must construct the observation directly.

The function is the canonical ledger → modelo bridge for the standard-case IVA flows. Callers iterate the invoice’s lines, call this helper for each, and pass the resulting tuple to resolve_ledger_iva_aggregation_binding_values() to populate the modelo’s binding values.

Parameters:
  • invoice_id (str) – Stable id of the source invoice (becomes ledger_id on the observation).

  • issued_at (date) – Invoice issue date (becomes transaction_date).

  • invoice_kind (InvoiceKind) – Whether the invoice was issued or received.

  • iva_rate (IvaRate) – IvaRate slot for the line. NOT_SUBJECT raises (substrate-NULL category needs explicit construction).

  • base_amount (Decimal) – Taxable base in EUR.

  • iva_amount (Decimal) – IVA amount in EUR.

Return type:

IvaLedgerObservation

Returns:

An IvaLedgerObservation with the full classification triple ready for binding-resolver consumption.

Raises:

InvoiceValidationError – If the classification produces a None rate_kind (e.g. when iva_rate is NOT_SUBJECT).