Prodaja: retry fiskalizacije kad ESIR nije dostupan pri kreiranju
This commit is contained in:
@@ -418,6 +418,7 @@ func main() {
|
|||||||
r.With(doz("prodaja.dodaj")).Post("/prodaja/nova", h.SacuvajProdaju)
|
r.With(doz("prodaja.dodaj")).Post("/prodaja/nova", h.SacuvajProdaju)
|
||||||
r.With(doz("prodaja.obrisi")).Post("/prodaja/obrisi/{id}", h.ObrisiProdaju)
|
r.With(doz("prodaja.obrisi")).Post("/prodaja/obrisi/{id}", h.ObrisiProdaju)
|
||||||
r.With(doz("prodaja.storno")).Post("/prodaja/storno/{id}", h.StornoProdaje)
|
r.With(doz("prodaja.storno")).Post("/prodaja/storno/{id}", h.StornoProdaje)
|
||||||
|
r.With(doz("prodaja.dodaj")).Post("/prodaja/{id}/fiskalizuj", h.RetryFiskalizacijaProdaje)
|
||||||
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "prodaja.pregled")).Get("/prodaja/{id}/stampa", h.StampaProdaje)
|
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "prodaja.pregled")).Get("/prodaja/{id}/stampa", h.StampaProdaje)
|
||||||
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "prodaja.pregled")).Get("/prodaja/{id}", h.DetaljiProdaje)
|
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "prodaja.pregled")).Get("/prodaja/{id}", h.DetaljiProdaje)
|
||||||
|
|
||||||
|
|||||||
@@ -385,6 +385,7 @@ type FiskalRepository interface {
|
|||||||
DohvatiPoServisu(ctx context.Context, servisID int64) (*model.FiskalniRacun, error)
|
DohvatiPoServisu(ctx context.Context, servisID int64) (*model.FiskalniRacun, error)
|
||||||
OznačiKaoStorniran(ctx context.Context, id int64) error
|
OznačiKaoStorniran(ctx context.Context, id int64) error
|
||||||
ServisiBezFiskalnog(ctx context.Context) (map[int64]bool, error)
|
ServisiBezFiskalnog(ctx context.Context) (map[int64]bool, error)
|
||||||
|
ProdajeBezFiskalnog(ctx context.Context) (map[int64]bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServisniLogRepository definiše operacije nad log-om događaja servisnog naloga.
|
// ServisniLogRepository definiše operacije nad log-om događaja servisnog naloga.
|
||||||
|
|||||||
@@ -137,6 +137,28 @@ func (r *FiskalRepo) ServisiBezFiskalnog(ctx context.Context) (map[int64]bool, e
|
|||||||
return out, rows.Err()
|
return out, rows.Err()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ProdajeBezFiskalnog vraća skup ID-eva prodajnih naloga koji nisu stornirani
|
||||||
|
// a nemaju fiskalni račun (za oznaku u listi naloga).
|
||||||
|
func (r *FiskalRepo) ProdajeBezFiskalnog(ctx context.Context) (map[int64]bool, error) {
|
||||||
|
rows, err := r.db.QueryContext(ctx, `
|
||||||
|
SELECT pn.id FROM prodajni_nalozi pn
|
||||||
|
LEFT JOIN fiskalni_racuni fr ON fr.prodaja_id = pn.id AND fr.storniran = 0
|
||||||
|
WHERE pn.stornirano = 0 AND fr.id IS NULL`)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
out := make(map[int64]bool)
|
||||||
|
for rows.Next() {
|
||||||
|
var id int64
|
||||||
|
if err := rows.Scan(&id); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out[id] = true
|
||||||
|
}
|
||||||
|
return out, rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
// OznačiKaoStorniran postavlja storniran=1 za fiskalni račun sa datim ID-jem.
|
// OznačiKaoStorniran postavlja storniran=1 za fiskalni račun sa datim ID-jem.
|
||||||
func (r *FiskalRepo) OznačiKaoStorniran(ctx context.Context, id int64) error {
|
func (r *FiskalRepo) OznačiKaoStorniran(ctx context.Context, id int64) error {
|
||||||
_, err := r.db.ExecContext(ctx, "UPDATE fiskalni_racuni SET storniran = 1 WHERE id = ?", id)
|
_, err := r.db.ExecContext(ctx, "UPDATE fiskalni_racuni SET storniran = 1 WHERE id = ?", id)
|
||||||
|
|||||||
+113
-52
@@ -1,6 +1,7 @@
|
|||||||
package handler
|
package handler
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@@ -28,6 +29,7 @@ type PodaciProdaje struct {
|
|||||||
Sacuvano bool
|
Sacuvano bool
|
||||||
Obrisan bool
|
Obrisan bool
|
||||||
Pretraga string
|
Pretraga string
|
||||||
|
NemaFiskalnog map[int64]bool // ID-evi naloga bez izdatog fiskalnog računa
|
||||||
}
|
}
|
||||||
|
|
||||||
// PodaciFormeProdaje su podaci za formu unosa nove prodaje
|
// PodaciFormeProdaje su podaci za formu unosa nove prodaje
|
||||||
@@ -46,6 +48,7 @@ type PodaciDetaljiProdaje struct {
|
|||||||
Stavke []model.StavkaProdajeSaArtiklom
|
Stavke []model.StavkaProdajeSaArtiklom
|
||||||
KlijentNaziv string
|
KlijentNaziv string
|
||||||
FiskalniRacun *model.FiskalniRacun // nil ako nije fiskalizovano
|
FiskalniRacun *model.FiskalniRacun // nil ako nije fiskalizovano
|
||||||
|
FiskalGreska bool // true: fiskalizacija aktivna, nalog nije storniran, ali nema fiskalnog računa
|
||||||
Sacuvano bool
|
Sacuvano bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,6 +113,11 @@ func (h *Handler) Prodaja(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var nemaFiskalnog map[int64]bool
|
||||||
|
if h.modulUkljucen(r.Context(), config.ModulFiskalizacija) {
|
||||||
|
nemaFiskalnog, _ = h.FiskalRepo.ProdajeBezFiskalnog(r.Context())
|
||||||
|
}
|
||||||
|
|
||||||
ps := h.popuniPodaciStranice(r, podesavanja)
|
ps := h.popuniPodaciStranice(r, podesavanja)
|
||||||
ps.Stranica = "prodaja"
|
ps.Stranica = "prodaja"
|
||||||
ps.NaslovStranice = "Prodaja"
|
ps.NaslovStranice = "Prodaja"
|
||||||
@@ -119,6 +127,7 @@ func (h *Handler) Prodaja(w http.ResponseWriter, r *http.Request) {
|
|||||||
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
|
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
|
||||||
Obrisan: r.URL.Query().Get("obrisan") == "1",
|
Obrisan: r.URL.Query().Get("obrisan") == "1",
|
||||||
Pretraga: pretraga,
|
Pretraga: pretraga,
|
||||||
|
NemaFiskalnog: nemaFiskalnog,
|
||||||
}
|
}
|
||||||
|
|
||||||
h.renderujTemplate(w, "prodaja", podaci)
|
h.renderujTemplate(w, "prodaja", podaci)
|
||||||
@@ -246,58 +255,8 @@ func (h *Handler) SacuvajProdaju(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Fiskalizacija — ako je modul uključen (best-effort: prodaja ostaje validna i bez fiskalizacije)
|
// Fiskalizacija — ako je modul uključen (best-effort: prodaja ostaje validna i bez fiskalizacije)
|
||||||
if h.modulUkljucen(r.Context(), config.ModulFiskalizacija) {
|
if h.modulUkljucen(r.Context(), config.ModulFiskalizacija) {
|
||||||
klijent := h.fiskalKlijent()
|
if klijent := h.fiskalKlijent(); klijent != nil {
|
||||||
if klijent != nil {
|
h.fiskalizujProdaju(r.Context(), id, klijent)
|
||||||
// učitaj nalog sa ID-jem (potreban za buyerId)
|
|
||||||
spremljen, e := h.ProdajaRepo.DohvatiID(r.Context(), id)
|
|
||||||
if e == nil {
|
|
||||||
spremljeneStavke, e2 := h.ProdajaRepo.DohvatiStavke(r.Context(), id)
|
|
||||||
if e2 == nil {
|
|
||||||
kasir, _ := sqlite.DohvatiPodesavanje(r.Context(), h.DB, "pfr_kasir")
|
|
||||||
if kasir == "" {
|
|
||||||
kasir = "NTech"
|
|
||||||
}
|
|
||||||
|
|
||||||
var pib, jmbg string
|
|
||||||
if spremljen.KlijentID != nil {
|
|
||||||
if kk, e3 := h.KlijentiRepo.DohvatiID(r.Context(), *spremljen.KlijentID); e3 == nil {
|
|
||||||
pib, jmbg = kk.PIB, kk.JMBG
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
zahtev := fiskal.NapraviZahtev(*spremljen, spremljeneStavke, pib, jmbg, kasir)
|
|
||||||
|
|
||||||
odgovor, errFisk := klijent.IzdajRacun(r.Context(), zahtev)
|
|
||||||
if errFisk != nil {
|
|
||||||
slog.Error("fiskalizacija nije uspela", "prodaja_id", id, "error", errFisk)
|
|
||||||
} else {
|
|
||||||
poreskeJSON, _ := json.Marshal(odgovor.TaxItems)
|
|
||||||
siroviJSON, _ := json.Marshal(odgovor)
|
|
||||||
|
|
||||||
fr := &model.FiskalniRacun{
|
|
||||||
ProdajaID: id,
|
|
||||||
TipRacuna: "Normal",
|
|
||||||
TipTransakcije: "Sale",
|
|
||||||
PfrBroj: odgovor.InvoiceNumber,
|
|
||||||
PfrVreme: odgovor.SdcDateTime,
|
|
||||||
Brojac: odgovor.InvoiceCounter,
|
|
||||||
EkstenzijaBrojaca: odgovor.InvoiceCounterExtension,
|
|
||||||
UrlVerifikacija: odgovor.VerificationURL,
|
|
||||||
QRKod: odgovor.VerificationQRCode,
|
|
||||||
PoreskeStavke: string(poreskeJSON),
|
|
||||||
UkupnoZaNaplatu: odgovor.TotalAmount,
|
|
||||||
UkupanPorez: odgovor.TotalTax,
|
|
||||||
SiroviOdgovor: string(siroviJSON),
|
|
||||||
Potpisao: odgovor.SignedBy,
|
|
||||||
Zatrazio: odgovor.RequestedBy,
|
|
||||||
Poruka: odgovor.Messages,
|
|
||||||
}
|
|
||||||
if _, errFisk := h.FiskalRepo.Kreiraj(r.Context(), fr); errFisk != nil {
|
|
||||||
slog.Error("greška pri čuvanju fiskalnog računa", "prodaja_id", id, "error", errFisk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -370,6 +329,7 @@ func (h *Handler) DetaljiProdaje(w http.ResponseWriter, r *http.Request) {
|
|||||||
Stavke: stavke,
|
Stavke: stavke,
|
||||||
KlijentNaziv: klijentNaziv,
|
KlijentNaziv: klijentNaziv,
|
||||||
FiskalniRacun: fr,
|
FiskalniRacun: fr,
|
||||||
|
FiskalGreska: h.modulUkljucen(r.Context(), config.ModulFiskalizacija) && !nalog.Stornirano && fr == nil,
|
||||||
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
|
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -619,6 +579,107 @@ func (h *Handler) StornoProdaje(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Redirect(w, r, "/prodaja/"+strconv.FormatInt(id, 10)+"?sacuvano=1", http.StatusSeeOther)
|
http.Redirect(w, r, "/prodaja/"+strconv.FormatInt(id, 10)+"?sacuvano=1", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fiskalizujProdaju šalje fiskalni zahtev za prodajni nalog. Best-effort: greške
|
||||||
|
// se loguju, ne zaustavljaju tok pozivaoca (kreiranje prodaje ili ručni retry).
|
||||||
|
func (h *Handler) fiskalizujProdaju(ctx context.Context, prodajaID int64, klijent *fiskal.Klijent) {
|
||||||
|
nalog, err := h.ProdajaRepo.DohvatiID(ctx, prodajaID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("fiskalizujProdaju: nije pronađen nalog", "prodaja_id", prodajaID, "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
stavke, err := h.ProdajaRepo.DohvatiStavke(ctx, prodajaID)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("fiskalizujProdaju: greška pri dohvatanju stavki", "prodaja_id", prodajaID, "error", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
kasir, _ := sqlite.DohvatiPodesavanje(ctx, h.DB, "pfr_kasir")
|
||||||
|
if kasir == "" {
|
||||||
|
kasir = "NTech"
|
||||||
|
}
|
||||||
|
|
||||||
|
var pib, jmbg string
|
||||||
|
if nalog.KlijentID != nil {
|
||||||
|
if kk, e := h.KlijentiRepo.DohvatiID(ctx, *nalog.KlijentID); e == nil {
|
||||||
|
pib, jmbg = kk.PIB, kk.JMBG
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zahtev := fiskal.NapraviZahtev(*nalog, stavke, pib, jmbg, kasir)
|
||||||
|
|
||||||
|
odgovor, errFisk := klijent.IzdajRacun(ctx, zahtev)
|
||||||
|
if errFisk != nil {
|
||||||
|
slog.Error("fiskalizacija prodaje nije uspela", "prodaja_id", prodajaID, "error", errFisk)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
poreskeJSON, _ := json.Marshal(odgovor.TaxItems)
|
||||||
|
siroviJSON, _ := json.Marshal(odgovor)
|
||||||
|
fr := &model.FiskalniRacun{
|
||||||
|
ProdajaID: prodajaID,
|
||||||
|
TipRacuna: "Normal",
|
||||||
|
TipTransakcije: "Sale",
|
||||||
|
PfrBroj: odgovor.InvoiceNumber,
|
||||||
|
PfrVreme: odgovor.SdcDateTime,
|
||||||
|
Brojac: odgovor.InvoiceCounter,
|
||||||
|
EkstenzijaBrojaca: odgovor.InvoiceCounterExtension,
|
||||||
|
UrlVerifikacija: odgovor.VerificationURL,
|
||||||
|
QRKod: odgovor.VerificationQRCode,
|
||||||
|
PoreskeStavke: string(poreskeJSON),
|
||||||
|
UkupnoZaNaplatu: odgovor.TotalAmount,
|
||||||
|
UkupanPorez: odgovor.TotalTax,
|
||||||
|
SiroviOdgovor: string(siroviJSON),
|
||||||
|
Potpisao: odgovor.SignedBy,
|
||||||
|
Zatrazio: odgovor.RequestedBy,
|
||||||
|
Poruka: odgovor.Messages,
|
||||||
|
}
|
||||||
|
if _, err := h.FiskalRepo.Kreiraj(ctx, fr); err != nil {
|
||||||
|
slog.Error("greška pri čuvanju fiskalnog računa", "prodaja_id", prodajaID, "error", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RetryFiskalizacijaProdaje pokušava ponovo da izda fiskalni račun za prodajni
|
||||||
|
// nalog čija je prethodna fiskalizacija pala (ESIR/PFR nedostupan pri kreiranju).
|
||||||
|
func (h *Handler) RetryFiskalizacijaProdaje(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if _, ok := h.zahtevajDozvolu(w, r, "prodaja.dodaj"); !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
id, err := parseID(chi.URLParam(r, "id"))
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Neispravan ID naloga", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// zaštita od duplikata — ako fiskalni račun već postoji, ne šalji ponovo
|
||||||
|
if fr, _ := h.FiskalRepo.DohvatiPoProdaji(r.Context(), id); fr != nil {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "uspeh", "Fiskalni račun već postoji.")
|
||||||
|
http.Redirect(w, r, "/prodaja/"+strconv.FormatInt(id, 10), http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
nalog, err := h.ProdajaRepo.DohvatiID(r.Context(), id)
|
||||||
|
if err != nil || nalog.Stornirano {
|
||||||
|
http.Error(w, "Nalog ne postoji ili je stornirano", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
klijent := h.fiskalKlijent()
|
||||||
|
if klijent == nil {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Fiskalni servis nije dostupan. Proverite vezu sa ESIR/PFR.")
|
||||||
|
http.Redirect(w, r, "/prodaja/"+strconv.FormatInt(id, 10), http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
h.fiskalizujProdaju(r.Context(), id, klijent)
|
||||||
|
|
||||||
|
if fr, _ := h.FiskalRepo.DohvatiPoProdaji(r.Context(), id); fr != nil {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "uspeh", "Fiskalni račun izdat.")
|
||||||
|
} else {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", "Fiskalizacija nije uspela. Proverite vezu sa ESIR/PFR i pokušajte ponovo.")
|
||||||
|
}
|
||||||
|
http.Redirect(w, r, "/prodaja/"+strconv.FormatInt(id, 10), http.StatusSeeOther)
|
||||||
|
}
|
||||||
|
|
||||||
// renderujFormuProdaje renderuje HTML šablon forme za unos nove prodaje
|
// renderujFormuProdaje renderuje HTML šablon forme za unos nove prodaje
|
||||||
func (h *Handler) renderujFormuProdaje(w http.ResponseWriter, podaci PodaciFormeProdaje) {
|
func (h *Handler) renderujFormuProdaje(w http.ResponseWriter, podaci PodaciFormeProdaje) {
|
||||||
h.renderujTemplate(w, "prodaja_forma", podaci)
|
h.renderujTemplate(w, "prodaja_forma", podaci)
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
<td style="padding: 12px 16px; font-size: 13px; font-family: monospace; color: var(--tekst-glavni)">
|
<td style="padding: 12px 16px; font-size: 13px; font-family: monospace; color: var(--tekst-glavni)">
|
||||||
{{.BrojNaloga}}
|
{{.BrojNaloga}}
|
||||||
{{if .Stornirano}}<span class="status-badge status-stornirano" style="margin-left: 8px">Stornirano</span>{{end}}
|
{{if .Stornirano}}<span class="status-badge status-stornirano" style="margin-left: 8px">Stornirano</span>{{end}}
|
||||||
|
{{if index $.NemaFiskalnog .ID}}<span style="display:inline-block;margin-left:6px;font-size:10px;font-weight:600;padding:1px 6px;border-radius:4px;background:#fef3c7;color:#92400e;border:1px solid #fde68a;vertical-align:middle;" title="Fiskalni račun nije izdat">!</span>{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td style="padding: 12px 16px; font-size: 13px; color: var(--tekst-sporedni); white-space: nowrap">{{.Datum.Format "02.01.2006."}}</td>
|
<td style="padding: 12px 16px; font-size: 13px; color: var(--tekst-sporedni); white-space: nowrap">{{.Datum.Format "02.01.2006."}}</td>
|
||||||
<td style="padding: 12px 16px; font-size: 14px; font-weight: 500; color: var(--tekst-glavni)">{{if .KlijentNaziv}}{{.KlijentNaziv}}{{else}}—{{end}}</td>
|
<td style="padding: 12px 16px; font-size: 14px; font-weight: 500; color: var(--tekst-glavni)">{{if .KlijentNaziv}}{{.KlijentNaziv}}{{else}}—{{end}}</td>
|
||||||
@@ -76,6 +77,7 @@
|
|||||||
<div style="font-size: 13px; font-family: monospace; color: var(--tekst-glavni)">
|
<div style="font-size: 13px; font-family: monospace; color: var(--tekst-glavni)">
|
||||||
{{.BrojNaloga}}
|
{{.BrojNaloga}}
|
||||||
{{if .Stornirano}}<span class="status-badge status-stornirano" style="margin-left: 8px">Stornirano</span>{{end}}
|
{{if .Stornirano}}<span class="status-badge status-stornirano" style="margin-left: 8px">Stornirano</span>{{end}}
|
||||||
|
{{if index $.NemaFiskalnog .ID}}<span style="display:inline-block;margin-left:4px;font-size:10px;font-weight:600;padding:1px 5px;border-radius:4px;background:#fef3c7;color:#92400e;border:1px solid #fde68a;vertical-align:middle;" title="Fiskalni račun nije izdat">!</span>{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div style="font-size: 14px; font-weight: 500; color: var(--tekst-glavni); margin-top: 2px">{{if .KlijentNaziv}}{{.KlijentNaziv}}{{else}}Bez klijenta{{end}}</div>
|
<div style="font-size: 14px; font-weight: 500; color: var(--tekst-glavni); margin-top: 2px">{{if .KlijentNaziv}}{{.KlijentNaziv}}{{else}}Bez klijenta{{end}}</div>
|
||||||
<div style="font-size: 12px; color: var(--tekst-sporedni); margin-top: 2px">{{.Datum.Format "02.01.2006."}}</div>
|
<div style="font-size: 12px; color: var(--tekst-sporedni); margin-top: 2px">{{.Datum.Format "02.01.2006."}}</div>
|
||||||
|
|||||||
@@ -286,6 +286,23 @@
|
|||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
{{if .FiskalGreska}}
|
||||||
|
<div class="fiskalni-racun" style="padding:14px;border:1.5px solid #f59e0b;border-radius:8px;background:color-mix(in srgb,#fef3c7 60%,var(--pozadina));">
|
||||||
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
||||||
|
<div style="flex:1;">
|
||||||
|
<div style="font-size:14px;font-weight:600;color:#92400e;margin-bottom:4px;">⚠ Fiskalni račun nije izdat</div>
|
||||||
|
<div style="font-size:13px;color:#78350f;">Veza sa ESIR/PFR serverom nije bila dostupna pri kreiranju prodaje.</div>
|
||||||
|
</div>
|
||||||
|
{{if index $.Dozvole "prodaja.dodaj"}}
|
||||||
|
<form method="POST" action="/prodaja/{{.Nalog.ID}}/fiskalizuj">
|
||||||
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
|
<button type="submit" class="btn-primarno" style="background:#d97706;border-color:#d97706;white-space:nowrap;">Pokušaj ponovo</button>
|
||||||
|
</form>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
{{if index $.Dozvole "prodaja.obrisi"}}
|
{{if index $.Dozvole "prodaja.obrisi"}}
|
||||||
<div style="display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap;padding-top:14px;border-top:0.5px solid var(--ivica);">
|
<div style="display:flex;align-items:flex-start;gap:12px;flex-wrap:wrap;padding-top:14px;border-top:0.5px solid var(--ivica);">
|
||||||
<div style="flex:1;min-width:200px;">
|
<div style="flex:1;min-width:200px;">
|
||||||
|
|||||||
Reference in New Issue
Block a user