Source code for cadrumo.application.wizard.catalogue

"""Operation-scoped construction of the wizard flow catalogue.

The descriptor is built only by :func:`build_setup_flow`, while a caller holds
one leased indexed authority operation.  Importing this module is therefore
safe for metadata/help paths and cannot silently select a process-global
registry generation.
"""

from __future__ import annotations

from functools import partial
from typing import TYPE_CHECKING, Final

from ...core.external_constants import SUPPORTED_OUTPUT_LANGUAGES
from ...core.i18n.translatable import Translatable as tr
from ...core.renta_declaracion_type import RentaDeclaracionType
from ...domain.contribuyente.renta_codes import (
    RentaDisabilityGrade,
    RentaMaritalStatus,
    RentaSexCode,
)
from .models import (
    WizardChoice,
    WizardCondition,
    WizardFlow,
    WizardQuestion,
    WizardSection,
    WizardVisibility,
    WizardWidget,
)

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


def _confirm(
    qid: str,
    profile_key: str,
    *,
    suffix: str,
    default: str | None = "false",
    required: bool = False,
    visible_when: WizardCondition | WizardVisibility | None = None,
) -> WizardQuestion:
    """Build a CONFIRM question that persists into ``profile_key``."""
    return WizardQuestion(
        id=qid,
        profile_key=profile_key,
        widget=WizardWidget.CONFIRM,
        prompt=tr(f"wizard.setup.{suffix}.{qid}.prompt"),
        default=default,
        required=required,
        visible_when=visible_when,
        answer_type=bool,
    )


# The entity-type axis decides whether the IRPF-personal surface — the
# spouse, family, and personal-biographic questions, plus the
# individual-vs-joint taxation choice — is collected at all. A legal
# entity (sociedad limitada, etc.) and an attribution entity have no
# spouse and no personal IRPF facts: those questions are gated to the
# natural-person path so the non-interactive `--quiet` flow never asks
# (or, before this gate, wrongly demands) them for a company.
[docs] def build_setup_flow(operation: PinnedAuthorityOperation) -> WizardFlow: """Build the setup flow from the caller's pinned authority operation.""" from ...domain.calculations.registry.ccaa_catalogue import ( ccaa_choices as _registry_ccaa_choices, ) from ...domain.calculations.registry.ccaa_catalogue import ( default_ccaa as _default_ccaa, ) from ...domain.calculations.registry.entity_type import ( entity_type_attribution_entity_token as _entity_type_attribution_entity_token, ) from ...domain.calculations.registry.entity_type import ( entity_type_legal_entity_token as _entity_type_legal_entity_token, ) from ...domain.calculations.registry.entity_type import ( entity_type_natural_person_token as _entity_type_natural_person_token, ) from ...domain.calculations.registry.entity_type import ( entity_type_tokens as _entity_type_tokens, ) from ...domain.calculations.registry.entity_type import ( legal_entity_form_choice_description_tokens as _legal_entity_form_choice_description_tokens, ) from ...domain.calculations.registry.entity_type import ( legal_entity_form_sin_fines_lucrativos_token as _legal_entity_form_sin_fines_lucrativos_token, ) from ...domain.calculations.registry.entity_type import ( legal_entity_form_tokens as _legal_entity_form_tokens, ) from ...domain.calculations.registry.irpf_income_categories import ( irpf_income_category_actividad_economica_token as _irpf_income_category_actividad_economica_token, ) from ...domain.calculations.registry.irpf_income_categories import ( irpf_income_category_choices as _registry_irpf_income_category_choices, ) from ...domain.calculations.registry.irpf_regimes import ( irpf_estimation_regime_objetiva_token as _irpf_estimation_regime_objetiva_token, ) from ...domain.calculations.registry.irpf_regimes import ( irpf_estimation_regime_tokens as _irpf_estimation_regime_tokens, ) from ...domain.calculations.registry.irpf_regimes import ( irpf_special_regime_impatriado_token as _irpf_special_regime_impatriado_token, ) from ...domain.calculations.registry.irpf_regimes import ( irpf_special_regime_tokens as _irpf_special_regime_tokens, ) from ...domain.calculations.registry.iva_schema_vocabulary import ( default_iva_regime as _default_iva_regime, ) from ...domain.calculations.registry.iva_schema_vocabulary import ( iva_regime_choices as _iva_regime_choices, ) from ...domain.calculations.registry.iva_schema_vocabulary import ( m303_regime_composition_choices as _registry_m303_regime_composition_choices, ) from ...domain.calculations.registry.iva_schema_vocabulary import ( m303_tax_territory_choices as _registry_m303_tax_territory_choices, ) from ...domain.calculations.registry.renta_codes_catalogue import ( default_fiscal_residency as _default_fiscal_residency, ) from ...domain.calculations.registry.renta_codes_catalogue import ( fiscal_residency_choices as _registry_fiscal_residency_choices, ) from ...domain.calculations.registry.renta_codes_catalogue import ( fiscal_residency_requires_country as _fiscal_residency_requires_country, ) from ...domain.calculations.registry.situacion_familiar_catalogue import ( situacion_familiar_choices as _registry_situacion_familiar_choices, ) from ...domain.calculations.registry.third_party_declaration_roles import ( third_party_declaration_role_choices as _registry_third_party_declaration_role_choices, ) from ...domain.user_profile.setup_answers import PROFILE_OUTPUT_LANGUAGE_PATH, SetupAnswers # Every registry projection below is evaluated through this operation. # The resulting descriptor must not escape the operation that supplied it # as a process-global catalogue. ccaa_choices = partial(_registry_ccaa_choices, authority=operation) default_ccaa = partial(_default_ccaa, authority=operation) irpf_income_category_actividad_economica_token = partial( _irpf_income_category_actividad_economica_token, authority=operation ) irpf_income_category_choices = partial(_registry_irpf_income_category_choices, authority=operation) irpf_estimation_regime_objetiva_token = partial(_irpf_estimation_regime_objetiva_token, authority=operation) irpf_estimation_regime_tokens = partial(_irpf_estimation_regime_tokens, authority=operation) irpf_special_regime_impatriado_token = partial(_irpf_special_regime_impatriado_token, authority=operation) irpf_special_regime_tokens = partial(_irpf_special_regime_tokens, authority=operation) default_iva_regime = partial(_default_iva_regime, authority=operation) iva_regime_choices = partial(_iva_regime_choices, authority=operation) m303_regime_composition_choices = partial(_registry_m303_regime_composition_choices, authority=operation) m303_tax_territory_choices = partial(_registry_m303_tax_territory_choices, authority=operation) default_fiscal_residency = partial(_default_fiscal_residency, authority=operation) fiscal_residency_choices = partial(_registry_fiscal_residency_choices, authority=operation) fiscal_residency_requires_country = partial(_fiscal_residency_requires_country, authority=operation) situacion_familiar_choices = partial(_registry_situacion_familiar_choices, authority=operation) third_party_declaration_role_choices = partial( _registry_third_party_declaration_role_choices, authority=operation, ) entity_type_attribution_entity_token = partial(_entity_type_attribution_entity_token, authority=operation) entity_type_legal_entity_token = partial(_entity_type_legal_entity_token, authority=operation) entity_type_natural_person_token = partial(_entity_type_natural_person_token, authority=operation) entity_type_tokens = partial(_entity_type_tokens, authority=operation) legal_entity_form_choice_description_tokens = partial( _legal_entity_form_choice_description_tokens, authority=operation ) legal_entity_form_sin_fines_lucrativos_token = partial( _legal_entity_form_sin_fines_lucrativos_token, authority=operation ) legal_entity_form_tokens = partial(_legal_entity_form_tokens, authority=operation) _natural_person = WizardCondition(question_id="entity-type", equals=entity_type_natural_person_token().value) _joint_declaration = WizardCondition(question_id="taxation-type", equals="2") _non_resident_irpf = WizardCondition(question_id="spouse-non-resident-irpf", equals="true") _eu_eea_resident = WizardCondition(question_id="spouse-eu-eea-resident", equals="true") _iva_regime_tokens = iva_regime_choices() _default_iva_regime_value: str = default_iva_regime().value """Wizard default for the iva.regime question. Shared between the :data:`wizard_iva_choices` default-marker and the :func:`_iva_section` question's ``default=`` so the two never drift.""" wizard_iva_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=token.value, label=tr( f"wizard.setup.profile.iva-regime.choices.{token.value.lower().replace('_', '-')}.label", ), description=tr( f"wizard.setup.profile.iva-regime.choices.{token.value.lower().replace('_', '-')}.description", ), ) for token in _iva_regime_tokens ) wizard_m303_tax_territory_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=token.value, label=tr( f"wizard.setup.residence.tax-residence-jurisdiction-scope.choices.{token.value}.label", ), ) for token in m303_tax_territory_choices() ) wizard_m303_regime_composition_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=token.value, label=tr( f"wizard.setup.iva.m303-regime-composition.choices.{token.value}.label", ), ) for token in m303_regime_composition_choices() ) wizard_entity_type_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=member.value, label=tr(f"wizard.setup.taxpayer-type.entity-type.choices.{member.value.replace('_', '-')}.label"), description=tr( f"wizard.setup.taxpayer-type.entity-type.choices.{member.value.replace('_', '-')}.description" ), ) for member in entity_type_tokens() ) # Only the forms whose choice carries curated explainer copy; the registry # metadata owns this selector so an unlisted member never mints an # unresolvable description ref. wizard_described_legal_entity_forms = frozenset( member.value for member in legal_entity_form_choice_description_tokens() ) wizard_legal_entity_form_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=member.value, label=tr(f"wizard.setup.taxpayer-type.legal-entity-form.choices.{member.value.replace('_', '-')}.label"), description=( tr(f"wizard.setup.taxpayer-type.legal-entity-form.choices.{member.value.replace('_', '-')}.description") if member.value in wizard_described_legal_entity_forms else None ), ) for member in legal_entity_form_tokens() ) wizard_irpf_income_category_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=member.value, label=tr( f"wizard.setup.taxpayer-type.irpf-income-categories.choices.{member.value.replace('_', '-')}.label" ), description=tr( "wizard.setup.taxpayer-type.irpf-income-categories.choices." f"{member.value.replace('_', '-')}.description", ), ) for member in irpf_income_category_choices() ) wizard_third_party_declaration_role_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=member.value, label=tr(f"wizard.setup.taxpayer-type.declaration-roles.choices.{member.value.replace('_', '-')}.label"), description=tr( f"wizard.setup.taxpayer-type.declaration-roles.choices.{member.value.replace('_', '-')}.description", ), ) for member in third_party_declaration_role_choices() ) wizard_irpf_estimation_regime_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=member.value, label=tr(f"wizard.setup.obligations.irpf-estimation-regime.choices.{member.value.replace('_', '-')}.label"), description=tr( f"wizard.setup.obligations.irpf-estimation-regime.choices.{member.value.replace('_', '-')}.description", ), ) for member in irpf_estimation_regime_tokens() ) wizard_irpf_special_regime_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=member.value, label=tr(f"wizard.setup.obligations.irpf-special-regime.choices.{member.value.replace('_', '-')}.label"), ) for member in irpf_special_regime_tokens() ) _impatriado_regime = WizardCondition( question_id="irpf-special-regime", equals=irpf_special_regime_impatriado_token().value, ) _irpf_objective_estimation = WizardCondition( question_id="irpf-estimation-regime", equals=irpf_estimation_regime_objetiva_token().value, ) wizard_fiscal_residency_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=member.value, label=tr(f"wizard.setup.residence.fiscal-residency.choices.{member.value.replace('_', '-')}.label"), description=tr( f"wizard.setup.residence.fiscal-residency.choices.{member.value.replace('_', '-')}.description" ), ) for member in fiscal_residency_choices() ) _fiscal_residency_default = default_fiscal_residency().value _non_resident_irnr = WizardCondition( question_id="fiscal-residency", equals=next(member.value for member in fiscal_residency_choices() if fiscal_residency_requires_country(member)), ) wizard_ccaa_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=member.value, label=tr(f"wizard.setup.residence.ccaa.choices.{member.value}.label"), ) for member in ccaa_choices() ) wizard_output_language_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=language, label=tr(f"wizard.setup.profile.output-language.choices.{language}.label"), ) for language in SUPPORTED_OUTPUT_LANGUAGES ) wizard_declaration_type_choices: tuple[WizardChoice, ...] = ( WizardChoice( value=RentaDeclaracionType.INDIVIDUAL.value, label=tr("wizard.setup.profile.taxation-type.choices.individual.label"), description=tr("wizard.setup.profile.taxation-type.choices.individual.description"), ), WizardChoice( value=RentaDeclaracionType.JOINT.value, label=tr("wizard.setup.profile.taxation-type.choices.joint.label"), description=tr("wizard.setup.profile.taxation-type.choices.joint.description"), ), ) wizard_sex_choices: tuple[WizardChoice, ...] = ( WizardChoice( value=RentaSexCode.HOMBRE.value, label=tr("wizard.setup.codes.sex.choices.h.label"), ), WizardChoice( value=RentaSexCode.MUJER.value, label=tr("wizard.setup.codes.sex.choices.m.label"), ), ) wizard_marital_status_choices: tuple[WizardChoice, ...] = ( WizardChoice( value=RentaMaritalStatus.SOLTERO.value, label=tr("wizard.setup.taxpayer.taxpayer-marital-status.choices.soltero.label"), ), WizardChoice( value=RentaMaritalStatus.CASADO.value, label=tr("wizard.setup.taxpayer.taxpayer-marital-status.choices.casado.label"), ), WizardChoice( value=RentaMaritalStatus.VIUDO.value, label=tr("wizard.setup.taxpayer.taxpayer-marital-status.choices.viudo.label"), ), WizardChoice( value=RentaMaritalStatus.SEPARADO_DIVORCIADO.value, label=tr("wizard.setup.taxpayer.taxpayer-marital-status.choices.separado-divorciado.label"), ), WizardChoice( value=RentaMaritalStatus.PAREJA_HECHO.value, label=tr("wizard.setup.taxpayer.taxpayer-marital-status.choices.pareja-hecho.label"), ), ) wizard_situacion_familiar_choices: tuple[WizardChoice, ...] = tuple( WizardChoice( value=member.value, label=tr(f"wizard.setup.taxpayer.situacion-familiar.choices.{member.value.replace('_', '-')}.label"), description=tr( f"wizard.setup.taxpayer.situacion-familiar.choices.{member.value.replace('_', '-')}.description", ), ) for member in situacion_familiar_choices() ) wizard_disability_grade_choices: tuple[WizardChoice, ...] = ( WizardChoice( value=RentaDisabilityGrade.GE_33_LT_65.value, label=tr("wizard.setup.codes.disability-grade.choices.33-64.label"), description=tr("wizard.setup.codes.disability-grade.choices.33-64.description"), ), WizardChoice( value=RentaDisabilityGrade.GE_65.value, label=tr("wizard.setup.codes.disability-grade.choices.65-plus.label"), description=tr("wizard.setup.codes.disability-grade.choices.65-plus.description"), ), WizardChoice( value=RentaDisabilityGrade.JUDICIAL_INCAPACITY.value, label=tr("wizard.setup.codes.disability-grade.choices.judicial-incapacity.label"), description=tr("wizard.setup.codes.disability-grade.choices.judicial-incapacity.description"), ), WizardChoice( value=RentaDisabilityGrade.ASSISTANCE_OR_REDUCED_MOBILITY.value, label=tr("wizard.setup.codes.disability-grade.choices.assistance-or-mobility.label"), description=tr("wizard.setup.codes.disability-grade.choices.assistance-or-mobility.description"), ), ) _entity_legal = WizardCondition(question_id="entity-type", equals=entity_type_legal_entity_token().value) _entity_attribution = WizardCondition( question_id="entity-type", equals=entity_type_attribution_entity_token().value ) _ley_49_2002_form = WizardCondition( question_id="legal-entity-form", equals=legal_entity_form_sin_fines_lucrativos_token().value, ) _ley_49_2002_option_declared = WizardCondition(question_id="ley-49-2002-option-declared", equals="true") _ley_49_2002_renunciation_declared = WizardCondition( question_id="ley-49-2002-renunciation-declared", equals="true", ) # `activity` (the actividad económica / epígrafe IAE free-text field) # is collected only for a taxpayer that actually carries on an # activity: a legal entity, or a natural person who declared the # `actividad_economica` IRPF income category. A pure landlord, # a salaried-only taxpayer, and a pensioner have no actividad # económica and must not be forced to invent one. _has_activity = WizardVisibility( any_of=( _entity_legal, WizardCondition( question_id="irpf-income-categories", contains=irpf_income_category_actividad_economica_token().value, ), ), ) _iva_regime_visible = WizardVisibility( any_of=( _entity_legal, _entity_attribution, WizardCondition( question_id="irpf-income-categories", contains=irpf_income_category_actividad_economica_token().value, ), ), ) # Every question whose answer CLAIMS the IVA block is gated here, not on # _iva_regime_visible. Declaring any IVA fact obliges the whole block, so a # gate that admits one block-owned question without admitting the questions # the block requires produces a profile the domain resolver then refuses. Six # enrolment confirms sat on the entity-shaped gate while the facts they oblige # sat on this one, so answering "no" to ROI without declaring a regime built a # claimed block the wizard never finished asking about. _iva_block_claimed = WizardVisibility( any_of=tuple(WizardCondition(question_id="iva-regime", equals=token.value) for token in _iva_regime_tokens), ) _identidad_section = WizardSection( id="identidad", title=tr("wizard.setup.identidad.title"), questions=( WizardQuestion( id="output-language", profile_key=PROFILE_OUTPUT_LANGUAGE_PATH, widget=WizardWidget.SELECT, prompt=tr("wizard.setup.profile.output-language.prompt"), choices=wizard_output_language_choices, default="es", required=False, answer_type=str, ), WizardQuestion( id="entity-type", profile_key="taxpayer_type.entity_type", widget=WizardWidget.SELECT, prompt=tr("wizard.setup.taxpayer-type.entity-type.prompt"), help=tr("wizard.setup.taxpayer-type.entity-type.help"), choices=wizard_entity_type_choices, required=False, answer_type=str, ), WizardQuestion( id="legal-entity-form", profile_key="taxpayer_type.legal_entity_form", widget=WizardWidget.SELECT, prompt=tr("wizard.setup.taxpayer-type.legal-entity-form.prompt"), choices=wizard_legal_entity_form_choices, required=False, visible_when=_entity_legal, answer_type=str, ), WizardQuestion( id="tax-id", profile_key="identity.tax_id", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.profile.tax-id.prompt"), required=True, answer_type=str, ), WizardQuestion( id="name", profile_key="identity.name", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.profile.name.prompt"), required=False, answer_type=str, ), WizardQuestion( id="surnames", profile_key="identity.surnames", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.profile.surnames.prompt"), required=False, answer_type=str, ), WizardQuestion( id="legal-name", profile_key="identity.legal_name", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.profile.legal-name.prompt"), help=tr("wizard.setup.profile.legal-name.help"), required=False, visible_when=_entity_legal, answer_type=str, ), ), ) _residence_section = WizardSection( id="residence", title=tr("wizard.setup.residence.title"), questions=( WizardQuestion( id="fiscal-residency", profile_key="taxpayer_type.fiscal_residency", widget=WizardWidget.SELECT, prompt=tr("wizard.setup.residence.fiscal-residency.prompt"), choices=wizard_fiscal_residency_choices, default=_fiscal_residency_default, required=False, answer_type=str, ), WizardQuestion( id="country-of-fiscal-residence", profile_key="taxpayer_type.country_of_fiscal_residence", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.residence.country-of-fiscal-residence.prompt"), required=False, visible_when=_non_resident_irnr, answer_type=str, ), WizardQuestion( id="representante-fiscal-nif", profile_key="taxpayer_type.representante_fiscal_nif", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.residence.representante-fiscal-nif.prompt"), required=False, visible_when=_non_resident_irnr, answer_type=str, ), WizardQuestion( id="representante-fiscal-nombre", profile_key="taxpayer_type.representante_fiscal_nombre", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.residence.representante-fiscal-nombre.prompt"), required=False, visible_when=_non_resident_irnr, answer_type=str, ), WizardQuestion( id="tax-residence-jurisdiction-scope", profile_key="tax_residence.jurisdiction_scope", widget=WizardWidget.SELECT, prompt=tr("wizard.setup.residence.tax-residence-jurisdiction-scope.prompt"), choices=wizard_m303_tax_territory_choices, required=True, answer_type=str, ), WizardQuestion( # IRNR non-residents have no CCAA residencia fiscal; only # IRPF residents are assigned a CCAA. Suppress the question # for NON_RESIDENT_IRNR so the wizard never silently defaults # a non-resident onto "madrid". id="tax-residence-ccaa", profile_key="tax_residence.ccaa", widget=WizardWidget.SELECT, prompt=tr("wizard.setup.residence.tax-residence-ccaa.prompt"), choices=wizard_ccaa_choices, default=default_ccaa().value, required=False, visible_when=WizardCondition( question_id="fiscal-residency", equals=_fiscal_residency_default, ), answer_type=str, ), WizardQuestion( id="address-postcode", profile_key="contact.postcode", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.profile.address-postcode.prompt"), required=False, answer_type=str, ), ), ) _actividad_section = WizardSection( id="actividad", title=tr("wizard.setup.actividad.title"), questions=( WizardQuestion( id="irpf-income-categories", profile_key="taxpayer_type.irpf_income_categories", widget=WizardWidget.CHECKBOX, prompt=tr("wizard.setup.taxpayer-type.irpf-income-categories.prompt"), help=tr("wizard.setup.taxpayer-type.irpf-income-categories.help"), choices=wizard_irpf_income_category_choices, required=False, visible_when=_natural_person, answer_type=str, ), WizardQuestion( id="declaration-roles", profile_key="taxpayer_type.declaration_roles", widget=WizardWidget.CHECKBOX, prompt=tr("wizard.setup.taxpayer-type.declaration-roles.prompt"), help=tr("wizard.setup.taxpayer-type.declaration-roles.help"), choices=wizard_third_party_declaration_role_choices, required=False, answer_type=str, ), WizardQuestion( id="activity", profile_key="activities.description", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.profile.activity.prompt"), required=False, visible_when=_has_activity, answer_type=str, ), WizardQuestion( # Optional censo alta date. When set, the deadline engine # suppresses obligation windows that close before it, so a # recent registrant is not shown overdue returns for periods # that precede their alta. id="activity-start-date", profile_key="censo.activity_start_date", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.profile.activity-start-date.prompt"), help=tr("wizard.setup.profile.activity-start-date.help"), required=False, answer_type=str, ), WizardQuestion( # Optional INCN (importe neto de la cifra de negocios) of # the prior 12 months. Gates the Modelo 202 modality split # at the 6.000.000 EUR threshold (LIS Art. 40.3): above it # Art. 40.3 is mandatory, below it both modalities are # reachable. An undeclared value yields an INCOMPLETE # downstream verdict, never a guessed modality. id="incn-prior-12-months", profile_key="taxpayer_type.incn_prior_12_months", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.taxpayer-type.incn-prior-12-months.prompt"), help=tr("wizard.setup.taxpayer-type.incn-prior-12-months.help"), required=False, answer_type=str, ), WizardQuestion( # Optional LIS Art. 29 first-two-profit-making-periods # state. Gates the 15 percent new-entity rate override. # Only meaningful for a legal entity; gated visible_when on # entity_type == legal_entity. No descriptor default: the # override is opt-in and three-state. A profile that has # not declared the fact must persist nothing, so the typed # projection reloads as ``None`` (undeclared) — never # collapsed onto declared-``False`` by a default value the # operator never approved. A positively-declared # ``--new-entity-first-two-profit-periods`` writes ``true`` # and opts into the override; ``--no-new-entity-...`` writes # ``false`` and positively declines it. id="new-entity-first-two-profit-periods", profile_key="taxpayer_type.new_entity_first_two_profit_periods", widget=WizardWidget.CONFIRM, prompt=tr("wizard.setup.taxpayer-type.new-entity-first-two-profit-periods.prompt"), help=tr("wizard.setup.taxpayer-type.new-entity-first-two-profit-periods.help"), required=False, visible_when=_entity_legal, answer_type=bool, ), WizardQuestion( id="ley-49-2002-option-declared", profile_key="taxpayer_type.ley_49_2002_special_regime_option_declared", widget=WizardWidget.CONFIRM, prompt=tr("wizard.setup.taxpayer-type.ley-49-2002-option-declared.prompt"), help=tr("wizard.setup.taxpayer-type.ley-49-2002-option-declared.help"), required=False, visible_when=_ley_49_2002_form, answer_type=bool, ), WizardQuestion( id="ley-49-2002-option-date", profile_key="taxpayer_type.ley_49_2002_special_regime_option_date", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.taxpayer-type.ley-49-2002-option-date.prompt"), help=tr("wizard.setup.taxpayer-type.ley-49-2002-option-date.help"), required=False, visible_when=_ley_49_2002_option_declared, answer_type=str, ), WizardQuestion( id="ley-49-2002-renunciation-declared", profile_key="taxpayer_type.ley_49_2002_special_regime_renunciation_declared", widget=WizardWidget.CONFIRM, prompt=tr("wizard.setup.taxpayer-type.ley-49-2002-renunciation-declared.prompt"), help=tr("wizard.setup.taxpayer-type.ley-49-2002-renunciation-declared.help"), required=False, visible_when=_ley_49_2002_form, answer_type=bool, ), WizardQuestion( id="ley-49-2002-renunciation-date", profile_key="taxpayer_type.ley_49_2002_special_regime_renunciation_date", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.taxpayer-type.ley-49-2002-renunciation-date.prompt"), help=tr("wizard.setup.taxpayer-type.ley-49-2002-renunciation-date.help"), required=False, visible_when=_ley_49_2002_renunciation_declared, answer_type=str, ), ), ) _iva_section = WizardSection( id="iva", title=tr("wizard.setup.iva.title"), questions=( WizardQuestion( id="iva-regime", profile_key="iva.regime", widget=WizardWidget.SELECT, prompt=tr("wizard.setup.profile.iva-regime.prompt"), choices=wizard_iva_choices, required=False, visible_when=_iva_regime_visible, answer_type=str, ), WizardQuestion( id="iva-m303-regime-composition", profile_key="iva.m303_regime_composition", widget=WizardWidget.SELECT, prompt=tr("wizard.setup.iva.m303-regime-composition.prompt"), choices=wizard_m303_regime_composition_choices, required=True, visible_when=_iva_block_claimed, answer_type=str, ), _confirm( "iva-roi-enrolled", "iva.roi_enrolled", suffix="iva", default=None, visible_when=_iva_block_claimed, ), _confirm( "iva-oss-enrolled", "iva.oss_enrolled", suffix="iva", default=None, visible_when=_iva_block_claimed, ), _confirm( "iva-group-member-enrolled", "iva.group_member_enrolled", suffix="iva", default=None, visible_when=_iva_block_claimed, ), _confirm( "iva-group-dominant-entity-enrolled", "iva.group_dominant_entity_enrolled", suffix="iva", default=None, visible_when=_iva_block_claimed, ), _confirm( "iva-sii-enrolled", "iva.sii_enrolled", suffix="iva", default=None, visible_when=_iva_block_claimed, ), _confirm( "iva-redeme-enrolled", "iva.redeme_enrolled", suffix="iva", default=None, required=True, visible_when=_iva_block_claimed, ), _confirm( "iva-intracommunity-operations-exceed-50000-eur", "iva.intracommunity_operations_exceed_50000_eur", suffix="iva", default=None, visible_when=_iva_block_claimed, ), _confirm( "iva-cash-accounting-regime-enrolled", "iva.cash_accounting_regime_enrolled", suffix="iva", default=None, required=True, visible_when=_iva_block_claimed, ), _confirm( "iva-voluntary-sii-enrolled", "iva.voluntary_sii_enrolled", suffix="iva", default=None, required=True, visible_when=_iva_block_claimed, ), _confirm( "iva-hydrocarbon-deposit-advance-payment-deduction-entitled", "iva.hydrocarbon_deposit_advance_payment_deduction_entitled", suffix="iva", default=None, required=True, visible_when=_iva_block_claimed, ), ), ) _enrollment_section = WizardSection( id="enrollment", title=tr("wizard.setup.enrollment.title"), questions=( _confirm("enrollment-large-company", "censo.large_company", suffix="enrollment"), _confirm( "enrollment-public-administration-budget-gt-6000000", "censo.public_administration_budget_gt_6000000", suffix="enrollment", ), ), ) _familia_section = WizardSection( id="familia", title=tr("wizard.setup.familia.title"), questions=( WizardQuestion( id="taxation-type", profile_key="renta_filing.declaration_type", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.profile.taxation-type.prompt"), choices=wizard_declaration_type_choices, required=False, visible_when=_natural_person, answer_type=str, ), WizardQuestion( id="taxpayer-sex", profile_key="renta_taxpayer.sex", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.taxpayer.taxpayer-sex.prompt"), choices=wizard_sex_choices, required=False, visible_when=_natural_person, answer_type=str, ), WizardQuestion( id="taxpayer-marital-status", profile_key="renta_taxpayer.marital_status", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.taxpayer.taxpayer-marital-status.prompt"), choices=wizard_marital_status_choices, required=False, visible_when=_natural_person, answer_type=str, ), WizardQuestion( # Art. 82 LIRPF: situación familiar determines whether conjunta # (joint) taxation is available and which unidad-familiar variant # applies. Distinct from the Modelo 100 marital-status code # (which maps to form rows); this axis drives the verifier check # and future casilla routing. id="situacion-familiar", profile_key="renta_family.situacion_familiar", widget=WizardWidget.SELECT, prompt=tr("wizard.setup.taxpayer.situacion-familiar.prompt"), help=tr("wizard.setup.taxpayer.situacion-familiar.help"), choices=wizard_situacion_familiar_choices, required=False, visible_when=_natural_person, answer_type=str, ), WizardQuestion( # Art. 82.1 LIRPF: matrimonio sobrevenido — records the date the # current marriage began. Drives casillas 0245 (vigente todo el año), # 0246 (primer mes), and 0247 (último mes completo). Only asked when # the taxpayer is married (marital_status = "2"). id="taxpayer-marriage-date", profile_key="renta_taxpayer.marriage_date", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.taxpayer.taxpayer-marriage-date.prompt"), required=False, visible_when=WizardCondition( question_id="taxpayer-marital-status", equals=RentaMaritalStatus.CASADO.value ), answer_type=str, ), WizardQuestion( id="taxpayer-birth-date", profile_key="renta_taxpayer.birth_date", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.taxpayer.taxpayer-birth-date.prompt"), required=False, visible_when=_natural_person, answer_type=str, ), WizardQuestion( id="taxpayer-disability-grade", profile_key="renta_taxpayer.disability_grade", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.taxpayer.taxpayer-disability-grade.prompt"), choices=wizard_disability_grade_choices, required=False, visible_when=_natural_person, answer_type=str, ), WizardQuestion( id="taxpayer-death-date", profile_key="renta_taxpayer.death_date", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.taxpayer.taxpayer-death-date.prompt"), required=False, visible_when=_natural_person, answer_type=str, ), WizardQuestion( # Required *when visible*: a joint declaration is invalid # without the spouse NIF (SetupAnswers enforces the same # invariant). The widget validator still accepts a blank # answer for a gated question; the conditional requirement # is enforced at `validate_profile_values` via the compiled # `required_when_*` pair. id="spouse-tax-id", profile_key="renta_spouse.tax_id", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.spouse.spouse-tax-id.prompt"), required=True, visible_when=_joint_declaration, answer_type=str, ), WizardQuestion( id="spouse-name", profile_key="renta_spouse.name", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.spouse.spouse-name.prompt"), required=False, visible_when=_joint_declaration, answer_type=str, ), WizardQuestion( id="spouse-surnames", profile_key="renta_spouse.surnames", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.spouse.spouse-surnames.prompt"), required=False, visible_when=_joint_declaration, answer_type=str, ), WizardQuestion( id="spouse-birth-date", profile_key="renta_spouse.birth_date", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.spouse.spouse-birth-date.prompt"), required=False, visible_when=_joint_declaration, answer_type=str, ), WizardQuestion( id="spouse-sex", profile_key="renta_spouse.sex", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.spouse.spouse-sex.prompt"), choices=wizard_sex_choices, required=False, visible_when=_joint_declaration, answer_type=str, ), WizardQuestion( id="spouse-disability-grade", profile_key="renta_spouse.disability_grade", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.spouse.spouse-disability-grade.prompt"), choices=wizard_disability_grade_choices, required=False, visible_when=_joint_declaration, answer_type=str, ), WizardQuestion( id="spouse-non-resident-irpf", profile_key="renta_spouse.non_resident_irpf", widget=WizardWidget.CONFIRM, prompt=tr("wizard.setup.spouse.spouse-non-resident-irpf.prompt"), required=False, default="false", visible_when=_joint_declaration, answer_type=bool, ), WizardQuestion( id="spouse-eu-eea-resident", profile_key="renta_spouse.eu_eea_resident", widget=WizardWidget.CONFIRM, prompt=tr("wizard.setup.spouse.spouse-eu-eea-resident.prompt"), required=False, default="false", visible_when=_non_resident_irpf, answer_type=bool, ), WizardQuestion( # Required *when visible*: an EU/EEA-resident spouse must # name the residence country (SetupAnswers enforces the # same invariant). The conditional requirement is compiled # into the `required_when_*` pair. id="spouse-eu-eea-country", profile_key="renta_spouse.eu_eea_country", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.spouse.spouse-eu-eea-country.prompt"), required=True, visible_when=_eu_eea_resident, answer_type=str, ), _confirm( "family-descendants-eu-eea-deduction", "renta_family.descendants_eu_eea_deduction", suffix="family", visible_when=_natural_person, ), _confirm( "family-minor-children-in-unit", "renta_family.minor_children_in_unit", suffix="family", visible_when=_natural_person, ), ), ) _obligations_section = WizardSection( id="obligations", title=tr("wizard.setup.obligations.title"), questions=( _confirm("has-employees", "withholding.has_employees", suffix="obligations"), _confirm( "pays-professionals-with-retencion", "withholding.pays_professionals_with_retencion", suffix="obligations", ), _confirm( "art109-activity-income-withholding-ge-70pct", "irpf.art109_activity_income_withholding_ge_70pct", suffix="obligations", ), _confirm("pays-rent-with-retencion", "withholding.pays_rent_with_retencion", suffix="obligations"), _confirm( "pays-capital-income-with-retencion", "withholding.pays_capital_income_with_retencion", suffix="obligations", ), WizardQuestion( id="modelo-111-no-retenciones-periods", profile_key="withholding.modelo_111_no_retenciones_periods", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.obligations.modelo-111-no-retenciones-periods.prompt"), help=tr("wizard.setup.obligations.modelo-111-no-retenciones-periods.help"), required=False, answer_type=str, ), WizardQuestion( id="irpf-estimation-regime", profile_key="irpf.estimation_regime", widget=WizardWidget.SELECT, prompt=tr("wizard.setup.obligations.irpf-estimation-regime.prompt"), help=tr("wizard.setup.obligations.irpf-estimation-regime.help"), choices=wizard_irpf_estimation_regime_choices, required=False, answer_type=str, ), WizardQuestion( id="objective-estimation-modulos-iae-epigraph", profile_key="irpf.objective_estimation_modulos_iae_epigraph", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.obligations.objective-estimation-modulos-iae-epigraph.prompt"), help=tr("wizard.setup.obligations.objective-estimation-modulos-iae-epigraph.help"), required=False, visible_when=_irpf_objective_estimation, answer_type=str, ), WizardQuestion( id="objective-estimation-modulos-module-1-units", profile_key="irpf.objective_estimation_modulos_module_1_units", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.obligations.objective-estimation-modulos-module-1-units.prompt"), help=tr("wizard.setup.obligations.objective-estimation-modulos-module-units.help"), required=False, visible_when=_irpf_objective_estimation, answer_type=str, ), WizardQuestion( id="objective-estimation-modulos-module-2-units", profile_key="irpf.objective_estimation_modulos_module_2_units", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.obligations.objective-estimation-modulos-module-2-units.prompt"), help=tr("wizard.setup.obligations.objective-estimation-modulos-module-units.help"), required=False, visible_when=_irpf_objective_estimation, answer_type=str, ), WizardQuestion( id="objective-estimation-modulos-module-3-units", profile_key="irpf.objective_estimation_modulos_module_3_units", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.obligations.objective-estimation-modulos-module-3-units.prompt"), help=tr("wizard.setup.obligations.objective-estimation-modulos-module-units.help"), required=False, visible_when=_irpf_objective_estimation, answer_type=str, ), WizardQuestion( id="objective-estimation-modulos-module-4-units", profile_key="irpf.objective_estimation_modulos_module_4_units", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.obligations.objective-estimation-modulos-module-4-units.prompt"), help=tr("wizard.setup.obligations.objective-estimation-modulos-module-units.help"), required=False, visible_when=_irpf_objective_estimation, answer_type=str, ), WizardQuestion( id="objective-estimation-modulos-module-5-units", profile_key="irpf.objective_estimation_modulos_module_5_units", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.obligations.objective-estimation-modulos-module-5-units.prompt"), help=tr("wizard.setup.obligations.objective-estimation-modulos-module-units.help"), required=False, visible_when=_irpf_objective_estimation, answer_type=str, ), WizardQuestion( id="objective-estimation-modulos-module-6-units", profile_key="irpf.objective_estimation_modulos_module_6_units", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.obligations.objective-estimation-modulos-module-6-units.prompt"), help=tr("wizard.setup.obligations.objective-estimation-modulos-module-units.help"), required=False, visible_when=_irpf_objective_estimation, answer_type=str, ), WizardQuestion( id="objective-estimation-modulos-module-7-units", profile_key="irpf.objective_estimation_modulos_module_7_units", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.obligations.objective-estimation-modulos-module-7-units.prompt"), help=tr("wizard.setup.obligations.objective-estimation-modulos-module-units.help"), required=False, visible_when=_irpf_objective_estimation, answer_type=str, ), WizardQuestion( id="irpf-special-regime", profile_key="irpf.special_regime", widget=WizardWidget.SELECT, prompt=tr("wizard.setup.obligations.irpf-special-regime.prompt"), help=tr("wizard.setup.obligations.irpf-special-regime.help"), choices=wizard_irpf_special_regime_choices, required=False, visible_when=_natural_person, answer_type=str, ), WizardQuestion( id="irpf-special-regime-start-date", profile_key="irpf.special_regime_start_date", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.obligations.irpf-special-regime-start-date.prompt"), help=tr("wizard.setup.obligations.irpf-special-regime-start-date.help"), required=False, visible_when=_impatriado_regime, answer_type=str, ), _confirm("does-intracomunitario", "iva.does_intracomunitario", suffix="obligations"), _confirm( "third-party-transactions-above-347-threshold", "obligations.third_party_transactions_above_347_threshold", suffix="obligations", ), _confirm( "bienes-extranjero-above-threshold", "obligations.bienes_extranjero_above_threshold", suffix="obligations", ), _confirm( "monedas-virtuales-extranjero-above-threshold", "obligations.monedas_virtuales_extranjero_above_threshold", suffix="obligations", ), ), ) _preferencias_section = WizardSection( id="preferencias", title=tr("wizard.setup.preferencias.title"), questions=( _confirm("llm-vision", "capabilities.llm_vision", suffix="capabilities", default="true"), _confirm("google-export", "capabilities.google_export", suffix="capabilities", default="true"), WizardQuestion( id="notes", profile_key="identity.notes", widget=WizardWidget.TEXT, prompt=tr("wizard.setup.notes.notes.prompt"), required=False, answer_type=str, ), ), ) return WizardFlow( id="setup", title=tr("wizard.setup.title"), description=tr("wizard.setup.description"), sections=( _identidad_section, _residence_section, _actividad_section, _iva_section, _enrollment_section, _familia_section, _obligations_section, _preferencias_section, ), answers_model=SetupAnswers, )
FAMILIA_SECTION_ID: Final = "familia" """The setup flow section carrying the family unit and its descendants.""" __all__ = ["FAMILIA_SECTION_ID", "build_setup_flow"]