Servis: potrazivani delovi, garancija, zakljucavanje statusa
Nova tabela servisni_potrazivani_delovi — visak preko stanja ide u potrazivane, lager NE ide u minus. Merge istog artikla na nalogu. Vizuelni indikator prekoracenja u tabeli (crveno). Status zakljucan na Ceka delove dok ima potrazivanih. Garancija: kalendar sa default vrednoscu. data-refresh podrzava vise selektora. Brisanje dela brise i potrazivane. Inline animation:none za AJAX refresh.
This commit is contained in:
@@ -47,12 +47,16 @@
|
||||
{{template "status-badge-detalji" .Nalog.Status}}
|
||||
<form method="post" action="/servis/{{.Nalog.ID}}/status" style="display:flex;align-items:center;gap:6px;" data-refresh="#nalog-zaglavlje">
|
||||
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||
<select name="status" style="font-size:13px;padding:4px 8px;border-radius:6px;border:0.5px solid var(--ivica);background:var(--kartica-pozadina);color:var(--tekst-glavni);cursor:pointer;">
|
||||
<select name="status" style="font-size:13px;padding:4px 8px;border-radius:6px;border:0.5px solid var(--ivica);background:var(--kartica-pozadina);color:var(--tekst-glavni);cursor:pointer;"{{if .ZakljucanStatus}} disabled title="Status zaključan dok ima delova koji nedostaju"{{end}}>
|
||||
{{range .SviStatusi}}
|
||||
<option value="{{.}}"{{if eq . $.Nalog.Status}} selected{{end}}>{{.}}</option>
|
||||
{{end}}
|
||||
</select>
|
||||
{{if .ZakljucanStatus}}
|
||||
<span style="font-size:12px;color:#dc2626;">🔒 Status zaključan — obrišite delove koji nedostaju</span>
|
||||
{{else}}
|
||||
<button type="submit" class="btn-sekundarno" style="font-size:13px;padding:4px 12px;">Promeni</button>
|
||||
{{end}}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,12 +90,30 @@
|
||||
<div style="font-size:14px;color:var(--tekst-glavni);">{{.Nalog.DatumZavrsetka.Format "02.01.2006."}}</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{if .Nalog.GarancijaDo}}
|
||||
<div>
|
||||
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Garancija do</div>
|
||||
<div style="font-size:14px;color:#16a34a;font-weight:500;">{{.Nalog.GarancijaDo.Format "02.01.2006."}}</div>
|
||||
{{if index .Dozvole "servis.izmeni"}}
|
||||
<form method="post" action="/servis/{{.Nalog.ID}}/garancija" data-refresh="#nalog-zaglavlje" style="display:flex;align-items:center;gap:8px;flex-wrap:wrap;">
|
||||
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||
<span style="font-size:14px;{{if .Nalog.GarancijaDo}}color:#16a34a;font-weight:500;{{else}}color:var(--tekst-sporedni);{{end}}">
|
||||
{{if .Nalog.GarancijaDo}}{{.Nalog.GarancijaDo.Format "02.01.2006."}}{{else}}—{{end}}
|
||||
</span>
|
||||
<input type="date" name="garancija_do" id="garancija-datum" value="{{if .Nalog.GarancijaDo}}{{.Nalog.GarancijaDo.Format "2006-01-02"}}{{end}}"
|
||||
onchange="var cb=this.form.querySelector('[name=bez_garancije]');if(cb)cb.checked=false;this.form.requestSubmit()"
|
||||
style="position:absolute;opacity:0;width:0;height:0;pointer-events:none;" tabindex="-1">
|
||||
<button type="button" onclick="document.getElementById('garancija-datum').showPicker()"
|
||||
style="background:none;border:0.5px solid var(--ivica);border-radius:6px;cursor:pointer;padding:4px 6px;font-size:14px;line-height:1;" title="Izmeni datum garancije">📅</button>
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);cursor:pointer;display:flex;align-items:center;gap:3px;">
|
||||
<input type="checkbox" name="bez_garancije" value="1" onchange="this.form.requestSubmit()" style="cursor:pointer;"
|
||||
{{if not .Nalog.GarancijaDo}}checked{{end}}> bez garancije
|
||||
</label>
|
||||
</form>
|
||||
{{else}}
|
||||
<div style="font-size:14px;{{if .Nalog.GarancijaDo}}color:#16a34a;font-weight:500;{{else}}color:var(--tekst-sporedni);{{end}}">
|
||||
{{if .Nalog.GarancijaDo}}{{.Nalog.GarancijaDo.Format "02.01.2006."}}{{else}}—{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
<div>
|
||||
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Klijent</div>
|
||||
<div style="font-size:14px;font-weight:500;color:var(--tekst-glavni);">
|
||||
@@ -245,7 +267,7 @@
|
||||
|
||||
<!-- forma za dodavanje dela -->
|
||||
{{if index .Dozvole "servis.izmeni"}}
|
||||
<form method="POST" action="/servis/{{.Nalog.ID}}/delovi" style="margin-bottom:16px;" data-refresh="#delovi-spisak">
|
||||
<form method="POST" action="/servis/{{.Nalog.ID}}/delovi" style="margin-bottom:16px;" data-refresh="#delovi-spisak, #nalog-zaglavlje">
|
||||
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||
<div style="margin-bottom:10px;">
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Kategorija</label>
|
||||
@@ -292,14 +314,20 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range .ServisniDelovi}}
|
||||
<tr style="border-bottom:0.5px solid var(--ivica);">
|
||||
<tr data-artikal-id="{{.ArtikalID}}" data-kolicina="{{.Kolicina}}" data-potrazivano="{{.Potrazivano}}" style="border-bottom:0.5px solid var(--ivica);{{if .Potrazivano}}background:rgba(220,38,38,0.06);{{end}}">
|
||||
<td style="padding:9px 10px;font-size:14px;color:var(--tekst-glavni);">{{.ArtikalNaziv}}</td>
|
||||
<td style="padding:9px 10px;text-align:center;font-size:14px;color:var(--tekst-glavni);">{{.Kolicina}}</td>
|
||||
<td style="padding:9px 10px;text-align:right;font-size:14px;color:var(--tekst-glavni);">{{dinari .CenaKomada}} din</td>
|
||||
<td style="padding:9px 10px;text-align:right;font-size:14px;font-weight:500;color:var(--tekst-glavni);">{{dinari .Ukupno}} din</td>
|
||||
<td style="padding:9px 10px;text-align:center;font-size:14px;{{if .Potrazivano}}color:#dc2626;font-weight:600;{{else}}color:var(--tekst-glavni);{{end}}">
|
||||
{{.Kolicina}}{{if .Potrazivano}}<span style="font-weight:400;"> + {{.Potrazivano}} na čekanju</span>{{end}}
|
||||
</td>
|
||||
<td style="padding:9px 10px;text-align:right;font-size:14px;color:var(--tekst-glavni);">
|
||||
{{if .Kolicina}}{{dinari .CenaKomada}} din{{else}}—{{end}}
|
||||
</td>
|
||||
<td style="padding:9px 10px;text-align:right;font-size:14px;font-weight:500;color:var(--tekst-glavni);">
|
||||
{{if .Kolicina}}{{dinari .Ukupno}} din{{else}}—{{end}}
|
||||
</td>
|
||||
<td style="padding:9px 10px;text-align:center;">
|
||||
{{if index $.Dozvole "servis.izmeni"}}
|
||||
<form method="POST" action="/servis/{{$.Nalog.ID}}/delovi/{{.ID}}/obrisi" style="display:inline;" data-refresh="#delovi-spisak">
|
||||
{{if and .ID (index $.Dozvole "servis.izmeni")}}
|
||||
<form method="POST" action="/servis/{{$.Nalog.ID}}/delovi/{{.ID}}/obrisi" style="display:inline;" data-refresh="#delovi-spisak, #nalog-zaglavlje">
|
||||
<input type="hidden" name="_csrf" value="{{$.CsrfToken}}">
|
||||
<button type="submit"
|
||||
data-potvrda="Ukloniti deo '{{.ArtikalNaziv}}'?"
|
||||
@@ -367,11 +395,21 @@
|
||||
var stanjeIzabrano = 0;
|
||||
if (!artikalSel) return;
|
||||
|
||||
// boji „U magacinu" i polje količine crveno kada tražimo više nego što ima na
|
||||
// stanju (dozvoljeno — backorder; nalog će preći u „Čeka delove" pri čuvanju);
|
||||
// koristi CSS klasu .prekoraceno umesto inline stilova (main.css)
|
||||
// boji „U magacinu" i polje količine crveno kada ukupno (postojeće u tabeli
|
||||
// + nova količina) prelazi stanje; čita direktno iz DOM-a tabele jer se ona
|
||||
// osvežava AJAX-om, pa JSON varijabla zastareva
|
||||
function osveziPrekoracenje() {
|
||||
var preko = (parseInt(kol && kol.value, 10) || 0) > stanjeIzabrano && artikalSel.value;
|
||||
var novaKol = parseInt(kol && kol.value, 10) || 0;
|
||||
var postojeciKol = 0;
|
||||
if (artikalSel.value) {
|
||||
var aid = artikalSel.value;
|
||||
var redovi = document.querySelectorAll('#delovi-spisak tr[data-artikal-id="' + aid + '"]');
|
||||
redovi.forEach(function (tr) {
|
||||
postojeciKol += (parseInt(tr.getAttribute('data-kolicina'), 10) || 0) +
|
||||
(parseInt(tr.getAttribute('data-potrazivano'), 10) || 0);
|
||||
});
|
||||
}
|
||||
var preko = (postojeciKol + novaKol) > stanjeIzabrano && artikalSel.value;
|
||||
if (umagacinu) umagacinu.classList.toggle('prekoraceno', preko);
|
||||
if (kol) kol.classList.toggle('prekoraceno', preko);
|
||||
}
|
||||
|
||||
@@ -391,36 +391,29 @@
|
||||
.then(function (rr) { return rr.text(); })
|
||||
.then(function (html) {
|
||||
var doc = new DOMParser().parseFromString(html, 'text/html');
|
||||
var novi = doc.querySelector(ciljSel);
|
||||
var stari = document.querySelector(ciljSel);
|
||||
if (novi && stari) {
|
||||
// bez-anim spречava treptaj re-animacije kartica pri zameni
|
||||
novi.classList.add('bez-anim');
|
||||
var sc = document.scrollingElement || document.documentElement;
|
||||
var syDoc = sc ? sc.scrollTop : 0;
|
||||
var gsStari = document.getElementById('sadrzaj');
|
||||
var syGs = gsStari ? gsStari.scrollTop : 0;
|
||||
stari.replaceWith(novi);
|
||||
void novi.offsetHeight;
|
||||
novi.classList.remove('bez-anim');
|
||||
// replaceWith ne pokreće <script> iz fragmenta — re-izvrši ih
|
||||
// ručno (npr. da se combobox/pretrage ponovo povežu)
|
||||
novi.querySelectorAll('script').forEach(function (old) {
|
||||
var s = document.createElement('script');
|
||||
if (old.src) s.src = old.src; else s.textContent = old.textContent;
|
||||
old.parentNode.replaceChild(s, old);
|
||||
});
|
||||
if (sc) sc.scrollTop = syDoc;
|
||||
var gsNovi = document.getElementById('sadrzaj');
|
||||
if (gsNovi) gsNovi.scrollTop = syGs;
|
||||
// re-veži event handlere (AJAX submit, potvrda brisanja...)
|
||||
// na sveže umetnute elemente — inače bi nove forme išle
|
||||
// punim POST-om (skok na vrh), a × bez potvrde
|
||||
ntechInicijalizuj();
|
||||
}
|
||||
// forma koja nije deo osveženog dela ostaje u DOM-u —
|
||||
// isprazni je za sledeći unos (za one koje osvežavaju sebe
|
||||
// ovo je bezuspešno jer su zamenjene, što je u redu)
|
||||
// podržava više selektora razdvojenih zarezom (npr. "#a, #b")
|
||||
var sc = document.scrollingElement || document.documentElement;
|
||||
var syDoc = sc ? sc.scrollTop : 0;
|
||||
var gsStari = document.getElementById('sadrzaj');
|
||||
var syGs = gsStari ? gsStari.scrollTop : 0;
|
||||
ciljSel.split(',').forEach(function (sel) {
|
||||
sel = sel.trim();
|
||||
var novi = doc.querySelector(sel);
|
||||
var stari = document.querySelector(sel);
|
||||
if (novi && stari) {
|
||||
novi.style.animation = 'none';
|
||||
stari.replaceWith(novi);
|
||||
novi.querySelectorAll('script').forEach(function (old) {
|
||||
var s = document.createElement('script');
|
||||
if (old.src) s.src = old.src; else s.textContent = old.textContent;
|
||||
old.parentNode.replaceChild(s, old);
|
||||
});
|
||||
}
|
||||
});
|
||||
if (sc) sc.scrollTop = syDoc;
|
||||
var gsNovi = document.getElementById('sadrzaj');
|
||||
if (gsNovi) gsNovi.scrollTop = syGs;
|
||||
ntechInicijalizuj();
|
||||
if (document.body.contains(f)) f.reset();
|
||||
if (btn) btn.disabled = false;
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user