Last active
November 10, 2023 15:54
-
-
Save annibal/2fcec8401707ad66c43cb77692ba7274 to your computer and use it in GitHub Desktop.
Functions to generate Brazil's Document validator number - CPF / CNPJ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Receives a part of the document and returns a validation digit | |
* Used mostly for CPF or CNPJ | |
* | |
* Ex.: | |
* getVerificationDigit("000000001") // 9 | |
* getVerificationDigit("0000000019") // 1 | |
* getVerificationDigit("100200300") // 8 | |
* getVerificationDigit(1, 9) // 9 | |
* getVerificationDigit(19, 9) // 1 | |
* @param partialDoc String|Number - the document number | |
* @param minLen Number - how many digits are expected (in case the doc was parsed from a string with leading zeros) | |
* @returns Number | |
*/ | |
function getVerificationDigit(partialDoc, minLen) { | |
const str = partialDoc.toString(); | |
let len = str.length; | |
if (minLen) { | |
len = minLen; | |
} | |
let digit = str.split("").reduce((all, digit, index) => { | |
return all + digit * (len + 1 - index); | |
}, 0); | |
digit = 11 - (digit % 11); | |
if (digit > 9) digit = 0; | |
return digit; | |
} | |
/** | |
* Removes ".", "-", "/" and anything else that is not a number from the document | |
* | |
* Ex.: | |
* parseDoc("000.000.001-91") // 191 | |
* parseDoc("192.168.0.1") // 19216801 | |
* @param strDoc String - The document with all visual aid characters | |
* @returns Number | |
*/ | |
function parseDoc(strDoc) { | |
const strDocOnlyNumbers = (strDoc + "").replace(/[^0-9]/gi, ""); | |
return parseInt(strDocOnlyNumbers); | |
} | |
/** | |
* Adds ".", "-", "/" and other aiding characters to a document. | |
* The character "0" in format will be replaced by the corresponding | |
* number at the same index in intDoc, traversing from the last to | |
* the first character in format. | |
* | |
* Ex.: | |
* formatDoc(191, "000.000.000-00") // "000.000.001-91" | |
* formatDoc(11122233300, "000.000.000-00") // "111.222.333-00" | |
* formatDoc(123456, "0 / 0 [0,0]") // "3 / 4 [5,6]" | |
* formatDoc(999, "0 / 0 [0,0]") // "0 / 9 [9,9]" | |
* @param intDoc Number - The document as a number | |
* @param format String - Format that will receive the numbers of the doc | |
* @returns String | |
*/ | |
function formatDoc(intDoc, format) { | |
const strDoc = intDoc.toString(); | |
let pos = strDoc.length; | |
return format | |
.split("") | |
.reverse() | |
.map((fChar) => { | |
if (fChar === "0") { | |
pos -= 1; | |
if (pos < 0) { | |
return fChar; | |
} else { | |
return strDoc.charAt(pos); | |
} | |
} else { | |
return fChar; | |
} | |
}) | |
.reverse() | |
.join(""); | |
} | |
/** | |
* Generates a random document number with a specific length. | |
* | |
* Ex.: | |
* generateRandomDoc(9) // "523784190" | |
* generateRandomDoc(9) // "154768391" | |
* generateRandomDoc(3) // "110" | |
* @param length Number - how many chars should the doc have | |
* @returns String - a string of numbers with the provided length | |
*/ | |
function generateRandomDoc(length) { | |
return Array(length) | |
.fill("") | |
.map(() => Math.round(Math.random())) | |
.join(""); | |
} | |
/** | |
* Adds the verification digits to the document and returns the whole string. | |
* | |
* Ex.: | |
* addVerificationDigits("000000001", 2) // "00000000191" | |
* addVerificationDigits("100200300", 2) // "10020030088" | |
* @param doc String|Number - the document. Must be a string because of the leading zeros | |
* @param amount Number - How many digits to add | |
*/ | |
function addVerificationDigits(doc, amount) { | |
let newDoc = doc.toString(); | |
for (let i = 0; i < amount; i++) { | |
newDoc = newDoc + "" + getVerificationDigit(newDoc); | |
} | |
return newDoc; | |
} | |
/** | |
* Checks if the document string is valid | |
* | |
* Ex.: | |
* validateDoc("00000000191", 2) // true | |
* validateDoc("00000000155", 2) // false | |
* validateDoc("10020030088", 2) // true | |
* | |
* If passing digits as null or 0, will always return isValid=true | |
* | |
* If using debug=true, will return an object with more info: | |
* validateDoc("00000000155", 2, true) // {doc: '00000000155', partial: '000000001', compare: '00000000191', isValid: false } | |
* @param doc String | |
* @param digits Number - default 2 | |
* @param debug Boolean - default false - if true, the return value will be an object | |
* @returns Boolean - true or false, or Object({ doc, partial, compare, isValid }) if debug=true | |
*/ | |
function validateDoc(doc, digits = 2, debug = false) { | |
const partialDoc = (digits === null || digits === 0) | |
? doc.toString() | |
: doc.toString().slice(0, digits * -1); | |
const compareDoc = addVerificationDigits(partialDoc, digits); | |
const isValid = doc === compareDoc; | |
if (debug) { | |
return { doc, partial: partialDoc, compare: compareDoc, isValid }; | |
} | |
return isValid; | |
} | |
// validateCPF("000.000.001-91") // {valid: true, formatted: '000.000.001-91'} | |
// validateCPF("000000001-91") // {valid: true, formatted: '000.000.001-91'} | |
// validateCPF(00000000191) // {valid: true, formatted: '000.000.001-91'} | |
// validateCPF(191) // {valid: true, formatted: '000.000.001-91'} | |
// validateCPF("000.000.001-55") // {valid: false, formatted: '000.000.001-55'} | |
// validateCPF("155") // {valid: false, formatted: '000.000.001-55'} | |
// validateCPF("19") // {valid: false, formatted: '000.000.000-19'} | |
function validateCPF(CPF) { | |
const doc = parseDoc(CPF); | |
const docZero = doc.toString().padStart(11, "0"); | |
const valid = validateDoc(docZero, 2); | |
const formatted = formatDoc(doc, "000.000.000-00"); | |
return { valid, formatted }; | |
} | |
// validateCNPJ("00.000.000/0001-91") // { valid: true, formatted: '00.000.001/0001-36'} | |
// validateCNPJ("00.000.001/0001-36") // { valid: true, formatted: '00.000.001/0001-91'} | |
// validateCNPJ(191) // {valid: true, formatted: '00.000.000/0001-91'} | |
// validateCNPJ(1000136) // {valid: true, formatted: '00.000.001/0001-36'} | |
// validateCNPJ("12312312312") // {valid: false, formatted: '00.012.312/3123-12'} | |
function validateCNPJ(CNPJ) { | |
const doc = parseDoc(CNPJ); | |
const docZero = doc.toString().padStart(14, "0"); | |
const valid = validateDoc(docZero, 2); | |
const formatted = formatDoc(doc, "00.000.000/0000-00"); | |
return { valid, formatted }; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Gets a part of a document and generates all possible full documents | |
* | |
* Ex.: | |
* getAllPossibleDocs("123.***.789-**", 2) // Array[100] like ["123.000.789-02", "123.001.789-58"] | |
* @param part String - what part of the doc you have, and the rest filled with "*" | |
* @param digits Number - how many chars of the "part" are the verification digits, counting from the end | |
* @param format String - how to format the returning value | |
* @param start Number - paginate results, starting index | |
* @param limit Number - paginate results, max return size | |
*/ | |
function getAllPossibleDocs({ part, digits, format, start = 0, limit }) { | |
const paramDoc = part.toString().replace(/[^0-9*]/gi, ""); | |
const wildChars = paramDoc.replace(/[^*]/gi, "").length; | |
const possibilities = 10 ** wildChars; | |
// console.log({ paramDoc, wildChars, possibilities, start, limit }) | |
const res = []; | |
for (let idx = start; idx < possibilities; idx++) { | |
const strIdx = idx.toString().padStart(wildChars, "0"); | |
let pos = strIdx.length; | |
const doc = paramDoc | |
.split("") | |
.reverse() | |
.map((fChar) => { | |
if (fChar === "*") { | |
pos -= 1; | |
return pos < 0 ? "0" : strIdx.charAt(pos); | |
} else { | |
return fChar; | |
} | |
}) | |
.reverse() | |
.join(""); | |
const isValid = validateDoc(doc, digits) | |
// console.log(idx, strIdx, doc, formatDoc(doc, format), isValid); | |
if (isValid) { | |
if (format) { | |
res.push(formatDoc(doc, format)); | |
} else { | |
res.push(doc); | |
} | |
} | |
if (limit != null && res.length >= limit) break; | |
} | |
return res; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment