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
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
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)
|
||||
}
|
||||
Reference in New Issue
Block a user