"""EU intra-community NIF-IVA identification-number format authority.A counterparty's intra-community IVA number — the *NIF-IVA intracomunitario*declared on Modelo 303 / Modelo 349 — has a distinct structural format for eachEU Member State. AEAT's M349 validator (and the VIES registry behind it) bouncesa number whose shape does not match its country's published pattern, so the onlydefence against silently building an un-fileable declaration is to validate the*structure* (not live VIES existence) at the boundary where the number isaccepted.This module is the typed kernel for that regulatory-shaped value. The country,prefix, and pattern records live in the non-Modelo facts registry; this modulekeeps only the opaque projected token, compiled-pattern value object, andnormalisation mechanics. Consumers (the ledger invoice counterparty boundarytoday; the Modelo 349 manual-entry row in future) resolve a Member State'sexpected shape through the domain registry and refuse a malformed number withan instructive, format-naming diagnostic.Authority: the European Commission VIES national IVA-number structure rules(``https://ec.europa.eu/taxation_customs/vies/``), grounded in Council Directive2006/112/EC. Spain (``ES``) is deliberately absent: a Spanish identifier isvalidated by the dedicated checksum authority :func:`validate_spanish_tax_id`,not by a structural pattern. Northern Ireland (``XI``) is included because itspost-Brexit goods IVA prefix mirrors the GB structure and is accepted inintra-community contexts."""from__future__importannotationsimportrefromdataclassesimportdataclassfrom...core.registry_tokenimportStrictRegistryToken__all__=["NifIvaFormatSpec","NifIvaPrefix","is_nif_iva_structurally_shaped","normalise_nif_iva",]
[docs]classNifIvaPrefix(StrictRegistryToken):"""Opaque NIF-IVA prefix projected from the facts registry. Membership is deliberately not represented by a Python enum. The registry owns the 27 prefixes, including the ``GR`` -> ``EL`` country divergence and ``XI``. A token can only be constructed by the typed registry projection. """__slots__=()_empty_value_message="NIF-IVA prefix must be a non-empty string"
[docs]@dataclass(frozen=True,slots=True)classNifIvaFormatSpec:"""The structural format of one Member State's NIF-IVA. Attributes: prefix: The leading IVA prefix this spec validates. country_name: Human-readable country name for instructive diagnostics. pattern: Anchored regex matched against the full normalised IVA number (prefix included). description: Operator-facing description of the expected shape, e.g. ``"DE + 9 digits"``. example: A well-formed example number for the instructive refusal. """prefix:NifIvaPrefixcountry_name:strpattern:re.Pattern[str]description:strexample:str
[docs]defnormalise_nif_iva(value:str)->str:"""Return the uppercased IVA number with whitespace and separators stripped. Operators routinely paste numbers carrying spaces, dots, or hyphens (``BE 0123.456.789``); the canonical form drops them so the structural pattern matches. """returnvalue.strip().upper().replace(" ","").replace("-","").replace(".","")
[docs]defis_nif_iva_structurally_shaped(value:str)->bool:"""Return whether *value* has a generic prefixed tax-identifier shape. This is deliberately only lexical structure. Country membership and dated per-country patterns are domain-registry policy and are resolved by :mod:`cadrumo.domain.calculations.registry.nif_iva_catalogue`. """normalised=normalise_nif_iva(value)iflen(normalised)<9orlen(normalised)>15:returnFalseprefix,body=normalised[:2],normalised[2:]ifnotprefix.isalpha()ornotbody.isalnum():returnFalseifprefixin{"AA","XX","ZZ"}:returnFalsedigits=sum(character.isdigit()forcharacterinbody)returndigits*2>=len(body)