Functional Weave
Code in TypeScript

validation.gs1-check-digit@1.0.0

README.md

2,669 bytes · view raw

# validation.gs1-check-digit

The GS1 mod-10 check digit, on its own, for every GS1 identification key
that ends in one: GTIN-8, GTIN-12 (UPC-A), GTIN-13 (EAN-13), GTIN-14,
SSCC-18, GLN-13, GSRN-18, GSIN-17, the 13-digit base of a GDTI, GRAI or
GCN, and so on. It is the arithmetic those keys share, for the capabilities
that know what each key means (`validation.gtin`, `logistics.sscc`).

- `gs1CheckDigit(data)` returns the digit (0-9) to append to `data`, the
  key's digits without its check digit.
- `isGs1CheckDigitValid(key)` says whether the last digit of a whole key is
  the check digit of the digits before it.

## The rule

Weight the data digits 3, 1, 3, 1 ... starting from the rightmost and moving
left, add them up, and the check digit is whatever brings the sum up to a
multiple of ten: `(10 - sum mod 10) mod 10`. A sum that is already a multiple
of ten gives 0, not 10.

Weighting from the right is what lets one rule serve every length, and it is
the detail home-grown code gets wrong. An EAN-13's first digit is weighted 1,
but a GTIN-8's, a UPC-A's and an SSCC's first digit is weighted 3, so code that
always starts at the left with 1 is right only for keys with an even number of
data digits.

GS1's worked example: the GTIN-13 data `629104150021` weighted from the right
sums to 57, so the check digit is 3 and the GTIN is `6291041500213`. The SSCC
data `37610425002123456` sums to 101, check digit 9. Both are vectors.

## Input

ASCII digits only, of any length (at least one data digit; at least two
digits for a whole key). Spaces, hyphens and the `(00)`-style application
identifiers printed on labels are not tidied away: strip them before calling,
because what counts as a separator is the key's business, not the checksum's.
Non-ASCII digits (Arabic-Indic, superscripts) are refused in every language.

- `gs1CheckDigit` throws on anything else, empty included, with
  `GS1 data must be one or more ASCII digits`: there is no digit to return.
- `isGs1CheckDigitValid` answers `false` instead, as validators do.

## What it does not check

Only the arithmetic. It does not know which lengths a key may have, what a
GS1 prefix means, or whether GS1 allocated the number. An all-zero key passes
the arithmetic (its check digit is 0); `validation.gtin` refuses it as a GTIN,
and any other key that should do the same says so itself. A passing key is
well formed: every single mistyped digit and most adjacent transpositions
(all except those of two digits differing by 5) change the check digit.

## Source

GS1 General Specifications, section 7.9 "Check digit calculation";
https://www.gs1.org/services/how-calculate-check-digit-manually