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:
StrEnumTerritorial-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 impuestoand thelugar de realizaciónrules 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 toaeat.domain.iva.IvaCategory.DOMESTIC_NOT_SUBJECTfor 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:
StrEnumWhether 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 betweenInvoiceDirection(substrate) andInvoiceKind(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:
StrEnumIVA-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.UNKNOWNis 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:
StrEnumKind-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_tieris 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_LAND_RELATED¶
- 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:
_IvaStrictFrozenInput 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:
transaction_date – When the supply takes place.
issuer_residency – Issuer’s tax residency.
customer_residency – Customer’s tax residency.
customer_tax_status – Customer’s IVA status.
kind – Kind of supply.
direction –
ISSUEDorRECEIVED.issuer_member_state – Issuer’s
aeat.domain.iva.EUMemberState, required whenissuer_residencyisIvaTerritorialScope.EU_MEMBER.customer_member_state – Customer’s
aeat.domain.iva.EUMemberState, required whencustomer_residencyisIvaTerritorialScope.EU_MEMBER.rate_tier – Explicit rate-tier axis for ES-to-ES domestic rules.
- 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)
- 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:
_IvaStrictFrozenOutput record returned by
classify_iva().Exposes the matched
aeat.domain.iva.IvaCategory, the resolvedaeat.domain.iva.IvaRateRecord(orNonefor 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_charge –
Truewhen 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)
- 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
IvaClassificationResultwhose predicate acceptscriteria. Falls through to theR99sentinel (aeat.domain.iva.IvaCategory.UNKNOWN) only when no rule matches — a state that requires human review per theaeat.domain.iva.IvaCategory.UNKNOWNcontract.- Parameters:
criteria (
IvaInvoiceClassificationCriteria) – TheIvaInvoiceClassificationCriteriacarrying every classification axis.- Return type:
- Returns:
A
IvaClassificationResultwith the resolved category, rate, reverse-charge flag, matched rule identifier, and any explanatory note.