Servis: nalepnica za uređaj (70×40 mm) sa QR kodom

This commit is contained in:
2026-06-23 02:46:36 +02:00
parent 7465b7e40f
commit 966e940993
5 changed files with 115 additions and 1 deletions
+1
View File
@@ -357,6 +357,7 @@ func main() {
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}", h.DetaljiNaloga)
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}/stampa", h.StampaServisa)
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}/otpremnica", h.StampaOtpremnice)
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}/nalepnica", h.StampaNalepnice)
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}/predracun", h.StampaPredracuna)
r.With(doz("servis.izmeni")).Post("/servis/{id}/status", h.PromeniStatus)
r.With(doz("servis.izmeni")).Post("/servis/{id}/garancija", h.AzurirajGaranciju)
+1 -1
View File
@@ -43,7 +43,7 @@ var saSidebar = []string{
// standalone su šabloni bez base layouta
var standaloneIme = []string{
"prijava", "setup", "totp_provera", "prodaja_stampa", "servis_stampa", "servis_otpremnica", "servis_predracun", "servis_status_javni",
"prijava", "setup", "totp_provera", "prodaja_stampa", "servis_stampa", "servis_otpremnica", "servis_predracun", "servis_nalepnica", "servis_status_javni",
}
// sablonskeFunkcije su pomoćne funkcije dostupne u svim šablonima.
+46
View File
@@ -1298,6 +1298,52 @@ func (h *Handler) StampaOtpremnice(w http.ResponseWriter, r *http.Request) {
})
}
// PodaciNalepnice su podaci za malu nalepnicu (70×40 mm) koja se lepi na uređaj
type PodaciNalepnice struct {
Nalog model.ServisniNalog
KlijentNaziv string
QRKod string // base64 PNG QR koda sa URL-om naloga
}
// StampaNalepnice renderuje malu nalepnicu sa brojem naloga, klijentom, uređajem i QR kodom
func (h *Handler) StampaNalepnice(w http.ResponseWriter, r *http.Request) {
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID naloga", http.StatusBadRequest)
return
}
nalog, err := h.ServisRepo.DohvatiID(r.Context(), id)
if err != nil {
http.Error(w, "Nalog nije pronađen", http.StatusNotFound)
return
}
klijentNaziv := ""
if nalog.KlijentID != nil {
k, err := h.KlijentiRepo.DohvatiID(r.Context(), *nalog.KlijentID)
if err == nil {
if k.NazivFirme != "" {
klijentNaziv = k.NazivFirme
} else {
klijentNaziv = strings.TrimSpace(k.Ime + " " + k.Prezime)
}
}
}
nalogURL := qrNalogURL(r, nalog.JavniToken)
var qrKod string
if png, err := qrcode.Encode(nalogURL, qrcode.Medium, 160); err == nil {
qrKod = base64.StdEncoding.EncodeToString(png)
}
h.renderujStandalone(w, "servis_nalepnica", PodaciNalepnice{
Nalog: *nalog,
KlijentNaziv: klijentNaziv,
QRKod: qrKod,
})
}
// PodaciPredracuna su podaci za predračun/ponudu koja se šalje klijentu pre rada
type PodaciPredracuna struct {
Nalog model.ServisniNalog
@@ -60,6 +60,9 @@
Otpremnica
</a>
{{end}}
<a href="/servis/{{.Nalog.ID}}/nalepnica" target="_blank" class="btn-sekundarno">
Nalepnica
</a>
<a href="/servis/izmeni/{{.Nalog.ID}}" class="btn-primarno" hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML">
Izmeni nalog
</a>
@@ -0,0 +1,64 @@
<!DOCTYPE html>
<html lang="sr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nalepnica — {{.Nalog.BrojNaloga}}</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { background: #f4f4f5; }
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif; color: #111; }
/* nalepnica 70×40 mm */
.nalepnica {
width: 70mm;
height: 40mm;
background: #fff;
padding: 3mm;
display: flex;
align-items: center;
gap: 3mm;
overflow: hidden;
}
/* na ekranu — centrirano sa okvirom radi pregleda */
@media screen {
.nalepnica { margin: 24px auto; border: 1px dashed #bbb; box-shadow: 0 1px 4px rgba(0,0,0,.1); }
}
.podaci { flex: 1; min-width: 0; display: flex; flex-direction: column; justify-content: center; gap: 1.5mm; }
.broj { font-family: monospace; font-size: 14pt; font-weight: 700; line-height: 1.1; }
.red { font-size: 8pt; line-height: 1.25; }
.red .labela { color: #777; text-transform: uppercase; letter-spacing: .3px; font-size: 6.5pt; }
.red .vrednost { font-weight: 600; word-break: break-word; }
.prazno { color: #aaa; font-style: italic; font-weight: 400; }
.qr { flex: 0 0 auto; }
.qr img { display: block; width: 26mm; height: 26mm; }
@page { size: 70mm 40mm; margin: 0; }
@media print {
html, body { background: #fff; }
.nalepnica { margin: 0; border: 0; box-shadow: none; }
}
</style>
</head>
<body>
<div class="nalepnica">
<div class="podaci">
<div class="broj">{{.Nalog.BrojNaloga}}</div>
<div class="red">
<div class="labela">Klijent</div>
<div class="vrednost">{{if .KlijentNaziv}}{{.KlijentNaziv}}{{else}}<span class="prazno"></span>{{end}}</div>
</div>
<div class="red">
<div class="labela">Uređaj</div>
<div class="vrednost">{{if .Nalog.Uredjaj}}{{.Nalog.Uredjaj}}{{else}}<span class="prazno"></span>{{end}}</div>
</div>
</div>
{{if .QRKod}}
<div class="qr"><img src="data:image/png;base64,{{.QRKod}}" alt="QR"></div>
{{end}}
</div>
<script>window.addEventListener('load', function(){ window.print(); });</script>
</body>
</html>