Podešavanja: matični broj, poslovna jedinica i kompaktniji raspored

Opšte → Firma: dodato polje Matični broj (validacija 8 cifara) i nova grupa
Poslovna jedinica (naziv, oznaka, opština, grad) za fiskalizaciju. Polja firme
upakovana u parove (forma-grid-3 klasa, responsive).
This commit is contained in:
2026-06-21 19:07:07 +02:00
parent 3a4b9f2c1f
commit fa2b3b2b12
3 changed files with 149 additions and 31 deletions
+62 -11
View File
@@ -23,11 +23,17 @@ import (
// PodaciPodesavanja su podaci za stranicu podešavanja
type PodaciPodesavanja struct {
model.PodaciStranice
NazivFirme string
Podnazlov string
Adresa string
Telefon string
PIB string
NazivFirme string
Podnazlov string
Adresa string
Telefon string
PIB string
MaticniBroj string
// poslovna jedinica — za fiskalizaciju (oznaka PJ se šalje uz svaki račun)
NazivPJ string
OznakaPJ string
Opstina string
Grad string
LogoPutanja string
TopbarLogoSlika bool
// profil firme — pravni/poreski status (Faza 0); određuje koji se zakonski moduli pale
@@ -83,6 +89,11 @@ func (h *Handler) Podesavanja(w http.ResponseWriter, r *http.Request) {
Adresa: podesavanja["adresa"],
Telefon: podesavanja["telefon"],
PIB: podesavanja["pib"],
MaticniBroj: podesavanja["maticni_broj"],
NazivPJ: podesavanja["poslovna_jedinica_naziv"],
OznakaPJ: podesavanja["poslovna_jedinica_oznaka"],
Opstina: podesavanja["opstina"],
Grad: podesavanja["grad"],
LogoPutanja: podesavanja["logo_putanja"],
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
@@ -225,6 +236,24 @@ func kopiraFajl(izvor, odrediste string) error {
return err
}
// validirajMaticniBroj proverava da li uneti matični broj ima tačno 8 cifara.
// Prazan broj je dozvoljen (polje nije obavezno). Vraća srpsku poruku o grešci
// ili prazan string ako je broj ispravan.
func validirajMaticniBroj(broj string) string {
if broj == "" {
return ""
}
if len(broj) != 8 {
return "Matični broj mora imati tačno 8 cifara."
}
for _, c := range broj {
if c < '0' || c > '9' {
return "Matični broj sme sadržati samo cifre."
}
}
return ""
}
// SacuvajPodesavanja prima POST i čuva podešavanja u bazu
func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) {
if _, ok := h.zahtevajDozvolu(w, r, "podesavanja.izmeni"); !ok {
@@ -241,13 +270,30 @@ func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) {
topbarLogoSlika = "1"
}
// matični broj — 8 cifara; prazno je dozvoljeno (polje nije obavezno)
maticniBroj := strings.TrimSpace(r.FormValue("maticni_broj"))
if greska := validirajMaticniBroj(maticniBroj); greska != "" {
middleware.SetFlash(w, r, h.DB, "greska", greska)
sledeci := "/podesavanja"
if r.FormValue("_next") == "/admin/podesavanja/opste" {
sledeci = "/admin/podesavanja/opste"
}
http.Redirect(w, r, sledeci, http.StatusSeeOther)
return
}
polja := map[string]string{
"naziv_firme": r.FormValue("naziv_firme"),
"podnazlov": r.FormValue("podnazlov"),
"adresa": r.FormValue("adresa"),
"telefon": r.FormValue("telefon"),
"pib": r.FormValue("pib"),
"topbar_logo_slika": topbarLogoSlika,
"naziv_firme": r.FormValue("naziv_firme"),
"podnazlov": r.FormValue("podnazlov"),
"adresa": r.FormValue("adresa"),
"telefon": r.FormValue("telefon"),
"pib": r.FormValue("pib"),
"maticni_broj": maticniBroj,
"poslovna_jedinica_naziv": r.FormValue("poslovna_jedinica_naziv"),
"poslovna_jedinica_oznaka": r.FormValue("poslovna_jedinica_oznaka"),
"opstina": r.FormValue("opstina"),
"grad": r.FormValue("grad"),
"topbar_logo_slika": topbarLogoSlika,
// profil firme (Faza 0) — radio dugmad uvek šalju vrednost, pa se uredno čuvaju
"firma_pravni_oblik": r.FormValue("firma_pravni_oblik"),
"firma_pdv_obveznik": r.FormValue("firma_pdv_obveznik"),
@@ -695,6 +741,11 @@ func (h *Handler) napuniPodaciPodesavanja(r *http.Request, naslov string) (Podac
Adresa: podesavanja["adresa"],
Telefon: podesavanja["telefon"],
PIB: podesavanja["pib"],
MaticniBroj: podesavanja["maticni_broj"],
NazivPJ: podesavanja["poslovna_jedinica_naziv"],
OznakaPJ: podesavanja["poslovna_jedinica_oznaka"],
Opstina: podesavanja["opstina"],
Grad: podesavanja["grad"],
LogoPutanja: podesavanja["logo_putanja"],
TopbarLogoSlika: podesavanja["topbar_logo_slika"] == "1",
FirmaPravniOblik: vrednostIliDefault(podesavanja, "firma_pravni_oblik", "pausalac"),
+6
View File
@@ -873,6 +873,11 @@ select {
grid-template-columns: 1fr 1fr;
gap: 12px;
}
.forma-grid-3 {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 12px;
}
.forma-grid-4 {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
@@ -1000,6 +1005,7 @@ select {
/* forme */
.forma-grid-2 { grid-template-columns: 1fr !important; }
.forma-grid-3 { grid-template-columns: 1fr !important; }
/* magacin traka */
.magacin-traka { flex-direction: column; align-items: stretch; }
+81 -20
View File
@@ -60,40 +60,49 @@
</div>
<div class="kolona" style="gap:14px;">
<div>
<label class="polje-labela">Naziv firme</label>
<input type="text" name="naziv_firme" value="{{.NazivFirme}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. NTech">
<div class="forma-grid-2">
<div>
<label class="polje-labela">Naziv firme</label>
<input type="text" name="naziv_firme" value="{{.NazivFirme}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. NTech">
</div>
<div>
<label class="polje-labela">Podnazlov</label>
<input type="text" name="podnazlov" value="{{.Podnazlov}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. Servis računara">
</div>
</div>
<div>
<label class="polje-labela">Podnazlov</label>
<input type="text" name="podnazlov" value="{{.Podnazlov}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. Servis računara">
</div>
<div>
<label class="polje-labela">Adresa</label>
<input type="text" name="adresa" value="{{.Adresa}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. Ulica i broj, Grad">
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;">
<div class="forma-grid-2">
<div>
<label class="polje-labela">Adresa</label>
<input type="text" name="adresa" value="{{.Adresa}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. Ulica i broj, Grad">
</div>
<div>
<label class="polje-labela">Telefon</label>
<input type="text" name="telefon" value="{{.Telefon}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. +381 11 123 4567">
</div>
</div>
<div class="forma-grid-2">
<div>
<label class="polje-labela">PIB</label>
<input type="text" name="pib" value="{{.PIB}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. 123456789">
</div>
<div>
<label class="polje-labela">Matični broj</label>
<input type="text" name="maticni_broj" value="{{.MaticniBroj}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. 12345678">
</div>
</div>
<div style="display:flex;align-items:center;justify-content:space-between;padding:10px 14px;border:0.5px solid var(--ivica);border-radius:8px;">
@@ -119,6 +128,58 @@
</div>
</form>
<!-- poslovna jedinica: podaci koje fiskalizacija (SUF) traži uz svaki račun -->
<form method="POST" action="/podesavanja/sacuvaj">
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">
<input type="hidden" name="_next" value="/admin/podesavanja/opste">
<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"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></svg>
<span style="font-size:15px;font-weight:500;color:var(--tekst-glavni);">Poslovna jedinica</span>
</div>
<div class="kolona" style="gap:14px;">
<div class="forma-grid-2">
<div>
<label class="polje-labela">Naziv poslovne jedinice</label>
<input type="text" name="poslovna_jedinica_naziv" value="{{.NazivPJ}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. Prodavnica centar">
</div>
<div>
<label class="polje-labela">Oznaka poslovne jedinice</label>
<input type="text" name="poslovna_jedinica_oznaka" value="{{.OznakaPJ}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. BU-001">
</div>
</div>
<div class="forma-grid-2">
<div>
<label class="polje-labela">Opština</label>
<input type="text" name="opstina" value="{{.Opstina}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. Savski venac">
</div>
<div>
<label class="polje-labela">Grad</label>
<input type="text" name="grad" value="{{.Grad}}"
style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;"
placeholder="npr. Beograd">
</div>
</div>
</div>
<div style="display:flex;justify-content:flex-end;margin-top:20px;">
<button type="submit"
style="background:var(--sb-akcent);color:#fff;border:none;padding:10px 24px;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;transition:opacity 0.2s;"
onmouseover="this.style.opacity='0.85'" onmouseout="this.style.opacity='1'">
Sačuvaj poslovnu jedinicu
</button>
</div>
</div>
</form>
<!-- profil firme: pravni/poreski status (Faza 0) — određuje koji se zakonski moduli pale -->
<form method="POST" action="/podesavanja/sacuvaj">
<input type="hidden" name="_csrf" value="{{.CsrfToken}}">