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

"""Typed projection of the invoice legal-classification vocabulary (0141)."""

from __future__ import annotations

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

from ....core.time.clock import today_madrid
from ...invoices.enums import InvoiceClass, InvoiceOperationDateRole
from .errors import RegistryValidationError
from .facts.resolution import MappingFactQuery, ResolvedMappingFact, required_mapping_entry, unique_mapping_tokens
from .governed_fact_scope import GovernedFactSource, cache_governed_projection, governed_facts_in_scope
from .schema_base import DateAxis

_ENTRY_SUBJECT: Final = "invoice legal-classification catalogue"

if TYPE_CHECKING:
    from .authority import ValidatedRegistryAuthority


_FACT_ID = "invoice-legal-classification-catalogue"
_INVOICE_CLASS_ORDER_KEY = "invoice_class.order"
_INVOICE_CLASS_PREFIX = "invoice_class."
_INVOICE_CLASS_ORDINARIA_KEY = "invoice_class.ordinaria_token"
_INVOICE_CLASS_SIMPLIFICADA_KEY = "invoice_class.simplificada_token"
_INVOICE_CLASS_RECTIFICATIVA_KEY = "invoice_class.rectificativa_token"
_OPERATION_DATE_ROLE_ORDER_KEY = "invoice_operation_date_role.order"
_OPERATION_DATE_ROLE_PREFIX = "invoice_operation_date_role."
_OPERATION_DATE_ROLE_PERFORMED_KEY = "invoice_operation_date_role.operation_performed_token"
_OPERATION_DATE_ROLE_ADVANCE_KEY = "invoice_operation_date_role.advance_payment_received_token"


[docs] @dataclass(frozen=True, slots=True) class InvoiceClassDefinition: """One registry-declared invoice class and its legal semantics.""" token: InvoiceClass description: str legal_refs: tuple[str, ...]
[docs] @dataclass(frozen=True, slots=True) class InvoiceOperationDateRoleDefinition: """One registry-declared operation-date role and its legal semantics.""" token: InvoiceOperationDateRole description: str legal_refs: tuple[str, ...]
[docs] @dataclass(frozen=True, slots=True) class InvoiceLegalClassificationCatalogue: """Complete dated projection of fact 0141.""" declarations: Mapping[str, str] invoice_classes: tuple[InvoiceClassDefinition, ...] operation_date_roles: tuple[InvoiceOperationDateRoleDefinition, ...] ordinaria_token: InvoiceClass simplificada_token: InvoiceClass rectificativa_token: InvoiceClass operation_performed_token: InvoiceOperationDateRole advance_payment_received_token: InvoiceOperationDateRole @property def invoice_class_choices(self) -> tuple[InvoiceClass, ...]: """Return invoice classes in the registry-authored order.""" return tuple(item.token for item in self.invoice_classes) @property def operation_date_role_choices(self) -> tuple[InvoiceOperationDateRole, ...]: """Return operation-date roles in the registry-authored order.""" return tuple(item.token for item in self.operation_date_roles)
[docs] def require_invoice_class(self, value: object) -> InvoiceClass: """Project an invoice class only when the selected fact declares it.""" accepted = ", ".join(str(choice) for choice in self.invoice_class_choices) if isinstance(value, InvoiceClass): token = value elif isinstance(value, str): raw = value.strip() if not raw: raise RegistryValidationError( f"invoice class must be a non-empty string token; accepted values: {accepted}", ) try: token = InvoiceClass.from_registry(raw) except (TypeError, ValueError) as exc: raise RegistryValidationError( f"invoice class must be a registry token; accepted values: {accepted}", ) from exc else: raise RegistryValidationError(f"invoice class must be a string token; accepted values: {accepted}") if token not in self.invoice_class_choices: raise RegistryValidationError( f"invoice class {str(token)!r} is not declared by fact {_FACT_ID!r}; accepted values: {accepted}", ) return token
[docs] def require_operation_date_role(self, value: object) -> InvoiceOperationDateRole: """Project an operation-date role only when the selected fact declares it.""" if isinstance(value, InvoiceOperationDateRole): token = value elif isinstance(value, str): raw = value.strip() if not raw: raise RegistryValidationError("invoice operation-date role must be a non-empty string token") try: token = InvoiceOperationDateRole.from_registry(raw) except (TypeError, ValueError) as exc: raise RegistryValidationError( "invoice operation-date role must be a non-empty string token", ) from exc else: raise RegistryValidationError("invoice operation-date role must be a string token") if token not in self.operation_date_role_choices: raise RegistryValidationError( f"invoice operation-date role {str(token)!r} is not declared by fact {_FACT_ID!r}", ) return token
def _legal_refs(entries: Mapping[str, str], key: str) -> tuple[str, ...]: values = tuple( token.strip() for token in required_mapping_entry(entries, key, subject=_ENTRY_SUBJECT).split(",") if token.strip() ) if not values or len(values) != len(set(values)): raise RegistryValidationError( f"invoice legal-classification catalogue {key!r} must contain unique legal references", ) return values def _mapping_entries(resolved: ResolvedMappingFact) -> Mapping[str, str]: entries: dict[str, str] = {} for entry in resolved.payload.entries: if not isinstance(entry.key, str) or not isinstance(entry.value, str): raise RegistryValidationError( "invoice legal-classification catalogue entries must be string-to-string", ) if entry.key in entries: raise RegistryValidationError( f"duplicate invoice legal-classification catalogue key {entry.key!r}", ) entries[entry.key] = entry.value return MappingProxyType(entries) def _resolve_entries(*, effective_date: date, authority: GovernedFactSource) -> Mapping[str, str]: resolved = authority.resolve_governed_fact( MappingFactQuery( fact_id=_FACT_ID, date_axis=DateAxis.FILING_PERIOD, effective_date=effective_date, ), ) if not isinstance(resolved, ResolvedMappingFact): raise RegistryValidationError("invoice legal-classification catalogue must resolve as a mapping fact") return _mapping_entries(resolved) @cache_governed_projection(maxsize=64) def _bundled_mapping_entries(effective_date: date) -> Mapping[str, str]: del effective_date raise RegistryValidationError( "invoice legal-classification catalogue requires an explicit authority operation or scope" ) def _selected_mapping_entries( *, effective_date: date, authority: GovernedFactSource | None, ) -> Mapping[str, str]: selected = authority or governed_facts_in_scope() if selected is None: return _bundled_mapping_entries(effective_date) return _resolve_entries(effective_date=effective_date, authority=selected) def _pointer( entries: Mapping[str, str], key: str, choices: tuple[str, ...], *, label: str, ) -> str: value = required_mapping_entry(entries, key, subject=_ENTRY_SUBJECT) if value not in choices: raise RegistryValidationError(f"invoice legal-classification {label} pointer {key!r} is undeclared") return value def _catalogue(entries: Mapping[str, str]) -> InvoiceLegalClassificationCatalogue: invoice_classes: list[InvoiceClassDefinition] = [] invoice_class_order = unique_mapping_tokens(entries, _INVOICE_CLASS_ORDER_KEY, subject=_ENTRY_SUBJECT) for raw_token in invoice_class_order: token = InvoiceClass.from_registry(raw_token) prefix = f"{_INVOICE_CLASS_PREFIX}{raw_token}." if required_mapping_entry(entries, f"{prefix}value", subject=_ENTRY_SUBJECT) != raw_token: raise RegistryValidationError(f"invoice class {raw_token!r} declares a mismatched value") invoice_classes.append( InvoiceClassDefinition( token=token, description=required_mapping_entry(entries, f"{prefix}description", subject=_ENTRY_SUBJECT), legal_refs=_legal_refs(entries, f"{prefix}legal_refs"), ), ) if len({item.token for item in invoice_classes}) != len(invoice_classes): raise RegistryValidationError("invoice legal-classification catalogue contains duplicate invoice classes") operation_date_roles: list[InvoiceOperationDateRoleDefinition] = [] operation_date_role_order = unique_mapping_tokens(entries, _OPERATION_DATE_ROLE_ORDER_KEY, subject=_ENTRY_SUBJECT) for raw_token in operation_date_role_order: token = InvoiceOperationDateRole.from_registry(raw_token) prefix = f"{_OPERATION_DATE_ROLE_PREFIX}{raw_token}." if required_mapping_entry(entries, f"{prefix}value", subject=_ENTRY_SUBJECT) != raw_token: raise RegistryValidationError(f"invoice operation-date role {raw_token!r} declares a mismatched value") operation_date_roles.append( InvoiceOperationDateRoleDefinition( token=token, description=required_mapping_entry(entries, f"{prefix}description", subject=_ENTRY_SUBJECT), legal_refs=_legal_refs(entries, f"{prefix}legal_refs"), ), ) if len({item.token for item in operation_date_roles}) != len(operation_date_roles): raise RegistryValidationError( "invoice legal-classification catalogue contains duplicate operation-date roles", ) invoice_class_choices = tuple(item.token.value for item in invoice_classes) operation_date_role_choices = tuple(item.token.value for item in operation_date_roles) ordinaria = _pointer( entries, _INVOICE_CLASS_ORDINARIA_KEY, invoice_class_choices, label="invoice-class", ) simplificada = _pointer( entries, _INVOICE_CLASS_SIMPLIFICADA_KEY, invoice_class_choices, label="invoice-class", ) rectificativa = _pointer( entries, _INVOICE_CLASS_RECTIFICATIVA_KEY, invoice_class_choices, label="invoice-class", ) if len({ordinaria, simplificada, rectificativa}) != 3: raise RegistryValidationError("invoice legal-classification class pointers must be distinct") operation_performed = _pointer( entries, _OPERATION_DATE_ROLE_PERFORMED_KEY, operation_date_role_choices, label="operation-date-role", ) advance_payment_received = _pointer( entries, _OPERATION_DATE_ROLE_ADVANCE_KEY, operation_date_role_choices, label="operation-date-role", ) if operation_performed == advance_payment_received: raise RegistryValidationError("invoice legal-classification role pointers must be distinct") return InvoiceLegalClassificationCatalogue( declarations=entries, invoice_classes=tuple(invoice_classes), operation_date_roles=tuple(operation_date_roles), ordinaria_token=InvoiceClass.from_registry(ordinaria), simplificada_token=InvoiceClass.from_registry(simplificada), rectificativa_token=InvoiceClass.from_registry(rectificativa), operation_performed_token=InvoiceOperationDateRole.from_registry(operation_performed), advance_payment_received_token=InvoiceOperationDateRole.from_registry(advance_payment_received), ) @cache_governed_projection(maxsize=64) def _bundled_catalogue(effective_date: date) -> InvoiceLegalClassificationCatalogue: return _catalogue(_bundled_mapping_entries(effective_date))
[docs] def require_invoice_class( value: object, *, effective_date: date | None = None, authority: ValidatedRegistryAuthority | None = None, ) -> InvoiceClass: """Return an invoice-class token only when fact 0141 declares it. Core types: :class:`~cadrumo.domain.calculations.registry.authority.ValidatedRegistryAuthority`. """ return resolve_invoice_legal_classification_catalogue( effective_date=effective_date, authority=authority, ).require_invoice_class(value)
[docs] def require_invoice_operation_date_role( value: object, *, effective_date: date | None = None, authority: ValidatedRegistryAuthority | None = None, ) -> InvoiceOperationDateRole: """Return an operation-date role only when fact 0141 declares it. Core types: :class:`~cadrumo.domain.calculations.registry.authority.ValidatedRegistryAuthority`. """ return resolve_invoice_legal_classification_catalogue( effective_date=effective_date, authority=authority, ).require_operation_date_role(value)
[docs] def invoice_class_ordinaria( *, effective_date: date | None = None, authority: ValidatedRegistryAuthority | None = None, ) -> InvoiceClass: """Return the registry-declared ordinary invoice-class token. Core types: :class:`~cadrumo.domain.calculations.registry.authority.ValidatedRegistryAuthority`. """ return resolve_invoice_legal_classification_catalogue( effective_date=effective_date, authority=authority, ).ordinaria_token
[docs] def invoice_class_simplificada( *, effective_date: date | None = None, authority: ValidatedRegistryAuthority | None = None, ) -> InvoiceClass: """Return the registry-declared simplified invoice-class token. Core types: :class:`~cadrumo.domain.calculations.registry.authority.ValidatedRegistryAuthority`. """ return resolve_invoice_legal_classification_catalogue( effective_date=effective_date, authority=authority, ).simplificada_token
[docs] def invoice_class_rectificativa( *, effective_date: date | None = None, authority: ValidatedRegistryAuthority | None = None, ) -> InvoiceClass: """Return the registry-declared corrective invoice-class token. Core types: :class:`~cadrumo.domain.calculations.registry.authority.ValidatedRegistryAuthority`. """ return resolve_invoice_legal_classification_catalogue( effective_date=effective_date, authority=authority, ).rectificativa_token
[docs] def operation_performed_role( *, effective_date: date | None = None, authority: ValidatedRegistryAuthority | None = None, ) -> InvoiceOperationDateRole: """Return the registry-declared performed-operation date role. Core types: :class:`~cadrumo.domain.calculations.registry.authority.ValidatedRegistryAuthority`. """ return resolve_invoice_legal_classification_catalogue( effective_date=effective_date, authority=authority, ).operation_performed_token
[docs] def advance_payment_received_role( *, effective_date: date | None = None, authority: ValidatedRegistryAuthority | None = None, ) -> InvoiceOperationDateRole: """Return the registry-declared advance-payment date role. Core types: :class:`~cadrumo.domain.calculations.registry.authority.ValidatedRegistryAuthority`. """ return resolve_invoice_legal_classification_catalogue( effective_date=effective_date, authority=authority, ).advance_payment_received_token
__all__ = [ "InvoiceClassDefinition", "InvoiceLegalClassificationCatalogue", "InvoiceOperationDateRoleDefinition", "advance_payment_received_role", "invoice_class_ordinaria", "invoice_class_rectificativa", "invoice_class_simplificada", "operation_performed_role", "require_invoice_class", "require_invoice_operation_date_role", "resolve_invoice_legal_classification_catalogue", ]