stdnum
Version:
Standard Number Validation
168 lines (149 loc) • 3.05 kB
text/typescript
/**
* Codice Fiscale (Italian tax code for individuals).
*
* The Codice Fiscale is an alphanumeric code of 16 characters used to identify
* individuals residing in Italy or 11 digits for non-individuals in which case
* it matches the Imposta sul valore aggiunto.
*
* The 16 digit number consists of three characters derived from the person's
* last name, three from the person's first name, five that hold information on
* the person's gender and birth date, four that represent the person's place of
* birth and one check digit.
*
* Source
* https://it.m.wikipedia.org/wiki/Codice_fiscale
*
* PERSON
*/
import * as exceptions from '../exceptions';
import { strings } from '../util';
import { Validator, ValidateReturn } from '../types';
const checkRe =
/^[A-Z]{6}[0-9LMNPQRSTUV]{2}[ABCDEHLMPRST]{1}[0-9LMNPQRSTUV]{2}[A-Z]{1}[0-9LMNPQRSTUV]{3}[A-Z]{1}$/;
const CHECK_CODE_ODD: Record<string, number> = {
0: 1,
1: 0,
2: 5,
3: 7,
4: 9,
5: 13,
6: 15,
7: 17,
8: 19,
9: 21,
A: 1,
B: 0,
C: 5,
D: 7,
E: 9,
F: 13,
G: 15,
H: 17,
I: 19,
J: 21,
K: 2,
L: 4,
M: 18,
N: 20,
O: 11,
P: 3,
Q: 6,
R: 8,
S: 12,
T: 14,
U: 16,
V: 10,
W: 22,
X: 25,
Y: 24,
Z: 23,
};
const CHECK_CODE_EVEN: Record<string, number> = {
0: 0,
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6,
7: 7,
8: 8,
9: 9,
A: 0,
B: 1,
C: 2,
D: 3,
E: 4,
F: 5,
G: 6,
H: 7,
I: 8,
J: 9,
K: 10,
L: 11,
M: 12,
N: 13,
O: 14,
P: 15,
Q: 16,
R: 17,
S: 18,
T: 19,
U: 20,
V: 21,
W: 22,
X: 23,
Y: 24,
Z: 25,
};
const CHECK_CODE_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
function clean(input: string): ReturnType<typeof strings.cleanUnicode> {
return strings.cleanUnicode(input, ' -:');
}
const impl: Validator = {
name: 'Italian Tax Code',
localName: 'Codice Fiscale',
abbreviation: 'CF',
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 !== 16) {
return { isValid: false, error: new exceptions.InvalidLength() };
}
if (!checkRe.test(value)) {
return { isValid: false, error: new exceptions.InvalidFormat() };
}
const [front, check] = strings.splitAt(value, -1);
const sum = front
.split('')
.reduce(
(acc, v, idx) =>
(acc + (idx % 2 === 1 ? CHECK_CODE_EVEN[v] : CHECK_CODE_ODD[v])) % 26,
0,
);
if (CHECK_CODE_CHARS[sum] !== check) {
return { isValid: false, error: new exceptions.InvalidChecksum() };
}
return {
isValid: true,
compact: value,
isIndividual: true,
isCompany: false,
};
},
};
export const { name, localName, abbreviation, validate, format, compact } =
impl;