"""Category profile schema consumed by downstream classifiers.:class:`CategoryProfile` binds a :class:`SpendingCategory` to its display label,:class:`ProportionalityRule`, and optional :class:`IvaDeductibilityHint`."""from__future__importannotationsfrompydanticimportBaseModel,model_validatorfrom...core.errors.hierarchyimportpydantic_validation_boundaryfrom...core.i18n.translatableimportTranslatableastrfrom...core.modelsimportSTRICT_FROZEN_CONFIGfrom.errorsimportCategoryValidationErrorfrom.proportionalityimportProportionalityRulefrom.spending_categoryimportSpendingCategoryclass_CategoryProfileStrictFrozenModel(BaseModel):"""Shared strict immutable boundary model."""model_config=STRICT_FROZEN_CONFIG
[docs]classIvaDeductibilityHint(str):"""Opaque registry-projected IVA deductibility hint token."""__slots__=()@classmethoddef__get_pydantic_core_schema__(cls,_source_type:object,_handler:object)->object:"""Expose the opaque token as a string to Pydantic without a catalogue."""frompydantic_coreimportcore_schemareturncore_schema.no_info_after_validator_function(pydantic_validation_boundary(cls),core_schema.str_schema())@propertydefvalue(self)->str:"""Return the opaque token for string-oriented serialization."""returnstr(self)
[docs]classCategoryProfile(_CategoryProfileStrictFrozenModel):"""Explainable category profile for one spending category."""category:SpendingCategorydisplay_label:trproportionality:ProportionalityRuleiva_hint:IvaDeductibilityHint|None=None@model_validator(mode="after")@pydantic_validation_boundarydef_validate_profile(self)->CategoryProfile:ifnotstr(self.display_label).strip():raiseCategoryValidationError("category profile display_label must not be blank")returnself