Source code for cadrumo.application.modelo.art52_advisory

"""Generic verification mechanics for registry-selected Art.52 declarations."""

from __future__ import annotations

from collections.abc import Mapping
from datetime import date
from decimal import Decimal
from typing import TYPE_CHECKING

from ...core.casilla_id import CasillaId
from ...core.time.clock import today_madrid
from ...domain.calculations.registry.facts.resolution import MappingFactQuery, ResolvedMappingFact
from ...domain.calculations.registry.schema_base import DateAxis
from ...domain.modelos.errors import ModeloError
from ...domain.modelos.modelo_fact_context import ModeloFactResolutionContext
from ...domain.modelos.verification_report import (
    ModeloVerificationFinding,
    ModeloVerificationFindingKind,
    ModeloVerificationFindingSeverity,
)
from .semantic_role_resolution import AmbiguousSemanticRoleCasillaError, casilla_id_for_unique_revision_semantic_role

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

__all__ = ["art52_reduccion_advisory_finding"]


_ART52_REDUCCION_TOTAL_ROLE = "irpf_reduccion_prevision_social_total"
_ART52_APORTACIONES_TRABAJADOR_CON_CONTRIBUCION_EMPRESARIAL_ROLE = (
    "irpf_red_prevision_social_aportaciones_trabajador_con_contribucion_empresarial"
)
_ART52_CONTRIBUCIONES_EMPRESARIALES_ROLE = "irpf_red_prevision_social_contribuciones_empresariales_excepto_scd"
_ART52_APORTACIONES_AUTONOMOS_EMPRESARIOS_ROLE = "irpf_red_prevision_social_aportaciones_autonomos_empresarios"
_ART52_INDIVIDUAL_SUBLIMIT_FACT_ID = "lirpf-art-52-individual-contribution-sublimit"


# Registry authority: selected Art.52 declarations are consumed through the pinned operation and dated mapping fact
def _registry_art52_declaration(
    revision: object,
    *,
    operation: PinnedAuthorityOperation,
    modelo: str,
    effective_date: date,
) -> ResolvedMappingFact:
    """Resolve selected model context and the dated Art.52 mapping fact."""
    del modelo
    resolved = operation.resolve_governed_fact(
        MappingFactQuery(
            fact_id="lirpf-art-52-reduction-verification-mapping",
            date_axis=DateAxis.FILING_PERIOD,
            effective_date=effective_date,
        ),
    )
    if not isinstance(resolved, ResolvedMappingFact):
        raise TypeError("selected Art.52 declaration must resolve as a mapping fact")
    del revision
    return resolved


[docs] def art52_reduccion_advisory_finding( revision: object, casilla_values: Mapping[CasillaId, Decimal], *, operation: PinnedAuthorityOperation, modelo: str, ) -> ModeloVerificationFinding | None: """Warn when individual previdencia-social reduction exceeds its sub-limit.""" try: reduccion_id = casilla_id_for_unique_revision_semantic_role( revision, _ART52_REDUCCION_TOTAL_ROLE, modelo_id=modelo, ) trabajador_con_contribucion_id = casilla_id_for_unique_revision_semantic_role( revision, _ART52_APORTACIONES_TRABAJADOR_CON_CONTRIBUCION_EMPRESARIAL_ROLE, modelo_id=modelo, ) empresarial_id = casilla_id_for_unique_revision_semantic_role( revision, _ART52_CONTRIBUCIONES_EMPRESARIALES_ROLE, modelo_id=modelo, ) autonomos_empresarios_id = casilla_id_for_unique_revision_semantic_role( revision, _ART52_APORTACIONES_AUTONOMOS_EMPRESARIOS_ROLE, modelo_id=modelo, ) except AmbiguousSemanticRoleCasillaError as exc: raise ModeloError(str(exc), context=exc.ambiguity.context()) from exc if ( reduccion_id is None or trabajador_con_contribucion_id is None or empresarial_id is None or autonomos_empresarios_id is None ): return None reduccion_value = casilla_values.get(reduccion_id, Decimal(0)) trabajador_con_contribucion_value = casilla_values.get(trabajador_con_contribucion_id, Decimal(0)) empresarial_value = casilla_values.get(empresarial_id, Decimal(0)) autonomos_empresarios_value = casilla_values.get(autonomos_empresarios_id, Decimal(0)) effective_date = getattr(revision, "valid_to", None) or today_madrid() _registry_art52_declaration( revision, operation=operation, modelo=modelo, effective_date=effective_date, ) context = ModeloFactResolutionContext( authority=operation, filing_period=effective_date, devengo_date=effective_date, ) resolved_sublimit = context.resolved_decimal(_ART52_INDIVIDUAL_SUBLIMIT_FACT_ID) sublimit = resolved_sublimit.payload.value if not isinstance(sublimit, Decimal): raise ModeloError("Art. 52 individual sublimit fact must resolve to a Decimal") if ( reduccion_value > sublimit and trabajador_con_contribucion_value == Decimal(0) and empresarial_value == Decimal(0) and autonomos_empresarios_value == Decimal(0) ): return ModeloVerificationFinding( kind=ModeloVerificationFindingKind.ADVISORY, severity=ModeloVerificationFindingSeverity.WARNING, casilla_id=reduccion_id, message_locale_key="application.modelo.findings.art52_reduccion_individual_sublimit_possible", message_facts={ "reduccion_id": reduccion_id, "reduccion_value": reduccion_value, "sublimit": sublimit, }, legal_refs=resolved_sublimit.legal_refs, source_refs=resolved_sublimit.source_refs, ) return None