1,817 bytes · the TypeScript implementation · view raw
/** * Build an 18-digit SSCC: extension digit, GS1 company prefix, zero-padded * serial reference and GS1 mod-10 check digit. */exportfunction buildSscc(extensionDigit: number, companyPrefix: string, serialReference: number): string {
if (!Number.isInteger(extensionDigit) || extensionDigit < 0 || extensionDigit > 9) {
thrownew RangeError(`extensionDigit must be 0 to 9, received ${extensionDigit}`);
}
if (typeof companyPrefix !== "string" || !/^[0-9]{4,12}$/.test(companyPrefix)) {
thrownew RangeError(`companyPrefix must be 4 to 12 digits, received "${companyPrefix}"`);
}
if (!Number.isInteger(serialReference)) {
thrownew TypeError(`serialReference must be an integer, received ${serialReference}`);
}
if (serialReference < 0) {
thrownew RangeError(`serialReference must not be negative, received ${serialReference}`);
}
// 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.const room = 16 - companyPrefix.length;
const serial = String(serialReference);
if (serial.length > room) {
thrownew RangeError(
`serialReference must fit in ${room} digits after a ${companyPrefix.length}-digit company prefix, received ${serialReference}`,
);
}
const data = String(extensionDigit) + companyPrefix + serial.padStart(room, "0");
// GS1 mod-10: weights 3, 1, 3, 1 ... from the rightmost data digit leftwards.// validation.gtin does the same sum but only exports validateGtin, which// takes 8 to 14 digits, not an 18-digit SSCC.let total = 0;
let weight = 3;
for (let i = data.length - 1; i >= 0; i--) {
total += (data.charCodeAt(i) - 48) * weight;
weight = weight === 3 ? 1 : 3;
}
return data + String((10 - (total % 10)) % 10);
}