Source code for cadrumo.domain.calculations.registry.prorrata_exclusions

"""Typed projection of the registry-owned LIVA art. 104.Tres vocabulary."""

from __future__ import annotations

from collections.abc import Mapping
from dataclasses import dataclass
from datetime import date
from types import MappingProxyType
from typing import Final

from ....core.prorrata_exclusions import Art104TresExclusion
from ....core.time.clock import today_madrid
from .errors import RegistryValidationError
from .facts.resolution import MappingFactQuery, ResolvedMappingFact, required_mapping_entry, unique_mapping_tokens
from .governed_fact_scope import GovernedFactSource, governed_facts_in_scope
from .schema_base import DateAxis

_ENTRY_SUBJECT: Final = "art. 104.Tres mapping"
_UNIQUE_TOKENS_REQUIREMENT: Final = "must declare unique non-empty tokens"

_FACT_ID = "renta-iva-deduction-ratio-policy"
_ORDER_KEY = "art104_tres.exclusion_order"
_OPERATOR_KEY = "art104_tres.operator_declared"
_AUTO_KEY = "art104_tres.auto_derived"


[docs] @dataclass(frozen=True, slots=True) class Art104TresExclusionDefinition: """One registry-projected exclusion token and its legal metadata.""" token: Art104TresExclusion article: str legal_ref: str description: str kind: str
[docs] @dataclass(frozen=True, slots=True) class Art104TresExclusionCatalogue: """Typed projection of the dated art. 104.Tres mapping fact.""" definitions: tuple[Art104TresExclusionDefinition, ...] @property def all_exclusions(self) -> frozenset[Art104TresExclusion]: """Return every registry-declared exclusion token.""" return frozenset(definition.token for definition in self.definitions) @property def operator_declared(self) -> frozenset[Art104TresExclusion]: """Return tokens the registry permits an operator to declare.""" return frozenset(definition.token for definition in self.definitions if definition.kind == "operator_declared") @property def auto_derived(self) -> frozenset[Art104TresExclusion]: """Return tokens the registry marks as structurally derived.""" return frozenset(definition.token for definition in self.definitions if definition.kind == "auto_derived")
[docs] def require(self, value: object) -> Art104TresExclusion: """Validate one opaque token against this projection, failing closed.""" if isinstance(value, Art104TresExclusion): token = value elif isinstance(value, str): token = Art104TresExclusion(value.strip()) else: raise RegistryValidationError("art. 104.Tres exclusion must be a string token") if not str(token): raise RegistryValidationError("art. 104.Tres exclusion token must not be blank") if token not in self.all_exclusions: raise RegistryValidationError( f"art. 104.Tres exclusion {str(token)!r} is not declared by the facts registry", ) return token
def _mapping_entries(resolved: ResolvedMappingFact) -> Mapping[str, str]: """Narrow a resolved mapping payload to a unique string-to-string map.""" entries: dict[str, str] = {} for entry in resolved.payload.entries: if not isinstance(entry.key, str) or not isinstance(entry.value, str): raise RegistryValidationError("art. 104.Tres mapping entries must be string-to-string") if entry.key in entries: raise RegistryValidationError(f"duplicate art. 104.Tres mapping key {entry.key!r}") entries[entry.key] = entry.value return MappingProxyType(entries)
[docs] def resolve_art104_tres_exclusion_catalogue( *, effective_date: date | None = None, authority: GovernedFactSource | None = None, ) -> Art104TresExclusionCatalogue: """Resolve the complete art. 104.Tres catalogue through facts authority.""" authority = authority or governed_facts_in_scope() if authority is None: raise RegistryValidationError("prorrata exclusion catalogue requires an explicit authority operation or scope") resolved = authority.resolve_governed_fact( MappingFactQuery( fact_id=_FACT_ID, date_axis=DateAxis.FILING_PERIOD, effective_date=effective_date or today_madrid(), ), ) if not isinstance(resolved, ResolvedMappingFact): raise RegistryValidationError("Renta IVA ratio policy must resolve as a mapping fact") entries = _mapping_entries(resolved) ordered_tokens = unique_mapping_tokens( entries, _ORDER_KEY, subject=_ENTRY_SUBJECT, requirement=_UNIQUE_TOKENS_REQUIREMENT ) operator_tokens = unique_mapping_tokens( entries, _OPERATOR_KEY, subject=_ENTRY_SUBJECT, requirement=_UNIQUE_TOKENS_REQUIREMENT ) auto_tokens = unique_mapping_tokens( entries, _AUTO_KEY, subject=_ENTRY_SUBJECT, requirement=_UNIQUE_TOKENS_REQUIREMENT ) ordered = tuple(Art104TresExclusion(token) for token in ordered_tokens) operator = frozenset(Art104TresExclusion(token) for token in operator_tokens) auto = frozenset(Art104TresExclusion(token) for token in auto_tokens) all_tokens = frozenset(ordered) if operator & auto or operator | auto != all_tokens: raise RegistryValidationError( "art. 104.Tres operator and auto-derived partitions must be disjoint and cover the exclusion order", ) definitions: list[Art104TresExclusionDefinition] = [] for token in ordered: prefix = f"art104_tres.exclusion.{token}" kind = required_mapping_entry(entries, f"{prefix}.kind", subject=_ENTRY_SUBJECT) expected_kind = "operator_declared" if token in operator else "auto_derived" if kind != expected_kind: raise RegistryValidationError( f"art. 104.Tres token {str(token)!r} has kind {kind!r}, expected {expected_kind!r}", ) definitions.append( Art104TresExclusionDefinition( token=token, article=required_mapping_entry(entries, f"{prefix}.article", subject=_ENTRY_SUBJECT), legal_ref=required_mapping_entry(entries, f"{prefix}.legal_ref", subject=_ENTRY_SUBJECT), description=required_mapping_entry(entries, f"{prefix}.description", subject=_ENTRY_SUBJECT), kind=kind, ), ) return Art104TresExclusionCatalogue(definitions=tuple(definitions))
[docs] def require_art104_tres_exclusion( value: object, *, effective_date: date | None = None, authority: GovernedFactSource | None = None, ) -> Art104TresExclusion: """Return one registry-declared opaque token or refuse it.""" catalogue = resolve_art104_tres_exclusion_catalogue( effective_date=effective_date, authority=authority, ) return catalogue.require(value)
__all__ = [ "Art104TresExclusionCatalogue", "Art104TresExclusionDefinition", "require_art104_tres_exclusion", "resolve_art104_tres_exclusion_catalogue", ]