Servis: razdvojeno prikazivanje avansa/konačnog/refunda na fiskalnim računima
Dashboard: 'bez fiskalnog računa' razdvojeno na prodaju i servis (posebna
dugmad koja vode na tačan spisak, ne uvek na /prodaja).
Servis detalji: kartica 'Fiskalni račun' sad jasno razlikuje avansni od
konačnog računa; dodata posebna kartica za povraćaj viška avansa. Modal
za preuzimanje više se ne zaglavljuje kad avans pokrije ceo iznos (dugme
'Preuzmi' bez obaveznog unosa u polju Primljeno).
Štampa fiskalnog računa (/servis/{id}/fiskalni-racun) sad prioritetno
vraća konačan (Normal/Sale) račun umesto poslednjeg upisanog — ranije je
avansni povraćaj (upisan posle konačnog računa) prekrivao prikaz stvarne
fakture. Kad postoji i povraćaj viška avansa, štampa se u istom prozoru
odmah posle konačnog računa (izbegnut drugi popup koji brauzer blokira).
This commit is contained in:
+77
-34
@@ -85,7 +85,8 @@ type PodaciDetaljiNaloga struct {
|
|||||||
CenaDijagnostikePredlog string // podrazumevana cena dijagnostike iz podešavanja (za prefill input-a)
|
CenaDijagnostikePredlog string // podrazumevana cena dijagnostike iz podešavanja (za prefill input-a)
|
||||||
KorisceneUsluge map[int64]bool // ID-evi usluga već dodatih na nalog — izostavljaju se iz dropdown-a
|
KorisceneUsluge map[int64]bool // ID-evi usluga već dodatih na nalog — izostavljaju se iz dropdown-a
|
||||||
SviStatusi []string
|
SviStatusi []string
|
||||||
FiskalniRacun *model.FiskalniRacun // nil ako nije fiskalizovano
|
FiskalniRacun *model.FiskalniRacun // konačan (Normal/Sale) ako postoji, inače najnoviji izdat; nil ako nije fiskalizovano
|
||||||
|
PovracajAvansa *model.FiskalniRacun // avansni povraćaj (Advance/Refund) — kad je avans premašio konačnu cenu
|
||||||
RokPodizanja *time.Time // DatumZavrsetka + 30 dana; nil dok nije završeno
|
RokPodizanja *time.Time // DatumZavrsetka + 30 dana; nil dok nije završeno
|
||||||
FiskalGreska bool // true: fiskalizacija aktivna, status Preuzeto, ali nema fiskalnog računa
|
FiskalGreska bool // true: fiskalizacija aktivna, status Preuzeto, ali nema fiskalnog računa
|
||||||
PotrebnaIdentKupca bool // true: refundacija zahteva ručni unos PIB/JMBG kupca (nema ga iz klijenta)
|
PotrebnaIdentKupca bool // true: refundacija zahteva ručni unos PIB/JMBG kupca (nema ga iz klijenta)
|
||||||
@@ -891,11 +892,20 @@ func (h *Handler) DetaljiNaloga(w http.ResponseWriter, r *http.Request) {
|
|||||||
RokPodizanja: rokPodizanja(nalog.DatumZavrsetka),
|
RokPodizanja: rokPodizanja(nalog.DatumZavrsetka),
|
||||||
}
|
}
|
||||||
|
|
||||||
// učitaj fiskalni račun ako postoji (za prikaz u detaljima)
|
// učitaj fiskalni račun ako postoji (za prikaz u detaljima) — preferira konačan
|
||||||
if fr, err := h.FiskalRepo.DohvatiPoServisu(r.Context(), id); fr != nil {
|
// (Normal/Sale) nad bilo kojim drugim, jer avansni povraćaj viška (upisan POSLE
|
||||||
podaci.FiskalniRacun = fr
|
// konačnog računa, kad avans premaši cenu) ne sme da ga "prekrije" u prikazu
|
||||||
} else if err != nil {
|
if fr, err := h.FiskalRepo.DohvatiPoServisuITip(r.Context(), id, "Normal", "Sale"); err != nil {
|
||||||
slog.Error("greška pri učitavanju fiskalnog računa za servis", "servis_id", id, "error", err)
|
slog.Error("greška pri učitavanju fiskalnog računa za servis", "servis_id", id, "error", err)
|
||||||
|
} else if fr != nil {
|
||||||
|
podaci.FiskalniRacun = fr
|
||||||
|
} else if fr2, err2 := h.FiskalRepo.DohvatiPoServisu(r.Context(), id); err2 != nil {
|
||||||
|
slog.Error("greška pri učitavanju fiskalnog računa za servis", "servis_id", id, "error", err2)
|
||||||
|
} else {
|
||||||
|
podaci.FiskalniRacun = fr2
|
||||||
|
}
|
||||||
|
if fr3, err := h.FiskalRepo.DohvatiPoServisuITip(r.Context(), id, "Advance", "Refund"); err == nil {
|
||||||
|
podaci.PovracajAvansa = fr3
|
||||||
}
|
}
|
||||||
// fiskalna greška: modul aktivan, nalog preuzet, ali nema KONAČNOG (Normal/Sale)
|
// fiskalna greška: modul aktivan, nalog preuzet, ali nema KONAČNOG (Normal/Sale)
|
||||||
// fiskalnog računa — avansni (Advance/Sale) se ne računa kao konačan, inače bi
|
// fiskalnog računa — avansni (Advance/Sale) se ne računa kao konačan, inače bi
|
||||||
@@ -2812,28 +2822,40 @@ func (h *Handler) StampaFiskalnog(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Neispravan ID", http.StatusBadRequest)
|
http.Error(w, "Neispravan ID", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fr, err := h.FiskalRepo.DohvatiPoServisu(r.Context(), id)
|
var racuni []*model.FiskalniRacun
|
||||||
if err != nil || fr == nil {
|
if r.URL.Query().Get("tip") == "povracaj" {
|
||||||
http.Error(w, "Fiskalni račun nije pronađen", http.StatusNotFound)
|
// avansni povraćaj viška — eksplicitno traženo (drugi dokument, ne konačan račun)
|
||||||
return
|
fr, err := h.FiskalRepo.DohvatiPoServisuITip(r.Context(), id, "Advance", "Refund")
|
||||||
}
|
if err != nil || fr == nil {
|
||||||
nalog, _ := h.ServisRepo.DohvatiID(r.Context(), id)
|
http.Error(w, "Fiskalni račun nije pronađen", http.StatusNotFound)
|
||||||
journal := ""
|
return
|
||||||
if fr.SiroviOdgovor != "" {
|
}
|
||||||
var raw map[string]any
|
racuni = []*model.FiskalniRacun{fr}
|
||||||
if json.Unmarshal([]byte(fr.SiroviOdgovor), &raw) == nil {
|
} else {
|
||||||
if j, ok := raw["journal"].(string); ok {
|
// podrazumevano: konačan (Normal/Sale) račun — ako još ne postoji (npr. samo avans
|
||||||
journal = j
|
// je fiskalizovan pre preuzimanja), padni nazad na najnoviji izdat
|
||||||
|
fr, err := h.FiskalRepo.DohvatiPoServisuITip(r.Context(), id, "Normal", "Sale")
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Fiskalni račun nije pronađen", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if fr == nil {
|
||||||
|
if fr, err = h.FiskalRepo.DohvatiPoServisu(r.Context(), id); err != nil || fr == nil {
|
||||||
|
http.Error(w, "Fiskalni račun nije pronađen", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
racuni = append(racuni, fr)
|
||||||
|
// avans premašio cenu → povraćaj viška je DRUGI fiskalni dokument, izdat odmah
|
||||||
|
// posle konačnog računa — štampa se u istom prozoru (izbegava se drugi popup,
|
||||||
|
// koji brauzer bez pravog user-gesture-a često blokira)
|
||||||
|
if fr.TipRacuna == "Normal" {
|
||||||
|
if povracaj, err := h.FiskalRepo.DohvatiPoServisuITip(r.Context(), id, "Advance", "Refund"); err == nil && povracaj != nil {
|
||||||
|
racuni = append(racuni, povracaj)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if nalog != nil {
|
nalog, _ := h.ServisRepo.DohvatiID(r.Context(), id)
|
||||||
journal += "\n\n--- NTech servisni nalog: " + nalog.BrojNaloga + " ---\n"
|
|
||||||
}
|
|
||||||
|
|
||||||
// Podeli journal na deo pre i posle QR placeholder-a
|
|
||||||
const qrPlaceholder = "{{{{QR-KOD}}}}"
|
|
||||||
pre, post, hasQR := strings.Cut(journal, qrPlaceholder)
|
|
||||||
|
|
||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
fmt.Fprint(w, `<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Fiskalni račun</title>
|
fmt.Fprint(w, `<!DOCTYPE html><html><head><meta charset="UTF-8"><title>Fiskalni račun</title>
|
||||||
@@ -2841,21 +2863,42 @@ func (h *Handler) StampaFiskalnog(w http.ResponseWriter, r *http.Request) {
|
|||||||
*{box-sizing:border-box;}
|
*{box-sizing:border-box;}
|
||||||
body{font-family:monospace;font-size:12px;padding:20px;max-width:max-content;margin:0 auto;}
|
body{font-family:monospace;font-size:12px;padding:20px;max-width:max-content;margin:0 auto;}
|
||||||
pre{white-space:pre;margin:0;padding:0;font-family:inherit;font-size:inherit;display:block;}
|
pre{white-space:pre;margin:0;padding:0;font-family:inherit;font-size:inherit;display:block;}
|
||||||
@media print{body{font-size:11px;padding:10px;}}
|
.ntech-razdvajac{margin:24px 0;border-top:2px dashed #999;padding-top:24px;}
|
||||||
|
@media print{body{font-size:11px;padding:10px;}.ntech-razdvajac{page-break-before:always;}}
|
||||||
</style></head><body>`)
|
</style></head><body>`)
|
||||||
|
|
||||||
fmt.Fprint(w, `<pre>`)
|
const qrPlaceholder = "{{{{QR-KOD}}}}"
|
||||||
fmt.Fprint(w, pre)
|
for i, fr := range racuni {
|
||||||
fmt.Fprint(w, `</pre>`)
|
if i > 0 {
|
||||||
|
fmt.Fprint(w, `<div class="ntech-razdvajac"></div>`)
|
||||||
|
}
|
||||||
|
journal := ""
|
||||||
|
if fr.SiroviOdgovor != "" {
|
||||||
|
var raw map[string]any
|
||||||
|
if json.Unmarshal([]byte(fr.SiroviOdgovor), &raw) == nil {
|
||||||
|
if j, ok := raw["journal"].(string); ok {
|
||||||
|
journal = j
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if nalog != nil {
|
||||||
|
journal += "\n\n--- NTech servisni nalog: " + nalog.BrojNaloga + " ---\n"
|
||||||
|
}
|
||||||
|
pre, post, hasQR := strings.Cut(journal, qrPlaceholder)
|
||||||
|
|
||||||
if hasQR && fr.QRKod != "" {
|
fmt.Fprint(w, `<pre>`)
|
||||||
fmt.Fprintf(w, `<div style="margin:10px 0;"><img src="data:image/png;base64,%s" style="display:block;margin:0 auto;width:72mm;height:72mm;"></div>`, fr.QRKod)
|
fmt.Fprint(w, pre)
|
||||||
|
fmt.Fprint(w, `</pre>`)
|
||||||
|
|
||||||
|
if hasQR && fr.QRKod != "" {
|
||||||
|
fmt.Fprintf(w, `<div style="margin:10px 0;"><img src="data:image/png;base64,%s" style="display:block;margin:0 auto;width:72mm;height:72mm;"></div>`, fr.QRKod)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(w, `<pre>`)
|
||||||
|
fmt.Fprint(w, post)
|
||||||
|
fmt.Fprint(w, `</pre>`)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Fprint(w, `<pre>`)
|
|
||||||
fmt.Fprint(w, post)
|
|
||||||
fmt.Fprint(w, `</pre>`)
|
|
||||||
|
|
||||||
fmt.Fprint(w, `<p style="margin-top:16px;"><button onclick="window.print()" style="padding:8px 16px;">Štampaj</button></p></body></html>`)
|
fmt.Fprint(w, `<p style="margin-top:16px;"><button onclick="window.print()" style="padding:8px 16px;">Štampaj</button></p></body></html>`)
|
||||||
}
|
}
|
||||||
func (h *Handler) AzurirajGaranciju(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) AzurirajGaranciju(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
@@ -243,10 +243,10 @@ func (h *Handler) PraznineKnjigovodstva(ctx context.Context) (model.PrazninaKnji
|
|||||||
|
|
||||||
if h.modulUkljucen(ctx, "fiskalizacija") {
|
if h.modulUkljucen(ctx, "fiskalizacija") {
|
||||||
if m, err := h.FiskalRepo.ProdajeBezFiskalnog(ctx); err == nil {
|
if m, err := h.FiskalRepo.ProdajeBezFiskalnog(ctx); err == nil {
|
||||||
p.BezFiskalnog += len(m)
|
p.BezFiskalnogProdaja += len(m)
|
||||||
}
|
}
|
||||||
if m, err := h.FiskalRepo.ServisiBezFiskalnog(ctx); err == nil {
|
if m, err := h.FiskalRepo.ServisiBezFiskalnog(ctx); err == nil {
|
||||||
p.BezFiskalnog += len(m)
|
p.BezFiskalnogServis += len(m)
|
||||||
}
|
}
|
||||||
if n, err := h.stornoBezRefunda(ctx); err == nil {
|
if n, err := h.stornoBezRefunda(ctx); err == nil {
|
||||||
p.BezRefunda = n
|
p.BezRefunda = n
|
||||||
|
|||||||
@@ -78,16 +78,23 @@ type PodaciDashboarda struct {
|
|||||||
// sumnjivih duplikata (naloga koje automatski backfill namerno preskače) — prikazuje
|
// sumnjivih duplikata (naloga koje automatski backfill namerno preskače) — prikazuje
|
||||||
// se kao kartica na dashboard-u.
|
// se kao kartica na dashboard-u.
|
||||||
type PrazninaKnjigovodstva struct {
|
type PrazninaKnjigovodstva struct {
|
||||||
BezKir int
|
BezKir int
|
||||||
BezKpo int
|
BezKpo int
|
||||||
BezFiskalnog int
|
BezFiskalnogProdaja int // prodajni nalozi bez izdatog fiskalnog računa
|
||||||
BezRefunda int // stornirani nalozi čiji fiskalni refund nije uspeo (best-effort poziv pao)
|
BezFiskalnogServis int // preuzeti servisni nalozi bez izdatog fiskalnog računa
|
||||||
SumnjiviDupli int
|
BezRefunda int // stornirani nalozi čiji fiskalni refund nije uspeo (best-effort poziv pao)
|
||||||
|
SumnjiviDupli int
|
||||||
|
}
|
||||||
|
|
||||||
|
// BezFiskalnog vraća ukupan broj naloga (prodaja + servis) bez izdatog fiskalnog
|
||||||
|
// računa — koristi se za prikaz zbirnog broja na dashboard-u.
|
||||||
|
func (p PrazninaKnjigovodstva) BezFiskalnog() int {
|
||||||
|
return p.BezFiskalnogProdaja + p.BezFiskalnogServis
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ukupno vraća zbir svih praznina UKLJUČUJUĆI sumnjive duplikate — koristi se da se
|
// Ukupno vraća zbir svih praznina UKLJUČUJUĆI sumnjive duplikate — koristi se da se
|
||||||
// odluči da li se kartica uopšte prikazuje na dashboard-u (i kad ostanu samo
|
// odluči da li se kartica uopšte prikazuje na dashboard-u (i kad ostanu samo
|
||||||
// duplikati za ručnu proveru, kartica mora da ostane vidljiva).
|
// duplikati za ručnu proveru, kartica mora da ostane vidljiva).
|
||||||
func (p PrazninaKnjigovodstva) Ukupno() int {
|
func (p PrazninaKnjigovodstva) Ukupno() int {
|
||||||
return p.BezKir + p.BezKpo + p.BezFiskalnog + p.BezRefunda + p.SumnjiviDupli
|
return p.BezKir + p.BezKpo + p.BezFiskalnog() + p.BezRefunda + p.SumnjiviDupli
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,14 +25,15 @@
|
|||||||
<div>
|
<div>
|
||||||
<div style="font-weight:600;color:#92400e;">Knjigovodstvo — praznine u evidenciji</div>
|
<div style="font-weight:600;color:#92400e;">Knjigovodstvo — praznine u evidenciji</div>
|
||||||
<div style="font-size:13px;color:#92400e;margin-top:2px;">
|
<div style="font-size:13px;color:#92400e;margin-top:2px;">
|
||||||
{{ if gt .PrazninaKnjige.BezKir 0 }}{{ .PrazninaKnjige.BezKir }} bez KIR upisa{{ if or (gt .PrazninaKnjige.BezKpo 0) (gt .PrazninaKnjige.BezFiskalnog 0) (gt .PrazninaKnjige.BezRefunda 0) }}, {{ end }}{{ end }}{{ if gt .PrazninaKnjige.BezKpo 0 }}{{ .PrazninaKnjige.BezKpo }} bez KPO upisa{{ if or (gt .PrazninaKnjige.BezFiskalnog 0) (gt .PrazninaKnjige.BezRefunda 0) }}, {{ end }}{{ end }}{{ if gt .PrazninaKnjige.BezFiskalnog 0 }}{{ .PrazninaKnjige.BezFiskalnog }} bez fiskalnog računa{{ if gt .PrazninaKnjige.BezRefunda 0 }}, {{ end }}{{ end }}{{ if gt .PrazninaKnjige.BezRefunda 0 }}{{ .PrazninaKnjige.BezRefunda }} storno bez fiskalnog refunda{{ end }}
|
{{ if gt .PrazninaKnjige.BezKir 0 }}{{ .PrazninaKnjige.BezKir }} bez KIR upisa{{ if or (gt .PrazninaKnjige.BezKpo 0) (gt .PrazninaKnjige.BezFiskalnogProdaja 0) (gt .PrazninaKnjige.BezFiskalnogServis 0) (gt .PrazninaKnjige.BezRefunda 0) }}, {{ end }}{{ end }}{{ if gt .PrazninaKnjige.BezKpo 0 }}{{ .PrazninaKnjige.BezKpo }} bez KPO upisa{{ if or (gt .PrazninaKnjige.BezFiskalnogProdaja 0) (gt .PrazninaKnjige.BezFiskalnogServis 0) (gt .PrazninaKnjige.BezRefunda 0) }}, {{ end }}{{ end }}{{ if gt .PrazninaKnjige.BezFiskalnogProdaja 0 }}{{ .PrazninaKnjige.BezFiskalnogProdaja }} prodaja bez fiskalnog računa{{ if or (gt .PrazninaKnjige.BezFiskalnogServis 0) (gt .PrazninaKnjige.BezRefunda 0) }}, {{ end }}{{ end }}{{ if gt .PrazninaKnjige.BezFiskalnogServis 0 }}{{ .PrazninaKnjige.BezFiskalnogServis }} servis bez fiskalnog računa{{ if gt .PrazninaKnjige.BezRefunda 0 }}, {{ end }}{{ end }}{{ if gt .PrazninaKnjige.BezRefunda 0 }}{{ .PrazninaKnjige.BezRefunda }} storno bez fiskalnog refunda{{ end }}
|
||||||
{{ if gt .PrazninaKnjige.SumnjiviDupli 0 }}{{ if or (gt .PrazninaKnjige.BezKir 0) (gt .PrazninaKnjige.BezKpo 0) (gt .PrazninaKnjige.BezFiskalnog 0) (gt .PrazninaKnjige.BezRefunda 0) }} · {{ end }}{{ .PrazninaKnjige.SumnjiviDupli }} sumnjivih duplikata (proveri ručno){{ end }}
|
{{ if gt .PrazninaKnjige.SumnjiviDupli 0 }}{{ if or (gt .PrazninaKnjige.BezKir 0) (gt .PrazninaKnjige.BezKpo 0) (gt .PrazninaKnjige.BezFiskalnogProdaja 0) (gt .PrazninaKnjige.BezFiskalnogServis 0) (gt .PrazninaKnjige.BezRefunda 0) }} · {{ end }}{{ .PrazninaKnjige.SumnjiviDupli }} sumnjivih duplikata (proveri ručno){{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
<div style="display:flex;gap:8px;flex-wrap:wrap;">
|
||||||
{{ if gt .PrazninaKnjige.BezKir 0 }}<a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/pdv/kir" class="btn-sekundarno" style="font-size:13px;">KIR</a>{{ end }}
|
{{ if gt .PrazninaKnjige.BezKir 0 }}<a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/pdv/kir" class="btn-sekundarno" style="font-size:13px;">KIR</a>{{ end }}
|
||||||
{{ if gt .PrazninaKnjige.BezKpo 0 }}<a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/kpo" class="btn-sekundarno" style="font-size:13px;">KPO</a>{{ end }}
|
{{ if gt .PrazninaKnjige.BezKpo 0 }}<a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/kpo" class="btn-sekundarno" style="font-size:13px;">KPO</a>{{ end }}
|
||||||
{{ if gt .PrazninaKnjige.BezFiskalnog 0 }}<a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/prodaja?prikazi=sve" class="btn-sekundarno" style="font-size:13px;">Fiskalizacija</a>{{ end }}
|
{{ if gt .PrazninaKnjige.BezFiskalnogProdaja 0 }}<a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/prodaja?prikazi=sve" class="btn-sekundarno" style="font-size:13px;">Fiskalizacija — prodaja</a>{{ end }}
|
||||||
|
{{ if gt .PrazninaKnjige.BezFiskalnogServis 0 }}<a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/servis/arhiva" class="btn-sekundarno" style="font-size:13px;">Fiskalizacija — servis</a>{{ end }}
|
||||||
{{ if gt .PrazninaKnjige.BezRefunda 0 }}<a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/prodaja?stornirano=1&prikazi=sve" class="btn-sekundarno" style="font-size:13px;">Storno bez refunda</a>{{ end }}
|
{{ if gt .PrazninaKnjige.BezRefunda 0 }}<a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/prodaja?stornirano=1&prikazi=sve" class="btn-sekundarno" style="font-size:13px;">Storno bez refunda</a>{{ end }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -138,10 +138,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="pomocni-tekst">Ovaj iznos se automatski vraća kroz avansni povraćaj na fiskalnom računu.</div>
|
<div class="pomocni-tekst">Ovaj iznos se automatski vraća kroz avansni povraćaj na fiskalnom računu.</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{$zaNaplatu := .PreostaloSve}}{{if index .Moduli "pdv"}}{{$zaNaplatu = .PreostaloSveSaPdv}}{{end}}
|
||||||
<form method="POST" action="/servis/{{.Nalog.ID}}/status" data-refresh="#sadrzaj" style="margin-top:8px;">
|
<form method="POST" action="/servis/{{.Nalog.ID}}/status" data-refresh="#sadrzaj" style="margin-top:8px;">
|
||||||
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
<input type="hidden" name="status" value="Preuzeto">
|
<input type="hidden" name="status" value="Preuzeto">
|
||||||
<input type="hidden" name="naplaceno" value="{{if index .Moduli "pdv"}}{{printf "%.2f" .PreostaloSveSaPdv}}{{else}}{{printf "%.2f" .PreostaloSve}}{{end}}">
|
<input type="hidden" name="naplaceno" value="{{printf "%.2f" $zaNaplatu}}">
|
||||||
|
{{if gt $zaNaplatu 0.0}}
|
||||||
<div style="margin-top:10px;padding-top:10px;border-top:0.5px solid var(--ivica);">
|
<div style="margin-top:10px;padding-top:10px;border-top:0.5px solid var(--ivica);">
|
||||||
<label class="polje-labela">Primljeno (din)</label>
|
<label class="polje-labela">Primljeno (din)</label>
|
||||||
<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)">
|
<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)">
|
||||||
@@ -150,6 +152,11 @@
|
|||||||
<span style="color:var(--uspeh);font-weight:500;">Kusur (vratiti)</span>
|
<span style="color:var(--uspeh);font-weight:500;">Kusur (vratiti)</span>
|
||||||
<span id="kusur" style="font-size:16px;font-weight:600;color:var(--uspeh);">0,00 din</span>
|
<span id="kusur" style="font-size:16px;font-weight:600;color:var(--uspeh);">0,00 din</span>
|
||||||
</div>
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div style="margin-top:10px;padding-top:10px;border-top:0.5px solid var(--ivica);font-size:13px;color:var(--tekst-sporedni);">
|
||||||
|
Avans pokriva ceo iznos — ništa se dodatno ne naplaćuje.
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
<div style="margin-bottom:10px;margin-top:10px;">
|
<div style="margin-bottom:10px;margin-top:10px;">
|
||||||
<label class="polje-labela">Način plaćanja</label>
|
<label class="polje-labela">Način plaćanja</label>
|
||||||
<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)">
|
<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)">
|
||||||
@@ -163,7 +170,11 @@
|
|||||||
style="width:16px;height:16px;accent-color:var(--sb-akcent);cursor:pointer;flex-shrink:0;">
|
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>
|
<span style="font-size:13px;color:var(--tekst-glavni);">Prikaži fiskalni račun</span>
|
||||||
</label>
|
</label>
|
||||||
|
{{if gt $zaNaplatu 0.0}}
|
||||||
<button type="submit" id="btn-naplati" class="btn-primarno" style="width:100%;opacity:0.4;cursor:not-allowed;" disabled>Naplati i preuzmi</button>
|
<button type="submit" id="btn-naplati" class="btn-primarno" style="width:100%;opacity:0.4;cursor:not-allowed;" disabled>Naplati i preuzmi</button>
|
||||||
|
{{else}}
|
||||||
|
<button type="submit" id="btn-naplati" class="btn-primarno" style="width:100%;">Preuzmi</button>
|
||||||
|
{{end}}
|
||||||
</form>
|
</form>
|
||||||
<script>
|
<script>
|
||||||
(function(){
|
(function(){
|
||||||
@@ -207,6 +218,8 @@
|
|||||||
if (!sadrzaj) return;
|
if (!sadrzaj) return;
|
||||||
var observer = new MutationObserver(function(){
|
var observer = new MutationObserver(function(){
|
||||||
observer.disconnect();
|
observer.disconnect();
|
||||||
|
// jedan prozor — server u istu stranicu ubacuje i povraćaj viška
|
||||||
|
// avansa (ako postoji), da brauzer ne blokira drugi popup
|
||||||
window.open('/servis/{{.Nalog.ID}}/fiskalni-racun', '_blank');
|
window.open('/servis/{{.Nalog.ID}}/fiskalni-racun', '_blank');
|
||||||
});
|
});
|
||||||
observer.observe(sadrzaj.parentElement, {childList: true});
|
observer.observe(sadrzaj.parentElement, {childList: true});
|
||||||
@@ -990,9 +1003,14 @@
|
|||||||
{{if .FiskalniRacun}}
|
{{if .FiskalniRacun}}
|
||||||
<div class="kartica detalji-kartica animiraj">
|
<div class="kartica detalji-kartica animiraj">
|
||||||
<div style="margin-bottom:12px;padding-bottom:10px;border-bottom:0.5px solid var(--ivica);display:flex;justify-content:space-between;align-items:center;">
|
<div style="margin-bottom:12px;padding-bottom:10px;border-bottom:0.5px solid var(--ivica);display:flex;justify-content:space-between;align-items:center;">
|
||||||
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Fiskalni račun</span>
|
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">
|
||||||
|
{{if eq .FiskalniRacun.TipRacuna "Advance"}}Avansni fiskalni račun{{else}}Fiskalni račun{{end}}
|
||||||
|
</span>
|
||||||
<a href="/servis/{{.Nalog.ID}}/fiskalni-racun" target="_blank" class="btn-sekundarno" style="font-size:12px;padding:4px 12px;">Štampaj kopiju</a>
|
<a href="/servis/{{.Nalog.ID}}/fiskalni-racun" target="_blank" class="btn-sekundarno" style="font-size:12px;padding:4px 12px;">Štampaj kopiju</a>
|
||||||
</div>
|
</div>
|
||||||
|
{{if eq .FiskalniRacun.TipRacuna "Advance"}}
|
||||||
|
<div style="font-size:12px;color:var(--tekst-sporedni);margin:-4px 0 10px 0;">Ovo je račun za primljeni avans — konačan račun se izdaje pri preuzimanju uređaja.</div>
|
||||||
|
{{end}}
|
||||||
<div style="display:flex;gap:16px;flex-wrap:wrap;align-items:flex-start;">
|
<div style="display:flex;gap:16px;flex-wrap:wrap;align-items:flex-start;">
|
||||||
<div style="flex:1;min-width:200px;">
|
<div style="flex:1;min-width:200px;">
|
||||||
<p style="margin:0 0 4px 0;"><strong>PFR broj:</strong> {{.FiskalniRacun.PfrBroj}}</p>
|
<p style="margin:0 0 4px 0;"><strong>PFR broj:</strong> {{.FiskalniRacun.PfrBroj}}</p>
|
||||||
@@ -1013,6 +1031,28 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if .PovracajAvansa}}
|
||||||
|
<div class="kartica detalji-kartica animiraj">
|
||||||
|
<div style="margin-bottom:12px;padding-bottom:10px;border-bottom:0.5px solid var(--ivica);display:flex;justify-content:space-between;align-items:center;">
|
||||||
|
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Povraćaj viška avansa</span>
|
||||||
|
<a href="/servis/{{.Nalog.ID}}/fiskalni-racun?tip=povracaj" target="_blank" class="btn-sekundarno" style="font-size:12px;padding:4px 12px;">Štampaj kopiju</a>
|
||||||
|
</div>
|
||||||
|
<div style="font-size:12px;color:var(--tekst-sporedni);margin:-4px 0 10px 0;">Avans je premašio konačnu cenu — razlika je vraćena klijentu posebnim fiskalnim dokumentom.</div>
|
||||||
|
<div style="display:flex;gap:16px;flex-wrap:wrap;align-items:flex-start;">
|
||||||
|
<div style="flex:1;min-width:200px;">
|
||||||
|
<p style="margin:0 0 4px 0;"><strong>PFR broj:</strong> {{.PovracajAvansa.PfrBroj}}</p>
|
||||||
|
<p style="margin:0 0 4px 0;"><strong>Vreme:</strong> {{.PovracajAvansa.PfrVreme}}</p>
|
||||||
|
<p style="margin:0 0 4px 0;color:var(--tekst-sporedno);"><strong>Brojač:</strong> {{.PovracajAvansa.Brojac}}</p>
|
||||||
|
</div>
|
||||||
|
{{if .PovracajAvansa.QRKod}}
|
||||||
|
<div>
|
||||||
|
<img src="data:image/png;base64,{{.PovracajAvansa.QRKod}}" alt="QR kod" style="width:25mm;height:25mm;">
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if .FiskalGreska}}
|
{{if .FiskalGreska}}
|
||||||
<div class="kartica detalji-kartica animiraj" style="border:1.5px solid #f59e0b;background:color-mix(in srgb,#fef3c7 60%,var(--pozadina));">
|
<div class="kartica detalji-kartica animiraj" style="border:1.5px solid #f59e0b;background:color-mix(in srgb,#fef3c7 60%,var(--pozadina));">
|
||||||
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
||||||
|
|||||||
Reference in New Issue
Block a user