Источник: Korisne JavaScript biblioteke za poslovni software u nas Srba
Osnovni podatak za identifikaciju osobe u Srbiji je JMBG (jedinstveni matični broj građana) (a i u republikama bivše SFRJ, mada ga Hrvati napuštaju od 2009. godine uvođenjem OIB-a (osobnog identifikacionog broja)) . Ovaj broj u sebi sadrži 13 cifara koje nam mogu mnogo toga reći od datuma rođenja, preko pola osobe, regiona u kom je nosilac broja rođen, do rednog broja rođenja u danu (odnosi se na dan, region i pol).
Primer JMBG-a:
DD MM GGG RR BBB K
DD – dan rođenja, MM – mesec rođenja, GGG – zadnje tri cifre godine rođenja
RR – region rođenja ili prebivališta
BBB – jedinstveni broj, redni broj rođenja (000-499 – muški, 500-999 – ženski)
K – kontrolna cifra
Kontrolni broj se izračunava sledećom formulom (brojeve iz JMBG-a ću prikazati redom kao b1, b2… b12):
K = 11 - ((7 x (b1 + b7) + 6 x (b2 + b8) + 5 x (b3 + b9) + 4 x (b4 + b10) + 3 x (b5 + b11) + 2 x (b6 + b12)) % 11)
Kako kontrolni broj mora biti jednocifren kao problem ostaju vrednosti 10 i 11. Na internetu postoje razni predlozi kako handleovati ove vrednosti (pretvoriti ih u 0, oduzeti 10) ali ispravan način je da se 11 pretvara u 0, a 10 je “X” odnosno nedozvoljena vrednost (u ovom slučaju se veštački uvećavao redni broj rođenja BBB kako bi se izbegla vrednost 10).
Postoje i matični brojevi koji nemaju ispravnu kontrolnu cifru. To su matični brojevi dodeljeni nerezidentima (licima na privremenom boravku u zemlji, tzv. gastarbeiter-ima u Srbiji) i oni se prepoznaju po broju regiona (60 ili 66).
Interpretacija prethodno napisanog u JavaScript-u bi izgledala ovako (ovo je samo deo veće biblioteke pa sam code snippet neće raditi kao takav):
dataValidator.prototype.validanJMBG = function (jmbg) { if (typeof jmbg !== "undefined" && jmbg !== null && jmbg.length === 13 && dataValidator.prototype.validanBroj(jmbg)) { var dan = parseInt(jmbg.substring(0, 2), 10); var mesec = parseInt(jmbg.substring(2, 4), 10) - 1; var godina = parseInt("2" + jmbg.substring(4, 7),10); if (dataValidator.prototype.validanDatum(new Date(godina, mesec, dan))) { return /^60|66$/.test(jmbg.substring(7, 9)) || parseInt(jmbg.charAt(12), 10) === mod11(jmbg.substring(0, 12), function (kb) { return kb === 11 ? 0 : ((kb === 10) ? "X" : kb); }); } } return false; }; function mod11(br, dodatni_uslov) { var kb = 0; for (var i = br.length - 1, mnozilac = 2; i >= 0; i--) { kb += parseInt(br.charAt(i), 10) * mnozilac; mnozilac = mnozilac === 7 ? 2 : mnozilac + 1; } kb = 11 - (kb % 11); return (typeof dodatni_uslov === "undefined") ? kb : dodatni_uslov(kb); }
Kao što se može videti iz primera potencijalne dvocifrene vrednosti obrađujemo callback funkcijom. Funkcija mod11 je interna funkcija veće biblioteke i služi, osim za validaciju JMBG-a, za validaciju matičnog broja pravnog lica ili preduzetničke radnje kao i za računanje kontrolnog broja za plaćanje npr. INFOSTAN JKP još uvek koristi model 11 na svojim uplatnicama.
U pomenutoj biblioteci se nalazi ne samo validator za JMBG već i kompletan parser svih podataka iz JMBG-a. Biblioteka je open source i nalazi se na GitHub-u. Projekat sadrži demo stranicu sa svim funkcionalnostima i test case-ovima (korišćen je QUnit).
Matični broj pravnog lica odnosno preduzetničke radnje ima 8 cifara gde je poslednja cifra kontrolni broj po modelu 11. Kontrolna cifra se računa na isti način kao i za JMBG sa različitom obradom dvocifrenog slučaja (ovde se 10 i 11 pretvaraju u 0).
U sledećem JavaScript kodu opet pozivamo funkciju mod11 samo menjamo callback funkciju za obradu dvocifrenih rezultata:
dataValidator.prototype.validanMB = function (mb) { return mb.length === 8 && dataValidator.prototype.validanBroj(mb) && parseInt(mb.charAt(7), 10) === mod11(mb.substring(0, 7), function (kb) { return kb > 9 ? 0 : kb; }); };
PIB ili poreski identifikacioni broj je jedan od identifikacionih podataka pravnog lica. JavaScript za validaciju PIB-a bi izgledao ovako:
dataValidator.prototype.validanPIB = function (pib) { if (pib.length === 9 && dataValidator.prototype.validanBroj(pib)) { var suma = 10; for (var i = 0; i < 8; i++) { suma = (suma + parseInt(pib.charAt(i), 10)) % 10; suma = (suma === 0 ? 10 : suma) * 2 % 11; } suma = (11 - suma) % 10; return parseInt(pib.charAt(8), 10) === suma; } return false; };
Model 97 se koristi za izračunavanje kontrolnog broja za naloge za plaćanje npr.obavezan je za uplatu javnih prihoda (Poreska uprava) a može se koristiti i za druge potrebe, za izračunavanje kontrolne cifre kod računa u domaćem platnom prometu (račun se sastoji iz tri grupe cifara BBB-PPPPPPPPPPPPP-KK gde je BBB jedinstveni kod banke npr. 265 – Raiffeisen Bank, PPP… – 13 cifara je jedinstveni broj partije u okviru banke – u kratkom zapisu se mogu izuzeti vodeće nule, KK predstavlja kontrolni broj po modelu 97).
Kontrolna cifra se računa sledećom formulom (za primer koristimo br koji predstavlja numerički niz):
K = 98 - ((br x 100) % 97)
Interpretacija ove formule u JavaScript-u je drugačija jer moduo 97 nije davao tačan rezultat u slučaju velikih numeričkih nizova!!!
function mod97(br) { var kb = 0, os = 100; for (var x = br.length - 1; x >= 0; x--) { kb = (kb + (os * parseInt(br.charAt(x), 10))) % 97; os = (os * 10) % 97; } kb = 98 - kb; return kb; }
Ovu funkciju smo iskoristili za praktičnu primenu na sledeći način. Kao što se može videti iz primera ispod validacija kontrolnog broja prima i callback funkciju kojoj predaje niz validacionih grešaka ukoliko ih ima:
dataValidator.prototype.validanMod97 = function (broj, validation_error_callback) { if (typeof validation_error_callback !== "undefined") { var validation_errors = []; if (broj.length <= 2) validation_errors.push("Nevalidna dužina broja"); for (var i = 0; i < broj.length; i++) { var validno = false; var slovo = broj.charAt(i); if (slovoUBroj(slovo) !== null) validno = true; else validation_errors.push("Nevalidan znak: (\'" + slovo + "\') na poziciji " + (i + 1).toString()); } if (validation_errors.length !== 0) validation_error_callback(validation_errors); } return dataValidator.prototype.kontrolniBrojMod97(broj.substring(2)) === broj.substring(0, 2); }; dataValidator.prototype.kontrolniBrojMod97 = function (broj) { if (typeof broj === "undefined" || broj === null || broj === "") return null; var zakontrolu = ""; for (var i = 0; i < broj.length; i++) { var vrednost = slovoUBroj(broj.charAt(i)); if (vrednost === null) return null; else zakontrolu += vrednost.toString(); } if (dataValidator.prototype.validanBroj(zakontrolu)) { var rez = mod97(zakontrolu); return rez.length === 1 ? rez = "0" + rez.toString() : rez.toString(); } else return null; };
Kako se u kontrolnim brojevima za uplatu poreza mogu naći i slova sledeća funkcija i JSON nam rešavaju problem prevođenja vrednosti:
function slovoUBroj(slovo) { return slovo === "-" ? "" : ((dataValidator.prototype.validanBroj(slovo)) ? slovo : (_slova_za_kontrolni_broj[slovo] || null)); } var _slova_za_kontrolni_broj = { "A": 10, "B": 11, "C": 12, "D": 13, "E": 14, "F": 15, "G": 16, "H": 17, "I": 18, "J": 19, "K": 20, "L": 21, "M": 22, "N": 23, "O": 24, "P": 25, "Q": 26, "R": 27, "S": 28, "T": 29, "U": 30, "V": 31, "W": 32, "X": 33, "Y": 34, "Z": 35 };
Svi mi koji smo razvijali softver za razmenu podataka sa državnim institucijama drage nam majke Srbije nailazili smo na spojeve nespojivog. Podaci iz lične karte su nam “ograničeni” na svega dva pisma, isto je sa podacima iz Agencije za privredne registre… no da ne nabrajam. Činjenica je da se susrećemo sa mešavinom dva pisma i da nam često zatreba konverzija iz jednog u друго:
var dataConvert = (function () { "use strict"; function dataConvert() { } dataConvert.prototype.cir2Lat = function (zapromenu) { var rez = ""; for (var i = 0; i < zapromenu.length; i++) { var slovo = zapromenu.charAt(i); rez += convert(slovo, _cir2lat); } return rez; }; dataConvert.prototype.lat2Cir = function (zapromenu) { var rez = ""; for (var i = 0; i < zapromenu.length; i++) { var slovo = zapromenu.charAt(i); var sledece = zapromenu.charAt(i + 1); if (typeof sledece !== "undefined" && sledece !== null) if (/^lj|Lj|LJ|nj|Nj|NJ|dž|Dž|DŽ$/.test(slovo + sledece)) { slovo += sledece; i++; } rez += convert(slovo, _lat2cir); } return rez; }; function convert(slovo, recnik) { return recnik[slovo] || slovo; } var _cir2lat = { //mala slova "а": "a", "б": "b", "в": "v", "г": "g", "д": "d", "ђ": "đ", "е": "e", "ж": "ž", "з": "z", "и": "i", "ј": "j", "к": "k", "л": "l", "љ": "lj", "м": "m", "н": "n", "њ": "nj", "о": "o", "п": "p", "р": "r", "с": "s", "т": "t", "ћ": "ć", "у": "u", "ф": "f", "х": "h", "ц": "c", "ч": "č", "џ": "dž", "ш": "š", //velika slova "А": "A", "Б": "B", "В": "V", "Г": "G", "Д": "D", "Ђ": "Đ", "Е": "E", "Ж": "Ž", "З": "Z", "И": "I", "Ј": "J", "К": "K", "Л": "L", "Љ": "LJ", "М": "M", "Н": "N", "Њ": "NJ", "О": "O", "П": "P", "Р": "R", "С": "S", "Т": "T", "Ћ": "Ć", "У": "U", "Ф": "F", "Х": "H", "Ц": "C", "Ч": "Č", "Џ": "DŽ", "Ш": "Š" }; var _lat2cir = { //mala slova "a": "а", "b": "б", "v": "в", "g": "г", "d": "д", "đ": "ђ", "e": "е", "ž": "ж", "z": "з", "i": "и", "j": "ј", "k": "к", "l": "л", "lj": "љ", "m": "м", "n": "н", "nj": "њ", "o": "о", "p": "п", "r": "р", "s": "с", "t": "т", "ć": "ћ", "u": "у", "f": "ф", "h": "х", "c": "ц", "č": "ч", "dž": "џ", "š": "ш", //velika slova "A": "А", "B": "Б", "V": "В", "G": "Г", "D": "Д", "Đ": "Ђ", "E": "Е", "Ž": "Ж", "Z": "З", "I": "И", "J": "Ј", "K": "К", "L": "Л", "LJ": "Љ", "Lj": "Љ", "M": "М", "N": "Н", "NJ": "Њ", "Nj": "Њ", "O": "О", "P": "П", "R": "Р", "S": "С", "T": "Т", "Ć": "Ћ", "U": "У", "F": "Ф", "H": "Х", "C": "Ц", "Č": "Ч", "DŽ": "Џ", "Dž": "Џ", "Š": "Ш" }; return dataConvert; })();