from .logistics_hs_code_validate_types import HsCodeCheck
_KINDS = {6: "hs", 8: "cn", 10: "taric"}
def _invalid(reason: str) -> HsCodeCheck:
return HsCodeCheck(
valid=False, normalised=None, kind=None, chapter=None, heading=None, subheading=None, reason=reason
)
def validate_hs_code(value: str) -> HsCodeCheck:
"""Check the format of a 6-digit HS, 8-digit CN or 10-digit TARIC/UK
commodity code. Validators answer rather than raise: a malformed code is
the answer "no"."""
if not isinstance(value, str):
return _invalid("empty")
chars = []
for ch in value:
if ch == " " or ch == ".":
continue
# Compare with ASCII explicitly: str.isdigit() accepts full-width digits.
if ch < "0" or ch > "9":
return _invalid("bad-character")
chars.append(ch)
digits = "".join(chars)
if not digits:
return _invalid("empty")
kind = _KINDS.get(len(digits))
if kind is None:
return _invalid("bad-length")
# Text, not a number: chapter "01" must keep its zero.
chapter = digits[0:2]
if chapter == "00":
return _invalid("bad-chapter")
if chapter == "77":
return _invalid("reserved-chapter")
# 98 and 99 are left to national tariffs, so only a national-length code may use them.
if chapter in ("98", "99") and kind == "hs":
return _invalid("national-chapter")
if digits[2:4] == "00":
return _invalid("bad-heading")
return HsCodeCheck(
valid=True,
normalised=digits,
kind=kind,
chapter=chapter,
heading=digits[0:4],
subheading=digits[0:6],
reason=None,
)