cadrumo.domain.categories.proportionality module¶
Proportionality and explainability primitives for category profiles.
Defines registry-projected vocabulary tokens and strict pydantic models that
encode how a spending category is deducted on the autónomo filings, plus the
citation chain back to the relevant authority that makes each rule
explainable. Every ProportionalityRule carries at least one
CategoryCitation; the consistency rules between
kind-specific fields (fixed_pct, default_ratio,
statutory_cap_*) are enforced by the model validator.
- class CategoryCitationSource(*values)[source]¶
Bases:
StrEnumAllowed citation sources for explainable category profiles.
- Variables:
MANUAL_RENTA – AEAT Manual práctico Renta.
MANUAL_IVA – AEAT Manual práctico IVA.
LEY_IRPF – Ley del Impuesto sobre la Renta de las Personas Físicas.
REGLAMENTO_IRPF – Reglamento del IRPF.
AEAT_HELP – AEAT online help / portal text.
- MANUAL_RENTA¶
- MANUAL_IVA¶
- LEY_IRPF¶
- REGLAMENTO_IRPF¶
- AEAT_HELP¶
- ANNUAL_EDITION_CITATION_SOURCES: frozenset[CategoryCitationSource]¶
Citation sources published as a dated ANNUAL EDITION, as opposed to a statute whose reference year is its enactment. The distinction is what makes the anti-mirror invariant precise rather than a year-sniffing heuristic: “Manual práctico Renta 2025” names the edition read, while “Ley 35/2006” names when the law was passed and says nothing about which filing year it covers.
- STATUTORY_CITATION_SOURCES: frozenset[CategoryCitationSource]¶
Citation sources that name a PROVISION rather than a dated publication. Their windows are bounded by the provision’s own effective span in the registry legal catalogue, which makes a multi-year statutory citation a derived fact instead of an author’s confidence. Complete by construction against the enum: a new source must land in one partition or the other, and the gate that reads these sets reds if it lands in neither.
- class CategoryCitation(**data)[source]¶
Bases:
_ProportionalityStrictFrozenModelTraceable citation backing one category or proportionality rule.
- Variables:
source – Originating
CategoryCitationSource.reference – Human-readable document reference (title, edition, BOE number).
locator – Section, article, or page locator within the referenced document.
url – Canonical URL where the citation can be checked. Constrained to an official AEAT or BOE origin over
https– see_validate_authoritative_url().quote – Verbatim Spanish from the bundled corpus, stored INLINE and never as a translation key. Verifying a quotation requires the literal text at the citation site; indirecting it means the record no longer carries its own evidence, whatever the key resolves to. Empty when, and only when,
groundingis notVERIFIED.grounding – Which of the three states this citation’s evidence is in – read against the corpus, examined and refused, or unreachable because the cited document is not bundled.
grounding_reason – Why no quotation is carried. Required when, and only when,
groundingis notVERIFIED.legal_ref – The registry legal-catalogue id of the provision cited. Required on a STATUTORY source and forbidden on an annual-edition one, so that every citation is bounded on exactly one axis: an edition-dated citation by the edition it names, a statutory one by the provision’s own effective span. Leaving it optional would let an author widen a statutory window with nothing able to check it.
valid_from – First date this citation is asserted to support the rule.
valid_to – Last date, inclusive. Both bounds are required: the span is the claim, and a defaulted one would assert grounding nobody typed.
- Parameters:
data (Any)
- source: CategoryCitationSource¶
- reference: str¶
- locator: str¶
- url: AnyHttpUrl¶
- quote: str¶
- grounding: CitationGrounding¶
- grounding_reason: str¶
- legal_ref: str | None¶
- valid_from: date¶
- valid_to: date¶
- property window: ValidityWindow¶
Return the closed span this citation is asserted over.
- Returns:
The
ValidityWindowthe two declared bounds describe.
- parse_http_url(value)[source]¶
Parse a string into a statically typed
AnyHttpUrl.- Parameters:
value (
str) – Raw HTTP / HTTPS URL string.- Return type:
AnyHttpUrl- Returns:
A validated
pydantic.AnyHttpUrl.
- class ProportionalityKind(token, *, is_full_deductible, is_usage_ratio, is_statutory_cap, requires_fixed_pct, is_non_deductible, requires_exclusive_use, _registry_validated=False)[source]¶
Bases:
objectOpaque proportionality token projected from the facts registry.
The token and its evaluator-role metadata are governed category facts. This object retains only the projected token plus mechanics-facing role flags; callers cannot mint a token without a validated registry projection.
- Parameters:
- Return type:
Self
- class StatutoryCapPeriod(token, *, is_per_person, _registry_validated=False)[source]¶
Bases:
objectOpaque statutory-cap period token projected from the facts registry.
- class StatutoryCapVariant(**data)[source]¶
Bases:
_ProportionalityStrictFrozenModelOne legally distinct cap inside a statutory-cap rule, selected by a condition.
The condition is what makes a variant a variant; the UNIT of the amount is incidental and the law uses both. RIRPF art. 9.A.3.a distinguishes con and sin pernocta and states DAILY amounts; LIRPF art. 30.2.5.a distinguishes a person with discapacidad from one without and states ANNUAL per-person amounts. Modelling only the daily unit is why the second provision could ship with one of its two limbs missing.
- Variables:
id – Stable identifier the calculation context selects on.
label – Translation key for the human-readable label.
statutory_cap_eur_per_day – A daily amount, for a rule capped per day.
statutory_cap_eur – An annual amount, for a rule capped per period. Exactly one of the two is set: a variant carrying both would leave the unit to the call site, and a variant carrying neither caps nothing.
- Parameters:
data (Any)
- id: str¶
- label: tr¶
- statutory_cap_eur_per_day: Decimal | None¶
- statutory_cap_eur: Decimal | None¶
- property is_per_day: bool¶
Return whether this variant’s amount is a daily one.
- Returns:
Truewhen the variant carriesstatutory_cap_eur_per_day.
- class StatutoryCapAmount(**data)[source]¶
Bases:
_ProportionalityStrictFrozenModelOne year-bounded value of a statutory cap whose amount the law varies.
Some statutory caps are fixed by the tax law and hold until it is amended – the 500 euro seguro de enfermedad limit of LIRPF art. 30.2.5.a is one. Others are fixed BY REFERENCE to a figure that moves every year: LIRPF art. 30.2.1 caps the deductible mutualidad alternativa premium at the cuota maxima por contingencias comunes established “en cada ejercicio economico” in the RETA.
A single
ProportionalityRule.statutory_cap_eurcannot express the second kind. Encoding one year’s figure – or worse, a round number close to none of them – silently applies it to every filing year, and the error is invisible because the value looks like a law-fixed constant. The registry shipped exactly that: a flat 15000 that matched no ejercicio at all.- Variables:
value – The cap amount in euros for this span.
valid_from – First date the amount applies.
valid_to – Last date, inclusive. Both bounds required and closed, for the reasons
ValidityWindowdocuments.
- Parameters:
data (Any)
- value: Decimal¶
- valid_from: date¶
- valid_to: date¶
- property window: ValidityWindow¶
Return the closed span this amount applies over.
- Returns:
The
ValidityWindowthe bounds describe.
- class ProportionalityRule(**data)[source]¶
Bases:
_ProportionalityStrictFrozenModelDeductibility and proportionality rule for one spending category.
- Variables:
kind – One of
ProportionalityKind.fixed_pct – Required when the projected kind declares a fixed-percent evaluator role; otherwise must be
None.default_ratio – Optional default usage ratio; only valid for usage-ratio kinds.
statutory_multiplier – Optional statutory factor applied on top of the operator-chosen usage ratio. Only valid for usage-ratio kinds. The canonical example is the LIRPF Art. 30.2 rule 5 (Ley 6/2017, BOE-A-2017-12544) 0.30 multiplier applied to suministros (utility) costs of the habitual vivienda when the operator deducts under estimacion directa:
effective_deductible_pct = operator_chosen_ratio * statutory_multiplier. WhenNoneno statutory factor is applied (equivalent toDecimal("1")); the operator’s chosen ratio is the effective deductible percentage.statutory_cap_eur_per_day – Daily statutory cap; only valid for a projected statutory-cap kind.
statutory_cap_eur – Generic statutory cap amount; only valid for a projected statutory-cap kind and must be paired with
statutory_cap_period.statutory_cap_period –
StatutoryCapPeriodthat the generic cap applies over; required whenstatutory_cap_euris set.statutory_cap_variants – Daily statutory caps selected by a legally relevant condition.
statutory_cap_schedule – Dated amounts for a cap the law re-fixes each ejercicio. Mutually exclusive with the fixed
statutory_cap_eur: a cap is either law-fixed or year-referenced, never both.citations – At least one
CategoryCitationproving the rule.notes – Translation key for the notes describing the rule.
- Parameters:
data (Any)
- kind: ProportionalityKind¶
- fixed_pct: UnitProportion | None¶
- default_ratio: UnitProportion | None¶
- statutory_multiplier: UnitProportion | None¶
- statutory_cap_eur_per_day: Decimal | None¶
- statutory_cap_eur: Decimal | None¶
- statutory_cap_period: StatutoryCapPeriod | None¶
- statutory_cap_variants: tuple[StatutoryCapVariant, ...]¶
- statutory_cap_schedule: tuple[StatutoryCapAmount, ...]¶
- citations: tuple[CategoryCitation, ...]¶
- notes: tr¶
- cap_amount_for_year(year)[source]¶
Return the statutory cap amount in force for
year.- Return type:
- Returns:
The scheduled amount covering
yearwhen the cap is year-referenced, the flatstatutory_cap_eurwhen it is law-fixed, andNonewhen this rule carries neither.- Parameters:
year (int)
- effective_usage_ratio(rule, chosen_ratio)[source]¶
Return the legally-effective deductible percentage for
chosen_ratio.Applies the rule’s
statutory_multiplieron top of the operator- chosen usage ratio. Only meaningful for usage-ratio kinds; raises when called on a non-usage-ratio rule (the caller is responsible for routing rules to the right evaluator).- Parameters:
rule (
ProportionalityRule) – AProportionalityRuleof a usage-ratio kind.chosen_ratio (
Decimal) – The operator’s stored usage ratio (typically derived from censooffice_m2 / total_m2for HOME_AREA kinds, or a personal-use proportion for PERSONAL kinds).
- Return type:
- Returns:
chosen_ratio * (rule.statutory_multiplier or Decimal("1")).- Raises:
CategoryValidationError – When
rule.kindis not a usage-ratio kind.