Source code for cadrumo.domain.calculations.registry.profile_bindings
"""The profile provider: a binding that reads the taxpayer's own censo record.A profile binding names a field of the taxpayer profile substrate (declarante,conyuge, hijos, ascendientes, ...) rather than a filed value or a ledger fold.The three shapes it admits -- a scalar key, a composite projection of severalkeys through a format renderer, and a field of a typed profile model -- aremutually exclusive by validation, because a declaration that named two of themwould leave the value's origin ambiguous."""from__future__importannotationsfromtypingimportLiteralfrompydanticimportBaseModel,Field,model_validatorfrom....core.aggregationimportBindingSourceKindfrom....core.errors.hierarchyimportpydantic_validation_boundaryfrom....core.modelsimportSTRICT_FROZEN_CONFIGfrom.errorsimportRegistryValidationError__all__=["ProfileProvider"]
[docs]classProfileProvider(BaseModel):"""Strict validator for the selector mapping of a profile-source binding. Profile-source bindings read values from the taxpayer profile substrate (declarante, conyuge, hijos, ascendientes, ...). They land on the fichero-BOE record either as a typed scalar (single ``profile_key``) or via a composite projection (``profile_keys`` with a ``format`` rendering function), and optionally as a sub-collection field of a typed profile model (``profile_model`` + ``collection`` + ``field``). Two cross-cutting fields apply to every shape: * ``xsd_path`` / ``xsd_attribute`` / ``dictionary_field``: how the value is addressed on the on-wire record. * ``required_when_profile_key`` / ``required_when_value``: a conditional applicability gate; only certain profile shapes set these. """model_config=STRICT_FROZEN_CONFIGkind:Literal[BindingSourceKind.PROFILE]=BindingSourceKind.PROFILE# Scalar shapeprofile_key:str|None=Field(default=None,min_length=1,max_length=128)# Composite shapeprofile_keys:tuple[str,...]=()# Collection shape (typed sub-models on the profile)profile_model:str|None=Field(default=None,min_length=1,max_length=128)collection:str|None=Field(default=None,min_length=1,max_length=64)field:str|None=Field(default=None,min_length=1,max_length=128)repeating:bool=False# On-wire addressingxsd_path:str|None=Field(default=None,min_length=1,max_length=512)xsd_attribute:str|None=Field(default=None,min_length=1,max_length=128)dictionary_field:str|None=Field(default=None,min_length=1,max_length=128)# Rendering / formattingformat:str|None=Field(default=None,min_length=1,max_length=64)# Conditional applicabilityrequired_when_profile_key:str|None=Field(default=None,min_length=1,max_length=128)required_when_value:str|None=Field(default=None,min_length=1,max_length=256)@model_validator(mode="after")@pydantic_validation_boundarydef_validate_profile_shape(self)->ProfileProvider:has_scalar=self.profile_keyisnotNonehas_composite=bool(self.profile_keys)has_collection=self.profile_modelisnotNoneshape_count=sum((has_scalar,has_composite,has_collection))ifshape_count!=1:raiseRegistryValidationError("profile selector must declare exactly one of profile_key (scalar), ""profile_keys (composite), or profile_model (collection)",)ifhas_compositeandself.formatisNone:raiseRegistryValidationError("profile composite selector (profile_keys) requires a format renderer")ifhas_collection:ifself.fieldisNone:raiseRegistryValidationError("profile model selector must declare field")# ``collection`` is only required when the profile model# selector targets a repeating sub-collection# (``repeating = true`` plus a named ``collection``). Scalar# fields on a typed profile model (e.g. ``profile_model =# "TaxResidenceProfile"`` + ``field = "ccaa"``) omit# ``collection`` because the field IS at the model root.ifself.repeatingandself.collectionisNone:raiseRegistryValidationError("profile collection selector with repeating=true must declare collection")# required_when_* must be pairedif(self.required_when_profile_keyisNone)!=(self.required_when_valueisNone):raiseRegistryValidationError("profile selector required_when_profile_key and required_when_value must be declared together",)returnself
# ---------------------------------------------------------------------------# Per-family build-time validator dispatch## The selector lookup table this module used to own is gone: the provider union# in :mod:`~domain.calculations.registry.binding_provider` hydrates a binding# into its family model at construction, so no second table is needed to say# which model a source kind validates against. What remains is the op/fact# cross-invariant validator per family, keyed by the same discriminator.# ---------------------------------------------------------------------------