PDV: profil firme blokira nemoguće kombinacije, stope iz šifarnika umesto hardkoda

- Podešavanja: nemoguća kombinacija (samo evidencija + PDV/fiskalizacija=da)
  se sad odbija i na serveru, ne samo u brauzeru (validirajProfilFirme).
- Servis: fiskalni račun/refund više ne iskazuje PDV kad firma nije PDV
  obveznik (stavkeFiskalnogServisa nulira stopu, isto kao Prodaja).
- Avans: uklonjena hardkodovana konstanta 20% PDV — stopa se sad uzima iz
  šifarnika (Podešavanja → Kalkulacija i PDV) ili je 0 kad firma nije
  obveznik.
- fiskal.NapraviZahtev: uklonjen fallback koji je prepisivao namerno
  postavljenu 0% stopu nazad na 20% (poništavao je zaštitu Prodaje za
  ne-PDV firme).
- Forme (usluge, artikli) i validacija PDV stope u prodaji sad koriste
  šifarnik umesto literala 20 / 0,10,20.

Detaljna pravna analiza i status svake stavke u docs/Greške.md (van git-a).
This commit is contained in:
2026-07-03 01:50:57 +02:00
parent 85a2ee1b64
commit 524c6de6ba
11 changed files with 174 additions and 57 deletions
+8 -6
View File
@@ -62,7 +62,7 @@ func (h *Handler) NoviArtikal(w http.ResponseWriter, r *http.Request) {
PodaciStranice: ps,
Kategorije: kategorije,
Dobavljaci: dobavljaci,
Artikal: model.Artikal{Sifra: predlogSifre, Tip: tip, JedinicaMere: "kom", PdvStopa: 20.0},
Artikal: model.Artikal{Sifra: predlogSifre, Tip: tip, JedinicaMere: "kom", PdvStopa: h.podrazumevanaPdvStopa(r.Context())},
Izmena: false,
})
}
@@ -79,7 +79,7 @@ func (h *Handler) SacuvajArtikal(w http.ResponseWriter, r *http.Request) {
return
}
artikal, greska := parseFormuArtikla(r)
artikal, greska := parseFormuArtikla(r, h.podrazumevanaPdvStopa(r.Context()))
if greska != "" {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
kategorije, _ := h.KategorijeRepo.Lista(r.Context())
@@ -210,7 +210,7 @@ func (h *Handler) SacuvajIzmenuArtikla(w http.ResponseWriter, r *http.Request) {
return
}
artikal, greska := parseFormuArtikla(r)
artikal, greska := parseFormuArtikla(r, h.podrazumevanaPdvStopa(r.Context()))
if greska != "" {
podesavanja, _ := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
kategorije, _ := h.KategorijeRepo.Lista(r.Context())
@@ -325,7 +325,9 @@ func citajDobavljaceForme(r *http.Request) []int64 {
}
// parseFormuArtikla čita polja iz forme i vraća artikal i eventualnu grešku
func parseFormuArtikla(r *http.Request) (model.Artikal, string) {
// podrazumevanaStopa je opšta PDV stopa iz šifarnika (v. Handler.podrazumevanaPdvStopa) —
// koristi se samo kad korisnik nije uneo stopu, nikad hardkodovana vrednost.
func parseFormuArtikla(r *http.Request, podrazumevanaStopa float64) (model.Artikal, string) {
naziv := r.FormValue("naziv")
if naziv == "" {
return model.Artikal{}, "Naziv artikla je obavezan."
@@ -389,8 +391,8 @@ func parseFormuArtikla(r *http.Request) (model.Artikal, string) {
artikal.NabavnaCena = v
}
// PDV stopa — podrazumevano 20%
pdvStopa := 20.0
// PDV stopa — podrazumevano opšta stopa iz šifarnika
pdvStopa := podrazumevanaStopa
if p := r.FormValue("pdv_stopa"); p != "" {
if v, err := strconv.ParseFloat(p, 64); err == nil && v >= 0 {
pdvStopa = v