Kartica emulator (BE) + fiskalna verifikacija + ime/prezime korisnika

- internal/be: emulator kartice (TCP listener za L-PFR mock)
- /v/: javna stranica za verifikaciju fiskalnog računa (QR kod)
- /servis/{id}/fiskalni-racun: štampa fiskalnog računa
- Podešavanja fiskalizacije: BE status/reset, proširen UI
- Korisnik: polja ime i prezime (model, DB, handler, admin profil)
- Fisk server: ažuriran receipt.py i server.py
-, : dokumentacija
This commit is contained in:
2026-06-26 23:17:52 +02:00
parent 985a2e7410
commit 97fd4490ea
21 changed files with 1425 additions and 116 deletions
+56 -3
View File
@@ -149,7 +149,7 @@
</div>
<div style="margin-top:10px;padding-top:10px;border-top:0.5px solid var(--ivica);">
<label class="polje-labela">Primljeno (din)</label>
<input type="number" id="primljeno" name="primljeno" min="0" step="0.01" value="0" style="width:100%;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:14px;" oninput="var t=document.getElementById('za-naplatu-iznos').textContent;var zn=t?parseFloat(t.replace(/\./g,'').replace(',','.').replace(/[^0-9.]/g,''))||0:0;var p=parseFloat(this.value)||0;var k=p-zn;document.getElementById('kusur').textContent=(k>=0?k.toFixed(2):'0.00')+' din';document.getElementById('kusur-blok').style.display=k>=0?'flex':'none'">
<input type="number" id="primljeno" name="primljeno" min="0" step="0.01" placeholder="Unesite primljeni iznos" style="width:100%;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:14px;box-sizing:border-box;" oninput="ntechPrimljenoUpdate(this)">
</div>
<div id="kusur-blok" style="display:none;justify-content:space-between;align-items:center;padding:8px 12px;background:color-mix(in srgb,var(--uspeh) 10%,transparent);border:0.5px solid var(--uspeh);border-radius:8px;">
<span style="color:var(--uspeh);font-weight:500;">Kusur (vratiti)</span>
@@ -161,14 +161,67 @@
<input type="hidden" name="naplaceno" value="{{printf "%.2f" .PreostaloSve}}">
<div style="margin-bottom:10px;">
<label class="polje-labela">Način plaćanja</label>
<select name="nacin_placanja" style="width:100%;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:13px;">
<select name="nacin_placanja" id="nacin-placanja-sel" style="width:100%;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:13px;" onchange="ntechNacinPlacanjaBroj(this)">
<option value="Gotovina">Gotovina</option>
<option value="Kartica">Kartica</option>
<option value="Virman">Virman</option>
</select>
</div>
<button type="submit" class="btn-primarno" style="width:100%;">Naplati i preuzmi</button>
<label style="display:flex;align-items:center;gap:10px;padding:10px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;cursor:pointer;margin-bottom:10px;">
<input type="checkbox" id="prikazi-racun" name="prikazi_racun" value="1" checked
style="width:16px;height:16px;accent-color:var(--sb-akcent);cursor:pointer;flex-shrink:0;">
<span style="font-size:13px;color:var(--tekst-glavni);">Prikaži fiskalni račun</span>
</label>
<button type="submit" id="btn-naplati" class="btn-primarno" style="width:100%;opacity:0.4;cursor:not-allowed;" disabled>Naplati i preuzmi</button>
</form>
<script>
(function(){
function zaNaplatu() {
var t = document.getElementById('za-naplatu-iznos');
return t ? parseFloat(t.textContent.replace(/\./g,'').replace(',','.').replace(/[^0-9.]/g,'')) || 0 : 0;
}
window.ntechPrimljenoUpdate = function(inp) {
var zn = zaNaplatu();
var p = parseFloat(inp.value) || 0;
var k = p - zn;
var kusurBlok = document.getElementById('kusur-blok');
if (k >= 0 && inp.value !== '') {
document.getElementById('kusur').textContent = k.toFixed(2).replace('.',',') + ' din';
kusurBlok.style.display = 'flex';
} else {
kusurBlok.style.display = 'none';
}
var btn = document.getElementById('btn-naplati');
var ok = inp.value !== '' && p >= zn;
btn.disabled = !ok;
btn.style.opacity = ok ? '1' : '0.4';
btn.style.cursor = ok ? 'pointer' : 'not-allowed';
};
window.ntechNacinPlacanjaBroj = function(sel) {
if (sel.value !== 'Gotovina') {
var inp = document.getElementById('primljeno');
if (inp && inp.value === '') {
inp.value = zaNaplatu().toFixed(2);
ntechPrimljenoUpdate(inp);
}
}
};
var dlg = document.currentScript.closest('dialog');
var f = dlg && dlg.querySelector('form[method="POST"]');
if (!f) return;
f.addEventListener('submit', function(){
var cb = f.querySelector('#prikazi-racun');
if (!cb || !cb.checked) return;
var sadrzaj = document.getElementById('sadrzaj');
if (!sadrzaj) return;
var observer = new MutationObserver(function(){
observer.disconnect();
window.open('/servis/{{.Nalog.ID}}/fiskalni-racun', '_blank');
});
observer.observe(sadrzaj.parentElement, {childList: true});
});
})();
</script>
</div>
</dialog>
{{end}}