Files
GoNtech/internal/handler/fiskal_pazar.go
T
Dasko 7c215e70e8 Fiskalni pazar: presek stanja, dnevni izveštaj i razdvajanje praznina po prodaji/servisu
- Nova stranica fiskal_pazar: presek prometa od poslednjeg zaključenja, zaključenje dana, generisanje PDF izveštaja
- fiskal.go: dodati pozivi Presek/ZakljuciDan/GenerisiIzvestaj ka /api/financial/summary i /api/financial/report/summary
- PrazninaKnjigovodstva razdvojena na Prodaja/Servis varijante sa ID listama za direktne linkove sa dashboard-a
2026-07-02 19:14:54 +02:00

121 lines
4.0 KiB
Go

package handler
import (
"encoding/base64"
"fmt"
"net/http"
"strings"
ntechsqlite "ntech/internal/db/sqlite"
"ntech/internal/fiskal"
"ntech/internal/middleware"
"ntech/internal/model"
)
// PodaciFiskalniPazar su podaci za stranicu dnevnog fiskalnog preseka (pazara).
type PodaciFiskalniPazar struct {
model.PodaciStranice
Presek *fiskal.FinancialSummary
Greska string
FiskalPodesen bool // true: pfr_url je podešen
}
// FiskalniPazar prikazuje trenutni promet od poslednjeg preseka stanja
// (GET /api/financial/summary na PFR-u) — read-only, ne menja stanje.
func (h *Handler) FiskalniPazar(w http.ResponseWriter, r *http.Request) {
if _, ok := h.zahtevajDozvolu(w, r, "fiskal.pazar"); !ok {
return
}
podesavanja, err := ntechsqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
if err != nil {
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
return
}
ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "fiskal-pazar"
ps.NaslovStranice = "Dnevni fiskalni presek"
podaci := PodaciFiskalniPazar{PodaciStranice: ps}
klijent := h.fiskalKlijent()
if klijent == nil {
podaci.Greska = "Fiskalizacija nije podešena — unesi URL PFR servera u Podešavanja → Fiskalizacija."
h.renderujTemplate(w, "fiskal_pazar", podaci)
return
}
podaci.FiskalPodesen = true
presek, err := klijent.Presek(r.Context())
if err != nil {
podaci.Greska = "Fiskalni server nije dostupan — proveri da li je PFR uređaj/mock pokrenut."
} else {
podaci.Presek = presek
}
h.renderujTemplate(w, "fiskal_pazar", podaci)
}
// ZakljuciFiskalniDan svodi promet PFR-a na nulu (DELETE /api/financial/summary).
// Nepovratna akcija — traži potvrdu na strani korisnika (confirm() u šablonu).
func (h *Handler) ZakljuciFiskalniDan(w http.ResponseWriter, r *http.Request) {
if _, ok := h.zahtevajDozvolu(w, r, "fiskal.zakljucenje"); !ok {
return
}
klijent := h.fiskalKlijent()
if klijent == nil {
middleware.SetFlash(w, r, h.DB, "greska", "Fiskalizacija nije podešena.")
http.Redirect(w, r, "/fiskal/pazar", http.StatusSeeOther)
return
}
if err := klijent.ZakljuciDan(r.Context()); err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Presek stanja nije uspeo — fiskalni server nije dostupan.")
http.Redirect(w, r, "/fiskal/pazar", http.StatusSeeOther)
return
}
middleware.SetFlash(w, r, h.DB, "uspeh", "Dan je zaključen — presek stanja urađen, brojači prometa resetovani.")
http.Redirect(w, r, "/fiskal/pazar", http.StatusSeeOther)
}
// FiskalniIzvestaj traži od PFR-a dnevni/periodični izveštaj (POST
// /api/financial/report/summary) i vraća ga korisniku kao fajl za preuzimanje.
// Bez fromDate/toDate parametara izveštaj pokriva period od poslednjeg preseka stanja.
func (h *Handler) FiskalniIzvestaj(w http.ResponseWriter, r *http.Request) {
if _, ok := h.zahtevajDozvolu(w, r, "fiskal.pazar"); !ok {
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
}
klijent := h.fiskalKlijent()
if klijent == nil {
middleware.SetFlash(w, r, h.DB, "greska", "Fiskalizacija nije podešena.")
http.Redirect(w, r, "/fiskal/pazar", http.StatusSeeOther)
return
}
zahtev := fiskal.ReportSummaryZahtev{
FromDate: strings.TrimSpace(r.FormValue("od_datuma")),
ToDate: strings.TrimSpace(r.FormValue("do_datuma")),
}
odgovor, err := klijent.GenerisiIzvestaj(r.Context(), zahtev)
if err != nil {
middleware.SetFlash(w, r, h.DB, "greska", "Generisanje izveštaja nije uspelo — fiskalni server nije dostupan.")
http.Redirect(w, r, "/fiskal/pazar", http.StatusSeeOther)
return
}
sadrzaj, err := base64.StdEncoding.DecodeString(odgovor.ReportPdfBase64)
if err != nil {
http.Error(w, "Neispravan sadržaj izveštaja", http.StatusBadGateway)
return
}
imeFajla := odgovor.Filename
if imeFajla == "" {
imeFajla = "dnevni-izvestaj.pdf"
}
w.Header().Set("Content-Type", "application/pdf")
w.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"`, imeFajla))
w.Write(sadrzaj)
}