cadrumo.domain.iva.saturation module¶
Grounded saturation primitives for the cadrumo.domain.iva subpackage.
Two reusable primitives that let a caller turn a selected
IvaCategory and a transaction gross into the regulated euro
substrate (rate, taxable base, IVA amount) without ever guessing a
number:
resolve_category_rate()maps anIvaCategoryto itsIvaRateKindand looks the applicable rate up viacadrumo.domain.iva.lookup.lookup_rate(), returning it as a decimal fraction (Decimal("0.21")) wrapped in a typedIvaRateResolution. Domestic general / reduced / super-reduced derive a positive rate; domestic zero and exempt derive0; every category with no simple derivable positive domestic rate (intra-community, export, reverse-charge, recargo, import, régimen simplificado, no-sujeta, erroneous, unknown) returns aderivable=Falseresolution carrying an explicit operator-facing reason — never a fabricated rate.A domestic tier ALSO refuses while a temporary statute has it carrying two rates at once. Asking a tier for “its” rate is well defined only while the tier has one: RDL 4/2024 art. 1 moved part of the reduced and super-reducido supplies onto a temporary rate and left the rest on the ordinary one, and which of the two applies turns on WHAT was supplied — an axis no bundled AEAT surface carries. Answering with the ordinary rate there would split a gross at the wrong rate, understating the base and overstating the cuota, so the ambiguity is surfaced rather than resolved by guess.
rate_kinds_for_declared_rate()is the well-defined inverse for a caller that already holds a rate.split_gross_at_rate()performs the inverse split of a gross at a rate fraction into(taxable_base, iva_amount)quantised with the AEAT-mandatedcadrumo.core.money.rounding.round_to_cents()(ROUND_HALF_UP).
The split formula is the canonical inverse of an IVA-inclusive gross:
base = round_to_cents(gross / (1 + rate)) and
iva = round_to_cents(gross - base). Quantising the base first and
deriving the IVA as the remainder guarantees base + iva == gross to
the cent regardless of the rounding residual, which is exactly the
invariant the cadrumo.domain.transactions.models.Transaction model
enforces.
The rate values are grounded in the authored iva-rate-schedule fact
(Spain general 21 / reduced 10 / super-reduced 4 / zero 0; LIVA art. 90/91,
year-scoped).
- class IvaRateResolution(**data)[source]¶
Bases:
BaseModelTyped outcome of resolving an
IvaCategoryto a rate fraction.A resolution is self-documenting: it never silently substitutes a fabricated rate for a category the system cannot ground. When
derivableisTruetheratecarries the applicable rate as a decimal fraction in[0, 1](e.g.Decimal("0.21");Decimal("0")for zero-rated and exempt categories). WhenderivableisFalsetherateisNoneandreasonstates why the operator must complete the field.- Variables:
category – The
IvaCategorythat was resolved.derivable –
Truewhen a Spanish domestic rate fraction was derived,Falsewhen the category has no simple derivable domestic rate and the operator must complete it.rate – The applicable IVA rate as a decimal fraction in
[0, 1]whenderivable, elseNone.rate_kind – The
IvaRateKindthe category maps to whenderivable, elseNone.reason – An operator-facing explanation present only when the rate is not derivable; the empty string otherwise.
- Parameters:
data (Any)
- category: IvaCategory¶
- derivable: bool¶
- rate: Decimal | None¶
- rate_kind: IvaRateKind | None¶
- reason: str¶
- resolve_category_rate(category, *, on_date, operation)[source]¶
Resolve an
IvaCategoryto its Spanish IVA rate fraction.Maps
categoryto itsIvaRateKindand looks the applicable rate up viacadrumo.domain.iva.lookup.lookup_rate()for registry-declared Spanish member-state token onon_date, returning the percentage as a decimal fraction (IvaRateRecord.pct / 100). Domestic general / reduced / super-reduced derive a positive fraction; domestic zero and exempt deriveDecimal("0"). Every category with no simple derivable positive domestic rate returns aderivable=Falseresolution carrying an operator-facing reason — the system never guesses a rate for those.- Parameters:
category (
IvaCategory) – The selected IVA category to resolve.on_date (
date) – The effective date used to resolve the registry rate.operation (
PinnedAuthorityOperation) – Caller-owned pinned authority operation for the complete category and rate lookup.
- Return type:
- Returns:
A typed
IvaRateResolution.derivableisTruewith aratefraction for domestic categories;Falsewith areasonfor every non-derivable category.- Raises:
IvaRateNotFoundError – If a domestic category maps to a rate kind that the registry has no record for on
on_date(a registry gap, not a category-shape problem).
- split_gross_at_rate(gross, rate)[source]¶
Inverse-split an IVA-inclusive gross into
(taxable_base, iva_amount).Computes the IVA-exclusive base and the IVA charged from an IVA-inclusive gross at
rate(a decimal fraction, e.g.Decimal("0.21")), quantising with the AEAT-mandatedcadrumo.core.money.rounding.round_to_cents()(ROUND_HALF_UP). The base is quantised first and the IVA is taken as the quantised remainder (gross - base), sotaxable_base + iva_amount == grossholds to the cent regardless of the rounding residual.A
rateof0(zero-rated or exempt) yields the whole gross as the base and a zero IVA amount.- Parameters:
- Return type:
- Returns:
A
(taxable_base, iva_amount)tuple, each quantised to euro cents, whose sum equalsround_to_cents(gross)to the cent.