Servis: storno naloga (kao Prodaja/Nabavka) — KIR/KPO storno stavke, fiskalni refund

Do sada je jedini način da se poništi naplaćen/preuzet servisni nalog bio
fizičko brisanje (ObrisiNalog), koje ostavlja KIR/KPO/fiskalne zapise sa mrtvim
izvor_id — prihod ostaje prijavljen za nalog koji više ne postoji.

Migracija 100: servisni_nalozi dobija stornirano/razlog_storniranja (kao
prodajni_nalozi/nabavke). ServisRepo.Storno vraća ugrađene delove na stanje
ali NE briše nalog. Novi handler StornoNaloga (dozvola servis.storno):
- KIR/KPO original ostaje, dograđuje se storno stavka (model.KirStorno/KpoStorno)
- fiskalni refund (best-effort) preko refundujServis — Refund zahtev sa
  ReferentDocumentNumber ka originalnom PFR broju, isti obrazac kao stornirajProdaju
- ObrisiNalog sad odbija brisanje ako nalog već ima fiskalni/KIR/KPO zapis i
  upućuje na Storno

stornoBezRefunda (dashboard upozorenje) prošireno da prati i servisne stornose
bez uspešnog fiskalnog refunda, ne samo prodaju.
This commit is contained in:
2026-07-02 11:30:55 +02:00
parent 3c61063bbb
commit f74cb04024
9 changed files with 392 additions and 41 deletions
@@ -45,6 +45,11 @@
<span style="font-size:20px;font-weight:600;color:var(--tekst-glavni);font-family:monospace;">
{{.Nalog.BrojNaloga}}
</span>
{{if .Nalog.Stornirano}}
<span style="display:inline-block;padding:3px 10px;border-radius:20px;font-size:12px;font-weight:500;background:rgba(220,38,38,0.12);color:#dc2626;">
Stornirano
</span>
{{end}}
{{if .Nalog.PopravkaOdbijena}}
<div style="padding:8px 14px;background:color-mix(in srgb, var(--crvena, #ef4444) 14%, transparent);border:1px solid color-mix(in srgb, var(--crvena, #ef4444) 35%, transparent);border-radius:8px;">
<span style="font-size:15px;font-weight:700;color:var(--crvena, #ef4444);">✕ Klijent je odbio popravku</span>
@@ -1017,6 +1022,31 @@
</div>
{{end}}
<!-- storno -->
{{if and (index .Dozvole "servis.storno") (eq .Nalog.Status "Preuzeto") (not .Nalog.Stornirano)}}
<div class="kartica detalji-kartica animiraj" style="border-color:#dc262633;">
<div style="display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap;">
<div style="flex:1;min-width:200px;">
<div style="font-size:14px;font-weight:500;color:#f97316;margin-bottom:4px;">Storniranje naloga</div>
<div class="pomocni-tekst">
Storno vraća ugrađene delove na stanje u magacinu. Nalog ostaje u evidenciji sa oznakom storniran.
</div>
</div>
<form method="POST" action="/servis/storno/{{.Nalog.ID}}" style="display:flex;flex-direction:column;gap:8px;align-items:flex-end;">
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
<input type="text" name="razlog" placeholder="Razlog storniranja (opciono)" style="width:240px;">
{{if .PotrebnaIdentKupca}}
<input type="text" name="poreski_broj_kupca" placeholder="PIB ili JMBG kupca (obavezno za refundaciju)" required style="width:240px;">
{{end}}
<button type="submit" class="btn-upozorenje"
data-potvrda="Stornirati nalog {{.Nalog.BrojNaloga}}?">
Storniraj nalog
</button>
</form>
</div>
</div>
{{end}}
<!-- zona za brisanje -->
{{/* Brisanje je moguće samo dok je „Primljeno" (greška pri unosu); čim uđe u proceduru naplaćuje se dijagnostika */}}
{{if eq .Nalog.Status "Primljeno"}}