@aidc-toolkit/gs1 0.9.17-beta → 0.9.19-beta

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/src/idkey.ts CHANGED
@@ -2,13 +2,13 @@ import {
2
2
  CharacterSetCreator,
3
3
  type CharacterSetValidation,
4
4
  Exclusion,
5
+ mapIterable,
5
6
  NUMERIC_CREATOR,
6
7
  RegExpValidator,
7
8
  type StringValidation,
8
9
  type StringValidator,
9
10
  type TransformerInput,
10
- type TransformerOutput,
11
- transformIterable
11
+ type TransformerOutput
12
12
  } from "@aidc-toolkit/utility";
13
13
  import { Mixin } from "ts-mixer";
14
14
  import { AI39_CREATOR, AI82_CREATOR } from "./character-set.js";
@@ -17,7 +17,9 @@ import {
17
17
  checkDigit,
18
18
  checkDigitSum,
19
19
  hasValidCheckCharacterPair,
20
- hasValidCheckDigit
20
+ hasValidCheckDigit,
21
+ isValidPriceOrWeightCheckDigit,
22
+ priceOrWeightCheckDigit
21
23
  } from "./check.js";
22
24
  import { i18nextGS1 } from "./locale/i18n.js";
23
25
 
@@ -141,6 +143,9 @@ export interface IdentificationKeyValidation extends StringValidation {
141
143
  /**
142
144
  * Identification key validator. Validates an identification key against its definition in section 3 of the {@link
143
145
  * https://www.gs1.org/genspecs | GS1 General Specifications}.
146
+ *
147
+ * @template TIdentificationKeyValidation
148
+ * Identification key validation type.
144
149
  */
145
150
  export interface IdentificationKeyValidator<TIdentificationKeyValidation extends IdentificationKeyValidation = IdentificationKeyValidation> extends StringValidator<TIdentificationKeyValidation> {
146
151
  /**
@@ -167,7 +172,7 @@ export interface IdentificationKeyValidator<TIdentificationKeyValidation extends
167
172
  get referenceCharacterSet(): ContentCharacterSet;
168
173
 
169
174
  /**
170
- * Get the reference validator.
175
+ * Get the reference creator.
171
176
  */
172
177
  get referenceCreator(): CharacterSetCreator;
173
178
 
@@ -467,6 +472,21 @@ export enum GTINLevel {
467
472
  OtherThanRetailConsumer
468
473
  }
469
474
 
475
+ /**
476
+ * Restricted Circulation Number reference.
477
+ */
478
+ export interface RCNReference {
479
+ /**
480
+ * Item reference.
481
+ */
482
+ itemReference: number;
483
+
484
+ /**
485
+ * Price or weight (whole number only).
486
+ */
487
+ priceOrWeight: number;
488
+ }
489
+
470
490
  /**
471
491
  * GTIN validator.
472
492
  */
@@ -629,10 +649,8 @@ export class GTINValidator extends AbstractNumericIdentificationKeyValidator {
629
649
 
630
650
  NUMERIC_CREATOR.validate(indicatorDigit, GTINValidator.OPTIONAL_INDICATOR_DIGIT_VALIDATION);
631
651
 
632
- const gtinLength = gtin.length;
633
-
634
652
  // Check digit doesn't change by prepending zeros.
635
- let gtin14 = "0".repeat(GTINType.GTIN14 - gtinLength) + gtin;
653
+ let gtin14 = gtin.padStart(GTINType.GTIN14, "0");
636
654
 
637
655
  // If indicator digit provided and is different, recalculate the check digit.
638
656
  if (indicatorDigit.length !== 0 && indicatorDigit !== gtin14.charAt(0)) {
@@ -802,6 +820,134 @@ export class GTINValidator extends AbstractNumericIdentificationKeyValidator {
802
820
 
803
821
  GTINCreator.validateAny(gtin14);
804
822
  }
823
+
824
+ /**
825
+ * Parse a Restricted Circulation Number (RCN) using a variable measure trade item format. The format is a 12- or
826
+ * 13-character string (for RCN-12 or RCN-13 respectively), containing the following:
827
+ *
828
+ * - '2' - The first character of the RCN.
829
+ * - '0'-'9' - The second character of the RCN (RCN-13 only).
830
+ * - 'I' - One or more, in sequence, for the item reference.
831
+ * - 'P' - One or more, in sequence, for the price or weight.
832
+ * - 'V' - Zero or one, for the price or weight check digit.
833
+ * - 'C' - The check digit of the entire RCN.
834
+ *
835
+ * The 'I', 'P', and 'V' formats may be in any order.
836
+ *
837
+ * Some examples:
838
+ *
839
+ * - 2IIIIIVPPPPC - RCN-12 with a five-digit item reference, a price or weight check digit, and a four-digit price
840
+ * or weight.
841
+ * - 23IIIIVPPPPPC - RCN-13 with a four-digit item reference, a price or weight check digit, and a five-digit price
842
+ * or weight.
843
+ * - 2IIIIIIPPPPC - RCN-12 with a six-digit item reference and a four-digit price or eight.
844
+ * - 29IIIIIPPPPPC - RCN-13 with a five-digit item reference and a five-digit price or weight.
845
+ *
846
+ * @param format
847
+ * Format.
848
+ *
849
+ * @param rcn
850
+ * RCN.
851
+ *
852
+ * @returns
853
+ * RCN reference.
854
+ */
855
+ static parseVariableMeasureRCN(format: string, rcn: string): RCNReference {
856
+ const formatLength = format.length;
857
+
858
+ if (rcn.length !== formatLength) {
859
+ throw new RangeError(i18nextGS1.t("IdentificationKey.invalidRCNLength"));
860
+ }
861
+
862
+ let validFormat = formatLength === 12 || formatLength === 13;
863
+ let validRCNPrefix = true;
864
+
865
+ let buildingItemReference = false;
866
+ let itemReference = "";
867
+
868
+ let buildingPriceOrWeight = false;
869
+ let priceOrWeight = "";
870
+
871
+ let priceOrWeightCheckDigit = "";
872
+
873
+ for (let index = 0; validFormat && index < formatLength; index++) {
874
+ const formatChar = format.charAt(index);
875
+ const rcnChar = rcn.charAt(index);
876
+
877
+ if (index === 0) {
878
+ validFormat = formatChar === "2";
879
+ validRCNPrefix = rcnChar === "2";
880
+ } else if (formatLength === 13 && index === 1) {
881
+ validFormat = NUMERIC_CREATOR.characterIndex(formatChar) !== undefined;
882
+ validRCNPrefix = rcnChar === formatChar;
883
+ } else if (index === formatLength - 1) {
884
+ validFormat = formatChar === "C";
885
+ } else {
886
+ switch (formatChar) {
887
+ case "I":
888
+ if (!buildingItemReference) {
889
+ // Item reference can't appear more than once.
890
+ validFormat = itemReference === "";
891
+
892
+ buildingItemReference = true;
893
+ buildingPriceOrWeight = false;
894
+ }
895
+
896
+ itemReference += rcnChar;
897
+ break;
898
+
899
+ case "P":
900
+ if (!buildingPriceOrWeight) {
901
+ // Price or weight can't appear more than once.
902
+ validFormat = priceOrWeight === "";
903
+
904
+ buildingPriceOrWeight = true;
905
+ buildingItemReference = false;
906
+ }
907
+
908
+ priceOrWeight += rcnChar;
909
+ break;
910
+
911
+ case "V":
912
+ // Price or weight check digit can't appear more than once.
913
+ validFormat = priceOrWeightCheckDigit === "";
914
+
915
+ buildingItemReference = false;
916
+ buildingPriceOrWeight = false;
917
+
918
+ priceOrWeightCheckDigit = rcnChar;
919
+ break;
920
+
921
+ default:
922
+ validFormat = false;
923
+ break;
924
+ }
925
+ }
926
+ }
927
+
928
+ validFormat &&= itemReference !== "" && priceOrWeight !== "";
929
+
930
+ if (!validFormat) {
931
+ throw new RangeError(i18nextGS1.t("IdentificationKey.invalidVariableMeasureRCNFormat"));
932
+ }
933
+
934
+ if (!validRCNPrefix) {
935
+ throw new RangeError(i18nextGS1.t("IdentificationKey.invalidVariableMeasureRCNPrefix"));
936
+ }
937
+
938
+ if (priceOrWeightCheckDigit !== "" && !isValidPriceOrWeightCheckDigit(priceOrWeight, priceOrWeightCheckDigit)) {
939
+ throw new RangeError(i18nextGS1.t("IdentificationKey.invalidVariableMeasurePriceOrWeight"));
940
+ }
941
+
942
+ if (!hasValidCheckDigit(rcn)) {
943
+ throw new RangeError(i18nextGS1.t("IdentificationKey.invalidCheckDigit"));
944
+ }
945
+
946
+ return {
947
+ itemReference: Number(itemReference),
948
+ priceOrWeight: Number(priceOrWeight)
949
+ };
950
+ }
805
951
  }
806
952
 
807
953
  /**
@@ -1194,6 +1340,9 @@ export interface NumericIdentificationKeyCreator extends NumericIdentificationKe
1194
1340
  * Create identification key(s) with reference(s) based on numeric value(s). The value(s) is/are converted to
1195
1341
  * references of the appropriate length using {@linkcode NUMERIC_CREATOR}.
1196
1342
  *
1343
+ * @template TTransformerInput
1344
+ * Transformer input type.
1345
+ *
1197
1346
  * @param valueOrValues
1198
1347
  * Numeric value(s).
1199
1348
  *
@@ -1219,7 +1368,8 @@ export interface NumericIdentificationKeyCreator extends NumericIdentificationKe
1219
1368
  }
1220
1369
 
1221
1370
  /**
1222
- * Abstract numeric identification key creator. Implements common functionality for a numeric identification key creator.
1371
+ * Abstract numeric identification key creator. Implements common functionality for a numeric identification key
1372
+ * creator.
1223
1373
  */
1224
1374
  abstract class AbstractNumericIdentificationKeyCreator extends AbstractIdentificationKeyCreator implements NumericIdentificationKeyCreator {
1225
1375
  /**
@@ -1405,6 +1555,9 @@ export class GTINCreator extends Mixin(GTINValidator, AbstractNumericIdentificat
1405
1555
  * Create GTIN-14(s) with an indicator digit and reference(s) based on numeric value(s). The value(s) is/are
1406
1556
  * converted to reference(s) of the appropriate length using {@linkcode NUMERIC_CREATOR}.
1407
1557
  *
1558
+ * @template TTransformerInput
1559
+ * Transformer input type.
1560
+ *
1408
1561
  * @param indicatorDigit
1409
1562
  * Indicator digit.
1410
1563
  *
@@ -1426,6 +1579,122 @@ export class GTINCreator extends Mixin(GTINValidator, AbstractNumericIdentificat
1426
1579
  return partialIdentificationKey + checkDigit(partialIdentificationKey);
1427
1580
  });
1428
1581
  }
1582
+
1583
+ /**
1584
+ * Create a Restricted Circulation Number (RCN) using a variable measure trade item format. See {@linkcode
1585
+ * GTINValidator.parseVariableMeasureRCN} for format details.
1586
+ *
1587
+ * @param format
1588
+ * Format.
1589
+ *
1590
+ * @param itemReference
1591
+ * Item reference.
1592
+ *
1593
+ * @param priceOrWeight
1594
+ * Price or weight (whole number only).
1595
+ *
1596
+ * @returns
1597
+ * RCN-12 or RCN-13.
1598
+ */
1599
+ static createVariableMeasureRCN(format: string, itemReference: number, priceOrWeight: number): string {
1600
+ const formatLength = format.length;
1601
+
1602
+ let validFormat = formatLength === 12 || formatLength === 13;
1603
+
1604
+ let rcnPrefix = "";
1605
+
1606
+ let buildingItemReference = false;
1607
+ let itemReferenceString = "";
1608
+ let itemReferenceLength = 0;
1609
+
1610
+ let buildingPriceOrWeight = false;
1611
+ let priceOrWeightString = "";
1612
+ let priceOrWeightLength = 0;
1613
+
1614
+ let calculatePriceOrWeightCheckDigit = false;
1615
+
1616
+ // RCN may be built in almost any order, so defer to builders that will be in ordered array.
1617
+ const rcnPrefixBuilder = (partialRCN: string): string => partialRCN + rcnPrefix;
1618
+ const itemReferenceBuilder = (partialRCN: string): string => partialRCN + itemReferenceString;
1619
+ const priceOrWeightBuilder = (partialRCN: string): string => partialRCN + priceOrWeightString;
1620
+ const priceOrWeightCheckDigitBuilder = (partialRCN: string): string => partialRCN + priceOrWeightCheckDigit(priceOrWeightString);
1621
+ const checkDigitBuilder = (partialRCN: string): string => partialRCN + checkDigit(partialRCN);
1622
+
1623
+ const rcnBuilders = [rcnPrefixBuilder];
1624
+
1625
+ for (let index = 0; validFormat && index < formatLength; index++) {
1626
+ const formatChar = format.charAt(index);
1627
+
1628
+ if (index === 0) {
1629
+ validFormat = formatChar === "2";
1630
+ rcnPrefix = formatChar;
1631
+ } else if (formatLength === 13 && index === 1) {
1632
+ validFormat = NUMERIC_CREATOR.characterIndex(formatChar) !== undefined;
1633
+ rcnPrefix += formatChar;
1634
+ } else if (index === formatLength - 1) {
1635
+ validFormat = formatChar === "C";
1636
+ } else {
1637
+ switch (formatChar) {
1638
+ case "I":
1639
+ if (!buildingItemReference) {
1640
+ // Item reference can't appear more than once.
1641
+ validFormat = itemReferenceLength === 0;
1642
+
1643
+ buildingItemReference = true;
1644
+ buildingPriceOrWeight = false;
1645
+
1646
+ rcnBuilders.push(itemReferenceBuilder);
1647
+ }
1648
+
1649
+ itemReferenceLength++;
1650
+ break;
1651
+
1652
+ case "P":
1653
+ if (!buildingPriceOrWeight) {
1654
+ // Price or weight can't appear more than once.
1655
+ validFormat = priceOrWeightLength === 0;
1656
+
1657
+ buildingPriceOrWeight = true;
1658
+ buildingItemReference = false;
1659
+
1660
+ rcnBuilders.push(priceOrWeightBuilder);
1661
+ }
1662
+
1663
+ priceOrWeightLength++;
1664
+ break;
1665
+
1666
+ case "V":
1667
+ // Price or weight check digit can't appear more than once.
1668
+ validFormat = !calculatePriceOrWeightCheckDigit;
1669
+
1670
+ buildingItemReference = false;
1671
+ buildingPriceOrWeight = false;
1672
+
1673
+ calculatePriceOrWeightCheckDigit = true;
1674
+
1675
+ rcnBuilders.push(priceOrWeightCheckDigitBuilder);
1676
+ break;
1677
+
1678
+ default:
1679
+ validFormat = false;
1680
+ break;
1681
+ }
1682
+ }
1683
+ }
1684
+
1685
+ validFormat &&= itemReferenceLength !== 0 && priceOrWeightLength !== 0;
1686
+
1687
+ if (!validFormat) {
1688
+ throw new RangeError(i18nextGS1.t("IdentificationKey.invalidVariableMeasureRCNFormat"));
1689
+ }
1690
+
1691
+ itemReferenceString = NUMERIC_CREATOR.create(itemReferenceLength, itemReference);
1692
+ priceOrWeightString = NUMERIC_CREATOR.create(priceOrWeightLength, priceOrWeight);
1693
+
1694
+ rcnBuilders.push(checkDigitBuilder);
1695
+
1696
+ return rcnBuilders.reduce((partialRCN, rcnBuilder) => rcnBuilder(partialRCN), "");
1697
+ }
1429
1698
  }
1430
1699
 
1431
1700
  /**
@@ -1487,6 +1756,9 @@ export class SerializableNumericIdentificationKeyCreator extends Mixin(Serializa
1487
1756
  /**
1488
1757
  * Concatenate a validated base identification key with serial component(s).
1489
1758
  *
1759
+ * @template TTransformerInput
1760
+ * Transformer input type.
1761
+ *
1490
1762
  * @param baseIdentificationKey
1491
1763
  * Base identification key.
1492
1764
  *
@@ -1521,7 +1793,7 @@ export class SerializableNumericIdentificationKeyCreator extends Mixin(Serializa
1521
1793
  if (typeof serialComponentOrComponents !== "object") {
1522
1794
  result = validateAndConcatenate(serialComponentOrComponents);
1523
1795
  } else {
1524
- result = transformIterable(serialComponentOrComponents, validateAndConcatenate);
1796
+ result = mapIterable(serialComponentOrComponents, validateAndConcatenate);
1525
1797
  }
1526
1798
 
1527
1799
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Type determination is handled above.
@@ -1532,6 +1804,9 @@ export class SerializableNumericIdentificationKeyCreator extends Mixin(Serializa
1532
1804
  * Create serialized identification key(s) with a reference based on a numeric value concatenated with serial
1533
1805
  * component(s). The value is converted to a reference of the appropriate length using {@linkcode NUMERIC_CREATOR}.
1534
1806
  *
1807
+ * @template TTransformerInput
1808
+ * Transformer input type.
1809
+ *
1535
1810
  * @param value
1536
1811
  * Numeric value of the reference.
1537
1812
  *
@@ -1551,6 +1826,9 @@ export class SerializableNumericIdentificationKeyCreator extends Mixin(Serializa
1551
1826
  /**
1552
1827
  * Concatenate a base identification key with serial component(s).
1553
1828
  *
1829
+ * @template TTransformerInput
1830
+ * Transformer input type.
1831
+ *
1554
1832
  * @param baseIdentificationKey
1555
1833
  * Base identification key.
1556
1834
  *
@@ -1618,6 +1896,9 @@ export class NonNumericIdentificationKeyCreator extends Mixin(NonNumericIdentifi
1618
1896
  /**
1619
1897
  * Create identification key(s) with reference(s).
1620
1898
  *
1899
+ * @template TTransformerInput
1900
+ * Transformer input type.
1901
+ *
1621
1902
  * @param referenceOrReferences
1622
1903
  * Reference(s).
1623
1904
  *
@@ -1653,7 +1934,7 @@ export class NonNumericIdentificationKeyCreator extends Mixin(NonNumericIdentifi
1653
1934
  if (typeof referenceOrReferences !== "object") {
1654
1935
  result = validateAndCreate(referenceOrReferences);
1655
1936
  } else {
1656
- result = transformIterable(referenceOrReferences, validateAndCreate);
1937
+ result = mapIterable(referenceOrReferences, validateAndCreate);
1657
1938
  }
1658
1939
 
1659
1940
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Type determination is handled above.
@@ -1913,7 +2194,7 @@ export class PrefixManager {
1913
2194
 
1914
2195
  // Creator tweak factor is defined for numeric identification keys only.
1915
2196
  if (creatorTweakFactor !== undefined) {
1916
- // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Explicit cast without testing is necessary as "instanceof" doesn't work for mixin types.
2197
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion,no-param-reassign -- Explicit cast without testing is necessary as "instanceof" doesn't work for mixin types. Method purpose is to set the tweak.
1917
2198
  (creator as AbstractNumericIdentificationKeyCreator).tweak = this.tweakFactor * creatorTweakFactor;
1918
2199
  }
1919
2200
  }
@@ -2089,7 +2370,7 @@ export class PrefixManager {
2089
2370
  */
2090
2371
  private getIdentificationKeyCreator<TIdentificationKeyCreator extends IdentificationKeyCreator>(identificationKeyType: IdentificationKeyType, constructorCallback: () => TIdentificationKeyCreator): TIdentificationKeyCreator {
2091
2372
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion -- Type is paired with constructor callback.
2092
- let creator = this._identificationKeyCreatorsMap.get(identificationKeyType) as (TIdentificationKeyCreator | undefined);
2373
+ let creator = this._identificationKeyCreatorsMap.get(identificationKeyType) as TIdentificationKeyCreator | undefined;
2093
2374
 
2094
2375
  if (creator === undefined) {
2095
2376
  if (this.prefixType === PrefixType.GS18Prefix && identificationKeyType !== IdentificationKeyType.GTIN) {
@@ -1,11 +1,11 @@
1
1
  export const localeStrings = {
2
2
  Check: {
3
- lengthOfStringForPriceOrWeightMustBeExactly: "Length {{length}} of string for price or weight sum must be exactly {{exactLength}}",
3
+ lengthOfStringForPriceOrWeightMustBeExactly: "Length {{length, number}} of string for price or weight must be 4 or 5",
4
4
  priceOrWeightComponent: "price or weight",
5
- lengthOfStringForCheckCharacterPairMustBeLessThanOrEqualTo: "Length {{length}} of string for check character pair must be less than or equal to {{maximumLength}}"
5
+ lengthOfStringForCheckCharacterPairMustBeLessThanOrEqualTo: "Length {{length, number}} of string for check character pair must be less than or equal to {{maximumLength, number}}"
6
6
  },
7
7
  IdentificationKey: {
8
- identificationKeyTypeLength: "{{identificationKeyType}} must be {{length}} digits long",
8
+ identificationKeyTypeLength: "{{identificationKeyType}} must be {{length, number}} digits long",
9
9
  invalidCheckDigit: "Invalid check digit",
10
10
  invalidGTINLength: "GTIN must be 13, 12, 8, or 14 digits long",
11
11
  invalidGTIN14Length: "GTIN must be 14 digits long",
@@ -16,6 +16,10 @@ export const localeStrings = {
16
16
  invalidGTIN13AtRetail: "GTIN-13 at retail consumer trade item level can't start with zero",
17
17
  invalidGTINAtRetail: "GTIN not supported at retail consumer trade item level",
18
18
  invalidGTINAtOtherThanRetail: "GTIN not supported at other than retail consumer trade item level",
19
+ invalidRCNLength: "RCN length must match format length",
20
+ invalidVariableMeasureRCNFormat: "Invalid variable measure RCN format",
21
+ invalidVariableMeasureRCNPrefix: "Invalid variable measure RCN prefix",
22
+ invalidVariableMeasurePriceOrWeight: "Invalid variable measure price or weight",
19
23
  indicatorDigit: "indicator digit",
20
24
  serialComponent: "serial component",
21
25
  reference: "reference",
@@ -1,11 +1,11 @@
1
1
  export const localeStrings = {
2
2
  Check: {
3
- lengthOfStringForPriceOrWeightMustBeExactly: "La longueur {{longueur}} de la chaîne pour le prix ou la somme du poids doit être exactement {{exactLength}}",
3
+ lengthOfStringForPriceOrWeightMustBeExactly: "La longueur {{length, number}} de la chaîne pour le prix ou le poids doit être 4 ou 5",
4
4
  priceOrWeightComponent: "prix ou poids",
5
- lengthOfStringForCheckCharacterPairMustBeLessThanOrEqualTo: "La longueur {{length}} de la chaîne pour la paire de caractères de vérification doit être inférieure ou égale à {{maximum Length}}"
5
+ lengthOfStringForCheckCharacterPairMustBeLessThanOrEqualTo: "La longueur {{length, number}} de la chaîne pour la paire de caractères de vérification doit être inférieure ou égale à {{maximum Length}}"
6
6
  },
7
7
  IdentificationKey: {
8
- identificationKeyTypeLength: "{{identificationKeyType}} doit comporter {{length}} chiffres",
8
+ identificationKeyTypeLength: "{{identificationKeyType}} doit comporter {{length, number}} chiffres",
9
9
  invalidCheckDigit: "Chiffre de contrôle non valide",
10
10
  invalidGTINLength: "Le GTIN doit comporter 13, 12, 8 ou 14 chiffres",
11
11
  invalidGTIN14Length: "Le GTIN doit comporter 14 chiffres",
@@ -16,6 +16,10 @@ export const localeStrings = {
16
16
  invalidGTIN13AtRetail: "Le GTIN-13 au niveau des articles de consommation au détail ne peut pas commencer par zéro",
17
17
  invalidGTINAtRetail: "Le GTIN n'est pas pris en charge au niveau des articles de consommation au détail",
18
18
  invalidGTINAtOtherThanRetail: "Le GTIN n'est pas pris en charge à d'autres niveaux que ceux des articles de consommation au détail",
19
+ invalidRCNLength: "La longueur du RCN doit correspondre à la longueur du format",
20
+ invalidVariableMeasureRCNFormat: "Format RCN de mesure variable non valide",
21
+ invalidVariableMeasureRCNPrefix: "Préfixe RCN de mesure variable non valide",
22
+ invalidVariableMeasurePriceOrWeight: "Mesure variable invalide : prix ou poids",
19
23
  indicatorDigit: "chiffre indicateur",
20
24
  serialComponent: "composant série",
21
25
  reference: "référence",
@@ -1,6 +1,6 @@
1
- import { i18nAssertValidResources, i18nCoreInit, type I18NEnvironment } from "@aidc-toolkit/core";
1
+ import { i18nAssertValidResources, i18nCoreInit, type I18nEnvironment } from "@aidc-toolkit/core";
2
2
  import { i18nUtilityInit, utilityResources } from "@aidc-toolkit/utility";
3
- import i18next, { type i18n } from "i18next";
3
+ import i18next, { type i18n, type Resource } from "i18next";
4
4
  import { localeStrings as enLocaleStrings } from "./en/locale-strings.js";
5
5
  import { localeStrings as frLocaleStrings } from "./fr/locale-strings.js";
6
6
 
@@ -16,7 +16,7 @@ i18nAssertValidResources(enLocaleStrings, "fr", frLocaleStrings);
16
16
  /**
17
17
  * GS1 resources.
18
18
  */
19
- export const gs1Resources = {
19
+ export const gs1Resources: Resource = {
20
20
  en: {
21
21
  aidct_gs1: enLocaleStrings
22
22
  },
@@ -40,7 +40,7 @@ export const i18nextGS1: i18n = i18next.createInstance();
40
40
  * @returns
41
41
  * Void promise.
42
42
  */
43
- export async function i18nGS1Init(environment: I18NEnvironment, debug = false): Promise<void> {
43
+ export async function i18nGS1Init(environment: I18nEnvironment, debug = false): Promise<void> {
44
44
  await i18nUtilityInit(environment, debug);
45
45
  await i18nCoreInit(i18nextGS1, environment, debug, gs1NS, utilityResources, gs1Resources);
46
46
  }
@@ -1,18 +1,18 @@
1
- import { I18NEnvironment } from "@aidc-toolkit/core";
1
+ import { I18nEnvironment } from "@aidc-toolkit/core";
2
2
  import { NUMERIC_CREATOR } from "@aidc-toolkit/utility";
3
3
  import { describe, expect, test } from "vitest";
4
4
  import {
5
5
  checkCharacterPair,
6
6
  checkDigit,
7
7
  checkDigitSum,
8
- fiveDigitPriceWeightCheckDigit,
9
- fourDigitPriceWeightCheckDigit,
10
8
  hasValidCheckCharacterPair,
11
9
  hasValidCheckDigit,
12
- i18nGS1Init
10
+ i18nGS1Init,
11
+ isValidPriceOrWeightCheckDigit,
12
+ priceOrWeightCheckDigit
13
13
  } from "../src";
14
14
 
15
- await i18nGS1Init(I18NEnvironment.CLI, true);
15
+ await i18nGS1Init(I18nEnvironment.CLI, true);
16
16
 
17
17
  describe("Check digit", () => {
18
18
  const testNumericString = "1234567890";
@@ -40,7 +40,7 @@ describe("Check digit", () => {
40
40
  });
41
41
  });
42
42
 
43
- describe("Price/weight check digit", () => {
43
+ describe("Price or weight check digit", () => {
44
44
  function weight2Minus(characterIndex: number): number {
45
45
  const product = characterIndex * 2;
46
46
 
@@ -69,7 +69,10 @@ describe("Price/weight check digit", () => {
69
69
 
70
70
  const sum = weight2Minus(characterIndexes[0]) + weight2Minus(characterIndexes[1]) + weight3(characterIndexes[2]) + weight5Minus(characterIndexes[3]);
71
71
 
72
- expect(fourDigitPriceWeightCheckDigit(s)).toBe(NUMERIC_CREATOR.character(sum * 3 % 10));
72
+ const checkDigit = priceOrWeightCheckDigit(s);
73
+
74
+ expect(checkDigit).toBe(NUMERIC_CREATOR.character(sum * 3 % 10));
75
+ expect(isValidPriceOrWeightCheckDigit(s, checkDigit)).toBe(true);
73
76
  }
74
77
 
75
78
  function testFiveDigitPriceWeightCheckDigit(s: string): void {
@@ -78,7 +81,10 @@ describe("Price/weight check digit", () => {
78
81
 
79
82
  const sum = weight5Plus(characterIndexes[0]) + weight2Minus(characterIndexes[1]) + weight5Minus(characterIndexes[2]) + weight5Plus(characterIndexes[3]) + weight2Minus(characterIndexes[4]);
80
83
 
81
- expect(weight5Minus(Number(fiveDigitPriceWeightCheckDigit(s)))).toBe(9 - (sum + 9) % 10);
84
+ const checkDigit = priceOrWeightCheckDigit(s);
85
+
86
+ expect(weight5Minus(Number(checkDigit))).toBe(9 - (sum + 9) % 10);
87
+ expect(isValidPriceOrWeightCheckDigit(s, checkDigit)).toBe(true);
82
88
  }
83
89
 
84
90
  test("Four-digit", () => {
@@ -92,10 +98,6 @@ describe("Price/weight check digit", () => {
92
98
  testFourDigitPriceWeightCheckDigit("7890");
93
99
  testFourDigitPriceWeightCheckDigit("8901");
94
100
  testFourDigitPriceWeightCheckDigit("9012");
95
-
96
- expect(() => fourDigitPriceWeightCheckDigit("l234")).toThrow("Invalid character 'l' at position 1");
97
- expect(() => fourDigitPriceWeightCheckDigit("123")).toThrow("Length 3 of string for price or weight sum must be exactly 4");
98
- expect(() => fourDigitPriceWeightCheckDigit("12345")).toThrow("Length 5 of string for price or weight sum must be exactly 4");
99
101
  });
100
102
 
101
103
  test("Five-digit", () => {
@@ -109,10 +111,12 @@ describe("Price/weight check digit", () => {
109
111
  testFiveDigitPriceWeightCheckDigit("78901");
110
112
  testFiveDigitPriceWeightCheckDigit("89012");
111
113
  testFiveDigitPriceWeightCheckDigit("90123");
114
+ });
112
115
 
113
- expect(() => fiveDigitPriceWeightCheckDigit("l2345")).toThrow("Invalid character 'l' at position 1 of price or weight");
114
- expect(() => fiveDigitPriceWeightCheckDigit("1234")).toThrow("Length 4 of string for price or weight sum must be exactly 5");
115
- expect(() => fiveDigitPriceWeightCheckDigit("123456")).toThrow("Length 6 of string for price or weight sum must be exactly 5");
116
+ test("Invalid", () => {
117
+ expect(() => priceOrWeightCheckDigit("l2345")).toThrow("Invalid character 'l' at position 1 of price or weight");
118
+ expect(() => priceOrWeightCheckDigit("123")).toThrow("Length 3 of string for price or weight must be 4 or 5");
119
+ expect(() => priceOrWeightCheckDigit("123456")).toThrow("Length 6 of string for price or weight must be 4 or 5");
116
120
  });
117
121
  });
118
122