Functional Weave
Code in TypeScript

validation.gtin@1.1.0

README.md

2,815 bytes · view raw

# validation.gtin

Checks a Global Trade Item Number - the number under a retail barcode - in any
of its four lengths: EAN-8 (8 digits), UPC-A (12), EAN-13 (13) and GTIN-14
(14, used on cases and pallets). All four share one GS1 check digit: weight the
digits 3, 1, 3, 1 ... starting from the digit just left of the check digit and
moving left, sum, and the check digit is whatever brings the sum up to a
multiple of ten.

Since 1.1.0 the check digit arithmetic comes from `validation.gs1-check-digit`,
which serves every GS1 key length; the answers are the same as 1.0.0's. Use
that capability directly to compute a check digit or to check a key that is
not a GTIN.

Weighting from the right is the detail home-grown validators get wrong. EAN-13
weights its first digit 1, but EAN-8 and UPC-A weight their first digit 3, so
code that always starts at the left with 1 accepts only EAN-13 and GTIN-14.

A PASSING NUMBER IS WELL FORMED, NOT ALLOCATED. The check catches every single
mistyped digit and most adjacent transpositions. It cannot say whether GS1 has
issued the number or what product it is on; only GS1's registry (Verified by
GS1) can.

RESULT: `valid`, then on success the digits (`normalised`), the `kind` implied
by the length, and `gtin14`, the number left-padded with zeros to 14 digits.
GS1 defines every GTIN as a 14-digit number with leading zeros, so `gtin14` is
the form to store and compare: the UPC-A 036000291452 and the EAN-13
0036000291452 are the same item. `kind` is read from the length as given; an
EAN-13 beginning with 0 is not re-labelled as a UPC-A.

ACCEPTED INPUT: ASCII digits, with ASCII spaces and hyphens ignored anywhere,
because barcodes are printed in groups ("5 012345 678900"). Anything else makes
the value invalid.

| reason            | meaning                                                  |
|-------------------|----------------------------------------------------------|
| `empty`           | nothing but spaces and hyphens, or not a string at all   |
| `bad-character`   | something other than a digit, space or hyphen            |
| `bad-length`      | not 8, 12, 13 or 14 digits                               |
| `all-zero`        | every digit is 0: satisfies the arithmetic, never issued  |
| `bad-check-digit` | the last digit does not match                            |

The checks run in that order and the first failure is the reason. Validators
answer rather than throw.

Out of scope: GS1 prefix meaning (country of the member organisation, 978/979
Bookland, 02/20-29 restricted in-store numbers), GTIN-12 zero-suppressed UPC-E,
and add-on codes. For ISBNs use validation.isbn, which also checks the prefix.

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