cadrumo.domain.iva.schema module

Strict pydantic v2 schema for the cadrumo.domain.iva subpackage.

Every record the subpackage exposes — enumerations, per-rate values, citations, regulations, catalogues, verification reports — is defined here. The schema is frozen and strict wherever the loader idiom permits it, matching the current registry-backed legal grounding conventions.

The IVA category, rate-kind, cash-treatment, exemption, and member-state vocabularies are registry-projected opaque tokens. Every prose field is stored inline and Spanish-authoritative: a citation’s quoted_text is verbatim BOE text, which is evidence rather than a label, and so has no locale-resolved form.

validate_orden_module_identities(modulos)[source]

Refuse an Orden activity whose modules are missing, misordered or non-positive.

Return type:

None

Parameters:

modulos (tuple[_ModuloOrdenAnualLike, ...])

class IvaCategory[source]

Bases: str

Opaque IVA-category token projected from the facts authority.

property value: str

Return the persisted registry token for serialization.

class IvaCashAccountingTreatment[source]

Bases: str

Opaque registry-projected IVA cash-accounting treatment token.

Treatment membership and legal semantics are selected from fact 0098; this type retains only the stable token shape.

property value: str

Return the opaque token for string-oriented serialization.

class IvaLedgerObservationRole(*values)[source]

Bases: StrEnum

The reporting role of an IVA ledger observation.

This is deliberately independent from IvaCashAccountingTreatment. A cash-accounting operation creates an informational art. 75 projection at its operation date and monetary settlement projections at each payment (or the statutory fallback) date. Both carry the same treatment affiliation, while their role tells registry bindings which projection they are permitted to consume.

Ordinary IVA observations are monetary projections too, so they use SETTLEMENT. That keeps normal IVA bindings on one role while reserving OPERATION_INFORMATIONAL for Modelo 303 boxes 62, 63, 74, and 75.

OPERATION_INFORMATIONAL
SETTLEMENT
class IvaCashAccountingPaymentEvidence(**data)[source]

Bases: BaseModel

Collection/payment evidence for an operation affected by criterio de caja.

The amounts are the IVA substrate settled by the collection/payment event, not a gross bank amount that downstream code must reinterpret. Partial evidence therefore remains explicit and auditable.

Parameters:

data (Any)

payment_date: date
taxable_base: Decimal
iva_amount: Decimal
recargo_amount: Decimal
class IvaExemptionArticle[source]

Bases: _OpaqueRegistryToken

Opaque registry-projected IVA exemption-article token.

class IvaArt69DosService[source]

Bases: _OpaqueRegistryToken

Opaque registry-projected Art. 69.Dos service token.

class EUMemberState(value, *, _registry_validated=False)[source]

Bases: str

Opaque EU IVA member-state token projected from fact 0131.

Parameters:
  • value (str)

  • _registry_validated (bool)

Return type:

EUMemberState

classmethod from_registry(value)[source]

Construct a token only at the typed registry projection boundary.

Return type:

EUMemberState

Parameters:

value (str)

property value: str

Return the persisted registry token for serialization.

require_eu_member_state(value, *, effective_date=None, authority=None)[source]

Project one EU member-state token through fact 0131.

Return type:

EUMemberState

Parameters:
require_registry_declared_eu_member_state(value, *, effective_date)[source]

Project a member-state token from the facts currently under validation.

Return type:

EUMemberState

Parameters:
spanish_eu_member_state(*, effective_date=None, authority=None)[source]

Return the registry-declared Spain token for domestic IVA projections.

Return type:

EUMemberState

Parameters:
class IvaRateKind[source]

Bases: str

Opaque registry-projected IVA rate-tier token.

property value: str

Return the persisted registry token for serialization.

default_iva_cash_accounting_treatment(on_date=None, *, authority=None)[source]

Return the registry-declared ordinary cash-accounting treatment.

Return type:

IvaCashAccountingTreatment

Parameters:
is_iva_cash_accounting_none(treatment, on_date=None, *, authority=None)[source]

Return whether a treatment is the registry-declared ordinary token.

Return type:

bool

Parameters:
is_iva_cash_accounting_supplier_regime(treatment, on_date=None, *, authority=None)[source]

Return whether a treatment is the registry-declared supplier token.

Return type:

bool

Parameters:
IvaStrictFrozen

alias of _IvaStrictFrozen

class IvaRateRecord(**data)[source]

Bases: _IvaStrictFrozen

A single IVA rate line item keyed by member state and rate kind.

Variables:
  • member_state – Issuing member state.

  • kind – Rate tier (general / reduced / …).

  • pct – Rate percentage as a Decimal in [0, 100].

  • effective_from – First date the rate applies.

  • effective_until – Last date the rate applies, or None for open-ended.

  • legal_refs – Binding registry legal-reference identities backing the numerical rate where provision-level authority is bundled.

  • source_refs – Registry source-reference identities backing the numerical foreign-rate evidence.

Parameters:

data (Any)

member_state: EUMemberState
kind: IvaRateKind
pct: Decimal
effective_from: date
effective_until: date | None
legal_refs: tuple[_RegistryLegalRef, ...]
source_refs: tuple[_RegistrySourceRef, ...]
supersedes_tier_default: bool

Whether this rate coexists with its tier’s ordinary rate rather than replacing it.

False – the ordinary case – means the record IS the tier’s rate for its window, so lookup_rate() can answer “what does this tier mean on this date” with one record and the no-overlap rule guarantees that answer is unambiguous.

True marks a rate that a statute applied to PART of a tier’s supplies while the rest stayed on the ordinary rate. Spain’s 2024 anti-inflation measures are the worked case: certain foodstuffs moved to 2 % while every other super-reducido supply stayed at 4 %, so both were simultaneously correct and neither replaced the other (RDL 4/2024 art. 1). Such a record is excluded from the no-overlap rule and from lookup_rate(), because including it would make the tier’s rate ambiguous for the far larger set of supplies that never moved.

The distinction is which QUESTION each record can answer. A coexisting rate cannot answer “what is this tier’s rate” – only the statute’s goods scope decides that, and no bundled AEAT surface carries a goods axis. It can answer “is this declared rate a legitimate one for this tier on this date”, which is what classification needs and what rate_kinds_for_declared_rate() serves.

class IvaCitation(**data)[source]

Bases: _IvaStrictFrozen

A legal or regulatory citation backing a IvaRegulation.

quoted_text holds the authoritative Spanish INLINE, not a translation key. Verifying a quotation against the bundled corpus requires the literal text at the citation site; indirecting it means the record no longer carries its own evidence, whatever the key resolves to.

Variables:
  • legal_reference – Article-qualified registry legal-reference id.

  • quoted_text – Verbatim Spanish from the bundled corpus. Empty only when grounding is UNRESOLVED.

  • grounding – Whether the text was verified or the citation refused.

  • unresolved_reason – Why the citation could not be grounded. Required when, and only when, grounding is UNRESOLVED.

  • valid_from – First date this citation is asserted to support the rule.

  • valid_to – Last date, inclusive.

Parameters:

data (Any)

Both window bounds are required and closed. The span is a grounding claim, not bookkeeping: it says the cited provision supports this rule across those years, and a gate holds it inside the provision’s own effective span in the registry legal catalogue. A defaulted or open end would assert grounding nobody typed and nothing could check.

legal_reference: _RegistryLegalRef
quoted_text: str
grounding: CitationGrounding
unresolved_reason: str
valid_from: date
valid_to: date
property window: ValidityWindow

Return the closed span this citation is asserted over.

Returns:

The ValidityWindow the declared bounds describe.

class IvaRegulation(**data)[source]

Bases: _IvaStrictFrozen

A single codified IVA rule for a IvaCategory.

Every regulation carries at least one IvaCitation so downstream tools can surface the legal backing of any classification.

Variables:
  • category – The IVA situation codified by this rule.

  • requires_reverse_charge – Whether the rule triggers inversión del sujeto pasivo.

  • requires_supplier_iva_id – Whether a supplier NIF-IVA is mandatory.

  • manual_references – Optional Manual práctico IVA rule ids or section references.

  • citations – At least one IvaCitation is required, unless legal_basis_exempt is set.

  • notes – Free-form reviewer notes.

  • legal_basis_exempt – True only for a category that codifies no tax treatment at all (an application-level classifier sentinel), so citing law for it would manufacture the appearance of a legal basis a construct without one by design. Citing an unrelated or over-broad article is not this: that is a wrong citation on a category that DOES need grounding, and must be fixed, not exempted.

Parameters:

data (Any)

category: IvaCategory
requires_reverse_charge: bool
requires_supplier_iva_id: bool
manual_references: tuple[_ManualRef, ...]
citations: tuple[IvaCitation, ...]
notes: str
legal_basis_exempt: bool
class IvaCatalogue(**data)[source]

Bases: _IvaStrictMutable

Aggregate view over a collection of IvaRegulation records.

The aggregate is mutable to keep the loader idiom simple — the loader populates the mapping incrementally. Individual IvaRegulation records remain frozen.

Variables:

regulations – Regulations keyed by their IvaCategory.

Parameters:

data (Any)

regulations: dict[IvaCategory, IvaRegulation]
get(category)[source]

Return the IvaRegulation for category or None if absent.

Return type:

IvaRegulation | None

Parameters:

category (IvaCategory)