"""Shared ISO 13616 IBAN shape and checksum primitives.One canonical home for the :data:`IBAN_SHAPE_RE` pattern and:func:`iban_mod_97` check residue, consumed by:data:`domain.calculations.registry._schema.IbanString` for registrycasillas declaring ``data_type = "iban"`` and by the secure-storage:class:`~domain.deadlines.RefundAccount` model. Keeping the primitives in``core`` lets each domain validate an IBAN without importing the other.This module does not canonicalise operator input, decide whether a blank IBAN isallowed, or choose the public error type. Registry casilla validation rejectsblank IBAN data, while the refund-account model treats ``None`` and blank inputas "no account on file"; both call these primitives after applying their ownboundary policy."""from__future__importannotationsimportreIBAN_SHAPE_RE=re.compile(r"^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$")"""ISO 13616 IBAN shape shared by registry and refund-account validators.The pattern checks uppercase canonical text only: country code, two checkdigits, and an alphanumeric BBAN for a total length of 15-34 characters. It isonly the structural gate; callers must also run :func:`iban_mod_97`."""
[docs]defiban_mod_97(canonical:str)->int:"""Compute the ISO 13616 IBAN mod-97 check residue for an already-canonical IBAN. Callers normalize separators and case before matching :data:`IBAN_SHAPE_RE`; see :func:`domain.calculations.registry._schema._validate_iban_string` and :meth:`domain.deadlines.RefundAccount._validate_iban`. This helper moves the leading four characters to the tail, replaces each letter with its ``A=10 ... Z=35`` numeric form, and returns the integer modulo 97. A valid IBAN yields a residue of 1. Args: canonical: Uppercase, separator-free IBAN text that has already matched :data:`IBAN_SHAPE_RE`. Returns: The ISO 13616 check residue. ``1`` means the check digits are valid. """rearranged=canonical[4:]+canonical[:4]numeric="".join(chifch.isdigit()elsestr(ord(ch)-ord("A")+10)forchinrearranged)returnint(numeric)%97