@aidc-toolkit/gs1 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/npm-publish.yml +38 -0
- package/.idea/gs1.iml +9 -0
- package/.idea/inspectionProfiles/Project_Default.xml +7 -0
- package/.idea/misc.xml +6 -0
- package/.idea/modules.xml +8 -0
- package/.idea/runConfigurations/Test_all.xml +12 -0
- package/.idea/runConfigurations/Test_check.xml +12 -0
- package/.idea/runConfigurations/Test_identification_key.xml +12 -0
- package/.idea/runConfigurations/build.xml +12 -0
- package/.idea/vcs.xml +6 -0
- package/LICENSE +201 -0
- package/README.md +12 -0
- package/eslint.config.js +15 -0
- package/package.json +44 -0
- package/src/character_set.ts +27 -0
- package/src/check.ts +232 -0
- package/src/idkey.ts +2370 -0
- package/src/index.ts +3 -0
- package/src/locale/en/locale_strings.ts +34 -0
- package/src/locale/i18n.ts +8 -0
- package/src/locale/i18next.d.ts +10 -0
- package/test/check.test.ts +148 -0
- package/test/idkey.test.ts +1033 -0
- package/tsconfig.json +3 -0
- package/typedoc.json +10 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export const localeStrings = {
|
|
2
|
+
Check: {
|
|
3
|
+
lengthOfStringForCheckCharacterPairMustBeLessThanOrEqualTo: "Length {{length}} of string for check character pair must be less than or equal to {{maximumLength}}"
|
|
4
|
+
},
|
|
5
|
+
IdentificationKey: {
|
|
6
|
+
identificationKeyTypeLength: "{{identificationKeyType}} must be {{length}} digits long",
|
|
7
|
+
invalidCheckDigit: "Invalid check digit",
|
|
8
|
+
invalidGTINLength: "GTIN must be 13, 12, 8, or 14 digits long",
|
|
9
|
+
invalidGTIN14Length: "GTIN must be 14 digits long",
|
|
10
|
+
invalidZeroSuppressedGTIN12: "Invalid zero-suppressed GTIN-12",
|
|
11
|
+
invalidZeroSuppressibleGTIN12: "GTIN-12 not zero-suppressible",
|
|
12
|
+
invalidZeroSuppressedGTIN12AsGTIN13: "Invalid zero-suppressed GTIN-12 as GTIN-13",
|
|
13
|
+
invalidZeroSuppressedGTIN12AsGTIN14: "Invalid zero-suppressed GTIN-12 as GTIN-14",
|
|
14
|
+
invalidGTIN13AtRetail: "GTIN-13 at retail consumer trade item level can't start with zero",
|
|
15
|
+
invalidGTINAtRetail: "GTIN not supported at retail consumer trade item level",
|
|
16
|
+
invalidGTINAtOtherThanRetail: "GTIN not supported at other than retail consumer trade item level",
|
|
17
|
+
indicatorDigit: "indicator digit",
|
|
18
|
+
serialComponent: "serial component",
|
|
19
|
+
reference: "reference",
|
|
20
|
+
referenceCantBeAllNumeric: "Reference can't be all-numeric",
|
|
21
|
+
invalidCheckCharacterPair: "Invalid check character pair"
|
|
22
|
+
},
|
|
23
|
+
Prefix: {
|
|
24
|
+
gs1CompanyPrefix: "GS1 Company Prefix",
|
|
25
|
+
upcCompanyPrefix: "U.P.C. Company Prefix",
|
|
26
|
+
gs18Prefix: "GS1-8 Prefix",
|
|
27
|
+
gs1CompanyPrefixCantStartWith0: "GS1 Company Prefix can't start with \"0\"",
|
|
28
|
+
gs1CompanyPrefixCantStartWith00000: "GS1 Company Prefix can't start with \"00000\"",
|
|
29
|
+
gs1CompanyPrefixCantStartWith000000: "GS1 Company Prefix can't start with \"000000\"",
|
|
30
|
+
upcCompanyPrefixCantStartWith0000: "U.P.C. Company Prefix can't start with \"0000\"",
|
|
31
|
+
gs18PrefixCantStartWith0: "GS1-8 Prefix can't start with \"0\"",
|
|
32
|
+
identificationKeyTypeNotSupportedByGS18Prefix: "{{identificationKeyType}} not supported by GS1-8 Prefix"
|
|
33
|
+
}
|
|
34
|
+
} as const;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { i18nAddResourceBundle, i18next } from "@aidc-toolkit/core";
|
|
2
|
+
import { localeStrings as enLocaleStrings } from "./en/locale_strings.js";
|
|
3
|
+
|
|
4
|
+
export const gs1NS = "aidct_gs1";
|
|
5
|
+
|
|
6
|
+
i18nAddResourceBundle("en", gs1NS, enLocaleStrings);
|
|
7
|
+
|
|
8
|
+
export default i18next;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { NUMERIC_CREATOR } from "@aidc-toolkit/utility";
|
|
2
|
+
import { describe, expect, test } from "vitest";
|
|
3
|
+
import { I18NEnvironment, i18nInit } from "../../core/dist/index.js";
|
|
4
|
+
import {
|
|
5
|
+
checkCharacterPair,
|
|
6
|
+
checkDigit,
|
|
7
|
+
checkDigitSum,
|
|
8
|
+
fiveDigitPriceWeightCheckDigit,
|
|
9
|
+
fourDigitPriceWeightCheckDigit,
|
|
10
|
+
hasValidCheckCharacterPair,
|
|
11
|
+
hasValidCheckDigit
|
|
12
|
+
} from "../src/index.js";
|
|
13
|
+
|
|
14
|
+
await i18nInit(I18NEnvironment.CLI, true);
|
|
15
|
+
|
|
16
|
+
describe("Check digit", () => {
|
|
17
|
+
const testNumericString = "1234567890";
|
|
18
|
+
|
|
19
|
+
test("Basic check digit sum", () => {
|
|
20
|
+
expect(checkDigitSum(false, testNumericString)).toBe((1 + 3 + 5 + 7 + 9) * 1 + (2 + 4 + 6 + 8 + 0) * 3);
|
|
21
|
+
expect(checkDigitSum(true, testNumericString)).toBe((1 + 3 + 5 + 7 + 9) * 3 + (2 + 4 + 6 + 8 + 0) * 1);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("Calculation and validation", () => {
|
|
25
|
+
const calculatedCheckDigit = String(9 - ((checkDigitSum(true, testNumericString) + 9) % 10));
|
|
26
|
+
|
|
27
|
+
expect(checkDigitSum(true, testNumericString + calculatedCheckDigit) % 10).toBe(0);
|
|
28
|
+
expect(checkDigit(testNumericString)).toBe(calculatedCheckDigit);
|
|
29
|
+
expect(hasValidCheckDigit(testNumericString + checkDigit(testNumericString))).toBe(true);
|
|
30
|
+
expect(hasValidCheckDigit(testNumericString.replace("5", "6") + calculatedCheckDigit)).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("Exception", () => {
|
|
34
|
+
const testNonNumericString = "123456789O";
|
|
35
|
+
|
|
36
|
+
expect(() => checkDigitSum(false, testNonNumericString)).toThrow("Invalid character 'O' at position 10");
|
|
37
|
+
expect(() => checkDigit(testNonNumericString)).toThrow("Invalid character 'O' at position 10");
|
|
38
|
+
expect(() => hasValidCheckDigit(testNonNumericString)).toThrow("Invalid character 'O' at position 10");
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe("Price/weight check digit", () => {
|
|
43
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
44
|
+
function weight2Minus(characterIndex: number): number {
|
|
45
|
+
const product = characterIndex * 2;
|
|
46
|
+
|
|
47
|
+
return (product - Math.trunc(product / 10)) % 10;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
51
|
+
function weight3(characterIndex: number): number {
|
|
52
|
+
return characterIndex * 3 % 10;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
56
|
+
function weight5Plus(characterIndex: number): number {
|
|
57
|
+
const product = characterIndex * 5;
|
|
58
|
+
|
|
59
|
+
return (product + Math.trunc(product / 10)) % 10;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
63
|
+
function weight5Minus(characterIndex: number): number {
|
|
64
|
+
const product = characterIndex * 5;
|
|
65
|
+
|
|
66
|
+
return (product - Math.trunc(product / 10)) % 10;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
70
|
+
function testFourDigitPriceWeightCheckDigit(s: string): void {
|
|
71
|
+
const characterIndexes = NUMERIC_CREATOR.characterIndexes(s);
|
|
72
|
+
|
|
73
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
74
|
+
const sum = weight2Minus(characterIndexes[0]!) + weight2Minus(characterIndexes[1]!) + weight3(characterIndexes[2]!) + weight5Minus(characterIndexes[3]!);
|
|
75
|
+
|
|
76
|
+
expect(fourDigitPriceWeightCheckDigit(s)).toBe(NUMERIC_CREATOR.character(sum * 3 % 10));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// eslint-disable-next-line jsdoc/require-jsdoc
|
|
80
|
+
function testFiveDigitPriceWeightCheckDigit(s: string): void {
|
|
81
|
+
const characterIndexes = NUMERIC_CREATOR.characterIndexes(s);
|
|
82
|
+
|
|
83
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
84
|
+
const sum = weight5Plus(characterIndexes[0]!) + weight2Minus(characterIndexes[1]!) + weight5Minus(characterIndexes[2]!) + weight5Plus(characterIndexes[3]!) + weight2Minus(characterIndexes[4]!);
|
|
85
|
+
|
|
86
|
+
expect(weight5Minus(Number(fiveDigitPriceWeightCheckDigit(s)))).toBe(9 - (sum + 9) % 10);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
test("Four-digit", () => {
|
|
90
|
+
testFourDigitPriceWeightCheckDigit("0123");
|
|
91
|
+
testFourDigitPriceWeightCheckDigit("1234");
|
|
92
|
+
testFourDigitPriceWeightCheckDigit("2345");
|
|
93
|
+
testFourDigitPriceWeightCheckDigit("3456");
|
|
94
|
+
testFourDigitPriceWeightCheckDigit("4567");
|
|
95
|
+
testFourDigitPriceWeightCheckDigit("5678");
|
|
96
|
+
testFourDigitPriceWeightCheckDigit("6789");
|
|
97
|
+
testFourDigitPriceWeightCheckDigit("7890");
|
|
98
|
+
testFourDigitPriceWeightCheckDigit("8901");
|
|
99
|
+
testFourDigitPriceWeightCheckDigit("9012");
|
|
100
|
+
|
|
101
|
+
expect(() => fourDigitPriceWeightCheckDigit("l234")).toThrow("Invalid character 'l' at position 1");
|
|
102
|
+
expect(() => fourDigitPriceWeightCheckDigit("123")).toThrow("String for price or weight sum must be exactly 4 characters");
|
|
103
|
+
expect(() => fourDigitPriceWeightCheckDigit("12345")).toThrow("String for price or weight sum must be exactly 4 characters");
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test("Five-digit", () => {
|
|
107
|
+
testFiveDigitPriceWeightCheckDigit("01234");
|
|
108
|
+
testFiveDigitPriceWeightCheckDigit("12345");
|
|
109
|
+
testFiveDigitPriceWeightCheckDigit("23456");
|
|
110
|
+
testFiveDigitPriceWeightCheckDigit("34567");
|
|
111
|
+
testFiveDigitPriceWeightCheckDigit("45678");
|
|
112
|
+
testFiveDigitPriceWeightCheckDigit("56789");
|
|
113
|
+
testFiveDigitPriceWeightCheckDigit("67890");
|
|
114
|
+
testFiveDigitPriceWeightCheckDigit("78901");
|
|
115
|
+
testFiveDigitPriceWeightCheckDigit("89012");
|
|
116
|
+
testFiveDigitPriceWeightCheckDigit("90123");
|
|
117
|
+
|
|
118
|
+
expect(() => fiveDigitPriceWeightCheckDigit("l2345")).toThrow("Invalid character 'l' at position 1");
|
|
119
|
+
expect(() => fiveDigitPriceWeightCheckDigit("1234")).toThrow("String for price or weight sum must be exactly 5 characters");
|
|
120
|
+
expect(() => fiveDigitPriceWeightCheckDigit("123456")).toThrow("String for price or weight sum must be exactly 5 characters");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("Check character pair", () => {
|
|
125
|
+
test("Calculation and validation", () => {
|
|
126
|
+
expect(checkCharacterPair("95212349521234")).toBe("R9");
|
|
127
|
+
expect(checkCharacterPair("9521234ABCDEFabcdef")).toBe("8T");
|
|
128
|
+
|
|
129
|
+
expect(checkCharacterPair("!\"%&'()*+,-./0123456789:;<=>")).toBe("TH");
|
|
130
|
+
expect(checkCharacterPair("?ABCDEFGHIJKLMNOPQRSTUVWXYZ_")).toBe("EP");
|
|
131
|
+
expect(checkCharacterPair("abcdefghijklmnopqrstuvwxyz")).toBe("5A");
|
|
132
|
+
|
|
133
|
+
expect(hasValidCheckCharacterPair("95212349521234R9")).toBe(true);
|
|
134
|
+
expect(hasValidCheckCharacterPair("9521234ABCDEFabcdef8T")).toBe(true);
|
|
135
|
+
|
|
136
|
+
expect(hasValidCheckCharacterPair("!\"%&'()*+,-./0123456789:;<=>TH")).toBe(true);
|
|
137
|
+
expect(hasValidCheckCharacterPair("?ABCDEFGHIJKLMNOPQRSTUVWXYZ_EP")).toBe(true);
|
|
138
|
+
expect(hasValidCheckCharacterPair("abcdefghijklmnopqrstuvwxyz5A")).toBe(true);
|
|
139
|
+
expect(hasValidCheckCharacterPair("abcdefghijklmnopqrstuvwxyz5B")).toBe(false);
|
|
140
|
+
expect(hasValidCheckCharacterPair("abcdefghijklmnopqrstuvwxyz5~")).toBe(false);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("Exception", () => {
|
|
144
|
+
expect(() => checkCharacterPair("?ABCDEFGHIJKLMNOPQRSTUVWXYZ_!")).toThrow("Length 29 of string for check character pair must be less than or equal to 28");
|
|
145
|
+
expect(() => checkCharacterPair("abcdefghijklmnopqrstuvwxyz~")).toThrow("Invalid character '~' at position 27");
|
|
146
|
+
expect(() => hasValidCheckCharacterPair("~abcdefghijklmnopqrstuvwxyz")).toThrow("Invalid character '~' at position 1");
|
|
147
|
+
});
|
|
148
|
+
});
|