UNPKG

oiblib

Version:

Library for validating and generating Croatian permanent national identification number "OIB", it is a number of every Croatian citizen and legal persons domiciled in the Republic of Croatia.

179 lines (148 loc) 7.14 kB
/* OIB Library in Javascript Author: Gordan Nekić <gordan@neki.ch> Email: <gordan@neki.ch> Date: 13.07.2019. */ // -------------------------------------------------------------------------------- // Helper functions. // Number padding with zero characters. (and yes, you can do it with padStart) const padNumber = (numIn, size, char = '0') => { const num = `${numIn}`; return (Array(size + 1).join(`${char}`) + num).substr(-size); }; // Trying to support regular Javascript number type as valid oib input. const transformJsTypeToValidOibString = (inputObj) => { if (typeof inputObj === 'number') { return `${padNumber(inputObj, 11, '0')}`; } if (typeof inputObj === 'string') { return inputObj; } return `${padNumber(inputObj, 11, '0')}`; }; // Transform input to array of numbers const transformStringToArrayOfDigits = (input) => { const oibStrInput = `${input}`; const arrayNumbersOibDigits = oibStrInput.split('').map((item) => { return parseInt(item, 10); }); return arrayNumbersOibDigits; }; // Used to get random first 10 digits of possible oib number or first 12 for jmbg. const randomId = (_n, _possible) => { let text = ''; const possible = _possible || 'abcdefghijklmnopqrstuvwxyz0123456789'; for (let i = 0; i < _n; i++) { text += possible.charAt(Math.floor(Math.random() * possible.length)); } return text; }; // Used to get random integer from min to max // The maximum is inclusive and the minimum is inclusive. // Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random const getRandomIntInclusive = (min, max) => { min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min + 1)) + min; } // -------------------------------------------------------------------------------- // OIB Calculations etc. // Calculate OIB control number. Ref: ISO7064, MOD 11,10 const calculateOIBControlNumber = (arrayOfTenNumbersOibDigits) => { // Reference: https://regos.hr/app/uploads/2018/07/KONTROLA-OIB-a.pdf const preCalculatedControlNumber = arrayOfTenNumbersOibDigits.reduce((acc, item) => { acc = acc + item; acc = acc % 10; if (acc === 0) { acc = 10; } acc = acc * 2; acc = acc % 11; return acc; }, 10); // Note that we start with the value of 10. // 11 has to be reduced by preCalculatedControlNumber, if it is 10 then it is 0. const elevenMinusPreCalculatedControlNumber = 11 - preCalculatedControlNumber; const calculatedControlNumber = (elevenMinusPreCalculatedControlNumber === 10) ? 0 : elevenMinusPreCalculatedControlNumber; // Return the final value. return calculatedControlNumber; }; // Validate OIB by parsing control number and comparing it with an input. const validateOIB = (oib) => { // Check if input is a string. if (typeof oib !== 'string') { return false; } // Check if string matches the 11 digit format 10 + "checksum" (control number). const oibPattern = /^(\d{11})$/; if (!oib.match(oibPattern)) { return false; } // Parse all string digits to Array of number types. const oibDigits = oib.split('').map((item) => { return parseInt(item, 10); }); // Extract control number. const inputControlNumber = oibDigits.pop(); // Get and remove the last digit. // Do the actual math. const calculatedControlNumber = calculateOIBControlNumber(oibDigits); // Boolean const isValid = inputControlNumber === calculatedControlNumber; return isValid; }; // Generate random number that has valid control number. (Fake but possible OIB) const generatePossibleOIB = () => { const oibWithoutControlNumberInput = `${randomId(10, '0123456789')}`; const calculatedControlNumber = calculateOIBControlNumber(transformStringToArrayOfDigits(oibWithoutControlNumberInput)); // Return both the random and control number return `${oibWithoutControlNumberInput}${calculatedControlNumber}`; }; // -------------------------------------------------------------------------------- // JMBG Calculations etc. // Check for JMBG control number. const calculateJMBGControlNumber = (jmbgWithoutControlNumber) => { const elevenMinusPreCalculatedControlNumber = 11 - ( 7*(jmbgWithoutControlNumber[0]+jmbgWithoutControlNumber[6]) + 6*(jmbgWithoutControlNumber[1]+jmbgWithoutControlNumber[7]) + 5*(jmbgWithoutControlNumber[2]+jmbgWithoutControlNumber[8]) + 4*(jmbgWithoutControlNumber[3]+jmbgWithoutControlNumber[9]) + 3*(jmbgWithoutControlNumber[4]+jmbgWithoutControlNumber[10]) + 2*(jmbgWithoutControlNumber[5]+jmbgWithoutControlNumber[11]) ) % 11; const calculatedControlNumber = (elevenMinusPreCalculatedControlNumber > 9) ? 0 : elevenMinusPreCalculatedControlNumber; // Return the final value. return calculatedControlNumber; }; // Validate JMBG by parsing control number and comparing it with an input. const validateJMBG = (jmbg) => { // Check if input is a string. if (typeof jmbg !== 'string') { return false; } // Check if string matches the 13 digit format 12 + "checksum" (control number). const jmbgPattern = /^(\d{13})$/; if (!jmbg.match(jmbgPattern)) { return false; } // Parse all string digits to Array of number types. const jmbgDigits = jmbg.split('').map((item) => { return parseInt(item, 10); }); // Extract control number. const inputControlNumber = jmbgDigits.pop(); // Get and remove the last digit. // Do the actual math. const calculatedControlNumber = calculateJMBGControlNumber(jmbgDigits); // Boolean const isValid = inputControlNumber === calculatedControlNumber; return isValid; }; // Generate random number that has valid control number. (Fake but possible JMBG) const generatePossibleJMBG = () => { const randomDate = new Date(getRandomIntInclusive(0, +(new Date))); // Get random unix timestamp const DD = `${('0' + randomDate.getDate()).slice(-2)}`; const MM = `${('0' + (randomDate.getMonth() + 1)).slice(-2)}`; const YYY = `${('' + randomDate.getFullYear()).slice(-3)}`; const RR = `${('0' + getRandomIntInclusive(0, 99)).slice(-2)}`; // We need better random distribution for this... because there is almost 0 chance that there // is 500 male or female children born on same day in one of YU political regions. // but nevertheless it is still a valid JMBG... :) const BBB = `${('00' + getRandomIntInclusive(0, 999)).slice(-3)}`; const jmbgWithoutControlNumberInput = `${DD}${MM}${YYY}${RR}${BBB}`; const calculatedControlNumber = calculateJMBGControlNumber(transformStringToArrayOfDigits(jmbgWithoutControlNumberInput)); // Return both the random and control number return `${jmbgWithoutControlNumberInput}${calculatedControlNumber}`; }; module.exports = { transformStringToArrayOfDigits, calculateOIBControlNumber, calculateJMBGControlNumber, validateOIB, validateJMBG, isOIB: validateOIB, isJMBG: validateJMBG, checkOIB: validateOIB, checkJMBG: validateJMBG, generatePossibleOIB, generatePossibleJMBG, };