Skip to content

Instantly share code, notes, and snippets.

@qgustavor
Last active July 25, 2024 13:18
Show Gist options
  • Save qgustavor/193ebe5ddedffe45a9897955625be90e to your computer and use it in GitHub Desktop.
Save qgustavor/193ebe5ddedffe45a9897955625be90e to your computer and use it in GitHub Desktop.
Code to check a CNPJ number and automatically suggest fixes in case of a single digit typo

If the code looks like it was generated by ChatGPT, is becaust it was: I wanted a quick soluton as I got a mistyped CNPJ in a message and I had to find the correct one. ChatGPT provided one and I'm happy with the result.

I'm publishing this (after some cleaning of its code to add modularity and improve some issues) as I think it should be a standard feature on most (if not all) websites asking for a CNPJ number. In fact, I think the same code could be adapted for CPF too. The way it implemented variations is crude, but since there is just 140-ish checks to be made, the code is fast. If I were to implement it myself I would try to use algebra, but brute forcing worked here.

If you have Deno installed, you can try out the example by running this command:

deno run -A https://gist.github.com/qgustavor/193ebe5ddedffe45a9897955625be90e/raw/check-cnpj-example.js

Which will return

The CNPJ 01.005.727/0001-23 is invalid.
Did you mean:
01.005.727/0001-24
import { checkCNPJ } from './check-cnpj.js'
const userCNPJ = '01.005.727/0001-23'
const result = checkCNPJ(userCNPJ)
if (result.valid) {
console.log(`The CNPJ ${result.formattedCNPJ} is valid.`)
} else {
console.log(`The CNPJ ${result.formattedCNPJ} is invalid.`)
if (result.possibleCorrections.length > 0) {
console.log('Did you mean:')
result.possibleCorrections.forEach(cnpj => console.log(cnpj))
}
}
export function validateCNPJ (cnpj) {
// Remove non-numeric characters
cnpj = cnpj.replace(/\D/g, '')
// Check if the CNPJ has 14 digits
if (cnpj.length !== 14) {
return false
}
// Define the weights for the calculations
const firstDigitWeights = [5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
const secondDigitWeights = [6, 5, 4, 3, 2, 9, 8, 7, 6, 5, 4, 3, 2]
// Calculate the first check digit
let sum = 0
for (let i = 0; i < 12; i++) {
sum += parseInt(cnpj[i]) * firstDigitWeights[i]
}
let remainder = sum % 11
const firstCheckDigit = remainder < 2 ? 0 : 11 - remainder
// Calculate the second check digit
sum = 0
for (let i = 0; i < 13; i++) {
sum += parseInt(cnpj[i]) * secondDigitWeights[i]
}
remainder = sum % 11
const secondCheckDigit = remainder < 2 ? 0 : 11 - remainder
// Check if the check digits are correct
return Number(cnpj[12]) === firstCheckDigit && Number(cnpj[13]) === secondCheckDigit
}
export function generateVariations (cnpj) {
cnpj = cnpj.replace(/\D/g, '')
const validCNPJs = []
if (cnpj.length !== 14) return validCNPJs
// That's crude, but works
for (let i = 0; i < 14; i++) {
for (let digit = 0; digit <= 9; digit++) {
if (Number(cnpj[i]) !== digit) {
const cnpjVariation = cnpj.substring(0, i) + digit + cnpj.substring(i + 1)
if (validateCNPJ(cnpjVariation)) {
validCNPJs.push(cnpjVariation)
}
}
}
}
return validCNPJs
}
export function formatCNPJ (cnpj) {
return cnpj.replace(/\D/g, '').replace(/^(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})$/, '$1.$2.$3/$4-$5')
}
export function checkCNPJ (cnpj) {
const isValid = validateCNPJ(cnpj)
if (isValid) {
return {
valid: true,
formattedCNPJ: formatCNPJ(cnpj),
possibleCorrections: []
}
}
const variations = generateVariations(cnpj)
const formattedVariations = variations.map(formatCNPJ)
return {
valid: false,
formattedCNPJ: formatCNPJ(cnpj),
possibleCorrections: formattedVariations
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment