-
Notifications
You must be signed in to change notification settings - Fork 8
/
gtin.ts
80 lines (65 loc) · 2.38 KB
/
gtin.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import type { GTIN } from '@/harmonizer/types.ts';
const gtinFormat = /^\d+$/;
const gtinLengths = [8, 12, 13, 14];
/** Calculates the checksum of the given (numeric) GTIN string, regardless of its length. */
function checksum(gtin: string) {
const length = gtin.length;
return gtin.split('')
.map((digit) => Number(digit))
// checksum factors alternate between 1 and 3, starting with 1 for the last digit
.reduce((checksum, digit, index) => (checksum + digit * ((length - index) % 2 ? 1 : 3)), 0);
}
/** Asserts that the given GTIN has an accepted format/length and a valid check digit. */
export function ensureValidGTIN(gtin: GTIN): void {
gtin = gtin.toString();
if (!gtinLengths.includes(gtin.length)) {
throw new TypeError(`GTIN '${gtin}' has an invalid length`);
}
if (!gtinFormat.test(gtin)) {
throw new TypeError(`GTIN '${gtin}' contains invalid non-numeric characters`);
}
// the checksum of the whole code (including the check digit) has to be a multiple of 10
if (checksum(gtin) % 10 !== 0) {
throw new TypeError(`Checksum of GTIN '${gtin}' is invalid`);
}
}
/** Checks whether the given GTIN has an accepted format/length and a valid check digit. */
export function isValidGTIN(gtin: GTIN): boolean {
try {
ensureValidGTIN(gtin);
} catch {
return false;
}
return true;
}
/**
* Calculates the check digit of the given GTIN, regardless of its length.
* The check digit at the last position has to be present or filled with an arbitrary placeholder.
*/
export function checkDigit(gtin: GTIN) {
// replace check digit or placeholder with zero, which has no effect on the checksum
gtin = gtin.toString().replace(/.$/, '0');
return (10 - checksum(gtin) % 10) % 10;
}
/** Pads the given GTIN to the given length (with zeros) or truncates it. */
export function formatGtin(gtin: GTIN, length = 0): string {
gtin = parseInt(gtin.toString(), 10);
return gtin.toFixed(0).padStart(length, '0');
}
/**
* Compares two GTINs and returns whether they are identical.
* Unless `strict` mode is enabled, leading zeros are ignored.
*/
export function isEqualGTIN(a: GTIN, b: GTIN, { strict = false } = {}): boolean {
a = a.toString();
b = b.toString();
if (strict) {
return a === b;
} else {
return Number(a) === Number(b);
}
}
/** Returns the numeric set of all unique GTIN. */
export function uniqueGtinSet(gtins: GTIN[]): Set<number> {
return new Set(gtins.map(Number));
}