import re from .validation_gs1_check_digit import gs1_check_digit _PREFIX = re.compile(r"^[0-9]{4,12}$") def _whole(value: object) -> bool: return isinstance(value, int) and not isinstance(value, bool) def build_sscc(extension_digit: int, company_prefix: str, serial_reference: int) -> str: """Build an 18-digit SSCC: extension digit, GS1 company prefix, zero-padded serial reference and GS1 mod-10 check digit.""" if not _whole(extension_digit) or extension_digit < 0 or extension_digit > 9: raise ValueError("extensionDigit must be 0 to 9, received %r" % (extension_digit,)) # fullmatch, not match: "$" in re also matches before a trailing newline. if not isinstance(company_prefix, str) or not _PREFIX.fullmatch(company_prefix): raise ValueError('companyPrefix must be 4 to 12 digits, received "%s"' % (company_prefix,)) if not _whole(serial_reference): raise TypeError("serialReference must be an integer, received %r" % (serial_reference,)) if serial_reference < 0: raise ValueError("serialReference must not be negative, received %d" % serial_reference) # Prefix and serial share 16 digits, so a longer prefix leaves less room. # Too large is an error: truncating would reuse another unit's number. room = 16 - len(company_prefix) serial = str(serial_reference) if len(serial) > room: raise ValueError( "serialReference must fit in %d digits after a %d-digit company prefix, received %d" % (room, len(company_prefix), serial_reference) ) data = str(extension_digit) + company_prefix + serial.rjust(room, "0") return data + str(gs1_check_digit(data))