Source code for cadrumo.domain.iva.identification

"""Resolve which Member State a party is IVA-identified in — never where it is established.

Separate from :mod:`.establishment` because the two answer legally distinct
questions, and conflating them is the defect this module exists to end. A
document printing ``DE811234567`` states two things that look like one: that the
party operates under a German IVA identification, and — the reader's inference —
that the party is in Germany. Only the first is evidence.

**Registration IS the identification fact, so a printed prefix is decisive
here.** Where :mod:`.establishment` refuses to let the same prefix settle a
place on its own -- the establishment ladder reaches a territory only once a
printed country name or postal code corroborates one -- this module treats it as
what it actually is: the Member State under whose identification the party
operates, stated by the party itself on its own invoice. There is nothing to
corroborate, because nothing further is being claimed.

**And it establishes NOTHING about establishment, symmetrically on both sides.**
Every Member State registers non-residents on the same terms Spain does, so a
German number is exactly as compatible with a *sede de actividad económica* in
Spain as a Spanish ``N``-leader CIF is with one in Germany (Ley 37/1992
arts. 69–70). The asymmetry that made a foreign prefix decisive for place while
a Spanish one was correctly refused is closed by splitting the fact, not by
demanding corroboration on one side of it.

**Spain cannot arise from a printed IVA number here**, and that is a property of
the prefix vocabulary rather than a judgement made in this module: Spanish
identifiers are checksum identifiers routed through the Spanish tax-id validator
and ``ES`` is absent from :class:`~core.identity.nif_iva.NifIvaPrefix`. A Spanish
identification is therefore established from the Spanish identifier authority or
declared, never inferred here.

**That absence is an implementation boundary, not a statement that the evidence
does not exist.** This path recognises a prefix by matching the number's body
against the structural pattern its own prefix claims, and a Spanish identifier is
validated by the AEAT control-letter checksum instead -- so ``ES`` could not join
the vocabulary as a pattern the way its siblings did. Whether it SHOULD join,
through the checksum validator this codebase already ships, is an open scope
question about the axis rather than a settled refusal, and the earlier wording
here read as the latter.

See Also:
    :class:`~domain.iva.classification.IvaTerritorialScope`
        The OTHER party fact — where the party is established. A value here
        never implies one there.
    :class:`~domain.iva.classification.PartyFact`
        The closed pair, and the axis a classification branch declares it
        consumes.
"""

from __future__ import annotations

from typing import TYPE_CHECKING

from ...core.identity.documents import IdentityError
from ...core.identity.tax_id import validate_spanish_tax_id
from ..calculations.registry.errors import RegistryValidationError
from ..calculations.registry.tax_id_format import runtime_tax_id_format, tax_id_format_value
from .establishment import country_code_for_printed_tax_identifier
from .schema import EUMemberState, require_eu_member_state

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

__all__ = ["identification_state_for_printed_tax_identifier"]


[docs] def identification_state_for_printed_tax_identifier( printed_identifier: str | None, *, operation: PinnedAuthorityOperation, ) -> EUMemberState | None: """Return the Member State a printed IVA number identifies the party in. A composition rather than a second rule set: :func:`._establishment.country_code_for_printed_tax_identifier` stays the single authority on which country a printed number NAMES — including the Greek ``EL``/``GR`` divergence and the requirement that the number's body match the structure its own prefix claims — and this adds only the step from that ISO code to the closed catalogue. Args: printed_identifier: The identifier as transcribed, or ``None``. operation: Caller-owned pinned authority operation for the country and member-state catalogues used by this lookup. Returns: The :class:`~domain.iva.schema.EUMemberState` the number identifies the party in, or ``None`` when no IVA number was recognised. ``None`` means the identification was not established, never that the party is identified nowhere and above all never that it is identified in Spain: a document printing a bare Spanish ``B``-CIF prints no prefix at all, so reading absence as a Spanish identification would manufacture the fact from its own silence. """ code = country_code_for_printed_tax_identifier(printed_identifier, operation=operation) if code is None: return _spanish_identification(printed_identifier, operation=operation) try: return require_eu_member_state(code.lower(), authority=operation) except (TypeError, ValueError, RegistryValidationError): # A prefix naming a country outside the rate-schedule catalogue states a # registration this fact's closed type cannot carry. Unestablished is # the honest answer; inventing a member would be worse than silence. return None
def _spanish_identification( printed_identifier: str | None, *, operation: PinnedAuthorityOperation, ) -> EUMemberState | None: """Return Spain when the printed number is an ES-prefixed Spanish identifier. The other half of an axis that was one-sided. Every sibling prefix is recognised by matching the number's BODY against the structure its prefix claims, and Spanish identifiers are checksum identifiers rather than structural ones -- so ``ES`` could not join the vocabulary the way its siblings did, and the filer's own identification stayed merely assertable while a counterparty's was established from the paper. RGAT art. 25 is what makes the printed form readable: for a party in the Registro de operadores intracomunitarios the identifier is the ordinary one "al que se antepondrá el prefijo ES, conforme al estándar internacional código ISO-3166 alfa 2". So the prefix is regulated rather than conventional, and the body is validated by the shipped AEAT control-letter algorithm instead of by a structural pattern. **This cannot leak a Spanish ESTABLISHMENT, and that is structural rather than careful.** It is reached only where the establishment resolver already returned nothing, it returns an IDENTIFICATION state, and the establishment ladder reads the country code -- which stays empty for Spain by design, because registration is not establishment. A bare Spanish CIF with no prefix is likewise untouched: it prints no prefix, so it states no identification, and reading absence as a Spanish one would manufacture the fact from silence. """ if printed_identifier is None: return None compact = "".join(printed_identifier.split()).replace("-", "").replace(".", "").upper() spanish_prefix = tax_id_format_value("tax_id.country_prefix") if not compact.startswith(spanish_prefix): return None try: validate_spanish_tax_id(compact[len(spanish_prefix) :], runtime_tax_id_format()) except IdentityError: # An ES prefix over a body that fails the control letter is not a # Spanish identification; it is a misread or a different country's # number wearing the wrong prefix. Silence is the honest answer. return None return require_eu_member_state("ES", authority=operation)