Servis: Code128 barkod na vrhu nalepnice (laserski skener)
This commit is contained in:
@@ -3,21 +3,21 @@ module ntech
|
||||
go 1.26.3
|
||||
|
||||
require (
|
||||
github.com/boombuler/barcode v1.1.0
|
||||
github.com/go-chi/chi/v5 v5.3.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/pquerna/otp v1.5.0
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
golang.org/x/crypto v0.52.0
|
||||
modernc.org/sqlite v1.51.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/stretchr/testify v1.11.1 // indirect
|
||||
golang.org/x/sys v0.45.0 // indirect
|
||||
golang.org/x/tools v0.44.0 // indirect
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"image/png"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -15,6 +17,8 @@ import (
|
||||
"ntech/internal/middleware"
|
||||
"ntech/internal/model"
|
||||
|
||||
"github.com/boombuler/barcode"
|
||||
"github.com/boombuler/barcode/code128"
|
||||
"github.com/go-chi/chi/v5"
|
||||
qrcode "github.com/skip2/go-qrcode"
|
||||
)
|
||||
@@ -1303,6 +1307,25 @@ type PodaciNalepnice struct {
|
||||
Nalog model.ServisniNalog
|
||||
KlijentNaziv string
|
||||
QRKod string // base64 PNG QR koda sa URL-om naloga
|
||||
Barkod string // base64 PNG Code128 barkoda sa brojem naloga (za laserski skener)
|
||||
}
|
||||
|
||||
// barkodNaloga generiše Code128 barkod broja naloga kao base64 PNG; prazan string ako ne uspe
|
||||
func barkodNaloga(broj string) string {
|
||||
kod, err := code128.Encode(broj)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
// razvuci na fiksnu širinu/visinu radi oštrog otiska na nalepnici
|
||||
skaliran, err := barcode.Scale(kod, 480, 90)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
var buf bytes.Buffer
|
||||
if err := png.Encode(&buf, skaliran); err != nil {
|
||||
return ""
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(buf.Bytes())
|
||||
}
|
||||
|
||||
// StampaNalepnice renderuje malu nalepnicu sa brojem naloga, klijentom, uređajem i QR kodom
|
||||
@@ -1341,6 +1364,7 @@ func (h *Handler) StampaNalepnice(w http.ResponseWriter, r *http.Request) {
|
||||
Nalog: *nalog,
|
||||
KlijentNaziv: klijentNaziv,
|
||||
QRKod: qrKod,
|
||||
Barkod: barkodNaloga(nalog.BrojNaloga),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
width: 70mm;
|
||||
height: 40mm;
|
||||
background: #fff;
|
||||
padding: 3mm;
|
||||
padding: 2.5mm 3mm;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 3mm;
|
||||
flex-direction: column;
|
||||
gap: 1.5mm;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* na ekranu — centrirano sa okvirom radi pregleda */
|
||||
@@ -25,6 +25,12 @@
|
||||
.nalepnica { margin: 24px auto; border: 1px dashed #bbb; box-shadow: 0 1px 4px rgba(0,0,0,.1); }
|
||||
}
|
||||
|
||||
/* barkod na vrhu — preko cele širine, za laserski skener */
|
||||
.barkod { flex: 0 0 auto; text-align: center; }
|
||||
.barkod img { display: block; width: 100%; height: 9mm; object-fit: fill; }
|
||||
|
||||
.donji-red { flex: 1; min-width: 0; display: flex; align-items: center; gap: 3mm; }
|
||||
|
||||
.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; }
|
||||
@@ -33,7 +39,7 @@
|
||||
.prazno { color: #aaa; font-style: italic; font-weight: 400; }
|
||||
|
||||
.qr { flex: 0 0 auto; }
|
||||
.qr img { display: block; width: 26mm; height: 26mm; }
|
||||
.qr img { display: block; width: 22mm; height: 22mm; }
|
||||
|
||||
@page { size: 70mm 40mm; margin: 0; }
|
||||
@media print {
|
||||
@@ -44,20 +50,25 @@
|
||||
</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>
|
||||
{{if .Barkod}}
|
||||
<div class="barkod"><img src="data:image/png;base64,{{.Barkod}}" alt="Barkod"></div>
|
||||
{{end}}
|
||||
<div class="donji-red">
|
||||
<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>
|
||||
</div>
|
||||
<script>window.addEventListener('load', function(){ window.print(); });</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user