1,927 bytes · the Python implementation · view raw
from typing import List
from .validation_uk_utr_types import UkUtr
#: Weights for the second to tenth digits; the first digit is the check digit.#: From HMRC's own reference checker (hmrc/domain, UtrReferenceChecker).
_WEIGHTS = (6, 7, 8, 9, 10, 5, 4, 3, 2)
#: The check digit for each remainder of the weighted sum modulo 11. It is#: 11 - remainder except that remainders 0 and 1 give 2 and 1, so a check digit#: is always a single digit and no reference is impossible.
_CHECK_DIGITS = (2, 1, 9, 8, 7, 6, 5, 4, 3, 2, 1)
def _invalid(reason: str) -> UkUtr:
return UkUtr(valid=False, normalised=None, reason=reason)
def uk_utr(value: str) -> UkUtr:
"""Check a Unique Taxpayer Reference (Self Assessment or Corporation Tax). A pass means the reference is well formed, not that HMRC issued it to this taxpayer. Checks run in a fixed order (characters, length, check digit) so every language gives the same first reason. """ifnot isinstance(value, str):
return _invalid("empty")
compact = "".join(ch for ch in value if ch != " ")
if compact == "":
return _invalid("empty")
# Self Assessment shows the UTR with a K after it and some letters put# it in front; one K at either end is stripped, as HMRC's checker does.if compact[0] in ("K", "k"):
compact = compact[1:]
elif compact[-1] in ("K", "k"):
compact = compact[:-1]
digits: List[int] = []
for ch in compact:
if ch < "0"or ch > "9":
return _invalid("bad-character")
digits.append(ord(ch) - 48)
if len(digits) == 13:
return _invalid("thirteen-digits")
if len(digits) != 10:
return _invalid("bad-length")
total = sum(d * w for d, w in zip(digits[1:], _WEIGHTS))
if _CHECK_DIGITS[total % 11] != digits[0]:
return _invalid("bad-check-digit")
return UkUtr(valid=True, normalised=compact, reason=None)