|
| 1 | +<!DOCTYPE html> |
| 2 | +<html> |
| 3 | +<head> |
| 4 | + <meta charset="UTF-8" /> |
| 5 | + <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| 6 | + <title>Pankkiviivakoodi</title> |
| 7 | + <script> |
| 8 | + function tarkasta_kerroin_137(num) { |
| 9 | + return num.match(/./g).reverse().reduce((a, b, i) => a + ((+b) * [1, 7, 3][i % 3]), 0) % 10 == 0; |
| 10 | + } |
| 11 | + |
| 12 | + function tarkasta_mod_97_10(num) { |
| 13 | + return BigInt(num.substr(4) + num.substr(0, 4).replace(/[A-Z]/g, c => c.charCodeAt(0) - 55)) % 97n == 1n; |
| 14 | + } |
| 15 | + |
| 16 | + function tarkista_tilinumero(tili) { |
| 17 | + let tiliStr = tili.toUpperCase().replace(/\s/g, ""); |
| 18 | + if (!tiliStr.match(/^[ABCDEFGHIJKLMNOPQRSTUVWXYZ]{2}[0123456789]{16}$/)) { |
| 19 | + throw new Error("Tilinumero ei ole IBAN-muotoinen"); |
| 20 | + } |
| 21 | + if (!tarkasta_mod_97_10(tiliStr)) { |
| 22 | + throw new Error("Tilinumero ei ole kelvollinen (väärä tarkistusluku)"); |
| 23 | + } |
| 24 | + if (!tiliStr.startsWith("FI")) { |
| 25 | + throw new Error("Tilinumero ei ole suomalainen"); |
| 26 | + } |
| 27 | + return tiliStr.substr(2); |
| 28 | + } |
| 29 | + |
| 30 | + function tarkista_summa(summa) { |
| 31 | + let summaArr = summa.replace(/\s|'/g, "").replace(/,/g, ".").split("."); |
| 32 | + if (summaArr.length > 2) { |
| 33 | + throw new Error("Summassa voi olla vain yksi desimaalierotin"); |
| 34 | + } |
| 35 | + let e = (summaArr[0] || "0").replace(/^\+?0*/, "").padStart(6, "0"); |
| 36 | + let c = (summaArr[1] || "0").replace(/0*$/, "").padEnd(2, "0"); |
| 37 | + let summaStr = e + c; |
| 38 | + if (summaStr.match(/[^0123456789]/)) { |
| 39 | + throw new Error("Summassa voi olla vain numeroita"); |
| 40 | + } |
| 41 | + if (e.length > 6) { |
| 42 | + throw new Error("Summa voi olla enintään 999999,99 euroa"); |
| 43 | + } |
| 44 | + if (c.length > 2) { |
| 45 | + throw new Error("Summassa voi olla korkeintaan kaksi desimaalia"); |
| 46 | + } |
| 47 | + return summaStr; |
| 48 | + } |
| 49 | + |
| 50 | + function tarkista_viite(viite) { |
| 51 | + let viiteStr = viite.toUpperCase().replace(/\s/g, "") || "0"; |
| 52 | + if (!viiteStr.match(/^RF[0123456789]{2}/)) { |
| 53 | + if (!viiteStr.match(/^[0123456789]{1,20}$/g)) { |
| 54 | + throw new Error("Viitenumeron tulee olla suomalainen tai RF-viite"); |
| 55 | + } |
| 56 | + if (!tarkasta_kerroin_137(viiteStr)) { |
| 57 | + throw new Error("Viitenumeron tarkistusluku on väärä"); |
| 58 | + } |
| 59 | + return viiteStr.padStart(23, "0"); |
| 60 | + } |
| 61 | + if (!viiteStr.match(/^RF[0123456789]{2,23}$/g)) { |
| 62 | + throw new Error("Viivakoodin RF-viitteessä tulee olla 2–23 numeroa"); |
| 63 | + } |
| 64 | + if (!tarkasta_mod_97_10(viiteStr)) { |
| 65 | + throw new Error("RF-viitteen tarkistusluku on väärä"); |
| 66 | + } |
| 67 | + return viiteStr.substr(2, 2) + viiteStr.substr(4).padStart(21, "0"); |
| 68 | + } |
| 69 | + |
| 70 | + function tarkista_erapaiva(erapaiva) { |
| 71 | + let m = erapaiva.match(/^\d\d(\d\d)-(\d\d)-(\d\d)$/); |
| 72 | + let erapaivaStr = m ? m[1] + m[2] + m[3] : ""; |
| 73 | + if (erapaivaStr.length != 6) { |
| 74 | + throw new Error("Eräpäivä on väärän muotoinen, syötä päivämäärä muodossa YYYY-MM-DD"); |
| 75 | + } |
| 76 | + return erapaivaStr; |
| 77 | + } |
| 78 | + |
| 79 | + function encode_Code_128C(s) { |
| 80 | + if (!s.match(/^([0-9][0-9])+$/)) { |
| 81 | + throw new Error("Viivakoodin tulee sisältää parillinen määrä numeroita"); |
| 82 | + } |
| 83 | + let symbols = [105, ...s.match(/\d\d/g).map(c => parseInt(c))]; |
| 84 | + let checksum = symbols.reduce((a, b, i) => a + b * (i || 1), 0) % 103; |
| 85 | + symbols.push(checksum); |
| 86 | + symbols.push(106); |
| 87 | + const widths = [212222, 222122, 222221, 121223, 121322, 131222, 122213, 122312, 132212, 221213, 221312, 231212, 112232, 122132, 122231, 113222, 123122, 123221, 223211, 221132, 221231, 213212, 223112, 312131, 311222, 321122, 321221, 312212, 322112, 322211, 212123, 212321, 232121, 111323, 131123, 131321, 112313, 132113, 132311, 211313, 231113, 231311, 112133, 112331, 132131, 113123, 113321, 133121, 313121, 211331, 231131, 213113, 213311, 213131, 311123, 311321, 331121, 312113, 312311, 332111, 314111, 221411, 431111, 111224, 111422, 121124, 121421, 141122, 141221, 112214, 112412, 122114, 122411, 142112, 142211, 241211, 221114, 413111, 241112, 134111, 111242, 121142, 121241, 114212, 124112, 124211, 411212, 421112, 421211, 212141, 214121, 412121, 111143, 111341, 131141, 114113, 114311, 411113, 411311, 113141, 114131, 311141, 411131, 211412, 211214, 211232, 2331112]; |
| 88 | + return symbols.map(s => "" + widths[s]).join(""); |
| 89 | + } |
| 90 | + |
| 91 | + document.addEventListener("DOMContentLoaded", function() { |
| 92 | + let form = document.querySelector("form"); |
| 93 | + let tilinumero = document.getElementById("tilinumero"); |
| 94 | + let viite = document.getElementById("viite"); |
| 95 | + let summa = document.getElementById("summa"); |
| 96 | + let erapaiva = document.getElementById("erapaiva"); |
| 97 | + let tiedot = document.getElementById("tiedot"); |
| 98 | + let viivakoodiTxt = document.getElementById("viivakoodi-txt"); |
| 99 | + let viivakoodiSvg = document.getElementById("viivakoodi-svg"); |
| 100 | + |
| 101 | + if (!erapaiva.value) { |
| 102 | + erapaiva.value = new Date().toISOString().slice(0, 10); |
| 103 | + } |
| 104 | + |
| 105 | + function tee_viivakoodi() { |
| 106 | + tiedot.textContent = ""; |
| 107 | + viivakoodiTxt.textContent = ""; |
| 108 | + viivakoodiSvg.innerHTML = ""; |
| 109 | + |
| 110 | + function tarkista(input, funktio) { |
| 111 | + let e; |
| 112 | + try { |
| 113 | + input.setCustomValidity(""); |
| 114 | + return funktio(input.value); |
| 115 | + } catch (e) { |
| 116 | + input.setCustomValidity(e.message); |
| 117 | + tiedot.textContent += e.message + "\n"; |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + let koodi = [ |
| 122 | + viite.value.match(/RF/) ? "5" : "4", |
| 123 | + tarkista(tilinumero, tarkista_tilinumero), |
| 124 | + tarkista(summa, tarkista_summa), |
| 125 | + tarkista(viite, tarkista_viite), |
| 126 | + tarkista(erapaiva, tarkista_erapaiva), |
| 127 | + ].join(""); |
| 128 | + |
| 129 | + if (!koodi.match(/^[0-9]{54}$/)) { |
| 130 | + return; |
| 131 | + } |
| 132 | + |
| 133 | + let purku = koodi.replace(/([45])(\d{16})(\d{8})(\d{23})(\d{6})/, (_, versio, tilinumero, summa, viite, erapaiva) => { |
| 134 | + tilinumero = "FI" + tilinumero; |
| 135 | + summa = summa.replace(/0*(\d+)(\d{2})/, "$1,$2"); |
| 136 | + viite_rf = "RF" + viite.substr(0, 2) + viite.substr(2).replace(/^0*/, ""); |
| 137 | + viite_fi = viite.replace(/^0*/, ""); |
| 138 | + viite = versio == 4 ? viite_fi : viite_rf; |
| 139 | + cc = new Date().getFullYear() / 100 | 0; |
| 140 | + erapaiva = erapaiva.replace(/(\d{2})(\d{2})(\d{2})/, cc + "$1-$2-$3"); |
| 141 | + return `Tili: ${tilinumero}\nSumma: ${summa} euroa\nViite: ${viite}\nEräpäivä: ${erapaiva}`; |
| 142 | + }); |
| 143 | + |
| 144 | + tiedot.textContent = purku; |
| 145 | + viivakoodiTxt.textContent = koodi; |
| 146 | + |
| 147 | + let w = 0, h = 40; |
| 148 | + let svg = encode_Code_128C(koodi).match(/[0-9]/g).map((bar_width, i) => { |
| 149 | + let x = w; |
| 150 | + w += +bar_width; |
| 151 | + return (i % 2) ? "" : `<rect x='${x}' y='0' width='${bar_width}' height='${h}' fill='black'/>\n`; |
| 152 | + }).join(""); |
| 153 | + |
| 154 | + viivakoodiSvg.innerHTML = ` |
| 155 | + <svg |
| 156 | + xmlns="http://www.w3.org/2000/svg" |
| 157 | + viewBox="0 0 ${w} ${h}" |
| 158 | + style='margin: 1em 0 1em; min-width: ${w}px; width: 40em; max-width: 100%;' |
| 159 | + > |
| 160 | + ${svg} |
| 161 | + </svg> |
| 162 | + `; |
| 163 | + } |
| 164 | + |
| 165 | + tilinumero.addEventListener("input", tee_viivakoodi); |
| 166 | + viite.addEventListener("input", tee_viivakoodi); |
| 167 | + summa.addEventListener("input", tee_viivakoodi); |
| 168 | + erapaiva.addEventListener("input", tee_viivakoodi); |
| 169 | + tee_viivakoodi(); |
| 170 | + }); |
| 171 | + </script> |
| 172 | +</head> |
| 173 | +<body> |
| 174 | + <h1>Pankkiviivakoodi</h1> |
| 175 | + <form> |
| 176 | + <dl> |
| 177 | + <dt>Saajan tilinumero</dt> |
| 178 | + <dd><input type="text" id="tilinumero" required /></dd> |
| 179 | + <dt>Viite</dt> |
| 180 | + <dd><input type="text" id="viite" /></dd> |
| 181 | + <dt>Summa</dt> |
| 182 | + <dd><input type="text" id="summa" /></dd> |
| 183 | + <dt>Eräpäivä</dt> |
| 184 | + <dd><input type="date" id="erapaiva" /></dd> |
| 185 | + </dl> |
| 186 | + </form> |
| 187 | + <dl> |
| 188 | + <dt>Tulos</dt> |
| 189 | + <dd id="tiedot" style="white-space: pre-wrap; margin-bottom: 1em;"></dd> |
| 190 | + <dd id="viivakoodi-txt" style="margin-bottom: 1em;"></dd> |
| 191 | + <dd id="viivakoodi-svg"></dd> |
| 192 | + </dl> |
| 193 | +</body> |
| 194 | +</html> |
0 commit comments