Skip to content

Commit f1c1945

Browse files
committed
feat: implement CNPJ validation with two versions and refactor validation logic
1 parent 0f1cf82 commit f1c1945

File tree

3 files changed

+151
-1
lines changed

3 files changed

+151
-1
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { ValidateFunctions } from "./types";
1+
import type { ValidateFunctions } from "../types";
22

33
// Função para calcular o primeiro dígito verificador
44
function calculateFirstVerifier(cnpjBase: number[]): number {
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/* eslint-disable @typescript-eslint/no-extraneous-class */
2+
3+
import type { ValidateFunctions } from "../types";
4+
5+
const defaultErrorMsg: string[] = [
6+
"CNPJ invalid",
7+
"CNPJ must have 14 numerical digits",
8+
"CNPJ is not valid",
9+
];
10+
11+
export class CNPJ {
12+
private static readonly tamanhoCNPJSemDV: number = 12;
13+
private static readonly regexCNPJSemDV: RegExp = /^([A-Z\d]){12}$/;
14+
private static readonly regexCNPJ: RegExp = /^([A-Z\d]){12}(\d){2}$/;
15+
private static readonly regexCaracteresMascara: RegExp = /[./-]/g;
16+
private static readonly regexCaracteresNaoPermitidos: RegExp = /[^A-Z\d./-]/i;
17+
private static readonly valorBase: number = "0".charCodeAt(0); // nosonar
18+
private static readonly pesosDV: number[] = [
19+
6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2,
20+
];
21+
private static readonly cnpjZerado: string = "00000000000000";
22+
public static isValid(cnpj: string): boolean {
23+
if (!this.regexCaracteresNaoPermitidos.test(cnpj)) {
24+
const cnpjSemMascara: string = this.removeMascaraCNPJ(cnpj);
25+
if (
26+
this.regexCNPJ.test(cnpjSemMascara) &&
27+
cnpjSemMascara !== CNPJ.cnpjZerado
28+
) {
29+
const dvInformado: string = cnpjSemMascara.substring(
30+
this.tamanhoCNPJSemDV,
31+
);
32+
const dvCalculado: string = this.calculaDV(
33+
cnpjSemMascara.substring(0, this.tamanhoCNPJSemDV),
34+
);
35+
return dvInformado === dvCalculado;
36+
}
37+
}
38+
return false;
39+
}
40+
public static calculaDV(cnpj: string): string {
41+
if (!this.regexCaracteresNaoPermitidos.test(cnpj)) {
42+
const cnpjSemMascara: string = this.removeMascaraCNPJ(cnpj);
43+
if (
44+
this.regexCNPJSemDV.test(cnpjSemMascara) &&
45+
cnpjSemMascara !== this.cnpjZerado.substring(0, this.tamanhoCNPJSemDV)
46+
) {
47+
let somatorioDV1: number = 0;
48+
let somatorioDV2: number = 0;
49+
for (let i: number = 0; i < this.tamanhoCNPJSemDV; i++) {
50+
const asciiDigito: number =
51+
cnpjSemMascara.charCodeAt(i) - this.valorBase; // nosonar
52+
somatorioDV1 += asciiDigito * this.pesosDV[i + 1];
53+
somatorioDV2 += asciiDigito * this.pesosDV[i];
54+
}
55+
const dv1: number =
56+
somatorioDV1 % 11 < 2 ? 0 : 11 - (somatorioDV1 % 11);
57+
somatorioDV2 += dv1 * this.pesosDV[this.tamanhoCNPJSemDV];
58+
59+
const dv2: number =
60+
somatorioDV2 % 11 < 2 ? 0 : 11 - (somatorioDV2 % 11);
61+
62+
return `${String(dv1)}${String(dv2)}`;
63+
}
64+
}
65+
throw new Error(
66+
"Não é possível calcular o DV pois o CNPJ fornecido é inválido",
67+
);
68+
}
69+
private static removeMascaraCNPJ(cnpj: string): string {
70+
return cnpj.replace(this.regexCaracteresMascara, "");
71+
}
72+
}
73+
74+
function cnpjIsValid(
75+
cnpj: string,
76+
errorMsg: (string | null)[] | null = defaultErrorMsg,
77+
): ValidateFunctions {
78+
if (typeof cnpj !== "string") {
79+
throw new TypeError("The input should be a string.");
80+
}
81+
// Check para saber se as mensagens que sao passadas sao validas
82+
// caso contrario retorna um ERRO
83+
if (errorMsg) {
84+
if (!Array.isArray(errorMsg)) throw new Error("Must be an Array");
85+
for (const element of errorMsg) {
86+
if (element != null && typeof element !== "string") {
87+
throw new TypeError(
88+
"All values within the array must be strings or null/undefined.",
89+
);
90+
}
91+
}
92+
}
93+
94+
// Função interna para obter a mensagem de erro
95+
function getErrorMessage(index: number): string {
96+
const errorMessage: string | null = errorMsg ? errorMsg[index] : null;
97+
return errorMessage ?? defaultErrorMsg[index];
98+
}
99+
100+
if (!cnpj) {
101+
return {
102+
isValid: false,
103+
errorMsg: getErrorMessage(0), // 'CNPJ invalid'
104+
};
105+
}
106+
// Check if the CNPJ has 14 digits
107+
if (cnpj.length !== 14 && cnpj.length !== 18) {
108+
return {
109+
isValid: false,
110+
errorMsg: getErrorMessage(1), // 'CNPJ must have 14 numerical digits'
111+
};
112+
}
113+
114+
if (CNPJ.isValid(cnpj)) {
115+
return {
116+
isValid: true,
117+
errorMsg: null,
118+
};
119+
}
120+
121+
return {
122+
isValid: false,
123+
errorMsg: getErrorMessage(2), // 'CNPJ is not valid'
124+
};
125+
}
126+
127+
export default cnpjIsValid;

src/cnpjValidator/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { ValidateFunctions } from "../types";
2+
import cnpjIsValid1 from "./cnpjValidator1";
3+
import cnpjIsValid2 from "./cnpjValidator2";
4+
5+
const defaultErrorMsg: string[] = [
6+
"CNPJ invalid",
7+
"CNPJ must have 14 numerical digits",
8+
"CNPJ is not valid",
9+
];
10+
11+
function cnpjIsValid(
12+
cnpj: string,
13+
errorMsg: (string | null)[] | null = defaultErrorMsg,
14+
cnpjVersion: "v1" | "v2" = "v1",
15+
): ValidateFunctions {
16+
if (cnpjVersion === "v2") {
17+
return cnpjIsValid2(cnpj, errorMsg);
18+
}
19+
20+
return cnpjIsValid1(cnpj, errorMsg);
21+
}
22+
23+
export default cnpjIsValid;

0 commit comments

Comments
 (0)