Fiskalni pazar: izveštaj po datumskom periodu, zaključenje dana postaje opciono

Novi sistem fiskalizacije izveštava SUF kontinuirano po računu, pa presek stanja
(zaključenje dana) više nije zakonska obaveza — samo opcioni reset brojača na
ekranu. Dodata polja od/do datuma (podrazumevano danas) za preuzimanje izveštaja
nezavisno od toga da li je ikad urađen presek. Dugme 'Zaključi dan' preimenovano
u 'Resetuj brojače (opciono)', manje istaknuto, sa jasnijim opisom u potvrdi.
This commit is contained in:
2026-07-02 21:24:17 +02:00
parent 576d963f28
commit b35b41b82e
2 changed files with 37 additions and 18 deletions
+11 -4
View File
@@ -6,6 +6,7 @@ import (
"net/http" "net/http"
"net/url" "net/url"
"strings" "strings"
"time"
"unicode" "unicode"
ntechsqlite "ntech/internal/db/sqlite" ntechsqlite "ntech/internal/db/sqlite"
@@ -35,6 +36,7 @@ type PodaciFiskalniPazar struct {
Presek *fiskal.FinancialSummary Presek *fiskal.FinancialSummary
Greska string Greska string
FiskalPodesen bool // true: pfr_url je podešen FiskalPodesen bool // true: pfr_url je podešen
Danas string // YYYY-MM-DD, podrazumevani datum za izveštaj (od=do=danas)
} }
// FiskalniPazar prikazuje trenutni promet od poslednjeg preseka stanja // FiskalniPazar prikazuje trenutni promet od poslednjeg preseka stanja
@@ -52,7 +54,7 @@ func (h *Handler) FiskalniPazar(w http.ResponseWriter, r *http.Request) {
ps.Stranica = "fiskal-pazar" ps.Stranica = "fiskal-pazar"
ps.NaslovStranice = "Dnevni fiskalni presek" ps.NaslovStranice = "Dnevni fiskalni presek"
podaci := PodaciFiskalniPazar{PodaciStranice: ps} podaci := PodaciFiskalniPazar{PodaciStranice: ps, Danas: time.Now().Format("2006-01-02")}
klijent := h.fiskalKlijent() klijent := h.fiskalKlijent()
if klijent == nil { if klijent == nil {
@@ -72,7 +74,10 @@ func (h *Handler) FiskalniPazar(w http.ResponseWriter, r *http.Request) {
} }
// ZakljuciFiskalniDan svodi promet PFR-a na nulu (DELETE /api/financial/summary). // ZakljuciFiskalniDan svodi promet PFR-a na nulu (DELETE /api/financial/summary).
// Nepovratna akcija — traži potvrdu na strani korisnika (confirm() u šablonu). // OPCIONA radnja — samo resetuje brojače na ekranu „Promet od poslednjeg preseka";
// nije zakonska obaveza (za razliku od starih fiskalnih kasa, novi sistem izveštava
// SUF kontinuirano po računu). Pravi dnevni/periodični izveštaj (FiskalniIzvestaj)
// radi nezavisno preko datumskog opsega, bez obzira na to da li je ovo ikad urađeno.
func (h *Handler) ZakljuciFiskalniDan(w http.ResponseWriter, r *http.Request) { func (h *Handler) ZakljuciFiskalniDan(w http.ResponseWriter, r *http.Request) {
if _, ok := h.zahtevajDozvolu(w, r, "fiskal.zakljucenje"); !ok { if _, ok := h.zahtevajDozvolu(w, r, "fiskal.zakljucenje"); !ok {
return return
@@ -88,13 +93,15 @@ func (h *Handler) ZakljuciFiskalniDan(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/fiskal/pazar", http.StatusSeeOther) http.Redirect(w, r, "/fiskal/pazar", http.StatusSeeOther)
return return
} }
middleware.SetFlash(w, r, h.DB, "uspeh", "Dan je zaključen — presek stanja urađen, brojači prometa resetovani.") middleware.SetFlash(w, r, h.DB, "uspeh", "Brojači prometa na ekranu su resetovani.")
http.Redirect(w, r, "/fiskal/pazar", http.StatusSeeOther) http.Redirect(w, r, "/fiskal/pazar", http.StatusSeeOther)
} }
// FiskalniIzvestaj traži od PFR-a dnevni/periodični izveštaj (POST // 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. // /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. // FromDate/ToDate dolaze iz forme (podrazumevano danas-danas) — izveštaj radi
// nezavisno od preseka stanja, pa je tačan bez obzira na to da li je Zaključi dan
// ikad korišćeno.
func (h *Handler) FiskalniIzvestaj(w http.ResponseWriter, r *http.Request) { func (h *Handler) FiskalniIzvestaj(w http.ResponseWriter, r *http.Request) {
if _, ok := h.zahtevajDozvolu(w, r, "fiskal.pazar"); !ok { if _, ok := h.zahtevajDozvolu(w, r, "fiskal.pazar"); !ok {
return return
+20 -8
View File
@@ -17,7 +17,8 @@
{{if .Presek}} {{if .Presek}}
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:16px;"> <div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:16px;">
Promet od poslednjeg preseka stanja: <strong>{{.Presek.StartOfPeriod}}</strong> do sada. Kumulativni promet od poslednjeg (opcionog) reseta brojača: <strong>{{.Presek.StartOfPeriod}}</strong> do sada.
Za tačan izveštaj za određeni dan/period koristi „Preuzmi izveštaj za period" ispod.
</div> </div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px;"> <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px;">
@@ -73,22 +74,33 @@
{{end}} {{end}}
{{if .FiskalPodesen}} {{if .FiskalPodesen}}
<div style="display:flex;gap:10px;flex-wrap:wrap;padding-top:12px;border-top:0.5px solid var(--ivica);"> <div style="padding-top:12px;border-top:0.5px solid var(--ivica);">
<a href="/fiskal/pazar" class="btn-sekundarno" style="font-size:13px;padding:8px 16px;">Osveži</a> <div style="font-size:13px;font-weight:500;color:var(--tekst-glavni);margin-bottom:8px;">Preuzmi izveštaj za period</div>
<form method="POST" action="/fiskal/pazar/izvestaj" data-full-reload style="display:flex;gap:10px;flex-wrap:wrap;align-items:end;margin-bottom:16px;">
<form method="POST" action="/fiskal/pazar/izvestaj" style="display:inline;" data-full-reload>
<input type="hidden" name="_csrf" value="{{.CsrfToken}}"> <input type="hidden" name="_csrf" value="{{.CsrfToken}}">
<button type="submit" class="btn-sekundarno" style="font-size:13px;padding:8px 16px;">Preuzmi dnevni izveštaj</button> <div>
<label style="display:block;font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Od datuma</label>
<input type="date" name="od_datuma" value="{{.Danas}}" class="polje-forme" style="font-size:13px;padding:7px 10px;">
</div>
<div>
<label style="display:block;font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Do datuma</label>
<input type="date" name="do_datuma" value="{{.Danas}}" class="polje-forme" style="font-size:13px;padding:7px 10px;">
</div>
<button type="submit" class="btn-sekundarno" style="font-size:13px;padding:8px 16px;">Preuzmi izveštaj</button>
</form> </form>
<div style="display:flex;gap:10px;flex-wrap:wrap;">
<a href="/fiskal/pazar" class="btn-sekundarno" style="font-size:13px;padding:8px 16px;">Osveži</a>
{{if index .Dozvole "fiskal.zakljucenje"}} {{if index .Dozvole "fiskal.zakljucenje"}}
<form method="POST" action="/fiskal/pazar/zakljuci" style="display:inline;" data-full-reload <form method="POST" action="/fiskal/pazar/zakljuci" style="display:inline;" data-full-reload
onsubmit="return confirm('Zaključiti dan? Ovo trajno svodi brojače prometa na nulu — radnja se ne može poništiti.')"> onsubmit="return confirm('Resetovati brojače na ekranu? Ovo NIJE zakonska obaveza — izveštaj po periodu radi nezavisno od ovoga. Samo svodi prikaz Promet od poslednjeg preseka na nulu.')">
<input type="hidden" name="_csrf" value="{{.CsrfToken}}"> <input type="hidden" name="_csrf" value="{{.CsrfToken}}">
<button type="submit" style="padding:8px 16px;background:#dc2626;border:none;border-radius:8px;color:#fff;font-size:13px;font-weight:500;cursor:pointer;">Zaključi dan</button> <button type="submit" class="btn-sekundarno" style="font-size:13px;padding:8px 16px;color:var(--tekst-sporedni);">Resetuj brojače (opciono)</button>
</form> </form>
{{end}} {{end}}
</div> </div>
</div>
{{end}} {{end}}
</div> </div>