Merge feature/kw-podesavanja-kalkulacija: podstavka „Kalkulacija i PDV" + ispravka mobilnih stavki nabavke

This commit is contained in:
2026-06-14 17:41:41 +02:00
6 changed files with 52 additions and 42 deletions
+1 -1
View File
@@ -247,7 +247,7 @@ func main() {
r.Get("/admin/podesavanja/opste", h.PodesavanjaOpste) r.Get("/admin/podesavanja/opste", h.PodesavanjaOpste)
r.Get("/admin/podesavanja/izgled", h.PodesavanjaIzgled) r.Get("/admin/podesavanja/izgled", h.PodesavanjaIzgled)
r.Get("/admin/podesavanja/sistem", h.PodesavanjaSistem) r.Get("/admin/podesavanja/sistem", h.PodesavanjaSistem)
r.Get("/admin/podesavanja/pdv-stope", h.PdvStope) r.Get("/admin/podesavanja/kalkulacija-pdv", h.PdvStope)
r.With(doz("podesavanja.izmeni")).Post("/podesavanja/pdv-stope/dodaj", h.DodajPdvStopu) r.With(doz("podesavanja.izmeni")).Post("/podesavanja/pdv-stope/dodaj", h.DodajPdvStopu)
r.With(doz("podesavanja.izmeni")).Post("/podesavanja/pdv-stope/{id}/izmeni", h.IzmeniPdvStopu) r.With(doz("podesavanja.izmeni")).Post("/podesavanja/pdv-stope/{id}/izmeni", h.IzmeniPdvStopu)
r.With(doz("podesavanja.izmeni")).Post("/podesavanja/pdv-stope/{id}/aktivnost", h.PromeniAktivnostPdvStope) r.With(doz("podesavanja.izmeni")).Post("/podesavanja/pdv-stope/{id}/aktivnost", h.PromeniAktivnostPdvStope)
+16 -10
View File
@@ -19,13 +19,15 @@ var validneOznakeStope = map[string]bool{
"oslobodjeno": true, "oslobodjeno": true,
} }
// PodaciPdvStope su podaci za stranicu šifarnika PDV stopa // PodaciPdvStope su podaci za stranicu „Kalkulacija i PDV" (marža + šifarnik PDV stopa)
type PodaciPdvStope struct { type PodaciPdvStope struct {
model.PodaciStranice model.PodaciStranice
Stope []model.PdvStopa Stope []model.PdvStopa
KalkulacijaMarza string // podrazumevana marža (%) za kalkulaciju
} }
// PdvStope renderuje šifarnik PDV stopa (sve stope, uključujući arhivirane) // PdvStope renderuje stranicu „Kalkulacija i PDV": podešavanje podrazumevane marže
// i šifarnik PDV stopa (sve stope, uključujući arhivirane)
func (h *Handler) PdvStope(w http.ResponseWriter, r *http.Request) { func (h *Handler) PdvStope(w http.ResponseWriter, r *http.Request) {
if _, ok := h.zahtevajDozvolu(w, r, "podesavanja.pregled"); !ok { if _, ok := h.zahtevajDozvolu(w, r, "podesavanja.pregled"); !ok {
return return
@@ -42,9 +44,13 @@ func (h *Handler) PdvStope(w http.ResponseWriter, r *http.Request) {
} }
ps := h.popuniPodaciStranice(r, podesavanja) ps := h.popuniPodaciStranice(r, podesavanja)
ps.Stranica = "podesavanja-pdv-stope" ps.Stranica = "podesavanja-kalkulacija-pdv"
ps.NaslovStranice = "PDV stope" ps.NaslovStranice = "Kalkulacija i PDV"
h.renderujTemplate(w, "pdv_stope", PodaciPdvStope{PodaciStranice: ps, Stope: stope}) h.renderujTemplate(w, "pdv_stope", PodaciPdvStope{
PodaciStranice: ps,
Stope: stope,
KalkulacijaMarza: vrednostIliDefault(podesavanja, "kalkulacija_marza", "20"),
})
} }
// parsePdvStopuForma čita i proverava polja forme; vraća popunjenu stopu i poruku o grešci // parsePdvStopuForma čita i proverava polja forme; vraća popunjenu stopu i poruku o grešci
@@ -86,7 +92,7 @@ func (h *Handler) DodajPdvStopu(w http.ResponseWriter, r *http.Request) {
stopa, greska := parsePdvStopuForma(r) stopa, greska := parsePdvStopuForma(r)
if greska != "" { if greska != "" {
middleware.SetFlash(w, r, h.DB, "greska", greska) middleware.SetFlash(w, r, h.DB, "greska", greska)
http.Redirect(w, r, "/admin/podesavanja/pdv-stope", http.StatusSeeOther) http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv", http.StatusSeeOther)
return return
} }
if _, err := h.PdvStopeRepo.Kreiraj(r.Context(), &stopa); err != nil { if _, err := h.PdvStopeRepo.Kreiraj(r.Context(), &stopa); err != nil {
@@ -94,7 +100,7 @@ func (h *Handler) DodajPdvStopu(w http.ResponseWriter, r *http.Request) {
return return
} }
middleware.SetFlash(w, r, h.DB, "uspeh", "PDV stopa je dodata.") middleware.SetFlash(w, r, h.DB, "uspeh", "PDV stopa je dodata.")
http.Redirect(w, r, "/admin/podesavanja/pdv-stope", http.StatusSeeOther) http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv", http.StatusSeeOther)
} }
// IzmeniPdvStopu prima POST i menja postojeću stopu // IzmeniPdvStopu prima POST i menja postojeću stopu
@@ -114,7 +120,7 @@ func (h *Handler) IzmeniPdvStopu(w http.ResponseWriter, r *http.Request) {
stopa, greska := parsePdvStopuForma(r) stopa, greska := parsePdvStopuForma(r)
if greska != "" { if greska != "" {
middleware.SetFlash(w, r, h.DB, "greska", greska) middleware.SetFlash(w, r, h.DB, "greska", greska)
http.Redirect(w, r, "/admin/podesavanja/pdv-stope", http.StatusSeeOther) http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv", http.StatusSeeOther)
return return
} }
stopa.ID = id stopa.ID = id
@@ -123,7 +129,7 @@ func (h *Handler) IzmeniPdvStopu(w http.ResponseWriter, r *http.Request) {
return return
} }
middleware.SetFlash(w, r, h.DB, "uspeh", "PDV stopa je izmenjena.") middleware.SetFlash(w, r, h.DB, "uspeh", "PDV stopa je izmenjena.")
http.Redirect(w, r, "/admin/podesavanja/pdv-stope", http.StatusSeeOther) http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv", http.StatusSeeOther)
} }
// PromeniAktivnostPdvStope arhivira ili vraća stopu u upotrebu (toggle, bez brisanja) // PromeniAktivnostPdvStope arhivira ili vraća stopu u upotrebu (toggle, bez brisanja)
@@ -150,5 +156,5 @@ func (h *Handler) PromeniAktivnostPdvStope(w http.ResponseWriter, r *http.Reques
poruka = "PDV stopa je vraćena u upotrebu." poruka = "PDV stopa je vraćena u upotrebu."
} }
middleware.SetFlash(w, r, h.DB, "uspeh", poruka) middleware.SetFlash(w, r, h.DB, "uspeh", poruka)
http.Redirect(w, r, "/admin/podesavanja/pdv-stope", http.StatusSeeOther) http.Redirect(w, r, "/admin/podesavanja/kalkulacija-pdv", http.StatusSeeOther)
} }
+6 -6
View File
@@ -195,19 +195,19 @@
{{if index .Dozvole "podesavanja.pregled"}} {{if index .Dozvole "podesavanja.pregled"}}
<div> <div>
<button type="button" data-podmeni-dugme <button type="button" data-podmeni-dugme
class="nav-stavka {{if or (eq .Stranica "podesavanja") (eq .Stranica "podesavanja-opste") (eq .Stranica "podesavanja-izgled") (eq .Stranica "podesavanja-sistem") (eq .Stranica "podesavanja-pdv-stope") (eq .Stranica "dozvole")}}aktivan{{end}}" class="nav-stavka {{if or (eq .Stranica "podesavanja") (eq .Stranica "podesavanja-opste") (eq .Stranica "podesavanja-izgled") (eq .Stranica "podesavanja-sistem") (eq .Stranica "podesavanja-kalkulacija-pdv") (eq .Stranica "dozvole")}}aktivan{{end}}"
style="width:100%;background:none;border:none;cursor:pointer;"> style="width:100%;background:none;border:none;cursor:pointer;">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06-.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06-.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
<span>Podešavanja</span> <span>Podešavanja</span>
<span class="nav-strelica"> <span class="nav-strelica">
<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
style="transition:transform 0.2s;transform:{{if or (eq .Stranica "podesavanja") (eq .Stranica "podesavanja-opste") (eq .Stranica "podesavanja-izgled") (eq .Stranica "podesavanja-sistem") (eq .Stranica "podesavanja-pdv-stope") (eq .Stranica "dozvole")}}rotate(180deg){{else}}rotate(0deg){{end}}"> style="transition:transform 0.2s;transform:{{if or (eq .Stranica "podesavanja") (eq .Stranica "podesavanja-opste") (eq .Stranica "podesavanja-izgled") (eq .Stranica "podesavanja-sistem") (eq .Stranica "podesavanja-kalkulacija-pdv") (eq .Stranica "dozvole")}}rotate(180deg){{else}}rotate(0deg){{end}}">
<polyline points="6 9 12 15 18 9"/> <polyline points="6 9 12 15 18 9"/>
</svg> </svg>
</span> </span>
<span class="nav-tooltip">Podešavanja</span> <span class="nav-tooltip">Podešavanja</span>
</button> </button>
<div class="nav-podmeni {{if or (eq .Stranica "podesavanja") (eq .Stranica "podesavanja-opste") (eq .Stranica "podesavanja-izgled") (eq .Stranica "podesavanja-sistem") (eq .Stranica "podesavanja-pdv-stope") (eq .Stranica "dozvole")}}otvoren{{end}}"> <div class="nav-podmeni {{if or (eq .Stranica "podesavanja") (eq .Stranica "podesavanja-opste") (eq .Stranica "podesavanja-izgled") (eq .Stranica "podesavanja-sistem") (eq .Stranica "podesavanja-kalkulacija-pdv") (eq .Stranica "dozvole")}}otvoren{{end}}">
<a href="/admin/podesavanja/opste" class="nav-stavka nav-podstavka {{if eq .Stranica "podesavanja-opste"}}aktivan{{end}}"> <a href="/admin/podesavanja/opste" class="nav-stavka nav-podstavka {{if eq .Stranica "podesavanja-opste"}}aktivan{{end}}">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21h18"/><path d="M5 21V7a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v14"/><path d="M9 21v-5h6v5"/><path d="M9 9h.01M15 9h.01M9 13h.01M15 13h.01"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21h18"/><path d="M5 21V7a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v14"/><path d="M9 21v-5h6v5"/><path d="M9 9h.01M15 9h.01M9 13h.01M15 13h.01"/></svg>
<span>Opšte</span> <span>Opšte</span>
@@ -223,10 +223,10 @@
<span>Sistem</span> <span>Sistem</span>
<span class="nav-tooltip">Sistem</span> <span class="nav-tooltip">Sistem</span>
</a> </a>
<a href="/admin/podesavanja/pdv-stope" class="nav-stavka nav-podstavka {{if eq .Stranica "podesavanja-pdv-stope"}}aktivan{{end}}"> <a href="/admin/podesavanja/kalkulacija-pdv" class="nav-stavka nav-podstavka {{if eq .Stranica "podesavanja-kalkulacija-pdv"}}aktivan{{end}}">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="5" x2="5" y2="19"/><circle cx="6.5" cy="6.5" r="2.5"/><circle cx="17.5" cy="17.5" r="2.5"/></svg> <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="5" x2="5" y2="19"/><circle cx="6.5" cy="6.5" r="2.5"/><circle cx="17.5" cy="17.5" r="2.5"/></svg>
<span>PDV stope</span> <span>Kalkulacija i PDV</span>
<span class="nav-tooltip">PDV stope</span> <span class="nav-tooltip">Kalkulacija i PDV</span>
</a> </a>
{{if or (eq .KorisnikUloga "superadmin") (eq .KorisnikUloga "admin")}} {{if or (eq .KorisnikUloga "superadmin") (eq .KorisnikUloga "admin")}}
<a href="/admin/dozvole" class="nav-stavka nav-podstavka {{if eq .Stranica "dozvole"}}aktivan{{end}}"> <a href="/admin/dozvole" class="nav-stavka nav-podstavka {{if eq .Stranica "dozvole"}}aktivan{{end}}">
+2 -2
View File
@@ -151,8 +151,8 @@
</table> </table>
</div> </div>
<!-- mobilne kartice stavki --> <!-- mobilne kartice stavki (prikaz/skrivanje vodi .stavke-kartice u main.css) -->
<div class="stavke-kartice" style="display:none;flex-direction:column;gap:10px;"> <div class="stavke-kartice">
<template x-for="(stavka, i) in stavke" :key="i"> <template x-for="(stavka, i) in stavke" :key="i">
<div style="border:0.5px solid var(--ivica);border-radius:8px;padding:12px;"> <div style="border:0.5px solid var(--ivica);border-radius:8px;padding:12px;">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;"> <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">
+26 -1
View File
@@ -1,10 +1,35 @@
{{template "base" .}} {{template "base" .}}
{{define "naslov"}}Podešavanja — PDV stope — NTech{{end}} {{define "naslov"}}Podešavanja — Kalkulacija i PDV — NTech{{end}}
{{define "sadrzaj"}} {{define "sadrzaj"}}
<div class="stranica-stack" style="width:100%;max-width:100%;"> <div class="stranica-stack" style="width:100%;max-width:100%;">
<!-- kalkulacija: podrazumevana marža za formiranje prodajne cene pri nabavci -->
<div class="kartica animiraj" style="margin-bottom:16px;">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="var(--sb-akcent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Kalkulacija</span>
</div>
<form method="POST" action="/podesavanja/sacuvaj">
<input type="hidden" name="_next" value="/admin/podesavanja/kalkulacija-pdv">
<div style="display:flex;gap:16px;flex-wrap:wrap;align-items:flex-end;">
<div>
<label for="kalkulacija_marza" class="polje-labela">Podrazumevana marža (%)</label>
<input type="number" id="kalkulacija_marza" name="kalkulacija_marza" min="0" max="1000" step="0.01" value="{{.KalkulacijaMarza}}"
style="width:140px;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:14px;">
</div>
<button type="submit"
style="padding:9px 18px;background:var(--sb-akcent);border:none;border-radius:8px;color:#fff;font-size:13px;font-weight:500;cursor:pointer;">
Sačuvaj
</button>
</div>
<div style="font-size:12px;color:var(--tekst-sporedni);margin-top:8px;">
Početna marža za formiranje prodajne cene pri nabavci. Po stavci je možeš promeniti.
</div>
</form>
</div>
<!-- postojeće stope: svaki red je forma za izmenu + zasebna forma za arhiviranje --> <!-- postojeće stope: svaki red je forma za izmenu + zasebna forma za arhiviranje -->
<div class="kartica animiraj" style="margin-bottom:16px;"> <div class="kartica animiraj" style="margin-bottom:16px;">
<div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);"> <div style="display:flex;align-items:center;gap:10px;margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">
@@ -56,27 +56,6 @@
</form> </form>
</div> </div>
<div style="margin-top:16px;border-top:0.5px solid var(--ivica);padding-top:16px;">
<div style="font-size:13px;font-weight:500;color:var(--tekst-glavni);margin-bottom:10px;">Kalkulacija</div>
<form method="POST" action="/podesavanja/sacuvaj">
<input type="hidden" name="_next" value="/admin/podesavanja/sistem">
<div style="display:flex;gap:16px;flex-wrap:wrap;align-items:flex-end;">
<div>
<label for="kalkulacija_marza" class="polje-labela">Podrazumevana marža (%)</label>
<input type="number" id="kalkulacija_marza" name="kalkulacija_marza" min="0" max="1000" step="0.01" value="{{.KalkulacijaMarza}}"
style="width:140px;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:14px;">
</div>
<button type="submit"
style="padding:9px 18px;background:var(--sb-akcent);border:none;border-radius:8px;color:#fff;font-size:13px;font-weight:500;cursor:pointer;">
Sačuvaj
</button>
</div>
<div style="font-size:12px;color:var(--tekst-sporedni);margin-top:8px;">
Početna marža za formiranje prodajne cene pri nabavci. Po stavci je možeš promeniti.
</div>
</form>
</div>
<!-- panel sa listom backupa --> <!-- panel sa listom backupa -->
<div id="backup-panel" style="display:none;margin-top:16px;border-top:0.5px solid var(--ivica);padding-top:16px;"> <div id="backup-panel" style="display:none;margin-top:16px;border-top:0.5px solid var(--ivica);padding-top:16px;">
<div style="font-size:13px;font-weight:500;color:var(--tekst-glavni);margin-bottom:10px;">Dostupne rezervne kopije</div> <div style="font-size:13px;font-weight:500;color:var(--tekst-glavni);margin-bottom:10px;">Dostupne rezervne kopije</div>