# Commerce, travel and health codes

These are the codes trade, transport and healthcare use: barcodes,
ISBNs, container numbers, delivery terms, airport codes, blood types,
diagnosis codes. Each is checked by its standard's pattern or check
digit, and the closed lists are enums.

Here a till scans a product's barcode, a supplier's order names its
delivery term in whatever case its system writes, and a dock prints each
container's number as its side is painted:

```jadpo
type ScannedText = Text {}

type NotAProductCode = Failure {
    kind: FailureKind.invalid_value
}

type UnknownTradeTerm = Failure {
    kind: FailureKind.invalid_value
}

/// The product a till's scanner read: 13 digits, the last GS1's check
/// digit.
function scanned_product(raw: ScannedText) fails NotAProductCode -> Ean13 {
    var till_scan: Ean13 = attempt Ean13(raw) {
        format: NotAProductCode
    }
    return till_scan
}

/// The delivery term a supplier's order names, by its Incoterms code.
function agreed_term(raw: ScannedText) fails UnknownTradeTerm -> Incoterm {
    var trade_term: Incoterm = attempt text.parse(raw, as: Incoterm) {
        malformed: UnknownTradeTerm
    }
    return trade_term
}

/// A container on the dock's manifest, as its side is painted.
function container_label(shipped_in: ContainerNumber, viewer: Locale) -> PresentationText {
    return text.format(shipped_in, locale: viewer)
}
```

```jadpo
test "a code is checked by its check digit as it is built" {
    var till_scan: Ean13 = attempt scanned_product("4006381333931")
    assert till_scan == Ean13("4006381333931")
    assert scanned_product("4006381333932") fails NotAProductCode
    assert scanned_product("400638133393") fails NotAProductCode
    assert container_label(ContainerNumber("CSQU3054383"), Locale.en_gb) == "CSQU 305438 3"
    assert text.format(UnNumber("UN1203"), locale: Locale.en_gb) == "UN 1203"
}

test "a closed list reads its codes in either case" {
    var trade_term: Incoterm = attempt agreed_term("fob")
    assert trade_term == Incoterm.fob
    assert agreed_term("Free On Board") fails UnknownTradeTerm
    assert text.from(BloodType.o_negative) == "O-"
    assert text.format(CabinClass.premium_economy, locale: Locale.fr_fr) == "Premium economy"
    assert list.count(Incoterm.all) == 11
    assert list.count(Allergen.all) == 14
}
```

## Codes held as text

The codes are held as text, as the payment numbers are. Each is a pattern
and, where the table under
[Groups with the baseline only](groups-with-the-baseline-only.md) names
one, a named validator. Its canonical text is the value, in upper case
where the standard writes it so and never read in another case. None is
ordered.

## Check digits

Where each check comes from:

- GS1's mod 10 (General Specifications 7.9) for `Ean13`, `UpcA`, `Sscc`
  and `Isbn`. An ISBN is its 13 digits with the prefix 978 or 979 and no
  hyphens, because where the hyphens go depends on the ISBN agency's
  ranges.
- ISO 6346's for `ContainerNumber`.
- ISO 7064 MOD 11-2 for `Orcid`.
- Verhoeff's for `SnomedConcept`, with a concept's partition identifier
  (`00`, or `10` in an extension), so a description's identifier is
  refused.
- Elexon's primes for an `Mpan` core.
- A `Vin`'s check digit is checked where 49 CFR 565 requires it: for a
  manufacturer whose identifier starts 1 to 5, in North America.
  Elsewhere ISO 3779 leaves the ninth character to the manufacturer, and
  European VINs often don't carry a check digit, so checking every VIN
  would refuse real ones.

## Patterns only

- a `Sku` is 1 to 64 characters with no space or control character, kept
  as written;
- a `LotNumber` and a `SerialNumber` are 1 to 20 characters of GS1's
  character set 82 (Application Identifiers 10 and 21);
- an `HsCode`, a customs tariff code, is six to ten digits from chapter
  01, as a tariff writes it without dots;
- a `Carrier` is its Standard Carrier Alpha Code, two to four capitals;
- an `Icd10Code` is its letter, two digits and, after the dot, up to four
  more, which covers ICD-10-CM;
- an `IataAirline` is two letters, or a letter and a digit;
- a `BookingReference` is six capitals and digits;
- a `UnNumber`, which names a dangerous good, is `UN` and four digits;
- an `Mprn`, a UK gas supply's number, is 6 to 10 digits.

## Lists that aren't shipped

IATA's airport and airline codes and ICAO's location indicators are sold
under licence. WHO's ICD-10 may be used, but not redistributed in
software without its permission. So `IataAirport`, `IcaoAirport`,
`IataAirline` and `Icd10Code` are checked by pattern only, as `UnLocode`
is. An application that holds a licence checks against its own copy.

A SNOMED CT concept's term belongs to its release, under SNOMED
International's licence, so `SnomedConcept` is its identifier alone,
without a term.

## Shown to people

A container number is shown as its side is painted, `CSQU 305438 3`, and
a UN number as a transport document writes it, `UN 1203`. The rest are
shown as their canonical text.

## The closed lists

Each of these is a built-in enum, sorted by its code. Its code is read
ignoring case and stored as written (`O-`, `FOB`):

- `Incoterm`: Incoterms 2020's eleven delivery terms, with `DAT`, `DAF`,
  `DES`, `DEQ` and `DDU` from earlier editions retired;
- `BloodType`: ABO and RhD, `A+` to `O-`, whose variants spell the sign
  out, `BloodType.o_negative`;
- `Allergen`: the 14 that the UK's Food Information Regulations 2014 and
  the EU's Regulation 1169/2011 name, without regional additions;
- `CabinClass`: economy, premium economy, business and first;
- `CefrLevel`: language levels `A1` to `C2`;
- `EpcRating`: energy performance bands `A` to `G`, without a score;
- `Tenure`: freehold, leasehold and commonhold, as HM Land Registry
  registers land in England and Wales; a share of a freehold is a
  leasehold with a share;
- `LawfulBasis`: GDPR Article 6(1)'s six lawful bases.

Where codes are words (`premium_economy`), each variant's code is its
own name, as `Continent`'s are.

**Shown by code or by the authority's wording.** CLDR names none of these
lists. A list whose codes are written the same in every language (`FOB`,
`B2`, `O-`, `C`) is shown by its code. A list whose codes are words is
shown by its authority's English wording in every locale
(`FormatKey::Names`, as a card brand is), just as a subdivision is shown
by its English name: "Premium economy", "Cereals containing gluten",
"Legitimate interests". To show them in another language, word them in
your app's own messages.

**None is ordered,** not even `CefrLevel` and `EpcRating`. A `match` can
say their order. The contract orders only what it lists.

## Personal data and test values

**Personal data:** an `Icd10Code`, a `SnomedConcept` and a `BloodType`
are `health`; an `Orcid` is `identifying`, as a username is; an `Mpan` and
an `Mprn` are `contact`, as a `Uprn` is, because each names a home's
supply. An `Allergen` isn't personal data: a menu's allergens describe
food, and a person's allergy is your application's own field, which you
mark.

**Test values are published ones** where a standard publishes them:
GS1's, the ISBN Users' Manual's, NHTSA's, ISO 6346's, ORCID's, SNOMED
CT's, and UPS's sample tracking number. Where a standard publishes none,
they are made up to the rules. Each type's entry says which.
