Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions src/br/cnpj.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,34 @@ describe('br/cnpj', () => {

expect(result.error).toBeInstanceOf(InvalidFormat);
});

it('format:12ABC34501DE35 (alphanumeric)', () => {
const result = format('12ABC34501DE35');

expect(result).toEqual('12.ABC.345/01DE-35');
});

it('validate:12.ABC.345/01DE-35 (alphanumeric)', () => {
const result = validate('12.ABC.345/01DE-35');

expect(result.isValid && result.compact).toEqual('12ABC34501DE35');
});

it('validate:12abc34501de35 (alphanumeric lowercase)', () => {
const result = validate('12abc34501de35');

expect(result.isValid && result.compact).toEqual('12ABC34501DE35');
});

it('validate:12.ABC.345/01DE-99 (alphanumeric bad checksum)', () => {
const result = validate('12.ABC.345/01DE-99');

expect(result.error).toBeInstanceOf(InvalidChecksum);
});

it('validate:12.ABC.345/01DE-AB (non-numeric check digits)', () => {
const result = validate('12.ABC.345/01DE-AB');

expect(result.error).toBeInstanceOf(InvalidFormat);
});
});
23 changes: 18 additions & 5 deletions src/br/cnpj.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
/**
* CNPJ (Cadastro Nacional da Pessoa Jurídica, Brazilian company identifier).
*
* Numbers from the national register of legal entities have 14 digits. The
* first 8 digits identify the company, the following 4 digits identify a
* business unit and the last 2 digits are check digits.
* Numbers from the national register of legal entities have 14 characters. The
* first 8 characters identify the company, the following 4 identify a business
* unit and the last 2 are check digits.
*
* Starting July 2026, new CNPJs may be alphanumeric: the first 12 positions
* can contain A-Z and 0-9, while the last 2 remain numeric check digits.
* Legacy numeric-only CNPJs remain valid. The check digit algorithm (mod 11)
* is unchanged; letters use their ASCII code minus 48 (A=17 … Z=42).
*
* Sources:
*
Expand All @@ -18,14 +23,18 @@ function clean(input: string): ReturnType<typeof strings.cleanUnicode> {
return strings.cleanUnicode(input, ' -./');
Comment thread
taj marked this conversation as resolved.
}

function charValue(ch: string): number {
return ch.charCodeAt(0) - 48;
}

function computeDigit(input: string): number {
const mlen = input.length + 7;

const value =
11 -
(input
.split('')
.map((v, idx) => parseInt(v, 10) * (((mlen - idx) % 8) + 2))
.map((v, idx) => charValue(v) * (((mlen - idx) % 8) + 2))
.reduce((acc, v) => acc + v) %
11);

Expand Down Expand Up @@ -63,12 +72,16 @@ const impl: Validator = {
if (value.length !== 14) {
return { isValid: false, error: new exceptions.InvalidLength() };
}
if (!strings.isdigits(value)) {
if (!strings.isalphanumeric(value)) {
return { isValid: false, error: new exceptions.InvalidFormat() };
}

const [front, c1, c2] = strings.splitAt(value, 12, 13);

if (!strings.isdigits(c1) || !strings.isdigits(c2)) {
return { isValid: false, error: new exceptions.InvalidFormat() };
}

const d1 = String(computeDigit(front));
const d2 = String(computeDigit(value.substr(0, 13)));

Expand Down