-
-
Save alexbruno/6623b5afa847f891de9cb6f704d86d02 to your computer and use it in GitHub Desktop.
// Regex para validação de string no formato CNPJ | |
export const regexCNPJ = /^\d{2}.\d{3}.\d{3}\/\d{4}-\d{2}$/ | |
// Método de validação | |
// Referência: https://pt.wikipedia.org/wiki/Cadastro_Nacional_da_Pessoa_Jur%C3%ADdica | |
export function validCNPJ(value: string | number | number[] = '') { | |
if (!value) return false | |
// Aceita receber o valor como string, número ou array com todos os dígitos | |
const isString = typeof value === 'string' | |
const validTypes = isString || Number.isInteger(value) || Array.isArray(value) | |
// Elimina valor de tipo inválido | |
if (!validTypes) return false | |
// Filtro inicial para entradas do tipo string | |
if (isString) { | |
// Teste Regex para veificar se é uma string apenas dígitos válida | |
const digitsOnly = /^\d{14}$/.test(value) | |
// Teste Regex para verificar se é uma string formatada válida | |
const validFormat = regexCNPJ.test(value) | |
// Verifica se o valor passou em ao menos 1 dos testes | |
const isValid = digitsOnly || validFormat | |
// Se o formato não é válido, retorna inválido | |
if (!isValid) return false | |
} | |
// Elimina tudo que não é dígito | |
const numbers = matchNumbers(value) | |
// Valida a quantidade de dígitos | |
if (numbers.length !== 14) return false | |
// Elimina inválidos com todos os dígitos iguais | |
const items = [...new Set(numbers)] | |
if (items.length === 1) return false | |
// Separa os 2 últimos dígitos verificadores | |
const digits = numbers.slice(12) | |
// Valida 1o. dígito verificador | |
const digit0 = validCalc(12, numbers) | |
if (digit0 !== digits[0]) return false | |
// Valida 2o. dígito verificador | |
const digit1 = validCalc(13, numbers) | |
return digit1 === digits[1] | |
} | |
// Método de formatação | |
export function formatCNPJ(value: string | number | number[] = '') { | |
// Verifica se o valor é válido | |
const valid = validCNPJ(value) | |
// Se o valor não for válido, retorna vazio | |
if (!valid) return '' | |
// Elimina tudo que não é dígito | |
const numbers = matchNumbers(value) | |
const text = numbers.join('') | |
// Formatação do CNPJ: 99.999.999/9999-99 | |
const format = text.replace( | |
/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/, | |
'$1.$2.$3/$4-$5', | |
) | |
// Retorna o valor formatado | |
return format | |
} | |
// Cálculo validador | |
function validCalc(x: number, numbers: number[]) { | |
const slice = numbers.slice(0, x) | |
let factor = x - 7 | |
let sum = 0 | |
for (let i = x; i >= 1; i--) { | |
const n = slice[x - i] | |
sum += n * factor-- | |
if (factor < 2) factor = 9 | |
} | |
const result = 11 - (sum % 11) | |
return result > 9 ? 0 : result | |
} | |
// Elimina tudo que não é dígito | |
function matchNumbers(value: string | number | number[] = '') { | |
const match = value.toString().match(/\d/g) | |
return Array.isArray(match) ? match.map(Number) : [] | |
} |
Sensacional irmão! Apenas um ponto, da linha 22 até a 24 deu erro aqui em 18/05/2023, então substituí pelo trecho abaixo:
return digitsOnly || validFormat;
Sensacional irmão! Apenas um ponto, da linha 22 até a 24 deu erro aqui em 18/05/2023, então substituí pelo trecho abaixo:
return digitsOnly || validFormat;
É @jorgemustafa, já saquei o erro, o código que usei ali na época não é uma boa prática atualmente.
Retornar naquele ponto como você fez não é o suficiente, pois se você passar uma string de CNPJ com formato válido mas que não seja um CNPJ válido de fato segundo o algoritmo de validação, você terá um falso positivo.
Atualizei o código corrigindo o erro. Obrigado pela contribuição!
O HOMEM É BRABOOO!!
Código atualizado:
- TypeScript para maior clareza dos tipos de dados
- Separação de funções reutilizáveis para maior clareza do que acontece no geral
- Bônus: Método de formatação CNPJ
Ajustes + Testes com JestJs
import { validCNPJ } from './validate-cnpj';
describe('validCNPJ', () => {
it('should validate a valid CNPJ', () => {
expect(validCNPJ('04.470.781/0001-39')).toBeTruthy();
});
it('should invalidate an invalid CNPJ', () => {
expect(validCNPJ('11.111.111/1111-11')).toBeFalsy();
});
it('should return false for an invalid CNPJ', () => {
const cnpj = '34.574.376/0001-93';
expect(validCNPJ(cnpj)).toBeFalsy();
});
it('should return false for a CNPJ with all identical digits', () => {
const cnpj = '11.111.111/1111-11';
expect(validCNPJ(cnpj)).toBeFalsy();
});
it('should return false for a CNPJ with incorrect length', () => {
const cnpj = '34.574.376/0001';
expect(validCNPJ(cnpj)).toBeFalsy();
});
it('should return false for a CNPJ with non-numeric characters', () => {
const cnpj = '34a574b376/0001c92';
expect(validCNPJ(cnpj)).toBeFalsy();
});
it('should return false for an empty string', () => {
const cnpj = '';
expect(validCNPJ(cnpj)).toBeFalsy();
});
it('should return false for a null value', () => {
const cnpj = null;
expect(validCNPJ(cnpj)).toBeFalsy();
});
});
Os dois primeiros tests quebraram, fiz ajuste na func para funcionar.
atualizei o regex para ficar dessa forma, ficando mas especifica para o metodo.
export const regexCNPJ = /^\d{2}\.\d{3}\.\d{3}\/\d{4}-\d{2}$/;
export const validCNPJ = (value: string | number | number[] = '') => {
if (!value) return false;
const isString = typeof value === 'string';
if (isString && !regexCNPJ.test(value)) return false;
const numbers = matchNumbers(value);
if (numbers.length !== 14) return false;
const items = [...new Set(numbers)];
if (items.length === 1) return false;
const digit0 = validCalc(12, numbers);
const digit1 = validCalc(13, numbers);
return digit0 === numbers[12] && digit1 === numbers[13];
};
function validCalc(x: number, numbers: number[]) {
let factor = x - 7;
let sum = 0;
for (let i = 0; i < x; i++) {
sum += numbers[i] * factor--;
if (factor < 2) factor = 9;
}
const result = 11 - (sum % 11);
return result > 9 ? 0 : result;
}
function matchNumbers(value: string | number | number[] = '') {
const match = value
.toString()
.replace(/[^\d]+/g, '')
.match(/\d/g);
return Array.isArray(match) ? match.map(Number) : [];
}
`
Salvou demais! Valeu
e eu agradeço a todos vocês pela contribuição usarei esse método agora e ta quebrando um galho kk