diff --git a/README.md b/README.md index 916e912..fe7cfc7 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,8 @@ How you can help! This library currently support about half the countries in the | Canada | CA | SIN | Person | Person Identifier (Social Insurance Number) | | Cuba | CU | NI | Person | Número de identidad, Cuban identity card numbers | | Cyprus | CY | VAT | Company | Αριθμός Εγγραφής Φ.Π.Α. (Cypriot VAT number) | -| Czech Republic | CZ | DIC | Company | Daňové identifikační číslo, Czech VAT number | +| Czech Republic | CZ | DIC | Vat | Daňové identifikační číslo, Czech VAT number | +| Czech Republic | CZ | ICO | Company | Identifikační číslo osoby, Czech Company Identification Number | | Czech Republic | CZ | RC | Person | Rodné číslo, the Czech birth number | | Swisserland | CH | SSN | Person | Swiss social security number ("Sozialversicherungsnummer") | | Swisserland | CH | UID | Company | Unternehmens-Identifikationsnummer, Swiss business identifier | diff --git a/src/cz/ico.spec.ts b/src/cz/ico.spec.ts new file mode 100644 index 0000000..4f318e6 --- /dev/null +++ b/src/cz/ico.spec.ts @@ -0,0 +1,64 @@ +import { validate, format } from './ico'; +import { InvalidLength, InvalidChecksum, InvalidComponent } from '../exceptions'; + +describe('cz/ico', () => { + it('format:25596641', () => { + const result = format('25596641'); + + expect(result).toEqual('25596641'); + }); + + it('validate:25596641 (Avast Software)', () => { + const result = validate('25596641'); + + expect(result.isValid && result.compact).toEqual('25596641'); + }); + + it('validate:27082440 (Alza.cz)', () => { + const result = validate('27082440'); + + expect(result.isValid && result.compact).toEqual('27082440'); + }); + + it('validate:45274649 (Škoda Auto)', () => { + const result = validate('45274649'); + + expect(result.isValid && result.compact).toEqual('45274649'); + }); + + it('validate:00023205 (ČEZ)', () => { + const result = validate('00023205'); + + expect(result.isValid && result.compact).toEqual('00023205'); + }); + + it('validate:00075370 (ČSÚ)', () => { + const result = validate('00075370'); + + expect(result.isValid && result.compact).toEqual('00075370'); + }); + + it('validate:12345678 (invalid checksum)', () => { + const result = validate('12345678'); + + expect(result.error).toBeInstanceOf(InvalidChecksum); + }); + + it('validate:1234567 (too short)', () => { + const result = validate('1234567'); + + expect(result.error).toBeInstanceOf(InvalidLength); + }); + + it('validate:90000001 (reserved range)', () => { + const result = validate('90000001'); + + expect(result.error).toBeInstanceOf(InvalidComponent); + }); + + it('validate with spaces: 255 96 641', () => { + const result = validate('255 96 641'); + + expect(result.isValid && result.compact).toEqual('25596641'); + }); +}); diff --git a/src/cz/ico.ts b/src/cz/ico.ts new file mode 100644 index 0000000..d56112a --- /dev/null +++ b/src/cz/ico.ts @@ -0,0 +1,81 @@ +/** + * IČO (Identifikační číslo osoby, Czech Company Identification Number). + * + * The IČO is an 8-digit identifier assigned to every business entity + * registered in the Czech Republic. It uses a weighted modulus-11 checksum. + * + * ENTITY + */ + +import * as exceptions from '../exceptions'; +import { strings, weightedSum } from '../util'; +import { Validator, ValidateReturn } from '../types'; + +function clean(input: string): ReturnType { + return strings.cleanUnicode(input, ' -./'); +} + +function checksum(value: string): boolean { + const [front, check] = strings.splitAt(value, -1); + + const sum = weightedSum(front, { + modulus: 11, + weights: [8, 7, 6, 5, 4, 3, 2, 1], + }); + + const v = (11 - sum) % 11; + + return (v === 0 ? '1' : String(v % 10)) === check; +} + +const impl: Validator = { + name: 'Czech Company Identification Number', + localName: 'Identifikační číslo osoby', + abbreviation: 'IČO', + + compact(input: string): string { + const [value, err] = clean(input); + + if (err) { + throw err; + } + + return value; + }, + + format(input: string): string { + const [value] = clean(input); + + return value; + }, + + validate(input: string): ValidateReturn { + const [value, error] = clean(input); + + if (error) { + return { isValid: false, error }; + } + if (value.length !== 8) { + return { isValid: false, error: new exceptions.InvalidLength() }; + } + if (!strings.isdigits(value)) { + return { isValid: false, error: new exceptions.InvalidFormat() }; + } + if (value.startsWith('9')) { + return { isValid: false, error: new exceptions.InvalidComponent() }; + } + if (!checksum(value)) { + return { isValid: false, error: new exceptions.InvalidChecksum() }; + } + + return { + isValid: true, + compact: value, + isIndividual: false, + isCompany: true, + }; + }, +}; + +export const { name, localName, abbreviation, validate, format, compact } = + impl; diff --git a/src/cz/index.ts b/src/cz/index.ts index 274ce10..d386074 100644 --- a/src/cz/index.ts +++ b/src/cz/index.ts @@ -1,2 +1,3 @@ export * as dic from './dic'; +export * as ico from './ico'; export * as rc from './rc';