aeat.domain.iva._classification module

Closed-table IVA classification (issuer / customer / kind / direction).

Layered on top of the aeat.domain.iva substrate (the aeat.domain.iva.IvaCategory enum, aeat.domain.iva.IvaRateRecord records, and aeat.domain.iva.lookup_rate()), this module adds the classification axes needed to tag a transaction deterministically based on the parties’ tax residency, the customer’s IVA status, the transaction kind, and the invoice direction.

The resolver implementation is a closed first-match-wins decision table over the rules R01 through R99. Each rule is a plain typing.NamedTuple carrying a stable identifier, a description and a predicate; the table itself is a module-level constant. There is no dynamic dispatch, no string expression evaluation, no caller-supplied callback — every classification outcome is reproducible by replaying the same IvaInvoiceClassificationCriteria.

Examples

>>> from datetime import date
>>> from . import (
...     IvaTerritorialScope,
...     CustomerTaxStatus,
...     EUMemberState,
...     IvaTerritorialScope,
...     InvoiceKind,
...     TransactionKind,
...     IvaRateKind,
...     IvaInvoiceClassificationCriteria,
...     classify_iva,
... )
>>> criteria = IvaInvoiceClassificationCriteria(
...     transaction_date=date(2025, 6, 15),
...     issuer_residency=IvaTerritorialScope.ES_MAINLAND,
...     customer_residency=IvaTerritorialScope.EU_MEMBER,
...     customer_member_state=EUMemberState.DE,
...     customer_tax_status=CustomerTaxStatus.B2B_IVA_REGISTERED,
...     kind=TransactionKind.GOODS,
...     direction=InvoiceKind.ISSUED,
... )
>>> classify_iva(criteria).category
<IvaCategory.INTRA_COMMUNITY_SUPPLY: 'intra_community_supply'>
class IvaTerritorialScope(*values)[source]

Bases: StrEnum

Territorial-scope classification of an invoice party.

Per Ley 37/1992 Art. 3.Dos and Arts. 68-72, the substrate segments parties by territorio de aplicación del impuesto and the lugar de realización rules rather than tax residency in the civil-law sense. The five values partition that territorial scope for both issuer and customer roles via field-name semantics (issuer_residency: IvaTerritorialScope, customer_residency: IvaTerritorialScope — the field name keeps the role label; the type carries the territorial framing). Parties in Canarias, Ceuta or Melilla are NOT subject to LIVA; the classifier short-circuits to aeat.domain.iva.IvaCategory.DOMESTIC_NOT_SUBJECT for issuers in those territories (out of TAI).

Variables:
  • ES_MAINLAND – Spanish mainland and Balearic Islands (TAI).

  • ES_CANARIAS – Canary Islands — IGIC territory, out of LIVA.

  • ES_CEUTA_MELILLA – Ceuta and Melilla — IPSI territory, out of LIVA.

  • EU_MEMBER – Any of the other 26 EU member states.

  • THIRD_COUNTRY – Any non-EU jurisdiction.

ES_MAINLAND
ES_CANARIAS
ES_CEUTA_MELILLA
EU_MEMBER
THIRD_COUNTRY
class InvoiceKind(*values)[source]

Bases: StrEnum

Whether the autónomo issued or received the invoice.

Single canonical enum spanning both the substrate classifier (IvaInvoiceClassificationCriteria.direction) and ledger / invoice records (Invoice.kind). Replaces the prior split between InvoiceDirection (substrate) and InvoiceKind (invoices) that carried identical semantics with mismatched lowercase / uppercase string values. Values are lowercase to align with TOML registry selectors (invoice_direction = "issued").

Variables:
  • ISSUED – The autónomo is the issuer (sale).

  • RECEIVED – The autónomo is the recipient (purchase).

ISSUED
RECEIVED
class CustomerTaxStatus(*values)[source]

Bases: StrEnum

IVA-status classification of the customer.

Classifier rules that depend on reverse-charge mechanics check B2B_IVA_REGISTERED, which requires a valid NIF-IVA on record. UNKNOWN is a sentinel for transactions whose counterparty status has not been resolved upstream.

Variables:
  • B2B_IVA_REGISTERED – Business customer with a valid IVA-ID.

  • B2B_NOT_REGISTERED – Business customer without a IVA-ID.

  • B2C_CONSUMER – Private individual.

  • PUBLIC_ADMINISTRATION – Public-sector body.

  • UNKNOWN – Counterparty status unresolved.

B2B_IVA_REGISTERED
B2B_NOT_REGISTERED
B2C_CONSUMER
PUBLIC_ADMINISTRATION
UNKNOWN
class TransactionKind(*values)[source]

Bases: StrEnum

Kind-of-supply classification.

Drives place-of-supply rules (services general vs. land-related vs. OSS), reverse-charge sub-rules (construction, waste, consumer electronics) and rate-tier defaulting (passenger transport at 10 %, restaurants at 10 %). Kind is orthogonal to rate tier; IvaInvoiceClassificationCriteria.rate_tier is the explicit rate-tier axis the caller supplies for ES-to-ES domestic rules.

Variables:
  • GOODS – Tangible goods supply.

  • SERVICES_GENERAL – Services not covered by a specialised category.

  • SERVICES_LAND_RELATED – Land-related services (Art. 70).

  • SERVICES_PASSENGER_TRANSPORT – Passenger transport service.

  • SERVICES_RESTAURANT – Restaurant or catering service.

  • IMMOVABLE_PROPERTY – Real-estate transaction.

  • PASSENGER_CAR – Private-use passenger vehicle (deductibility flag).

  • CONSTRUCTION_REVERSE_CHARGE – Art. 84.Uno.2º.f construction works.

  • WASTE_REVERSE_CHARGE – Art. 84.Uno.2º.c waste / recovery materials.

  • ELECTRONICS_REVERSE_CHARGE – Art. 84.Uno.2º.g B2B consumer electronics.

  • EXTERNAL_SCHEME_SERVICES – Services from a non-EU taxable person to an EU-resident consumer routed through Esquema Exterior. LIVA art. 163 octiesdecies.

  • OSS_UNION_GOODS_DISTANCE_SALE – Intra-community distance sale of goods routed through Esquema Unión. LIVA art. 163 unvicies.

  • OSS_UNION_GOODS_INTERFACE_FACILITATED – Interior supply of goods facilitated by an electronic interface, routed through Esquema Unión. LIVA art. 163 unvicies.

  • OSS_UNION_SERVICES – Services from an EU-established taxable person to a consumer in another Member State routed through Esquema Unión. LIVA art. 163 unvicies.

  • IOSS_DISTANCE_SALE_LOW_VALUE – Distance sale of imported goods with intrinsic value at or below 150 EUR routed through Esquema de Importación (IOSS). LIVA art. 163 quinvicies.

GOODS
SERVICES_GENERAL
SERVICES_PASSENGER_TRANSPORT
SERVICES_RESTAURANT
IMMOVABLE_PROPERTY
PASSENGER_CAR
CONSTRUCTION_REVERSE_CHARGE
WASTE_REVERSE_CHARGE
ELECTRONICS_REVERSE_CHARGE
EXTERNAL_SCHEME_SERVICES
OSS_UNION_GOODS_DISTANCE_SALE
OSS_UNION_GOODS_INTERFACE_FACILITATED
OSS_UNION_SERVICES
IOSS_DISTANCE_SALE_LOW_VALUE
class IvaInvoiceClassificationCriteria(**data)[source]

Bases: _IvaStrictFrozen

Input record for classify_iva().

Carries every axis the closed decision table inspects. The record is strict and frozen so it can be used as a dict key in upstream caches.

Variables:
Parameters:
transaction_date: date
issuer_residency: IvaTerritorialScope
customer_residency: IvaTerritorialScope
customer_tax_status: CustomerTaxStatus
kind: TransactionKind
direction: InvoiceKind
issuer_member_state: EUMemberState | None
customer_member_state: EUMemberState | None
rate_tier: IvaRateKind | None
class IvaClassificationResult(**data)[source]

Bases: _IvaStrictFrozen

Output record returned by classify_iva().

Exposes the matched aeat.domain.iva.IvaCategory, the resolved aeat.domain.iva.IvaRateRecord (or None for rate-irrelevant categories), a reverse-charge flag, the matched rule identifier, and any free-form note the resolver emits (typically used for fall-through documentation).

Variables:
  • category – Resolved IVA category.

  • rate – Applicable aeat.domain.iva.IvaRateRecord, when relevant.

  • requires_reverse_chargeTrue when the rule triggers inversión del sujeto pasivo.

  • matched_rule_id – Stable rule identifier (e.g. R10_intra_community_supply).

  • notes – Free-form explanatory note.

Parameters:
category: IvaCategory
rate: IvaRateRecord | None
requires_reverse_charge: bool
matched_rule_id: str
notes: str
exemption_article: IvaExemptionArticle | None
classify_iva(criteria)[source]

Apply the closed decision table; first match wins.

Iterates the module-level rule table in declaration order, returning the first IvaClassificationResult whose predicate accepts criteria. Falls through to the R99 sentinel (aeat.domain.iva.IvaCategory.UNKNOWN) only when no rule matches — a state that requires human review per the aeat.domain.iva.IvaCategory.UNKNOWN contract.

Parameters:

criteria (IvaInvoiceClassificationCriteria) – The IvaInvoiceClassificationCriteria carrying every classification axis.

Return type:

IvaClassificationResult

Returns:

A IvaClassificationResult with the resolved category, rate, reverse-charge flag, matched rule identifier, and any explanatory note.