-
-
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) : [] | |
} |
Alex, muito obrigado, ajudou muito.
Salvou a minha vida também,.,,,,
Saudações meu amigo, também sou de Uberlândia.
Tive de adicionar uma nova regra abaixo da linha 12 do código para verificar se quando o CNPJ for do tipo string há somente números, pois se digitássemos os dígitos do cnpj seguidos de uma letra o código retornava true.
Como estou usando somente o método por string, acredito que será necessário aplicar para todas as formas de verificação.
// Se for do tipo string verifica se há somente numeros
const isnum = /^\d+$/.test(value);
if (!isnum && typeof value === 'string') return false;
Muito obrigado @felipe2g ! Acho que entendi o problema.
Como o método ignora qualquer caractere que não seja dígito para fazer a validação apenas da quantidade de dígitos e do cálculo, então pode-se passar junto com o valor de um CNPJ válido qualquer outra coisa que será ignorada e o retornará válido.
Vou fazer uns testes assim que puder e aplicar sua sugestão.
Valeu pela contribuição!
Exatamente, o problema é quando usamos a validação pra saber se vamos inserir ou não no banco de dados, como estava retornando true, estava indo os caracteres também para o banco.
Valeu!
Muito obrigado de verdade me salvou também!! Parabéns!!
@felipe2g depois do seu comentário, eu acrescentei uma validação robusta para entradas do tipo string.
Uma validação apenas do tipo "Se for string verifica se há somente números" não seria completa o suficiente, pois não funcionaria para validar um CNPJ válido e formatado no padrão de formatação: XX.XXX.XXX/XXXX-XX
.
Então incluí alguns passos extras para validar o formato de entrada de strings. Acredito que tenha ficado bem mais robusto e confiável agora.
Obrigado mais uma vez, ajudou a melhorar o método e todos podem aproveitar!
e eu agradeço a todos vocês pela contribuição usarei esse método agora e ta quebrando um galho kk
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
Muito obrigado pela contribuição!