PDV KIR: zaštita od duplog upisa dnevnog pazara maloprodaje
SacuvajDnevniPazarKir je upisivao ručni zbirni KIR zapis za dati dan bez provere da li već postoji — za razliku od auto-KIR-a iz pojedinačnih faktura (koji ima PostojiZaIzvor zaštitu), ovde ništa nije sprečavalo duplo klikanje/ponovni unos istog dana, što bi duplo precenilo PDV osnov u zvaničnoj evidenciji. Broj dokumenta (FISK-YYYYMMDD) je već deterministički po danu, pa se koristi kao prirodni ključ za proveru. Forma sad prikazuje upozorenje i sakriva dugme za upis ako je dan već zaveden.
This commit is contained in:
@@ -70,6 +70,9 @@ type PdvKirRepository interface {
|
|||||||
ObrisiPoIzvoru(ctx context.Context, izvor string, izvorID int64) error
|
ObrisiPoIzvoru(ctx context.Context, izvor string, izvorID int64) error
|
||||||
// PostojiZaIzvor vraća true ako postoji bar jedan zapis za dati izvor i izvorID
|
// PostojiZaIzvor vraća true ako postoji bar jedan zapis za dati izvor i izvorID
|
||||||
PostojiZaIzvor(ctx context.Context, izvor string, izvorID int64) (bool, error)
|
PostojiZaIzvor(ctx context.Context, izvor string, izvorID int64) (bool, error)
|
||||||
|
// PostojiPoBrojuDokumenta vraća true ako postoji zapis sa datim brojem dokumenta
|
||||||
|
// (npr. zaštita od duplog unosa dnevnog pazara "FISK-YYYYMMDD")
|
||||||
|
PostojiPoBrojuDokumenta(ctx context.Context, brojDokumenta string) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PdvKprRepository definiše operacije nad knjigom primljenih računa (KPR)
|
// PdvKprRepository definiše operacije nad knjigom primljenih računa (KPR)
|
||||||
|
|||||||
@@ -148,6 +148,18 @@ func (r *PdvKirRepo) PostojiZaIzvor(ctx context.Context, izvor string, izvorID i
|
|||||||
return n > 0, nil
|
return n > 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PostojiPoBrojuDokumenta vraća true ako postoji KIR zapis sa datim brojem dokumenta
|
||||||
|
func (r *PdvKirRepo) PostojiPoBrojuDokumenta(ctx context.Context, brojDokumenta string) (bool, error) {
|
||||||
|
var n int
|
||||||
|
err := r.db.QueryRowContext(ctx,
|
||||||
|
"SELECT COUNT(*) FROM pdv_kir WHERE broj_dokumenta = ?", brojDokumenta,
|
||||||
|
).Scan(&n)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("ntech: PdvKirRepo.PostojiPoBrojuDokumenta: %w", err)
|
||||||
|
}
|
||||||
|
return n > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
// izvorIliRucno vraća izvor ili podrazumevano „rucno" (izvor kolona je NOT NULL)
|
// izvorIliRucno vraća izvor ili podrazumevano „rucno" (izvor kolona je NOT NULL)
|
||||||
func izvorIliRucno(izvor string) string {
|
func izvorIliRucno(izvor string) string {
|
||||||
if izvor == "" {
|
if izvor == "" {
|
||||||
|
|||||||
@@ -251,9 +251,10 @@ func (h *Handler) KirBackfillProdaje(w http.ResponseWriter, r *http.Request) {
|
|||||||
// PodaciDnevniPazar su podaci za formu zbirnog KIR unosa (maloprodaja)
|
// PodaciDnevniPazar su podaci za formu zbirnog KIR unosa (maloprodaja)
|
||||||
type PodaciDnevniPazar struct {
|
type PodaciDnevniPazar struct {
|
||||||
model.PodaciStranice
|
model.PodaciStranice
|
||||||
Datum string
|
Datum string
|
||||||
Promet model.DnevniPrometKir
|
Promet model.DnevniPrometKir
|
||||||
Ukupno float64
|
Ukupno float64
|
||||||
|
VecUpisano bool // true: za ovaj datum već postoji zbirni KIR zapis
|
||||||
}
|
}
|
||||||
|
|
||||||
// DnevniPazarKir prikazuje formu za potvrdu zbirnog KIR unosa za izabrani datum.
|
// DnevniPazarKir prikazuje formu za potvrdu zbirnog KIR unosa za izabrani datum.
|
||||||
@@ -281,6 +282,11 @@ func (h *Handler) DnevniPazarKir(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
ukupno := math.Round((promet.OsnovicaOpsta+promet.PdvOpsta+promet.OsnovicaPosebna+promet.PdvPosebna)*100) / 100
|
ukupno := math.Round((promet.OsnovicaOpsta+promet.PdvOpsta+promet.OsnovicaPosebna+promet.PdvPosebna)*100) / 100
|
||||||
|
|
||||||
|
var vecUpisano bool
|
||||||
|
if brojDok, e := brojDokumentaDnevniPazar(datum); e == nil {
|
||||||
|
vecUpisano, _ = h.PdvKirRepo.PostojiPoBrojuDokumenta(r.Context(), brojDok)
|
||||||
|
}
|
||||||
|
|
||||||
ps := h.popuniPodaciStranice(r, podesavanja)
|
ps := h.popuniPodaciStranice(r, podesavanja)
|
||||||
ps.Stranica = "pdv-kir"
|
ps.Stranica = "pdv-kir"
|
||||||
ps.NaslovStranice = "Dnevni pazar — KIR unos"
|
ps.NaslovStranice = "Dnevni pazar — KIR unos"
|
||||||
@@ -289,9 +295,20 @@ func (h *Handler) DnevniPazarKir(w http.ResponseWriter, r *http.Request) {
|
|||||||
Datum: datum,
|
Datum: datum,
|
||||||
Promet: promet,
|
Promet: promet,
|
||||||
Ukupno: ukupno,
|
Ukupno: ukupno,
|
||||||
|
VecUpisano: vecUpisano,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// brojDokumentaDnevniPazar gradi deterministički broj dokumenta za zbirni KIR unos
|
||||||
|
// dnevnog pazara ("FISK-YYYYMMDD") — koristi se i za upis i za proveru duplikata.
|
||||||
|
func brojDokumentaDnevniPazar(datum string) (string, error) {
|
||||||
|
d, err := time.Parse("2006-01-02", datum)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("FISK-%s", d.Format("20060102")), nil
|
||||||
|
}
|
||||||
|
|
||||||
// SacuvajDnevniPazarKir prima POST i upisuje zbirni KIR zapis za dnevni pazar.
|
// SacuvajDnevniPazarKir prima POST i upisuje zbirni KIR zapis za dnevni pazar.
|
||||||
func (h *Handler) SacuvajDnevniPazarKir(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) SacuvajDnevniPazarKir(w http.ResponseWriter, r *http.Request) {
|
||||||
if _, ok := h.zahtevajDozvolu(w, r, "pdv.dodaj"); !ok {
|
if _, ok := h.zahtevajDozvolu(w, r, "pdv.dodaj"); !ok {
|
||||||
@@ -302,17 +319,25 @@ func (h *Handler) SacuvajDnevniPazarKir(w http.ResponseWriter, r *http.Request)
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
datum, e1 := time.Parse("2006-01-02", strings.TrimSpace(r.FormValue("datum")))
|
datumStr := strings.TrimSpace(r.FormValue("datum"))
|
||||||
|
datum, e1 := time.Parse("2006-01-02", datumStr)
|
||||||
if e1 != nil {
|
if e1 != nil {
|
||||||
middleware.SetFlash(w, r, h.DB, "greska", "Datum je obavezan.")
|
middleware.SetFlash(w, r, h.DB, "greska", "Datum je obavezan.")
|
||||||
http.Redirect(w, r, "/pdv/kir/dnevni-pazar", http.StatusSeeOther)
|
http.Redirect(w, r, "/pdv/kir/dnevni-pazar", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
brojDokumenta := fmt.Sprintf("FISK-%s", datum.Format("20060102"))
|
||||||
|
if postoji, _ := h.PdvKirRepo.PostojiPoBrojuDokumenta(r.Context(), brojDokumenta); postoji {
|
||||||
|
middleware.SetFlash(w, r, h.DB, "greska", fmt.Sprintf("Dnevni pazar za %s je već upisan u KIR.", datum.Format("02.01.2006.")))
|
||||||
|
http.Redirect(w, r, "/pdv/kir/dnevni-pazar?datum="+datumStr, http.StatusSeeOther)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
z := model.PdvKir{
|
z := model.PdvKir{
|
||||||
DatumPrometa: datum,
|
DatumPrometa: datum,
|
||||||
DatumKnjizenja: datum,
|
DatumKnjizenja: datum,
|
||||||
BrojDokumenta: fmt.Sprintf("FISK-%s", datum.Format("20060102")),
|
BrojDokumenta: brojDokumenta,
|
||||||
KupacNaziv: "Promet fizičkim licima",
|
KupacNaziv: "Promet fizičkim licima",
|
||||||
OsnovicaOpsta: parsiraIznos(r.FormValue("osnovica_opsta")),
|
OsnovicaOpsta: parsiraIznos(r.FormValue("osnovica_opsta")),
|
||||||
PdvOpsta: parsiraIznos(r.FormValue("pdv_opsta")),
|
PdvOpsta: parsiraIznos(r.FormValue("pdv_opsta")),
|
||||||
|
|||||||
@@ -78,7 +78,16 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{if gt .Promet.BrojNaloga 0}}
|
{{if .VecUpisano}}
|
||||||
|
<div class="kartica animiraj" style="margin-bottom:16px;border:1.5px solid #f59e0b;background:color-mix(in srgb,#fef3c7 60%,var(--pozadina));">
|
||||||
|
<div style="padding:16px;">
|
||||||
|
<div style="font-size:14px;font-weight:600;color:#92400e;margin-bottom:4px;">⚠ Već upisano</div>
|
||||||
|
<div style="font-size:13px;color:#78350f;">Dnevni pazar za {{.Datum}} je već upisan u KIR. Ponovni upis bi duplirao promet za ovaj dan.</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
{{if and (gt .Promet.BrojNaloga 0) (not .VecUpisano)}}
|
||||||
<!-- forma za upis -->
|
<!-- forma za upis -->
|
||||||
<form method="POST" action="/pdv/kir/dnevni-pazar">
|
<form method="POST" action="/pdv/kir/dnevni-pazar">
|
||||||
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
|
||||||
|
|||||||
Reference in New Issue
Block a user