Files
pankkimaksu/index.html
Ville Rantanen d9b1b4944f viilauksia
2024-03-16 18:05:02 +02:00

526 lines
18 KiB
HTML

<html>
<head>
<TITLE>Banqqo</TITLE>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="theme-color" content="lightcyan">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<STYLE type="text/css">
BODY {
font-family: sans-serif;
background-color: #fafaff;
}
TEXTAREA {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
padding: 5px;
width: 100%;
}
INPUT {
-webkit-border-radius: 3px;
-moz-border-radius: 3px;
border-radius: 3px;
}
#block {
margin-left: auto;
margin-right: auto;
width: 69%;
min-width: 69%;
max-width: 100%; }
LABEL {
width: 25ex;
display: inline-block;
}
.error {
color: salmon;
}
.hidden {
display: none;
}
#barcode {
margin-top: 0em;
margin-bottom: 0em;
width: 80%;
opacity: 1;
transition: opacity 0.2s;
}
#barcodePNG {
margin-top: 1em;
margin-bottom: 1em;
width: 25%;
opacity: 1;
transition: opacity 0.2s;
display:block;
margin-left:auto;
margin-right:auto;
}
#barcodeBW {
display: none;
}
#barcodes {
margin: 1em;
opacity: 1;
transition: opacity 0.2s;
display: grid;
}
#barcodes.fade {
opacity: 0;
}
/* Lasku */
#jaettu_lasku_nappi { width: 25%; }
#lasku {
border: 1px solid;
border-collapse: collapse;
}
#lasku th, #lasku td {
border: 1px solid;
padding: 1ex;
vertical-align: top;
}
#iban { width: 23ch; }
#viite { width: 100%; }
#laskun_tiedot { max-width: 80ch; }
#summa { max-width: 12ch; }
#lasku_summa, #summa {
text-align: right;
}
#lasku_summa::after {
content: " €";
padding-right: 8px;
}
#lasku_barcode {
text-align:center;
}
.print_help {
display: none;
}
.arvo {
font-family: monospace;
}
.kayttaja {
font-family: monospace;
}
.kayttaja_pdf {
display: none;
font-family: monospace;
}
.print_help_pdf {
display: block;
overflow: visible;
white-space: pre;
white-space: pre-wrap;
font-family: monospace;
}
@media print {
.no-print, .no-print * {
display: none !important;
}
#block {
width: 90vw;
min-width: 90vw;
max-width: 90vw;
}
.kayttaja {
display: none;
}
.print_help {
display: block;
overflow: visible;
white-space: pre;
white-space: pre-wrap;
font-family: monospace;
}
}
@media only screen and (max-width: 600px) {
#block {
margin-left: auto;
margin-right: auto;
width: 100%;
min-width: 100%;
max-width: 100%; }
}
</STYLE>
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.5/dist/barcodes/JsBarcode.code128.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js" integrity="sha512-GsLlZN/3F2ErC5ifS5QtgpiJtWd43JWSuIgh7mbzZ8zBps+dvLusV+eNQATqgA/HdeKFVgA5v3S/cIrLF7QnIg==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script>
let state = {
'iban': null,
'paiva': null,
'summa': null,
'viite': null,
'virtuaali': null,
'jaettu': null
}
function clear_barcode() {
document.getElementById('barcode').classList.add("fade");
setTimeout(function() {
document.getElementById('barcode').innerHTML = "";
}, 200);
}
function flash_generate() {
clear_barcode();
setTimeout(function() { generate() }, 200);
}
function generate(final = true) {
let code = validate(final = final);
if (!code) {
clear_barcode();
clear_lasku();
return
}
document.getElementById('virtuaali').value = code;
state['virtuaali'] = code;
JsBarcode("#barcode", code, {
format: "CODE128",
lineColor: "#404",
background: "transparent",
width: 2,
height: 80,
displayValue: false
});
JsBarcode("#barcodeBW", code, {
format: "CODE128",
lineColor: "black",
background: "transparent",
width: 2,
height: 80,
displayValue: true
});
document.getElementById('barcodes').classList.remove("fade");
convertSVGAsPNG(true);
modify_lasku();
return;
}
function make_error(id, text) {
document.getElementById(id).innerHTML = text;
return false
}
function clear_error() {
document.getElementById('iban_error').innerHTML = '';
document.getElementById('summa_error').innerHTML = '';
document.getElementById('viite_error').innerHTML = '';
document.getElementById('paiva_error').innerHTML = '';
}
function modify_request(iban,summa,viite,paiva) {
let URL = window.location.href.split('?')[0]
let saaja_s = encodeURIComponent(document.getElementById('saaja').value);
let maksaja_s = encodeURIComponent(document.getElementById('maksaja').value);
let lasku_s = encodeURIComponent(document.getElementById('laskun_tiedot').value);
let jaettu = state['jaettu'] === true ? "1" : ""
const nextURL = `${URL}?FI${state['iban']},${state['summa']},${state['viite']},${state['paiva']},${saaja_s},${maksaja_s},${lasku_s},${jaettu}`;
window.history.replaceState({}, document.title, nextURL);
const shareURL = `${URL}?FI${state['iban']},${state['summa']},${state['viite']},${state['paiva']},${saaja_s},${maksaja_s},${lasku_s},1`;
document.getElementById('lasku_linkki').href = shareURL;
}
function calculate_viite_tarkiste(ilman_tarkiste) {
let kertoimet = Array(7, 3, 1);
let tulosumma = 0;
for (const [index, element] of ilman_tarkiste.entries()) {
tulosumma += kertoimet[index % 3] * parseInt(element);
}
let tarkiste = (10 - (tulosumma % 10)) % 10;
return tarkiste
}
function validate_viite(viite) {
if (! /^\d+$/.test(viite)) {
return Array(false, "Viitteen joukossa muutakin kun numeroita!");
}
let nurin = reversed(viite);
if (nurin.length < 3) return Array(false, "Liian lyhyt viite")
if (nurin.length > 20) return Array(false, "Liian pitkä viite")
let ilman_tarkiste = nurin.substring(1).split('');
let tarkiste = calculate_viite_tarkiste(ilman_tarkiste);
if (tarkiste != parseInt(nurin.substring(0,1))) {
return Array(false, "Viitteen tarkiste ei täsmää, viite väärin, tai jos olet luomassa omaa, lisää tarkiste: " + calculate_viite_tarkiste(nurin.split('')).toString());
}
return Array(true, String(viite).padStart(20,'0'));
}
function validate(final = true) {
clear_error();
// IBAN
let iban = document.getElementById('iban').value.replaceAll(' ','').toUpperCase().replace(/^FI/,"");
if (iban.length != 16) return make_error('iban_error',"IBAN on 16 numeroa ja pitää alkaa FI!");
if (! /^\d+$/.test(iban)) return make_error('iban_error',"IBAN Joukossa muutakin kun numeroita!");
// SUMMA
let summa = parseFloat(
document.getElementById('summa').value.replaceAll(' ','').replace(',','.')
);
if (isNaN(summa)) {
return make_error('summa_error', "Summa Ei numero");
}
if ( summa <= 0 ) {
return make_error('summa_error', "Summan pitää olla positiivinen");
}
if ( summa > 999999 ) {
return make_error('summa_error', "Summan pitää olla alle miljoona");
}
let sumsplit = summa.toFixed(2).split(".");
let euro = String(sumsplit[0]).padStart(6,'0');
let cents = String(sumsplit[1]).padStart(2,'0');
// VIITE
let viite = document.getElementById('viite').value.replaceAll(/\s/g,'').replace(/^0+/,"");
let viite_valid = validate_viite(viite);
if (! viite_valid[0]) return make_error("viite_error", viite_valid[1]);
// PAIVA
if (! /^[\d-]+$/.test(document.getElementById('paiva').value)) {
return make_error('paiva_error',"Eräpäivän joukossa muutakin kun numeroita ja - !");
}
let paiva_obj = new Date(Date.parse(document.getElementById('paiva').value));
if (isNaN(paiva_obj)) return make_error('paiva_error', "Eräpäivä muotoa VVVV-KK-PP");
let lyhyt_paiva = paiva_obj.getFullYear().toString().slice(-2) + String(paiva_obj.getMonth() + 1).padStart(2,'0') + String(paiva_obj.getDate()).padStart(2,'0');
let oikea_paiva = Array(
paiva_obj.getFullYear().toString(),
String(paiva_obj.getMonth() + 1).padStart(2,'0'),
String(paiva_obj.getDate()).padStart(2,'0')
).join("-");
// Modify form values when validation is final
if (final) {
document.getElementById('iban').value = (('FI' + iban).match(/.{1,4}/g)).join(" ");
document.getElementById('summa').value = summa.toFixed(2);
document.getElementById('viite').value = reversed(reversed(viite).match(/.{1,5}/g).join(" "));
document.getElementById('paiva').value = oikea_paiva;
state['iban'] = iban;
state['summa'] = document.getElementById('summa').value;
state['viite'] = viite;
state['paiva'] = oikea_paiva;
modify_request();
}
// Yhdistely
let fin = '4';
let spare = '000';
let code = fin + iban + euro + cents + spare + viite_valid[1] + lyhyt_paiva;
return code;
}
function parse_virtual() {
document.getElementById('virtuaali_error').innerHTML = "";
let vkoodi = document.getElementById('virtuaali').value.replaceAll(" ","");
if (vkoodi.length != 54) return make_error('virtuaali_error', 'Koodi väärän mittainen');
if (! /^\d+$/.test(vkoodi)) {
return make_error('virtuaali_error',"Joukossa muutakin kun numeroita!");
}
if (vkoodi.substring(0,1) != '4') {
return make_error('virtuaali_error',"Ei kotimainen maksukoodi!");
}
let iban = "FI" + vkoodi.substring(1,17);
let summa = (parseFloat(vkoodi.substring(17,25))/100).toFixed(2);
let viite = vkoodi.substring(28,48).replace(/^0+/,'');
let vuosisata = (new Date()).getFullYear().toString().substring(0,2);
let vuosi = vkoodi.substring(48,50);
let kuu = vkoodi.substring(50,52);
let paiva = vkoodi.substring(52,54);
let paivays = `${vuosisata}${vuosi}-${kuu}-${paiva}`;
document.getElementById('iban').value = iban;
document.getElementById('summa').value = summa;
document.getElementById('viite').value = viite;
document.getElementById('paiva').value = paivays;
state['iban'] = vkoodi.substring(1,17);
state['summa'] = summa;
state['viite'] = viite;
state['paiva'] = paivays;
flash_generate();
}
function convertSVGAsPNG(e){
const svg = document.getElementById('barcodeBW');
const base64doc = btoa(unescape(encodeURIComponent(svg.outerHTML)));
const w = parseInt(svg.getAttribute('width'));
const h = parseInt(svg.getAttribute('height'));
const img_to_download = document.getElementById('barcodePNG');
img_to_download.src = 'data:image/svg+xml;base64,' + base64doc;
}
function print_help() {
let fields = ['saaja','maksaja','laskun_tiedot'];
for (let i = 0; i < fields.length; i++) {
let source = document.getElementById(fields[i]);
let target = document.getElementById('print_' + fields[i]);
target.innerHTML = source.value;
}
modify_request();
}
function modify_lasku() {
let fields = ['iban','summa','viite','paiva','virtuaali'];
for (let i = 0; i < fields.length; i++) {
try {
document.getElementById('lasku_' + fields[i]).innerHTML = document.getElementById(fields[i]).value;
} catch {}
}
print_help();
}
function clear_lasku() {
let fields = ['iban','summa','viite','paiva','virtuaali'];
for (let i = 0; i < fields.length; i++) {
try {
document.getElementById('lasku_' + fields[i]).innerHTML = "";
} catch {}
}
}
function lasku_pdf() {
var element = document.getElementById('barcodes');
const editables = document.getElementsByClassName("kayttaja");
const printables = document.getElementsByClassName("print_help");
const button = document.getElementById('jaettu_lasku_nappi');
button.classList.add("hidden");
for (let i = 0; i < editables.length; i++) {
editables[i].classList.add("kayttaja_pdf");
}
for (let i = 0; i < printables.length; i++) {
printables[i].classList.add("print_help_pdf");
}
var opt = {
margin: 10,
filename: `lasku-${state['paiva']}-${state['viite']}.pdf`,
html2canvas: { scale: 2 },
jsPDF: { unit: 'mm', format: 'A4', orientation: 'portrait' }
};
html2pdf().set(opt).from(element).save().then(function(results){
if (! state['jaettu']) {
for (let i = 0; i < editables.length; i++) {
editables[i].classList.remove("kayttaja_pdf");
}
for (let i = 0; i < printables.length; i++) {
printables[i].classList.remove("print_help_pdf");
}
} else {
button.classList.remove("hidden");
}
});
}
function show_shared() {
const editables = document.getElementsByClassName("kayttaja");
const printables = document.getElementsByClassName("print_help");
const non_printables = document.getElementsByClassName("no-print");
for (let i = 0; i < editables.length; i++) {
editables[i].classList.add("kayttaja_pdf");
}
for (let i = 0; i < printables.length; i++) {
printables[i].classList.add("print_help_pdf");
}
for (let i = 0; i < non_printables.length; i++) {
non_printables[i].classList.add("hidden");
}
document.getElementById('jaettu_lasku_nappi').classList.remove('hidden');
}
function init() {
const date = new Date();
document.getElementById('paiva').value = date.toISOString().substring(0,10);
let request = window.location.search.substring(1);
let vars = request.split(',');
for (let i = 0; i < vars.length; i++) {
if (vars[i].length == 0) continue
if (i==0) document.getElementById('iban').value = vars[i];
if (i==1) document.getElementById('summa').value = vars[i];
if (i==2) document.getElementById('viite').value = vars[i];
if (i==3) document.getElementById('paiva').value = vars[i];
if (i==4) document.getElementById('saaja').value = decodeURIComponent(vars[i]);
if (i==5) document.getElementById('maksaja').value = decodeURIComponent(vars[i]);
if (i==6) document.getElementById('laskun_tiedot').value = decodeURIComponent(vars[i]);
if (i==7) {
if (vars[i] == "1") { state['jaettu'] = true; }
}
}
flash_generate();
if (state['jaettu']) show_shared();
}
function reversed(str) {
return str.split("").reverse().join("");
}
</script>
</head>
<body onload="init()">
<div id=block>
<div class="no-print">
<p>Maksun virtuaali viivakoodi / viivakoodi <small>Nothing ever leaves this page.</small></p>
<span id="iban_error" class="error" ></span>
<span id="summa_error" class="error" ></span>
<span id="viite_error" class="error" ></span>
<span id="paiva_error" class="error" ></span>
<br>
<input type="submit" onclick="flash_generate()" value="Generoi"/>
</div>
<div id="barcodes">
<input id=jaettu_lasku_nappi class=hidden type="submit" onclick="lasku_pdf()" value="Tallenna lasku PDF"/>
<table id="lasku">
<tbody>
<tr>
<td class=col1>Saajan IBAN</td>
<td class="col2, arvo" id="">
<input id="iban" class="kayttaja" size=23 value="FI12 3456 7890 1234 56" onblur="generate()" onkeyup="generate(false)" />
<div class="print_help" id=lasku_iban>[tilinro]</div>
</td>
<td colspan=3 rowspan=2><textarea class=kayttaja rows=10 id=laskun_tiedot placeholder="Laskun tiedot" onblur="print_help()" onkeyup="print_help()"></textarea><div class="print_help" id="print_laskun_tiedot"></div></td>
</tr>
<tr>
<td class=col1>Saaja</td>
<td class=col2>
<textarea id=saaja class=kayttaja rows=3 placeholder="Saajan tiedot" onblur="print_help()" onkeyup="print_help()"></textarea><div class="print_help" id="print_saaja"></div>
</td>
</tr>
<tr>
<td class=col1 rowspan=2>Maksaja</td>
<td class=col2 rowspan=2>
<textarea id=maksaja class=kayttaja rows=3 placeholder="Maksajan tiedot" onblur="print_help()" onkeyup="print_help()"></textarea><div class="print_help" id="print_maksaja"></div>
</td>
<td class=col3>Viite</td>
<td colspan=2 class="arvo">
<input pattern="[0-9 ]+" class="kayttaja" id="viite" size=20 value=1054 onblur="generate()" onkeyup="generate(false)" placeholder="1054"/>
<div class="print_help" id="lasku_viite">[viite]</div>
</td>
</tr>
<tr>
<td class=col3>Eräpäivä</td>
<td class="col4, arvo" >
<input type="text" pattern="[0-9\-]+" class="kayttaja" id="paiva" size=10 onblur="generate()" onkeyup="generate(false)" placeholder="VVVV-KK-PP" />
<div class="print_help" id="lasku_paiva">[paiva]</div></td>
<td class="col5, arvo">
<input type="number" class="kayttaja" id="summa" size=8 value=10 onblur="generate()" onkeyup="generate(false)" />
<div class="print_help" id="lasku_summa">[summa]</div>
</td>
</tr>
<tr>
<td colspan=5 id=lasku_barcode><svg id="barcode"></svg>
<p id="lasku_virtuaali" class="arvo">[virtuaaliviivakoodi]</p>
</td>
</tr>
</tbody>
</table>
</div>
<br>
<div class="no-print">
<input type="submit" onclick="lasku_pdf()" value="Tallenna lasku PDF"/><br><br>
<label>Jaettava <a id="lasku_linkki" href="#"/>linkki</a></label><br>
<br>
<label>Virtuaaliviivakoodi:</label><br>
<input id="virtuaali" size=54 /><br>
<label>&nbsp;</label><span id="virtuaali_error" class="error" ></span><br>
<input type="submit" onclick="parse_virtual()" value="Muunna virtuaalikoodi kenttiin"/>
<br>
<svg id="barcodeBW"></svg>
<p>Talleta kuvamuodossa:</p>
<img id="barcodePNG" alt="Talleta kuvana" title="Talleta kuvana"></img>
</div>
</div>
</body>
</html>