Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,19 @@

public class CnpjRule implements Rule<String> {

// Constantes estruturais do CNPJ
private static final int TAMANHO_BASE_CNPJ = 12;
private static final int DIVISOR_MODULO_11 = 11;

// A regra oficial diz para extrair 48 do valor ASCII do caractere alfanumérico
private static final int OFFSET_ASCII = 48;

// Pesos para o cálculo dos Dígitos Verificadores
private static final int[] PESOS_DV1 = {5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};
private static final int[] PESOS_DV2 = {6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2};

@Override public String validate(String value) {
// Ignora valores vazios pois existe a regra do "Required" separada
if (!Util.isEmpty(value) && !this.isValid(value)) {
return "Campo inválido";
}
Expand All @@ -14,52 +26,53 @@ public class CnpjRule implements Rule<String> {
}

public boolean isValid(String cnpjValue) {
if (!cnpjValue.matches("^[0-9]*$")) {
if (cnpjValue == null || cnpjValue.length() != 14) {
return false;
}
if (cnpjValue == "00000000000000"
|| cnpjValue == "11111111111111"
|| cnpjValue == "22222222222222"
|| cnpjValue == "33333333333333"
|| cnpjValue == "44444444444444"
|| cnpjValue == "55555555555555"
|| cnpjValue == "66666666666666"
|| cnpjValue == "77777777777777"
|| cnpjValue == "88888888888888"
|| cnpjValue == "99999999999999"
|| cnpjValue.length() != 14) {

// 1. Limpeza e Normalização: Remove pontuações e garante maiúsculas
String cnpjLimpo = cnpjValue.replaceAll("[^a-zA-Z\\d]", "").toUpperCase();

// 2. Validação Estrutural (Regex)
// Exige 12 caracteres alfanuméricos seguidos obrigatoriamente por 2 numéricos
if (!cnpjLimpo.matches("^[A-Z\\d]{12}\\d{2}$")) {
return false;
}
int tamanho = cnpjValue.length() - 2;
String numeros = cnpjValue.substring(0, tamanho);
String digitos = cnpjValue.substring(tamanho);

// 3. Extrai os dígitos verificadores informados na string
int dv1Informado = Character.getNumericValue(cnpjLimpo.charAt(12));
int dv2Informado = Character.getNumericValue(cnpjLimpo.charAt(13));

// 4. Calcula os dígitos reais esperados com base na raiz
int dv1Calculado = calcularDigitoVerificador(cnpjLimpo, PESOS_DV1, TAMANHO_BASE_CNPJ);
int dv2Calculado = calcularDigitoVerificador(cnpjLimpo, PESOS_DV2, TAMANHO_BASE_CNPJ + 1);

// 5. O CNPJ só é válido se ambos os dígitos baterem
return (dv1Informado == dv1Calculado) && (dv2Informado == dv2Calculado);
}

/**
* Calcula o dígito verificador aplicando o Módulo 11 com a regra alfanumérica.
*/
private static int calcularDigitoVerificador(String cnpj, int[] pesos, int tamanhoConsiderado) {
int soma = 0;
int pos = tamanho - 7;
for (int i = tamanho; i >= 1; i--) {
soma += Integer.parseInt(numeros.charAt(tamanho - i) + "") * pos--;
if (pos < 2) {
pos = 9;
}
}
int resultado = soma % 11 < 2 ? 0 : 11 - soma % 11;
if (resultado != Integer.parseInt(digitos.charAt(0) + "")) {
return false;
}

tamanho = tamanho + 1;
numeros = cnpjValue.substring(0, tamanho);
soma = 0;
pos = tamanho - 7;
for (int i = tamanho; i >= 1; i--) {
soma += Integer.parseInt(numeros.charAt(tamanho - i) + "") * pos--;
if (pos < 2) {
pos = 9;
}
}
resultado = soma % 11 < 2 ? 0 : 11 - soma % 11;
if (resultado != Integer.parseInt(digitos.charAt(1) + "")) {
return false;
for (int i = 0; i < tamanhoConsiderado; i++) {
char caractere = cnpj.charAt(i);

// Regra da Receita Federal: O valor numérico do caractere para o cálculo
// equivale ao seu código na tabela ASCII subtraído de 48.
// Exemplo 1: '0' (ASCII 48) - 48 = 0
// Exemplo 2: 'A' (ASCII 65) - 48 = 17
int valorConvertido = caractere - OFFSET_ASCII;

soma += valorConvertido * pesos[i];
}
return true;

int resto = soma % DIVISOR_MODULO_11;

// Regra padrão do Módulo 11: Se o resto for menor que 2, o dígito é 0.
// Caso contrário, o dígito é a diferença entre o divisor (11) e o resto.
return resto < 2 ? 0 : (DIVISOR_MODULO_11 - resto);
}
}
134 changes: 76 additions & 58 deletions src/main/script/rules/cnpj.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,83 @@
import { msg } from '..'
import { msg } from ".."

import { length } from './length'
import { length } from "./length"

function cnpjRule(value: string) {
if (!isValid(value)) {
return msg('cnpj', value)
}
// Constantes estruturais do CNPJ
const DIVISOR_MODULO_11 = 11
const TAMANHO_BASE_CNPJ = 12

// A regra oficial diz para extrair 48 do valor ASCII do caractere alfanumérico
const OFFSET_ASCII = 48

// Pesos para o cálculo dos Dígitos Verificadores
const PESOS_DV1 = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
const PESOS_DV2 = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]

export function cnpjRule(value: string) {
// Ignora valores vazios pois existe a regra do "Required" separada
if (value && !isValid(value)) {
return msg("cnpj", value)
}
}

function isValid(cnpjValue: string) {
if (!cnpjValue) {
return true
}
if (cnpjValue === ('00000000000000')
|| cnpjValue === ('11111111111111')
|| cnpjValue === ('22222222222222')
|| cnpjValue === ('33333333333333')
|| cnpjValue === ('44444444444444')
|| cnpjValue === ('55555555555555')
|| cnpjValue === ('66666666666666')
|| cnpjValue === ('77777777777777')
|| cnpjValue === ('88888888888888')
|| cnpjValue === ('99999999999999')
|| cnpjValue.length !== 14) {
return false
}
let tamanho = cnpjValue.length - 2
let numeros = cnpjValue.substring(0, tamanho)
const digitos = cnpjValue.substring(tamanho)
let soma = 0
let pos = tamanho - 7
for (let i = tamanho; i >= 1; i--) {
soma += parseInt(numeros.charAt(tamanho - i)) * pos--
if (pos < 2) {
pos = 9
}
}
let resultado = soma % 11 < 2 ? 0 : 11 - soma % 11
if (resultado !== parseInt(digitos.charAt(0))) {
return false
}

tamanho = tamanho + 1
numeros = cnpjValue.substring(0, tamanho)
soma = 0
pos = tamanho - 7
for (let i = tamanho; i >= 1; i--) {
soma += parseInt(numeros.charAt(tamanho - i)) * pos--
if (pos < 2) {
pos = 9
}
}
resultado = soma % 11 < 2 ? 0 : 11 - soma % 11
if (resultado !== parseInt(digitos.charAt(1))) {
return false
}

return true
if (cnpjValue?.length !== 14) return false

// 1. Limpeza e Normalização
const cnpjLimpo = cnpjValue.replace(/[^a-zA-Z\d]/g, "").toUpperCase()

// 2. Validação Estrutural (Regex)
if (!/^[A-Z\d]{12}\d{2}$/.test(cnpjLimpo)) {
return false
}

// 3. Extração dos dígitos informados no final da string para validação
const dv1Informado = Number.parseInt(cnpjLimpo.charAt(12), 10)
const dv2Informado = Number.parseInt(cnpjLimpo.charAt(13), 10)

// 4. Cálculo dos dígitos esperados
const dv1Calculado = calcularDigitoVerificador(
cnpjLimpo,
PESOS_DV1,
TAMANHO_BASE_CNPJ,
)
const dv2Calculado = calcularDigitoVerificador(
cnpjLimpo,
PESOS_DV2,
TAMANHO_BASE_CNPJ + 1,
)

// 5. O CNPJ só é válido se ambos os dígitos baterem
return dv1Calculado === dv1Informado && dv2Calculado === dv2Informado
}

/**
* Calcula o dígito verificador aplicando o Módulo 11 com a regra alfanumérica.
*/
function calcularDigitoVerificador(
cnpj: string,
pesos: number[],
tamanhoConsiderado: number,
): number {
let soma = 0

for (let i = 0; i < tamanhoConsiderado; i++) {
const codigoAscii = cnpj.charCodeAt(i)

// Regra da Receita Federal: O valor numérico do caractere para o cálculo
// equivale ao seu código na tabela ASCII subtraído de 48.
// Exemplo 1: '0' (ASCII 48) - 48 = 0
// Exemplo 2: 'A' (ASCII 65) - 48 = 17
const valorConvertido = codigoAscii - OFFSET_ASCII

soma += valorConvertido * pesos[i]
}

const resto = soma % DIVISOR_MODULO_11

// Regra padrão do Módulo 11: Se o resto for menor que 2, o dígito é 0.
// Caso contrário, o dígito é a diferença entre o divisor (11) e o resto.
return resto < 2 ? 0 : DIVISOR_MODULO_11 - resto
}

export const cnpj = [
length(14),
cnpjRule,
]
export const cnpj = [length(14), cnpjRule]
Original file line number Diff line number Diff line change
Expand Up @@ -10,46 +10,58 @@ public class CnpjTest extends ValidationTest {
@Test
public void testarRequired() {
Assert.assertNull("Cnpj não deve ser requerido por padrão", this.validate(null, Rules.cnpj));

Assert.assertNull("Cnpj não deve ser requerido por padrão", this.validate("", Rules.cnpj));
}

@Test
public void testarValorDeCnpj() {
Assert.assertNull("23348194000116 é um Cnpj válido", this.validate("23348194000116", Rules.cnpj));

Assert.assertNull("10781193000119 é um Cnpj válido", this.validate("10781193000119", Rules.cnpj));

Assert.assertNull("78471826000126 é um Cnpj válido", this.validate("78471826000126", Rules.cnpj));
public void testarCnpjNumericoValido() {
Assert.assertNull("23348194000116 é um Cnpj numérico válido", this.validate("23348194000116", Rules.cnpj));
Assert.assertNull("10781193000119 é um Cnpj numérico válido", this.validate("10781193000119", Rules.cnpj));
Assert.assertNull("78471826000126 é um Cnpj numérico válido", this.validate("78471826000126", Rules.cnpj));
Assert.assertNull("86462814000163 é um Cnpj numérico válido", this.validate("86462814000163", Rules.cnpj));
Assert.assertNull("53110873000113 é um Cnpj numérico válido", this.validate("53110873000113", Rules.cnpj));
}

Assert.assertNull("86462814000163 é um Cnpj válido", this.validate("86462814000163", Rules.cnpj));
@Test
public void testarCnpjAlfanumericoValido() {
Assert.assertNull("12ABCDEF345621 é um Cnpj alfanumérico válido", this.validate("12ABCDEF345621", Rules.cnpj));
Assert.assertNull("A1B2C3D4E5F668 é um Cnpj alfanumérico válido", this.validate("A1B2C3D4E5F668", Rules.cnpj));
}

@Test
public void testarCnpjInvalido() {
// Numéricos Inválidos (DVs incorretos ou repetidos)
Assert.assertNotNull("32432423424234 não é um Cnpj válido", this.validate("32432423424234", Rules.cnpj));

Assert.assertNotNull("92876904876903 não é um Cnpj válido", this.validate("92876904876903", Rules.cnpj));

Assert.assertNotNull("85478704024331 não é um Cnpj válido", this.validate("85478704024331", Rules.cnpj));

Assert.assertNotNull("03216987964642 não é um Cnpj válido", this.validate("03216987964642", Rules.cnpj));
Assert.assertNotNull("11111111111111 não é um Cnpj válido (Dígitos repetidos)", this.validate("11111111111111", Rules.cnpj));
Assert.assertNotNull("32758217350712 não é um Cnpj válido", this.validate("32758217350712", Rules.cnpj));

Assert.assertNotNull("11111111111111 não é um Cnpj válido", this.validate("11111111111111", Rules.cnpj));
// Alfanuméricos Inválidos (DVs calculados incorretamente)
Assert.assertNotNull("12ABCDEF345600 não é um Cnpj válido (DVs não batem)", this.validate("12ABCDEF345600", Rules.cnpj));
Assert.assertNotNull("A1B2C3D4E5F600 não é um Cnpj válido (DVs não batem)", this.validate("A1B2C3D4E5F600", Rules.cnpj));
}

Assert.assertNotNull("32758217350712 não é um Cnpj válido", this.validate("32758217350712", Rules.cnpj));
@Test
public void testarCaracteresNaoPermitidos() {
Assert.assertNotNull("Os Dígitos Verificadores finais devem ser obrigatoriamente números", this.validate("12ABCDEF3456AB", Rules.cnpj));

Assert.assertNotNull("No Cnpj são aceitos apenas dígitos", this.validate("23.348.194/0001-16", Rules.cnpj));
Assert.assertNotNull("Não deve aceitar símbolos ou caracteres especiais (#, $)", this.validate("#123456789$056", Rules.cnpj));
Assert.assertNotNull("Não deve aceitar símbolos comuns (@)", this.validate("12@BCDEF345621", Rules.cnpj));

Assert.assertNotNull("No Cnpj são aceitos apenas dígitos", this.validate("012abcdefg9539", Rules.cnpj));
Assert.assertNotNull("Não deve aceitar caracteres com acentuação", this.validate("12ÁBCDEF345621", Rules.cnpj));

Assert.assertNotNull("No Cnpj são aceitos apenas dígitos", this.validate("#123456789$056", Rules.cnpj));
Assert.assertNotNull("Não deve aceitar formatação/pontuação", this.validate("23.348.194/0001-16", Rules.cnpj));
}

@Test
public void testarTamanho() {
Assert.assertNotNull("Cnpj com menos de 14 dígitos não deve ser aceito", this.validate("0123456", Rules.cnpj));
Assert.assertNotNull("Cnpj alfanumérico com menos de 14 dígitos não deve ser aceito", this.validate("12ABCDEF34562", Rules.cnpj));

Assert.assertNotNull("Cnpj com mais de 14 dígitos não deve ser aceito", this.validate("01234567890123456", Rules.cnpj));

Assert.assertNull("Cnpj com 14 dígitos deve ser aceito", this.validate("53110873000113", Rules.cnpj));
Assert.assertNotNull("Cnpj alfanumérico com mais de 14 dígitos não deve ser aceito", this.validate("12ABCDEF3456210", Rules.cnpj));
}

}
Loading