Source code for aeat.application.modelo._work_create_policy

"""Application policies for creating modelo work units.

The work-create command runs these guards before provisioning a work unit. They
cover three distinct policy surfaces: modelo codes that are still stub-only in
the local work-unit flow, modelos that do not apply to the active profile's
taxpayer model, and active profiles whose CCAA points at a foral tax regime.

Stub-only checks are locale-key lookups over :class:`Modelo` members and a few
registry-known numeric forms. Applicability checks derive a profile projection
from the active workflow state, then ask the registry-owned applicability rules
whether the requested modelo is excluded. The foral check delegates to the
canonical tax-region parser so the CLI renders the same refusal text as profile
setup. Once these CLI policy guards pass,
:func:`aeat.application.modelo.create_work_unit` performs the application
readiness and registry revision checks before inserting the
:class:`aeat.domain.modelos.WorkUnit`.

See Also:
    :mod:`aeat.entrypoints.cli._modelo_work_lifecycle_cli`:
        Calls these guards from ``app modelo work create``.
    :func:`aeat.domain.calculations.registry.derive_modelo_applicability`:
        Registry-owned modelo applicability classifier used by this guard.
    :func:`aeat.domain.contribuyente.parse_tax_region`:
        Canonical CCAA parser that raises foral-regime refusals.
"""

from __future__ import annotations

from dataclasses import dataclass

from pydantic import ValidationError

from ...core import Modelo
from ...core.config import load_settings

STUB_MODELO_LOCALE_KEYS: dict[str, str] = {
    Modelo.M151.value: "cli.app.modelo.work.create_stub_modelo_151_refused",
    Modelo.M210.value: "cli.app.modelo.work.create_stub_modelo_210_refused",
    "600": "cli.app.modelo.work.create_stub_modelo_600_refused",
    "620": "cli.app.modelo.work.create_stub_modelo_620_refused",
    "650": "cli.app.modelo.work.create_stub_modelo_650_refused",
    "660": "cli.app.modelo.work.create_stub_modelo_660_refused",
    Modelo.M714.value: "cli.app.modelo.work.create_stub_modelo_714_refused",
    Modelo.M721.value: "cli.app.modelo.work.create_stub_modelo_refused",
}

STUB_ONLY_MODELOS: frozenset[str] = frozenset(STUB_MODELO_LOCALE_KEYS)

#: Ceded autonomic-tax modelos that are administered by the Comunidades
#: Autónomas rather than the AEAT, and are therefore absent from the
#: calculation registry entirely: ITP-AJD (``600``, ``620``) and ISD (``650``,
#: ``660``) are tributos cedidos. A registry discovery lookup for one of these
#: codes must redirect the operator to the competent regional Hacienda / CCAA
#: instead of surfacing a generic not-present-in-registry error. The values
#: reuse the work-create refusal copy, which already names the ceded tax, its
#: enabling law, and the regional filing route in every locale.
CEDED_AUTONOMIC_MODELO_LOCALE_KEYS: dict[str, str] = {
    "600": "cli.app.modelo.work.create_stub_modelo_600_refused",
    "620": "cli.app.modelo.work.create_stub_modelo_620_refused",
    "650": "cli.app.modelo.work.create_stub_modelo_650_refused",
    "660": "cli.app.modelo.work.create_stub_modelo_660_refused",
}

CEDED_AUTONOMIC_MODELOS: frozenset[str] = frozenset(CEDED_AUTONOMIC_MODELO_LOCALE_KEYS)


[docs] def ceded_autonomic_modelo_locale_key(modelo: str) -> str | None: """Return the instructive autonomic-redirect locale key for a ceded modelo. ITP-AJD (``600`` / ``620``) and ISD (``650`` / ``660``) are ceded autonomic taxes administered by the Comunidades Autónomas, not AEAT modelos present in the calculation registry. Discovery surfaces (``describe`` / ``casillas`` / ``formulas``) must redirect an operator who references one of these codes to the competent regional Hacienda / CCAA rather than surface a generic not-present error. Whitespace is normalised; ``None`` is returned for any other modelo, including registry-backed and genuinely unknown codes. See Also: :func:`modelo_work_create_refusal_locale_key`: The sibling work-create refusal this discovery redirect mirrors. """ return CEDED_AUTONOMIC_MODELO_LOCALE_KEYS.get(modelo.strip())
[docs] @dataclass(frozen=True, slots=True) class ModeloWorkCreateApplicabilityRefusal: """Application refusal for a modelo the active profile should not file. ``modelo`` is the stripped requested modelo code. ``reason`` is the registry-derived applicability reason surfaced by the CLI together with its ``--allow-not-applicable`` escape hatch. """ modelo: str reason: str
[docs] def modelo_work_create_refusal_locale_key(modelo: str) -> str | None: """Return the locale key for a refused stub-modelo create request. The lookup normalises whitespace and returns ``None`` when the modelo is not in :data:`STUB_ONLY_MODELOS`. Modelo 210 is conditionally released when the ``aeat_m210_engine_live`` setting is enabled; all other entries remain refused by this policy surface. See Also: :class:`aeat.core.Modelo`: Closed modelo enum used for the core stub-only entries. :mod:`aeat.entrypoints.cli._modelo_work_lifecycle_cli`: Converts the locale key into a typed CLI refusal. """ modelo_code = modelo.strip() if modelo_code not in STUB_ONLY_MODELOS: return None if modelo_code == Modelo.M210 and load_settings().aeat_m210_engine_live: return None return STUB_MODELO_LOCALE_KEYS[modelo_code]
[docs] def modelo_work_create_applicability_refusal( modelo: str, *, allow_not_applicable: bool, ) -> ModeloWorkCreateApplicabilityRefusal | None: """Return an applicability refusal for the active profile, if one applies. When ``allow_not_applicable`` is true, the guard deliberately returns ``None`` so the CLI can provision the work unit and record that the operator bypassed the applicability guard. Otherwise the active profile record is projected into :class:`aeat.domain.deadlines.TaxpayerProfile` facts and checked against the registry-owned applicability rules. Only ``NOT_APPLICABLE`` and ``ATTRIBUTION_PASS_THROUGH`` verdicts block creation. Returns: A :class:`ModeloWorkCreateApplicabilityRefusal` for a blocking verdict, or ``None`` when the modelo is applicable, unsupported by an explicit rule, or deliberately bypassed. See Also: :func:`aeat.domain.calculations.registry.derive_modelo_applicability`: Registry-owned applicability classifier used by this guard. :func:`aeat.application.user_profile.projection_for_taxpayer`: Builds the taxpayer profile consumed by the classifier. """ if allow_not_applicable: return None from ...application.user_profile import projection_for_taxpayer from ...application.workflow import workflow_state_repository from ...domain.calculations.registry.applicability import ( ApplicabilityVerdict, derive_modelo_applicability, ) state = workflow_state_repository().load() record = state.active_profile_record() try: profile = projection_for_taxpayer(record or {}, tax_id_default="00000000T") except ValidationError: return None applicability = derive_modelo_applicability(profile, modelo.strip()) blocking = { ApplicabilityVerdict.NOT_APPLICABLE, ApplicabilityVerdict.ATTRIBUTION_PASS_THROUGH, } if applicability.verdict not in blocking: return None return ModeloWorkCreateApplicabilityRefusal(modelo=modelo.strip(), reason=applicability.reason)
[docs] def guard_active_profile_foral_ccaa() -> None: """Raise the canonical foral-regime refusal for the active profile, if present. The guard reads ``tax_residence.ccaa`` from the active profile and delegates to :func:`aeat.domain.contribuyente.parse_tax_region`. Common-regime CCAA values pass through; foral values raise the domain refusal before work-unit creation reaches the generic unsupported-modelo checks. """ from ...application.user_profile import fact_value from ...application.workflow import workflow_state_repository from ...domain.contribuyente import parse_tax_region state = workflow_state_repository().load() record = state.active_profile_record() raw_ccaa = fact_value(record, "tax_residence.ccaa") if raw_ccaa: parse_tax_region(raw_ccaa)
__all__ = [ "CEDED_AUTONOMIC_MODELOS", "CEDED_AUTONOMIC_MODELO_LOCALE_KEYS", "STUB_MODELO_LOCALE_KEYS", "STUB_ONLY_MODELOS", "ModeloWorkCreateApplicabilityRefusal", "ceded_autonomic_modelo_locale_key", "guard_active_profile_foral_ccaa", "modelo_work_create_applicability_refusal", "modelo_work_create_refusal_locale_key", ]