Skip to content
Closed
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
5 changes: 4 additions & 1 deletion src/__tests__/usevalidationsbr.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,16 @@ describe('UseValidationBr Cep', () => {
describe('UseValidationBr CNPJ', () => {
it('should be able return true to valid CNPJ', () => {
expect(useValidationsBR('cnpj', '66.919.381/0001-15')).toBe(true);
expect(useValidationsBR('cnpj', '12.ABC.345/01DE-35')).toBe(true);
});

it('should be able return false to invalid CNPJ', () => {
expect.assertions(2);
expect.assertions(4);

expect(useValidationsBR('cnpj', '12.732.455/0001-25')).toBe(false);
expect(useValidationsBR('cnpj', '66.919.381/0001-10')).toBe(false);
expect(useValidationsBR('cnpj', '00000000000192')).toBe(false);
expect(useValidationsBR('cnpj', 'ABCDEFGHIJKL81')).toBe(false);
});

it('should be able return false to pass a empty string', () => {
Expand Down
30 changes: 29 additions & 1 deletion src/__tests__/validateCNPJ.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,48 @@ import { validateCNPJ } from '../validations/validateCNPJ';
describe('validateCNPJ', () => {
it('should return true for valid CNPJs', () => {
expect(validateCNPJ('66.919.381/0001-15')).toBe(true);
expect(validateCNPJ('12.ABC.345/01DE-35')).toBe(true);
expect(validateCNPJ('90.021.382/0001-22')).toBe(true);
expect(validateCNPJ('90.024.778/0001-23')).toBe(true);
expect(validateCNPJ('90.025.108/0001-21')).toBe(true);
expect(validateCNPJ('90.025.255/0001-00')).toBe(true);
expect(validateCNPJ('90.024.420/0001-09')).toBe(true);
expect(validateCNPJ('90.024.781/0001-47')).toBe(true);
expect(validateCNPJ('04.740.714/0001-97')).toBe(true);
expect(validateCNPJ('44.108.058/0001-29')).toBe(true);
expect(validateCNPJ('90.024.780/0001-00')).toBe(true);
expect(validateCNPJ('90.024.779/0001-78')).toBe(true);
expect(validateCNPJ('00000000000191')).toBe(true);
expect(validateCNPJ('ABCDEFGHIJKL80')).toBe(true);
});

it('should return false for invalid CNPJs', () => {
expect(validateCNPJ('48.514.588/0001-70')).toBe(false);
expect(validateCNPJ('48514588000170')).toBe(false);
expect(validateCNPJ('12.732.455/0001-25')).toBe(false);
expect(validateCNPJ('66.919.381/0001-10')).toBe(false);
expect(validateCNPJ('123')).toBe(false);
expect(validateCNPJ('00000000000192')).toBe(false);
expect(validateCNPJ('ABCDEFGHIJKL81')).toBe(false);
});

it('should return false for empty or null strings', () => {
expect(validateCNPJ('')).toBe(false);
});

it('should return false for invalid characters', () => {
expect(validateCNPJ("'!@#$%&*-_=+^~")).toBe(false);
expect(validateCNPJ('$0123456789ABC')).toBe(false);
expect(validateCNPJ('0123456?789ABC')).toBe(false);
expect(validateCNPJ('0123456789ABC#')).toBe(false);
});

it('should return false for letter in DV position', () => {
expect(validateCNPJ('0000000000019L')).toBe(false);
expect(validateCNPJ('000000000001P1')).toBe(false);
});

it('should return false for CNPJs with all digits the same', () => {
expect(validateCNPJ('00000000000000')).toBe(false);
expect(validateCNPJ('11.111.111/1111-11')).toBe(false);
expect(validateCNPJ('22222222222222')).toBe(false);
});
Expand All @@ -28,5 +55,6 @@ describe('validateCNPJ', () => {
expect(validateCNPJ('66.919.381/0001-155')).toBe(false);
expect(validateCNPJ('6691938100011')).toBe(false);
expect(validateCNPJ('669193810001155')).toBe(false);
expect(validateCNPJ('123')).toBe(false);
});
});
85 changes: 56 additions & 29 deletions src/validations/validateCNPJ.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,58 @@
import { isRepeated } from './utils';
/**
* Reference: https://www.gov.br/receitafederal/pt-br/centrais-de-conteudo/publicacoes/documentos-tecnicos/cnpj
*/

const cnpjSizeWithoutDv: number = 12;
const cnpjRegexWithoutDv: RegExp = /^([A-Z\d]){12}$/;
const cnpjRegex: RegExp = /^([A-Z\d]){12}(\d){2}$/;
const maskCharactersRegex: RegExp = /[./-]/g;
const notAllowedCharactersRegex: RegExp = /[^A-Z\d./-]/i;
const baseValue: number = '0'.charCodeAt(0);
const dvWeights: number[] = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
const zeroedCnpj: string = '00000000000000';

function removeCnpjMask(cnpj: string): string {
return cnpj.replace(maskCharactersRegex, '');
}

function calcCnpjDv(cnpj: string): string {
if (!notAllowedCharactersRegex.test(cnpj)) {
const cnpjSemMascara = removeCnpjMask(cnpj);
if (
cnpjRegexWithoutDv.test(cnpjSemMascara) &&
cnpjSemMascara !== zeroedCnpj.substring(0, cnpjSizeWithoutDv)
) {
let somatorioDV1 = 0;
let somatorioDV2 = 0;
for (let i = 0; i < cnpjSizeWithoutDv; i++) {
const asciiDigito = cnpjSemMascara.charCodeAt(i) - baseValue;
somatorioDV1 += asciiDigito * dvWeights[i + 1];
somatorioDV2 += asciiDigito * dvWeights[i];
}
const dv1 = somatorioDV1 % 11 < 2 ? 0 : 11 - (somatorioDV1 % 11);
somatorioDV2 += dv1 * dvWeights[cnpjSizeWithoutDv];
const dv2 = somatorioDV2 % 11 < 2 ? 0 : 11 - (somatorioDV2 % 11);
return `${dv1}${dv2}`;
}
}
throw new Error(
'Não é possível calcular o DV pois o CNPJ fornecido é inválido',
);
}

function isValidCnpj(cnpj: string): boolean {
if (!notAllowedCharactersRegex.test(cnpj)) {
const unmaskedCnpj = removeCnpjMask(cnpj);
if (cnpjRegex.test(unmaskedCnpj) && unmaskedCnpj !== zeroedCnpj) {
const givenDv = unmaskedCnpj.substring(cnpjSizeWithoutDv);
const calculatedDv = calcCnpjDv(
unmaskedCnpj.substring(0, cnpjSizeWithoutDv),
);
return givenDv === calculatedDv;
}
}
return false;
}

/**
* The function `validateCNPJ` validates a Brazilian CNPJ number.
Expand All @@ -9,32 +63,5 @@ import { isRepeated } from './utils';
* valid, and `false` otherwise.
*/
export function validateCNPJ(value: string): boolean {
const cnpj = String(value).replace(/[^\d]+/g, '');

if (cnpj === '' || cnpj.length !== 14 || isRepeated(cnpj)) {
return false;
}

const calculateDigit = (numbers: string, weights: number[]): number => {
let sum = 0;
for (let i = 0; i < numbers.length; i++) {
sum += parseInt(numbers.charAt(i), 10) * weights[i];
}
const remainder = sum % 11;
return remainder < 2 ? 0 : 11 - remainder;
};

const weights1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
const first12 = cnpj.substring(0, 12);
const dv1 = calculateDigit(first12, weights1);

if (dv1 !== parseInt(cnpj.charAt(12), 10)) {
return false;
}

const weights2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2];
const first13 = cnpj.substring(0, 13);
const dv2 = calculateDigit(first13, weights2);

return dv2 === parseInt(cnpj.charAt(13), 10);
return isValidCnpj(value);
}
Loading