"""Shared validating identifiers and hash-payload primitives.Provides :class:`ModeloIdentifier` for modelo-code shape validation and:func:`canonical_decimal_string` for hash-stable decimal payloads. Invalidmodelo-code shapes raise :class:`DomainValidationError`, matching the domainvalidation contract used by Pydantic-backed records.This module is deliberately narrower than the registry-backed modelo catalogue.:class:`ModeloIdentifier` preserves leading zeros and validates the textualidentifier shape only; it does not prove that a modelo is present in the bundledregistry or in the closed :class:`core.Modelo` enum. Callers that need aloadable revision must ask the registry authority."""from__future__importannotationsimportrefromdecimalimportDecimalfrompydanticimportGetCoreSchemaHandlerfrompydantic_coreimportCoreSchema,core_schemafrom._errorsimportDomainValidationError_MODELO_RE=re.compile(r"^\d{3}[A-Z]?$")
[docs]classModeloIdentifier(str):"""Typed string identifier for the textual AEAT modelo-code shape. The type preserves the incoming string and accepts three digits plus an optional uppercase suffix. It is suitable for lightweight domain records and Pydantic schemas that need syntactic validation without importing the registry authority. It is not a membership check against the current registry or the closed :class:`core.Modelo` enum. """__slots__=()def__new__(cls,value:str)->ModeloIdentifier:ifnotisinstance(value,str)ornot_MODELO_RE.match(value):raiseDomainValidationError(f"Invalid modelo identifier: {value!r}")returnsuper().__new__(cls,value)@classmethoddef__get_pydantic_core_schema__(cls,source_type:type[object],handler:GetCoreSchemaHandler,)->CoreSchema:delsource_type,handlerreturncore_schema.no_info_after_validator_function(cls,core_schema.str_schema(pattern=_MODELO_RE.pattern),)
[docs]defcanonical_decimal_string(value:Decimal)->str:"""Render a :class:`~decimal.Decimal` into a stable fixed-point string for hashing. Used by domain ``derive_*_id`` helpers to canonicalise monetary fields before they enter a SHA-256 hash payload, so two semantically equal amounts (``Decimal("10")`` vs ``Decimal("10.00")``) hash to the same identifier. Zero collapses to ``"0"`` regardless of input precision; non-zero values are normalised (trailing zeros removed) and formatted without exponent notation. This helper does not round, quantize, localize, or format amounts for display. Callers that need a legal scale or currency presentation must enforce that contract before or after using this hash-normalization helper. """ifvalue.is_zero():return"0"returnformat(value.normalize(),"f")