457 lines
21 KiB
HTML
457 lines
21 KiB
HTML
{{template "base" .}}
|
|
|
|
{{define "naslov"}}Popis — NTech{{end}}
|
|
|
|
{{define "dodatni-css"}}
|
|
<style>
|
|
.popis-input {
|
|
width: 90px;
|
|
text-align: right;
|
|
font-family: monospace;
|
|
font-size: 14px;
|
|
padding: 5px 8px;
|
|
}
|
|
.popis-input.izmenjeno {
|
|
border-color: #f97316;
|
|
background: rgba(249,115,22,0.06);
|
|
}
|
|
.popis-razlika, .popis-vrednost-razlika {
|
|
font-family: monospace;
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
min-width: 40px;
|
|
text-align: right;
|
|
}
|
|
.red-kriticno td { background: rgba(220,38,38,0.04); }
|
|
.popis-suma {
|
|
display: flex;
|
|
gap: 24px;
|
|
flex-wrap: wrap;
|
|
font-size: 13px;
|
|
}
|
|
.popis-suma-stavka { display: flex; flex-direction: column; gap: 2px; }
|
|
.popis-suma-labela { color: var(--tekst-slabi); font-size: 12px; }
|
|
.popis-suma-vrednost { font-weight: 600; font-family: monospace; font-size: 14px; }
|
|
.boja-manjak { color: #dc2626; }
|
|
.boja-visak { color: #22c55e; }
|
|
|
|
/* ── MOBILNI (≤768px): tabela → kartice ──────────────────── */
|
|
@media (max-width: 768px) {
|
|
/* zaglavlje forme: Tip i Napomena u svoj red, jednake pune širine */
|
|
.popis-polje { width: 100%; }
|
|
#tip-popisa, #napomena-polje { width: 100% !important; }
|
|
#tip-popisa { padding-right: 28px; }
|
|
|
|
/* kartica prikazuje SVE kolone kao desktop; samo zbirni red (tfoot)
|
|
se sakriva — ukupno je u sumarnim karticama ispod */
|
|
#popis-tabela tfoot { display: none !important; }
|
|
|
|
/* svaki red postaje kartica, svaka ćelija je labela:vrednost */
|
|
#popis-tabela, #popis-tabela tbody, #popis-tabela tr, #popis-tabela td { display: block; width: auto; }
|
|
#popis-tabela thead { display: none; }
|
|
#popis-tabela tr {
|
|
border: 0.5px solid var(--ivica);
|
|
border-radius: 10px;
|
|
padding: 10px 14px;
|
|
margin-bottom: 10px;
|
|
}
|
|
#popis-tabela td {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 12px;
|
|
text-align: right !important;
|
|
padding: 5px 0 !important;
|
|
border: none !important;
|
|
font-size: 14px;
|
|
}
|
|
#popis-tabela td::before {
|
|
content: attr(data-label);
|
|
font-weight: 500;
|
|
color: var(--tekst-sporedni);
|
|
text-align: left;
|
|
white-space: nowrap;
|
|
}
|
|
/* prva ćelija (naziv) je naslov kartice */
|
|
#popis-tabela td.kart-naslov {
|
|
justify-content: flex-start;
|
|
text-align: left !important;
|
|
font-size: 15px;
|
|
font-weight: 600;
|
|
border-bottom: 0.5px solid var(--ivica) !important;
|
|
padding-bottom: 9px !important;
|
|
margin-bottom: 5px;
|
|
}
|
|
#popis-tabela td.kart-naslov::before { content: none; }
|
|
/* „Stvarno kol." input — bez spiner strelica (Firefox seče broj) */
|
|
.popis-input {
|
|
width: 130px;
|
|
-moz-appearance: textfield;
|
|
appearance: textfield;
|
|
}
|
|
.popis-input::-webkit-outer-spin-button,
|
|
.popis-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
|
|
}
|
|
|
|
/* ── ŠTAMPA: A4 landscape, fiksni layout, NE responsive ───────
|
|
Print poništava mobilne kartice (!important) — i sa telefona
|
|
se štampa A4 tabela, ne prilagođava se ekranu. */
|
|
@media print {
|
|
@page { size: A4 landscape; margin: 10mm 8mm; }
|
|
|
|
/* ── resetuj pozadinu i boje (dark tema + glass efekti) ── */
|
|
html, body, .raspored, .glavni-sadrzaj, .sadrzaj, main, .kolona,
|
|
.kartica, .kartica div, .kartica p, .kartica span, .kartica label {
|
|
background: #fff !important;
|
|
color: #111 !important;
|
|
text-shadow: none !important;
|
|
backdrop-filter: none !important;
|
|
-webkit-backdrop-filter: none !important;
|
|
}
|
|
html { background: #fff !important; }
|
|
body { min-width: 780px; font-size: 12px; }
|
|
body::before, body::after { display: none !important; }
|
|
.raspored { position: static !important; z-index: auto !important; }
|
|
.glavni-sadrzaj { margin-left: 0 !important; }
|
|
.sadrzaj { padding: 0 !important; max-width: none !important; }
|
|
|
|
/* sakrij hrom */
|
|
.sidebar, .sidebar-overlay, .topbar, #hamburger,
|
|
.btn-primarno, .btn-sekundarno, .nazad-link,
|
|
.poruka-uspeh, .poruka-greska, .flash-toast,
|
|
#potvrda-modal, .dugme-stampa, .popis-suma { display: none !important; }
|
|
|
|
/* sakrij info karticu (uputstvo) */
|
|
.popis-info-kartica { display: none !important; }
|
|
|
|
/* sakrij okvire forme koji nisu tabela */
|
|
.popis-forma-zaglavlje,
|
|
.popis-forma-sumarno,
|
|
.popis-forma-dugme { display: none !important; }
|
|
|
|
/* ništa ne sme da seče/skroluje */
|
|
* { overflow: visible !important; }
|
|
|
|
/* zaglavlje štampe — naziv firme, datum, tip popisa */
|
|
.stampa-zaglavlje { display: block !important; }
|
|
|
|
/* vrati tabelu iz kartica (mobilni) nazad u pravu tabelu */
|
|
#popis-tabela {
|
|
display: table !important;
|
|
width: 100% !important;
|
|
border-collapse: collapse;
|
|
font-size: 10px;
|
|
}
|
|
#popis-tabela thead { display: table-header-group !important; }
|
|
#popis-tabela tbody { display: table-row-group !important; }
|
|
#popis-tabela tfoot { display: table-footer-group !important; }
|
|
#popis-tabela tr {
|
|
display: table-row !important;
|
|
border: none !important;
|
|
padding: 0 !important;
|
|
margin: 0 !important;
|
|
break-inside: avoid;
|
|
}
|
|
#popis-tabela th {
|
|
display: table-cell !important;
|
|
border: 0.5px solid #888 !important;
|
|
padding: 4px 6px !important;
|
|
font-size: 9px !important;
|
|
font-weight: 600 !important;
|
|
text-align: center !important;
|
|
background: #f0f0f0 !important;
|
|
color: #111 !important;
|
|
}
|
|
#popis-tabela th:first-child { text-align: left !important; }
|
|
#popis-tabela td {
|
|
display: table-cell !important;
|
|
border: 0.5px solid #ccc !important;
|
|
padding: 3px 6px !important;
|
|
text-align: right !important;
|
|
font-size: 10px !important;
|
|
color: #111 !important;
|
|
}
|
|
#popis-tabela td.kart-naslov {
|
|
text-align: left !important;
|
|
font-size: 10px !important;
|
|
font-weight: 500 !important;
|
|
}
|
|
#popis-tabela td::before, #popis-tabela th::before { content: none !important; }
|
|
|
|
/* inputi — izgledaju kao običan tekst */
|
|
.popis-input {
|
|
-moz-appearance: textfield !important;
|
|
appearance: textfield !important;
|
|
border: none !important;
|
|
background: transparent !important;
|
|
width: auto !important;
|
|
max-width: 60px !important;
|
|
padding: 0 !important;
|
|
font-size: 10px !important;
|
|
color: #111 !important;
|
|
text-align: right !important;
|
|
}
|
|
|
|
/* sakrij labele iz zaglavlja forme (Tip, Napomena) */
|
|
.popis-polje, form .polje-labela { display: none !important; }
|
|
}
|
|
</style>
|
|
{{end}}
|
|
|
|
{{define "sadrzaj"}}
|
|
<div class="kolona" style="gap:16px;">
|
|
|
|
<!-- zaglavlje štampe (skriveno na ekranu, vidljivo pri štampi) -->
|
|
<div class="stampa-zaglavlje" style="display:none;padding:0;margin-bottom:14px;">
|
|
<div style="font-size:16px;font-weight:700;margin-bottom:2px;">{{.NazivFirme}}</div>
|
|
<div style="font-size:11px;color:#555;">Popis magacina — {{.TipPopisaNaziv}} | Datum: {{.DatumPopisa}} | Sačinio: {{.KorisnikIme}}</div>
|
|
</div>
|
|
|
|
{{if .Sacuvano}}
|
|
<div class="poruka-uspeh poruka-animacija">Popis je uspešno sačuvan. Korekcije su upisane u magacinsku evidenciju.</div>
|
|
{{end}}
|
|
{{if .Greska}}
|
|
<div class="poruka-greska greska-animacija">{{.Greska}}</div>
|
|
{{end}}
|
|
|
|
<!-- uputstvo -->
|
|
<div class="kartica animiraj popis-info-kartica" style="padding:14px 18px;">
|
|
<div style="display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap;">
|
|
<div style="flex:1;min-width:260px;">
|
|
<div style="font-size:14px;font-weight:500;color:var(--tekst-glavni);margin-bottom:4px;">Popis magacina</div>
|
|
<div style="font-size:13px;color:var(--tekst-slabi);line-height:1.6;">
|
|
Unesite stvarno prebrojano stanje za svaki artikal. Polja sa razlikom biće istaknuta narandžasto.
|
|
Samo artikli sa izmenjenom količinom biće korigovani — ostali ostaju nepromenjeni.
|
|
</div>
|
|
<div style="font-size:12px;color:var(--tekst-slabi);margin-top:6px;">
|
|
Datum popisa: <strong style="color:var(--tekst-glavni);">{{.DatumPopisa}}</strong>
|
|
| Sačinio: <strong style="color:var(--tekst-glavni);">{{.KorisnikIme}}</strong>
|
|
</div>
|
|
</div>
|
|
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;">
|
|
<a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/izvestaji" class="btn-sekundarno" style="white-space:nowrap;">← Izveštaji</a>
|
|
<button type="button" onclick="window.print()" class="btn-sekundarno" style="white-space:nowrap;">🖨 Štampaj</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- forma -->
|
|
<form method="POST" action="/izvestaji/popis">
|
|
<input type="hidden" name="csrf_token" value="{{.CsrfToken}}">
|
|
|
|
<div class="kartica animiraj" style="padding:0;overflow:hidden;">
|
|
<div style="padding:12px 20px;border-bottom:0.5px solid var(--ivica);display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;" class="popis-forma-zaglavlje">
|
|
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Artikli</span>
|
|
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;">
|
|
<div class="popis-polje">
|
|
<label class="polje-labela" style="display:inline;margin-right:6px;">Tip:</label>
|
|
<select name="tip_popisa" id="tip-popisa" style="width:auto;height:auto;font-size:13px;padding:5px 28px 5px 8px;" onchange="azurirajNapomenu()">
|
|
<option value="godišnji"{{if eq .TipPopisa "godišnji"}} selected{{end}}>Godišnji</option>
|
|
<option value="vanredni"{{if eq .TipPopisa "vanredni"}} selected{{end}}>Vanredni</option>
|
|
<option value="preuzimanje"{{if eq .TipPopisa "preuzimanje"}} selected{{end}}>Preuzimanje dužnosti</option>
|
|
</select>
|
|
</div>
|
|
<div class="popis-polje">
|
|
<label class="polje-labela" style="display:inline;margin-right:6px;">Napomena:</label>
|
|
<input type="text" name="napomena" id="napomena-polje" value="Godišnji popis" style="width:220px;font-size:13px;padding:5px 8px;">
|
|
</div>
|
|
<button type="submit" class="btn-primarno">Sačuvaj popis</button>
|
|
</div>
|
|
</div>
|
|
|
|
{{if not .Artikli}}
|
|
<div style="padding:40px;text-align:center;color:var(--tekst-slabi);font-size:14px;">
|
|
Nema artikala u magacinu.
|
|
</div>
|
|
{{else}}
|
|
<div style="overflow-x:auto;">
|
|
<table class="tabela" id="popis-tabela">
|
|
<thead>
|
|
<tr>
|
|
<th>Artikal</th>
|
|
<th class="kol-sek">Šifra</th>
|
|
<th class="kol-sek">Kategorija</th>
|
|
<th class="kol-sek" style="text-align:right;">JM</th>
|
|
<th class="kol-sek" style="text-align:right;">Nab. cena</th>
|
|
<th style="text-align:right;">Knj. kol.</th>
|
|
<th class="kol-sek" style="text-align:right;">Knj. vrednost</th>
|
|
<th style="text-align:right;">Stvarno kol.</th>
|
|
<th style="text-align:right;">Razlika kol.</th>
|
|
<th class="kol-sek" style="text-align:right;">Razlika vrednost</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{{range .Artikli}}
|
|
<tr class="{{if le .Kolicina .KolicinMin}}red-kriticno{{end}}" id="red-{{.ID}}">
|
|
<td class="kart-naslov" style="font-weight:500;">{{.Naziv}}</td>
|
|
<td class="kol-sek" data-label="Šifra" style="font-family:monospace;font-size:12px;color:var(--tekst-slabi);">{{if .Sifra}}{{.Sifra}}{{else}}—{{end}}</td>
|
|
<td class="kol-sek" data-label="Kategorija" style="font-size:12px;color:var(--tekst-slabi);">{{if .KategorijaNaziv}}{{.KategorijaNaziv}}{{else}}—{{end}}</td>
|
|
<td class="kol-sek" data-label="JM" style="text-align:right;font-size:12px;color:var(--tekst-slabi);">{{if .JedinicaMere}}{{.JedinicaMere}}{{else}}kom{{end}}</td>
|
|
<td class="kol-sek" data-label="Nab. cena" style="text-align:right;font-family:monospace;font-size:13px;">{{dinariCeli .NabavnaCena}}</td>
|
|
<td data-label="Knj. kol." style="text-align:right;font-family:monospace;font-weight:600;" id="knjizno-{{.ID}}">{{.Kolicina}}</td>
|
|
<td class="kol-sek" data-label="Knj. vrednost" style="text-align:right;font-family:monospace;font-size:13px;" id="knjvred-{{.ID}}">—</td>
|
|
<td data-label="Stvarno kol." style="text-align:right;">
|
|
<input type="number"
|
|
class="popis-input"
|
|
name="kolicina_{{.ID}}"
|
|
id="input-{{.ID}}"
|
|
value="{{.Kolicina}}"
|
|
min="0"
|
|
data-knjizno="{{.Kolicina}}"
|
|
data-cena="{{printf "%.4f" .NabavnaCena}}"
|
|
data-id="{{.ID}}"
|
|
oninput="azurirajRed(this)">
|
|
</td>
|
|
<td data-label="Razlika kol." style="text-align:right;">
|
|
<span class="popis-razlika" id="razlika-{{.ID}}">—</span>
|
|
</td>
|
|
<td class="kol-sek" data-label="Razlika vrednost" style="text-align:right;">
|
|
<span class="popis-vrednost-razlika" id="vrazlika-{{.ID}}">—</span>
|
|
</td>
|
|
</tr>
|
|
{{end}}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr style="font-weight:600;background:var(--pozadina);">
|
|
<td colspan="5" style="text-align:right;font-size:12px;color:var(--tekst-slabi);">UKUPNO:</td>
|
|
<td style="text-align:right;font-family:monospace;" id="suma-knjizno-kol">—</td>
|
|
<td style="text-align:right;font-family:monospace;" id="suma-knjizno-vred">—</td>
|
|
<td style="text-align:right;font-family:monospace;" id="suma-stvarno-kol">—</td>
|
|
<td style="text-align:right;font-family:monospace;" id="suma-razlika-kol">—</td>
|
|
<td style="text-align:right;font-family:monospace;" id="suma-razlika-vred">—</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- sumarni pregled manjkova/viškova -->
|
|
<div class="popis-forma-sumarno" style="padding:14px 20px;border-top:0.5px solid var(--ivica);">
|
|
<div class="popis-suma">
|
|
<div class="popis-suma-stavka">
|
|
<span class="popis-suma-labela">Manjkovi (kom)</span>
|
|
<span class="popis-suma-vrednost boja-manjak" id="sum-manjak-kol">0</span>
|
|
</div>
|
|
<div class="popis-suma-stavka">
|
|
<span class="popis-suma-labela">Manjkovi (vrednost)</span>
|
|
<span class="popis-suma-vrednost boja-manjak" id="sum-manjak-vred">0,00</span>
|
|
</div>
|
|
<div class="popis-suma-stavka">
|
|
<span class="popis-suma-labela">Viškovi (kom)</span>
|
|
<span class="popis-suma-vrednost boja-visak" id="sum-visak-kol">0</span>
|
|
</div>
|
|
<div class="popis-suma-stavka">
|
|
<span class="popis-suma-labela">Viškovi (vrednost)</span>
|
|
<span class="popis-suma-vrednost boja-visak" id="sum-visak-vred">0,00</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- dugme na dnu -->
|
|
<div class="popis-forma-dugme" style="padding:14px 20px;border-top:0.5px solid var(--ivica);display:flex;justify-content:flex-end;">
|
|
<button type="submit" class="btn-primarno">Sačuvaj popis</button>
|
|
</div>
|
|
{{end}}
|
|
</div>
|
|
</form>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
var napomePoTipu = {
|
|
"godišnji": "Godišnji popis",
|
|
"vanredni": "Vanredni popis",
|
|
"preuzimanje": "Popis pri preuzimanju dužnosti"
|
|
};
|
|
|
|
function azurirajNapomenu() {
|
|
var tip = document.getElementById('tip-popisa').value;
|
|
document.getElementById('napomena-polje').value = napomePoTipu[tip] || "Godišnji popis";
|
|
}
|
|
|
|
function fmt(n) {
|
|
return n.toLocaleString('sr-RS', {minimumFractionDigits: 2, maximumFractionDigits: 2});
|
|
}
|
|
|
|
function azurirajRed(input) {
|
|
var id = input.dataset.id;
|
|
var knjizno = parseInt(input.dataset.knjizno, 10);
|
|
var cena = parseFloat(input.dataset.cena) || 0;
|
|
var stvarno = parseInt(input.value, 10);
|
|
var spanKol = document.getElementById('razlika-' + id);
|
|
var spanVred = document.getElementById('vrazlika-' + id);
|
|
|
|
if (isNaN(stvarno)) {
|
|
spanKol.textContent = '—'; spanVred.textContent = '—';
|
|
spanKol.style.color = ''; spanVred.style.color = '';
|
|
input.classList.remove('izmenjeno');
|
|
} else {
|
|
var razlika = stvarno - knjizno;
|
|
if (razlika === 0) {
|
|
spanKol.textContent = '—'; spanVred.textContent = '—';
|
|
spanKol.style.color = ''; spanVred.style.color = '';
|
|
input.classList.remove('izmenjeno');
|
|
} else {
|
|
var boja = razlika > 0 ? '#22c55e' : '#dc2626';
|
|
spanKol.textContent = (razlika > 0 ? '+' : '') + razlika;
|
|
spanVred.textContent = (razlika > 0 ? '+' : '') + fmt(razlika * cena);
|
|
spanKol.style.color = boja; spanVred.style.color = boja;
|
|
input.classList.add('izmenjeno');
|
|
}
|
|
}
|
|
azurirajSumu();
|
|
}
|
|
|
|
function azurirajSumu() {
|
|
var inputs = document.querySelectorAll('.popis-input');
|
|
var knjiznoKol = 0, knjiznoVred = 0, stvarnoKol = 0;
|
|
var manjKol = 0, manjVred = 0, visakKol = 0, visakVred = 0;
|
|
|
|
inputs.forEach(function(inp) {
|
|
var kn = parseInt(inp.dataset.knjizno, 10) || 0;
|
|
var cena = parseFloat(inp.dataset.cena) || 0;
|
|
var st = parseInt(inp.value, 10);
|
|
if (isNaN(st)) st = kn;
|
|
|
|
knjiznoKol += kn;
|
|
knjiznoVred += kn * cena;
|
|
stvarnoKol += st;
|
|
|
|
var r = st - kn;
|
|
if (r < 0) { manjKol += Math.abs(r); manjVred += Math.abs(r) * cena; }
|
|
if (r > 0) { visakKol += r; visakVred += r * cena; }
|
|
});
|
|
|
|
var razlikaKol = stvarnoKol - knjiznoKol;
|
|
var razlikaVred = visakVred - manjVred;
|
|
var el = function(id) { return document.getElementById(id); };
|
|
|
|
if (el('suma-knjizno-kol')) el('suma-knjizno-kol').textContent = knjiznoKol;
|
|
if (el('suma-knjizno-vred')) el('suma-knjizno-vred').textContent = fmt(knjiznoVred);
|
|
if (el('suma-stvarno-kol')) el('suma-stvarno-kol').textContent = stvarnoKol;
|
|
if (el('suma-razlika-kol')) {
|
|
el('suma-razlika-kol').textContent = (razlikaKol >= 0 ? '+' : '') + razlikaKol;
|
|
el('suma-razlika-kol').style.color = razlikaKol === 0 ? '' : (razlikaKol > 0 ? '#22c55e' : '#dc2626');
|
|
}
|
|
if (el('suma-razlika-vred')) {
|
|
el('suma-razlika-vred').textContent = (razlikaVred >= 0 ? '+' : '') + fmt(razlikaVred);
|
|
el('suma-razlika-vred').style.color = razlikaVred === 0 ? '' : (razlikaVred > 0 ? '#22c55e' : '#dc2626');
|
|
}
|
|
if (el('sum-manjak-kol')) el('sum-manjak-kol').textContent = manjKol;
|
|
if (el('sum-manjak-vred')) el('sum-manjak-vred').textContent = fmt(manjVred);
|
|
if (el('sum-visak-kol')) el('sum-visak-kol').textContent = visakKol;
|
|
if (el('sum-visak-vred')) el('sum-visak-vred').textContent = fmt(visakVred);
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
document.querySelectorAll('.popis-input').forEach(function(inp) {
|
|
var id = inp.dataset.id;
|
|
var kn = parseInt(inp.dataset.knjizno, 10) || 0;
|
|
var cena = parseFloat(inp.dataset.cena) || 0;
|
|
var el = document.getElementById('knjvred-' + id);
|
|
if (el) el.textContent = fmt(kn * cena);
|
|
});
|
|
azurirajSumu();
|
|
});
|
|
</script>
|
|
{{end}}
|