Source code for aeat.application.wizard._status

"""Profile-status projection and the active-profile to ``TaxpayerProfile`` bridge.

``build_wizard_status`` projects the active workflow state into a
strict :class:`WizardStatusReport` consumed by the config repair surface
and the ``aeat config profile status`` command. ``load_active_taxpayer_profile``
is the typed bridge the deadline engine and the filing runtime call
to obtain an ``TaxpayerProfile`` from the active profile bucket.
"""

from __future__ import annotations

from pydantic import BaseModel

from ...core import STRICT_FROZEN_CONFIG, resolve_active_bucket_id
from ...domain.deadlines import TaxpayerProfile
from ..user_profile import (
    iva_regime_required,
    list_profile_key_records,
    projection_for_taxpayer,
    record_to_path_values,
    validate_profile_values,
)
from ..workflow import WorkflowState
from . import _compiler as _compiler  # side-effect: registers PROFILE_KEYS before _keys_validation
from ._errors import WizardError

_ENROLMENT_KEY = "iva.regime"
"""Profile key whose presence flips an IVA-liable operator profile into
``ready-to-file``.

Natural-person profiles without economic-activity income do not need this
fact to be ready: they are not silently enrolled into a Modelo 303 path.
"""


[docs] class WizardStatusReport(BaseModel): """Readiness summary for the active configuration profile. Surfaces both the structural readiness (identity-required keys present) and the enrolment readiness (IVA regime declared) so the config repair's profile + auth checks can render a precise ``next_action`` for the operator. The semantic shape is the contract that the repair renderer reads from. """ model_config = STRICT_FROZEN_CONFIG active_profile: str | None profile_ready: bool identity_ready: bool = False enrolment_ready: bool = False missing_required: tuple[str, ...] = () missing_enrolment: tuple[str, ...] = () profile_present_keys: int profile_total_keys: int auth_provider: str login_ready: bool next_action: str
[docs] class WizardStatusError(WizardError): """Raised when the wizard status projection cannot resolve the active profile."""
[docs] def build_wizard_status(state: WorkflowState) -> WizardStatusReport: """Return the :class:`WizardStatusReport` readiness for the current workflow state. ``profile_ready`` is true only when the registry-required keys (identity) are present and, when the taxpayer shape requires IVA enrolment, the IVA regime is also present. A natural-person non-activity profile must not be blocked on a regime declaration it does not need. """ record = state.active_profile_record() identity_ready = False missing_required: tuple[str, ...] = () profile_present_keys = 0 profile_total_keys = len(list_profile_key_records()) enrolment_ready = False missing_enrolment: tuple[str, ...] = () if record is not None: values = record_to_path_values(record) validation = validate_profile_values(values) identity_ready = validation.valid missing_required = validation.missing_required profile_present_keys = validation.present_keys profile_total_keys = validation.total_keys enrolment_required = iva_regime_required(values) enrolment_value = (values.get(_ENROLMENT_KEY) or "").strip() enrolment_ready = not enrolment_required or bool(enrolment_value) if enrolment_required and not enrolment_ready: missing_enrolment = (_ENROLMENT_KEY,) profile_ready = identity_ready and enrolment_ready auth_provider = state.auth.provider or "" login_ready = state.auth.authenticated_at is not None return WizardStatusReport( active_profile=resolve_active_bucket_id(), profile_ready=profile_ready, identity_ready=identity_ready, enrolment_ready=enrolment_ready, missing_required=missing_required, missing_enrolment=missing_enrolment, profile_present_keys=profile_present_keys, profile_total_keys=profile_total_keys, auth_provider=auth_provider, login_ready=login_ready, next_action=_next_wizard_action( has_profile=record is not None, missing_required=missing_required, missing_enrolment=missing_enrolment, auth_provider=auth_provider, login_ready=login_ready, ), )
def _next_wizard_action( *, has_profile: bool, missing_required: tuple[str, ...], missing_enrolment: tuple[str, ...], auth_provider: str, login_ready: bool, ) -> str: if not has_profile: return "aeat config profile create NAME" if missing_required: return "aeat config profile edit NAME" if missing_enrolment: return "aeat config profile edit NAME" if not auth_provider: return "aeat config auth configure --provider certificate --file PATH" if not login_ready: return f"aeat config auth test --provider {auth_provider}" return "aeat app overview status"
[docs] def load_active_taxpayer_profile(state: WorkflowState) -> TaxpayerProfile: """Build an :class:`TaxpayerProfile` from the active profile values. The bridge runs the active profile record through the canonical user-profile taxpayer projection, which in turn builds the ``TaxpayerProfile`` consumed by the deadline engine and filing runtime. Values come from the profile bucket selected by the workflow state. Args: state: The current :class:`WorkflowState` from which the active profile record is resolved. Returns: A :class:`TaxpayerProfile` populated from the active profile's canonical answer values. Raises: WizardStatusError: When no profile is active or the active profile does not carry a ``tax.id``. """ record = state.active_profile_record() if record is None: raise WizardStatusError( translated_message="application.wizard.status.errors.no_active_profile", context={"workflow_state": "no_active_profile"}, ) values: dict[str, str] = dict(record_to_path_values(record)) if not values.get("identity.tax_id"): raise WizardStatusError( translated_message="application.wizard.status.errors.missing_tax_id", context={"active_profile": resolve_active_bucket_id()}, ) return projection_for_taxpayer(record)
__all__ = [ "WizardStatusError", "WizardStatusReport", "build_wizard_status", "load_active_taxpayer_profile", ]