PFR API ključ, fiskalni račun formatiranje, popravke

- PFR API ključ (pfr_api_key) u podešavanjima i fiskalizaciji
- Formatiranje fiskalnog računa: veličina fonta, QR kod, layout
- PFR mock: QR box_size/border, automatski PIN retry posle BE restarta
- Servis: animirane kanban kartice, formatiranje računa
- Dashboard i testovi: sitne ispravke
This commit is contained in:
2026-07-30 02:25:12 +02:00
parent ee265f81a9
commit 0fb9fe1e37
16 changed files with 119 additions and 36 deletions
+52 -2
View File
@@ -2,9 +2,12 @@ package handler
import (
"context"
"net/http"
"strconv"
"time"
appdb "ntech/internal/db"
"ntech/internal/middleware"
"ntech/internal/model"
)
@@ -174,12 +177,14 @@ func (h *Handler) kirKandidatiServisa(ctx context.Context) ([]model.ServisniNalo
// (double-submit) pa ih automatski backfill preskače.
func (h *Handler) PraznineKnjigovodstva(ctx context.Context) (model.PrazninaKnjigovodstva, error) {
var p model.PrazninaKnjigovodstva
var kandidatiP []kandidatUskladjivanja
if h.modulUkljucen(ctx, "pdv") {
_, postojiMapP, kandidatiP, err := h.kirKandidatiProdaje(ctx)
_, postojiMapP, kp, err := h.kirKandidatiProdaje(ctx)
if err != nil {
return p, err
}
kandidatiP = kp
_, postojiMapS, kandidatiS, err := h.kirKandidatiServisa(ctx)
if err != nil {
return p, err
@@ -211,10 +216,11 @@ func (h *Handler) PraznineKnjigovodstva(ctx context.Context) (model.PrazninaKnji
}
if h.modulUkljucen(ctx, "kpo") {
_, postojiMapP, kandidatiP, err := h.kpoKandidatiProdaje(ctx)
_, postojiMapP, kp, err := h.kpoKandidatiProdaje(ctx)
if err != nil {
return p, err
}
kandidatiP = kp
_, postojiMapS, kandidatiS, _, err := h.kpoKandidatiServisa(ctx)
if err != nil {
return p, err
@@ -258,6 +264,17 @@ func (h *Handler) PraznineKnjigovodstva(ctx context.Context) (model.PrazninaKnji
}
p.BezFiskalnogServis = len(p.BezFiskalnogServisID)
}
// sumnjivi duplikati za fiskal — isti kandidati kao za KPO/PDV,
// ako su već učitani; ako ne, učitavamo iz prodaje (samo brojimo,
// preskaču se u automatskoj batch fiskalizaciji)
if len(kandidatiP) > 0 {
dupli := sumnjiviDuplikati(kandidatiP)
for id := range dupli {
p.SumnjiviFiskalID = append(p.SumnjiviFiskalID, id)
}
}
if idsP, idsS, err := h.stornoBezRefunda(ctx); err == nil {
p.BezRefundaProdajaID = idsP
p.BezRefundaServisID = idsS
@@ -330,3 +347,36 @@ func istKlijent(a, b *int64) bool {
}
return *a == *b
}
// BatchFiskalizacija pokreće fiskalizaciju svih prodaja i servisa koji nemaju
// fiskalni račun. Best-effort: ako neka fiskalizacija padne, nastavlja se.
func (h *Handler) BatchFiskalizacija(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
klijent := h.fiskalKlijent(ctx)
if klijent == nil {
middleware.SetFlash(w, r, h.DB, "greska", "Fiskalni servis nije dostupan. Proverite vezu sa ESIR/PFR.")
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
return
}
izdato := 0
if m, err := h.FiskalRepo.ProdajeBezFiskalnog(ctx); err == nil {
for id := range m {
h.fiskalizujProdaju(ctx, id, klijent, 0)
izdato++
}
}
if m, err := h.FiskalRepo.ServisiBezFiskalnog(ctx); err == nil {
for id := range m {
h.fiskalizujServis(ctx, id, klijent, "Gotovina", 0, 0)
izdato++
}
}
if izdato == 0 {
middleware.SetFlash(w, r, h.DB, "uspeh", "Svi nalozi već imaju fiskalni račun.")
} else {
middleware.SetFlash(w, r, h.DB, "uspeh", "Pokrenuta fiskalizacija za "+strconv.Itoa(izdato)+" naloga.")
}
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
}