Source code for cadrumo.domain.iva.place_of_supply

"""Which provision places an operation, and whether it fixes the supply's nature.

The classification rules decide WHERE an operation is located and therefore how
it is treated. Until now they carried none of the law that establishes those
decisions: the predicates encoded the conditions in Python and the regulatory
content -- which article places the operation, and whether that article speaks to
goods or to services -- existed nowhere. A predicate is logic and belongs in
Python; the provision behind it is regulatory content, versioned by filing year,
and belongs in the registry.

**The goods/services fork is why this is not the per-category table.** LIVA art.
68 locates *entregas de bienes*; arts. 69 and 70 locate *prestaciones de
servicios*. On a cross-border branch the same two parties, the same amount and
the same day are placed by different articles and can reach different treatments.
Several rules share one category while resting on different provisions -- three
separate reverse-charge rules all resolve to ``DOMESTIC_REVERSE_CHARGE`` -- so a
table keyed by category cannot express what establishes each one.

**An absent nature is a finding, not a blank.** A rule whose provisions do not fix
whether goods or services were supplied omits ``supply_nature``, and that omission
means the articles are silent. It never means the row is unfinished. The worked
case is the Union scheme: LIVA art. 163 unvicies reaches "presten servicios"
*and* "ventas a distancia intracomunitarias de bienes", so citing it alone
determines nothing -- the goods rules that ride it are fixed by art. 68 instead,
and the services rule by art. 69.

**The domestic rules omit it deliberately.** Both placement rules put a domestic
operation in the same territory, so its treatment is settled by the rate tier
rather than by what was supplied. Demanding the distinction there would refuse
invoices for a fact their own treatment ignores, which is the laziness property
the supply-nature axis exists to keep.

**Refusal rather than a guess.** :func:`place_of_supply_rule` raises for a rule
with no row, and carries a ``supply_nature`` of ``None`` for a rule whose
provisions are silent. Neither substitutes a default; a caller that cannot
determine the placement is expected to say so, and the two conditions stay
distinguishable -- an ungrounded rule raises where a silent one answers.

See Also:
    :class:`~domain.iva.schema.IvaCategory`
        The treatments these rules resolve into.
    :class:`~domain.iva.supply_nature.SupplyNature`
        The axis a cross-border rule's provisions may or may not fix.
"""

from __future__ import annotations

from collections.abc import Mapping
from datetime import date
from types import MappingProxyType
from typing import TYPE_CHECKING, TypeGuard

from pydantic import BaseModel, Field, model_validator

from ...core.errors.hierarchy import pydantic_validation_boundary
from ...core.models import STRICT_FROZEN_CONFIG
from ...core.validity_window import ValidityWindow, years_covered_by_every_group
from .errors import IvaCatalogueError
from .supply_nature import SupplyNature

if TYPE_CHECKING:
    from ..calculations.registry.authority import PinnedAuthorityOperation


def _is_object_mapping(value: object) -> TypeGuard[Mapping[object, object]]:
    """Narrow one runtime component to an object-keyed mapping before validation."""
    return isinstance(value, Mapping)


__all__ = [
    "IvaPlaceOfSupplyRule",
    "load_place_of_supply_table",
    "place_of_supply_rule",
    "place_of_supply_years",
]


[docs] class IvaPlaceOfSupplyRule(BaseModel): """The legal grounding of one classification rule's placement. Attributes: rule_id: The classification rule this grounds, matching the id the decision table declares. A parity gate proves the two sets are equal in both directions, so a rule cannot ship ungrounded and a row cannot outlive its rule. supply_nature: The nature the cited provisions fix, or ``None`` when they are silent on it. ``None`` is a statement about the statute. legal_references: Every provision the rule rests on, in reading order. At least one. establishing_reference: The one provision that establishes the treatment, which must also appear in :attr:`legal_references`. Citing the whole reading list leaves unanswered which article actually decides. notes: Reviewer prose in Spanish, matching the sibling catalogue. valid_from: First date this grounding is asserted to hold. Required on a grounded row, absent on a legal-basis-exempt one. valid_to: Last date, inclusive. Same rule. The window attaches to the RULE rather than to a citation because the rule is the grounding-bearing row here: its provisions are bare reference ids with no table of their own to carry a span. The bounds are closed and a gate holds them inside the intersection of the cited provisions' effective spans. """ model_config = STRICT_FROZEN_CONFIG rule_id: str = Field(min_length=1) supply_nature: SupplyNature | None = None legal_references: tuple[str, ...] = () establishing_reference: str = "" notes: str = "" legal_basis_exempt: bool = False valid_from: date | None = None valid_to: date | None = None @property def window(self) -> ValidityWindow | None: """Return the span this grounding is asserted over, or ``None`` when exempt. Returns: The declared :class:`~core.validity_window.ValidityWindow`, or ``None`` for a legal-basis-exempt row, which grounds nothing and so is asserted over nothing. """ if self.valid_from is None or self.valid_to is None: return None return ValidityWindow(valid_from=self.valid_from, valid_to=self.valid_to) @model_validator(mode="after") @pydantic_validation_boundary def _each_row_carries_exactly_what_its_kind_claims(self) -> IvaPlaceOfSupplyRule: """Refuse a row whose citations do not match the kind of rule it grounds. A grounded row must cite at least one provision and must name a deciding one among them; the two fields would otherwise drift apart silently, leaving a row that calls an article decisive while reading a different set. An exempt row must cite nothing at all, which is what keeps the exemption from becoming a place to park a half-filled row. A rule that codifies no tax treatment cannot be grounded, and demanding an article for it would manufacture the appearance of a legal basis it has none of by design. That is the same distinction the sibling regulation table draws, and it is narrow: it is not an escape for a rule that DOES need grounding and has not been given it yet. """ if self.legal_basis_exempt: _validate_legal_basis_exempt_row(self) else: _validate_grounded_row(self) return self
def _validate_legal_basis_exempt_row(rule: IvaPlaceOfSupplyRule) -> None: """Require an exempt row to carry no grounding claim at all.""" if rule.legal_references or rule.establishing_reference: raise IvaCatalogueError( f"place-of-supply rule {rule.rule_id!r}: a legal-basis-exempt row cites no provision, " "because it codifies no tax treatment to ground", ) if rule.supply_nature is not None: raise IvaCatalogueError( f"place-of-supply rule {rule.rule_id!r}: a legal-basis-exempt row fixes no supply nature", ) if rule.valid_from is not None or rule.valid_to is not None: raise IvaCatalogueError( f"place-of-supply rule {rule.rule_id!r}: a legal-basis-exempt row declares no validity " "window, because a row that grounds nothing is asserted over nothing", ) def _validate_grounded_row(rule: IvaPlaceOfSupplyRule) -> None: """Require a grounded row to carry a bounded, internally cited provision.""" if rule.valid_from is None or rule.valid_to is None: raise IvaCatalogueError( f"place-of-supply rule {rule.rule_id!r}: a grounded row must declare both validity bounds; " "an absent bound would read as effective from the beginning of time or until further " "notice, and neither is a claim anybody made", ) # Constructing the window is the validation: an inverted span covers no # year and would make the rule vanish from every filing year instead of # failing where it was written. _ = rule.window if not rule.legal_references: raise IvaCatalogueError( f"place-of-supply rule {rule.rule_id!r}: must cite the provision that establishes its placement", ) if rule.establishing_reference not in rule.legal_references: raise IvaCatalogueError( f"place-of-supply rule {rule.rule_id!r}: establishing_reference " f"{rule.establishing_reference!r} is not among its legal_references", )
[docs] def load_place_of_supply_table( *, operation: PinnedAuthorityOperation, ) -> Mapping[str, IvaPlaceOfSupplyRule]: """Adapt the published place-of-supply grounding table. Returns: Rules keyed by rule id, each carrying the span it is asserted over. """ from ..calculations.registry.runtime_catalogues import PublishedIvaPlaceOfSupplyRule loaded = operation.runtime_catalogue("iva_place_of_supply") if not _is_object_mapping(loaded): raise IvaCatalogueError("indexed authority place-of-supply component has an invalid shape") published_values: list[tuple[str, PublishedIvaPlaceOfSupplyRule]] = [] for rule_id, published in loaded.items(): if not isinstance(rule_id, str) or not isinstance(published, PublishedIvaPlaceOfSupplyRule): raise IvaCatalogueError("indexed authority place-of-supply component has an invalid shape") published_values.append((rule_id, published)) return MappingProxyType( { rule_id: IvaPlaceOfSupplyRule.model_validate( { "rule_id": published.rule_id, "supply_nature": ( SupplyNature(published.supply_nature) if published.supply_nature is not None else None ), "legal_references": published.legal_references, "establishing_reference": published.establishing_reference, "notes": published.notes, "legal_basis_exempt": published.legal_basis_exempt, "valid_from": published.valid_from, "valid_to": published.valid_to, } ) for rule_id, published in published_values } )
[docs] def place_of_supply_years(*, operation: PinnedAuthorityOperation) -> frozenset[int]: """Return every filing year the table can be resolved for. A year counts only when EVERY grounded rule is asserted over it. A rule whose provisions stop earlier stops the table, because a classification answered from a rule that does not apply is worse than a refusal. Legal-basis-exempt rows ground nothing and are excluded rather than treated as covering everything or nothing. Returns: The derived set of resolvable filing years. """ windows = [ rule.window for rule in load_place_of_supply_table(operation=operation).values() if rule.window is not None ] return years_covered_by_every_group([window] for window in windows)
[docs] def place_of_supply_rule( rule_id: str, *, on: date, operation: PinnedAuthorityOperation, projected_year: int, ) -> IvaPlaceOfSupplyRule: """Return the grounding for ``rule_id`` in the filing year of ``on``. Args: rule_id: The classification rule's declared id. on: A date in the filing year whose table applies. operation: The pinned authority operation supplying the place-of-supply table. projected_year: The filing year whose grounded rules are required. Returns: :class:`IvaPlaceOfSupplyRule`: The grounding row. Raises: IvaCatalogueError: When the table grounds no such year, or carries no row for the rule, or the row is not asserted over that year. Raising is correct rather than returning a permissive default: an ungrounded placement has no provision behind it, and answering anyway would manufacture one. """ rules = load_place_of_supply_table(operation=operation) grounded_years = place_of_supply_years(operation=operation) if projected_year not in grounded_years: raise IvaCatalogueError( f"no place-of-supply grounding for year={on.year}; the table grounds " f"{sorted(grounded_years)}. Ground the year against BOE or AEAT -- never widen a rule's " "window to admit it.", ) rule = rules.get(rule_id) if rule is None or (rule.window is not None and not rule.window.covers_year(projected_year)): raise IvaCatalogueError( f"place-of-supply rule {rule_id!r} is not grounded for year={on.year}; " "every classification rule must cite the provision that establishes its placement", ) return rule