Servis: garancija kao trajanje u danima + Tehničar → Serviser

- Garancija se u detaljima prikazuje kao trajanje na srpskom (npr. „1 mesec i 15 dana",
„2 meseca", „20 dana"); helper garancijaTekst + srpska deklinacija
- Nova kolona garancija_dana (trajanje od završetka); garancija_do ostaje za fazu Završeno
- Modal: textbox „Trajanje garancije (dana)" umesto kalendara; Podrazumevano i Bez garancije
- Nova ruta/handler AzurirajGarancijaDana; forma izmene čuva trajanje (ne briše)
- Podešavanja → Servis: rok garancije prebačen iz meseci u dane (servis_garancija_dana, default 60)
- „Garancija do" uklonjena iz forme Izmeni nalog (kod arhiviran u docs/);
SacuvajIzmenaNaloga čuva garanciju iz baze da je auto-snimanje ne pregazi
- Prikazni tekst „Tehničar" → „Serviser" svuda (identifikatori Tehnicar/tehnicar_id netaknuti)
This commit is contained in:
2026-06-23 02:35:49 +02:00
parent c3d42ec821
commit 7465b7e40f
15 changed files with 189 additions and 83 deletions
+1
View File
@@ -360,6 +360,7 @@ func main() {
r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}/predracun", h.StampaPredracuna) r.With(ntechmw.RequireDozvola(h.DozvoleRepo.ImaDozvolu, "servis.pregled")).Get("/servis/{id}/predracun", h.StampaPredracuna)
r.With(doz("servis.izmeni")).Post("/servis/{id}/status", h.PromeniStatus) r.With(doz("servis.izmeni")).Post("/servis/{id}/status", h.PromeniStatus)
r.With(doz("servis.izmeni")).Post("/servis/{id}/garancija", h.AzurirajGaranciju) r.With(doz("servis.izmeni")).Post("/servis/{id}/garancija", h.AzurirajGaranciju)
r.With(doz("servis.izmeni")).Post("/servis/{id}/garancija-dana", h.AzurirajGarancijaDana)
r.With(doz("servis.izmeni")).Post("/servis/{id}/predvidjen-datum", h.AzurirajPredvidjenDatum) r.With(doz("servis.izmeni")).Post("/servis/{id}/predvidjen-datum", h.AzurirajPredvidjenDatum)
r.With(doz("servis.izmeni")).Post("/servis/{id}/tehnicar", h.AzurirajTehnicar) r.With(doz("servis.izmeni")).Post("/servis/{id}/tehnicar", h.AzurirajTehnicar)
r.With(doz("servis.izmeni")).Post("/servis/{id}/napomena-klijentu", h.SacuvajNapomenuKlijentu) r.With(doz("servis.izmeni")).Post("/servis/{id}/napomena-klijentu", h.SacuvajNapomenuKlijentu)
+1
View File
@@ -192,6 +192,7 @@ type ServisRepository interface {
Izmeni(ctx context.Context, n *model.ServisniNalog) error Izmeni(ctx context.Context, n *model.ServisniNalog) error
AzurirajStatus(ctx context.Context, id int64, status string) error AzurirajStatus(ctx context.Context, id int64, status string) error
AzurirajGaranciju(ctx context.Context, id int64, garancijaDo *time.Time) error AzurirajGaranciju(ctx context.Context, id int64, garancijaDo *time.Time) error
AzurirajGarancijaDana(ctx context.Context, id int64, dana *int) error
AzurirajPredvidjenDatum(ctx context.Context, id int64, predvidjenDatum *time.Time) error AzurirajPredvidjenDatum(ctx context.Context, id int64, predvidjenDatum *time.Time) error
AzurirajTehnicar(ctx context.Context, id int64, tehnicarID *int64) error AzurirajTehnicar(ctx context.Context, id int64, tehnicarID *int64) error
AzurirajNapomenuKlijentu(ctx context.Context, id int64, tekst string) error AzurirajNapomenuKlijentu(ctx context.Context, id int64, tekst string) error
+28 -10
View File
@@ -57,7 +57,7 @@ func (r *ServisRepo) Lista(ctx context.Context, pretraga, status string) ([]mode
SELECT SELECT
sn.id, sn.klijent_id, sn.tehnicar_id, sn.broj_naloga, sn.uredjaj, sn.serijski_broj, sn.id, sn.klijent_id, sn.tehnicar_id, sn.broj_naloga, sn.uredjaj, sn.serijski_broj,
sn.opis_kvara, sn.trazene_nadogradnje, sn.status, sn.cena_od, sn.cena_do, sn.cena_konacna, sn.opis_kvara, sn.trazene_nadogradnje, sn.status, sn.cena_od, sn.cena_do, sn.cena_konacna,
sn.avans, sn.napomena, sn.garancija_do, sn.datum_prijema, sn.datum_zavrsetka, sn.predvidjen_datum, sn.avans, sn.napomena, sn.garancija_do, sn.garancija_dana, sn.datum_prijema, sn.datum_zavrsetka, sn.predvidjen_datum,
sn.ostecenja, sn.pin_uredjaja, sn.pribor, sn.napomena_klijentu, sn.javni_token, sn.ostecenja, sn.pin_uredjaja, sn.pribor, sn.napomena_klijentu, sn.javni_token,
COALESCE(kp.naziv, '') AS klijent_naziv COALESCE(kp.naziv, '') AS klijent_naziv
FROM servisni_nalozi sn FROM servisni_nalozi sn
@@ -104,7 +104,7 @@ func (r *ServisRepo) DohvatiID(ctx context.Context, id int64) (*model.ServisniNa
SELECT SELECT
id, klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj, id, klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj,
opis_kvara, trazene_nadogradnje, status, cena_od, cena_do, cena_konacna, opis_kvara, trazene_nadogradnje, status, cena_od, cena_do, cena_konacna,
avans, napomena, garancija_do, datum_prijema, datum_zavrsetka, predvidjen_datum, avans, napomena, garancija_do, garancija_dana, datum_prijema, datum_zavrsetka, predvidjen_datum,
ostecenja, pin_uredjaja, pribor, napomena_klijentu, javni_token ostecenja, pin_uredjaja, pribor, napomena_klijentu, javni_token
FROM servisni_nalozi WHERE id = ?`, id) FROM servisni_nalozi WHERE id = ?`, id)
@@ -127,14 +127,14 @@ func (r *ServisRepo) Kreiraj(ctx context.Context, n *model.ServisniNalog) (int64
rezultat, err := r.db.ExecContext(ctx, ` rezultat, err := r.db.ExecContext(ctx, `
INSERT INTO servisni_nalozi INSERT INTO servisni_nalozi
(klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj, opis_kvara, trazene_nadogradnje, (klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj, opis_kvara, trazene_nadogradnje,
status, cena_od, cena_do, cena_konacna, avans, napomena, garancija_do, datum_zavrsetka, predvidjen_datum, status, cena_od, cena_do, cena_konacna, avans, napomena, garancija_do, garancija_dana, datum_zavrsetka, predvidjen_datum,
ostecenja, pin_uredjaja, pribor, datum_prijema, javni_token) ostecenja, pin_uredjaja, pribor, datum_prijema, javni_token)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
nullInt64(n.KlijentID), nullInt64(n.TehnicarID), n.BrojNaloga, n.Uredjaj, nullInt64(n.KlijentID), nullInt64(n.TehnicarID), n.BrojNaloga, n.Uredjaj,
nullString(n.SerijskiBroj), n.OpisKvara, n.TrazeneNadogradnje, n.Status, nullString(n.SerijskiBroj), n.OpisKvara, n.TrazeneNadogradnje, n.Status,
nullFloat64(n.CenaOd), nullFloat64(n.CenaDo), nullFloat64(n.CenaKonacna), nullFloat64(n.CenaOd), nullFloat64(n.CenaDo), nullFloat64(n.CenaKonacna),
nullFloat64(n.Avans), nullString(n.Napomena), nullFloat64(n.Avans), nullString(n.Napomena),
nullTime(n.GarancijaDo), nullTime(n.DatumZavrsetka), nullTime(n.PredvidjenDatum), nullTime(n.GarancijaDo), nullInt(n.GarancijaDana), nullTime(n.DatumZavrsetka), nullTime(n.PredvidjenDatum),
nullString(n.Ostecenja), nullString(n.PinUredjaja), nullString(n.Pribor), nullString(n.Ostecenja), nullString(n.PinUredjaja), nullString(n.Pribor),
n.DatumPrijema, token, n.DatumPrijema, token,
) )
@@ -156,7 +156,7 @@ func (r *ServisRepo) DohvatiJavniToken(ctx context.Context, token string) (*mode
SELECT SELECT
id, klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj, id, klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj,
opis_kvara, trazene_nadogradnje, status, cena_od, cena_do, cena_konacna, opis_kvara, trazene_nadogradnje, status, cena_od, cena_do, cena_konacna,
avans, napomena, garancija_do, datum_prijema, datum_zavrsetka, predvidjen_datum, avans, napomena, garancija_do, garancija_dana, datum_prijema, datum_zavrsetka, predvidjen_datum,
ostecenja, pin_uredjaja, pribor, napomena_klijentu, javni_token ostecenja, pin_uredjaja, pribor, napomena_klijentu, javni_token
FROM servisni_nalozi WHERE javni_token = ?`, token) FROM servisni_nalozi WHERE javni_token = ?`, token)
@@ -173,12 +173,12 @@ func (r *ServisRepo) Izmeni(ctx context.Context, n *model.ServisniNalog) error {
UPDATE servisni_nalozi SET UPDATE servisni_nalozi SET
klijent_id = ?, tehnicar_id = ?, uredjaj = ?, serijski_broj = ?, opis_kvara = ?, trazene_nadogradnje = ?, klijent_id = ?, tehnicar_id = ?, uredjaj = ?, serijski_broj = ?, opis_kvara = ?, trazene_nadogradnje = ?,
status = ?, cena_od = ?, cena_do = ?, cena_konacna = ?, status = ?, cena_od = ?, cena_do = ?, cena_konacna = ?,
avans = ?, napomena = ?, garancija_do = ?, datum_zavrsetka = ?, predvidjen_datum = ?, avans = ?, napomena = ?, garancija_do = ?, garancija_dana = ?, datum_zavrsetka = ?, predvidjen_datum = ?,
ostecenja = ?, pin_uredjaja = ?, pribor = ? ostecenja = ?, pin_uredjaja = ?, pribor = ?
WHERE id = ?`, WHERE id = ?`,
nullInt64(n.KlijentID), nullInt64(n.TehnicarID), n.Uredjaj, nullString(n.SerijskiBroj), n.OpisKvara, n.TrazeneNadogradnje, nullInt64(n.KlijentID), nullInt64(n.TehnicarID), n.Uredjaj, nullString(n.SerijskiBroj), n.OpisKvara, n.TrazeneNadogradnje,
n.Status, nullFloat64(n.CenaOd), nullFloat64(n.CenaDo), nullFloat64(n.CenaKonacna), n.Status, nullFloat64(n.CenaOd), nullFloat64(n.CenaDo), nullFloat64(n.CenaKonacna),
nullFloat64(n.Avans), nullString(n.Napomena), nullTime(n.GarancijaDo), nullTime(n.DatumZavrsetka), nullTime(n.PredvidjenDatum), nullFloat64(n.Avans), nullString(n.Napomena), nullTime(n.GarancijaDo), nullInt(n.GarancijaDana), nullTime(n.DatumZavrsetka), nullTime(n.PredvidjenDatum),
nullString(n.Ostecenja), nullString(n.PinUredjaja), nullString(n.Pribor), nullString(n.Ostecenja), nullString(n.PinUredjaja), nullString(n.Pribor),
n.ID, n.ID,
) )
@@ -229,6 +229,19 @@ func (r *ServisRepo) AzurirajGaranciju(ctx context.Context, id int64, garancijaD
return nil return nil
} }
// AzurirajGarancijaDana postavlja trajanje garancije u danima (od završetka radova).
// dana == nil ili 0 → bez garancije.
func (r *ServisRepo) AzurirajGarancijaDana(ctx context.Context, id int64, dana *int) error {
_, err := r.db.ExecContext(ctx,
"UPDATE servisni_nalozi SET garancija_dana = ? WHERE id = ?",
nullInt(dana), id,
)
if err != nil {
return fmt.Errorf("ntech: ServisRepo.AzurirajGarancijaDana: %w", err)
}
return nil
}
// AzurirajPredvidjenDatum postavlja ili briše ručni override predviđenog datuma popravke. // AzurirajPredvidjenDatum postavlja ili briše ručni override predviđenog datuma popravke.
// predvidjenDatum == nil → vraća se na izvedeni default (prijem + rok iz podešavanja). // predvidjenDatum == nil → vraća se na izvedeni default (prijem + rok iz podešavanja).
func (r *ServisRepo) AzurirajPredvidjenDatum(ctx context.Context, id int64, predvidjenDatum *time.Time) error { func (r *ServisRepo) AzurirajPredvidjenDatum(ctx context.Context, id int64, predvidjenDatum *time.Time) error {
@@ -251,7 +264,7 @@ func (r *ServisRepo) AzurirajPredvidjenDatum(ctx context.Context, id int64, pred
return nil return nil
} }
// AzurirajTehnicar postavlja ili uklanja dodeljenog tehničara na nalogu. // AzurirajTehnicar postavlja ili uklanja dodeljenog servisera na nalogu.
// tehnicarID == nil → nedodeljen. // tehnicarID == nil → nedodeljen.
func (r *ServisRepo) AzurirajTehnicar(ctx context.Context, id int64, tehnicarID *int64) error { func (r *ServisRepo) AzurirajTehnicar(ctx context.Context, id int64, tehnicarID *int64) error {
_, err := r.db.ExecContext(ctx, _, err := r.db.ExecContext(ctx,
@@ -359,11 +372,12 @@ func scanNalog(scan func(...any) error, n *model.ServisniNalog, klijentNaziv *st
var serijskiBroj, napomena, ostecenja, pinUredjaja, pribor, napomenaKlijentu, javniToken sql.NullString var serijskiBroj, napomena, ostecenja, pinUredjaja, pribor, napomenaKlijentu, javniToken sql.NullString
var cenaOd, cenaDo, cenaKonacna, avans sql.NullFloat64 var cenaOd, cenaDo, cenaKonacna, avans sql.NullFloat64
var garancijaDo, datumZavrsetka, predvidjenDatum sql.NullTime var garancijaDo, datumZavrsetka, predvidjenDatum sql.NullTime
var garancijaDana sql.NullInt64
args := []any{ args := []any{
&n.ID, &klijentID, &tehnicarID, &n.BrojNaloga, &n.Uredjaj, &serijskiBroj, &n.ID, &klijentID, &tehnicarID, &n.BrojNaloga, &n.Uredjaj, &serijskiBroj,
&n.OpisKvara, &n.TrazeneNadogradnje, &n.Status, &cenaOd, &cenaDo, &cenaKonacna, &n.OpisKvara, &n.TrazeneNadogradnje, &n.Status, &cenaOd, &cenaDo, &cenaKonacna,
&avans, &napomena, &garancijaDo, &n.DatumPrijema, &datumZavrsetka, &predvidjenDatum, &avans, &napomena, &garancijaDo, &garancijaDana, &n.DatumPrijema, &datumZavrsetka, &predvidjenDatum,
&ostecenja, &pinUredjaja, &pribor, &napomenaKlijentu, &javniToken, &ostecenja, &pinUredjaja, &pribor, &napomenaKlijentu, &javniToken,
} }
@@ -417,6 +431,10 @@ func scanNalog(scan func(...any) error, n *model.ServisniNalog, klijentNaziv *st
v := predvidjenDatum.Time v := predvidjenDatum.Time
n.PredvidjenDatum = &v n.PredvidjenDatum = &v
} }
if garancijaDana.Valid {
v := int(garancijaDana.Int64)
n.GarancijaDana = &v
}
n.JavniToken = javniToken.String n.JavniToken = javniToken.String
return nil return nil
+9
View File
@@ -20,6 +20,15 @@ func nullInt64(v *int64) sql.NullInt64 {
return sql.NullInt64{Int64: *v, Valid: true} return sql.NullInt64{Int64: *v, Valid: true}
} }
// nullInt pretvara *int pokazivač u sql.NullInt64 —
// koristi se za opciona celobrojna polja (npr. trajanje garancije u danima)
func nullInt(v *int) sql.NullInt64 {
if v == nil {
return sql.NullInt64{}
}
return sql.NullInt64{Int64: int64(*v), Valid: true}
}
// nullFloat64 pretvara *float64 pokazivač u sql.NullFloat64 — // nullFloat64 pretvara *float64 pokazivač u sql.NullFloat64 —
// koristi se za opciona numerička polja kao što su cene // koristi se za opciona numerička polja kao što su cene
func nullFloat64(v *float64) sql.NullFloat64 { func nullFloat64(v *float64) sql.NullFloat64 {
+2
View File
@@ -68,6 +68,8 @@ var sablonskeFunkcije = template.FuncMap{
}, },
// telefon formatira srpski broj telefona radi lakšeg čitanja: "0641234567" → "064 123 4567" // telefon formatira srpski broj telefona radi lakšeg čitanja: "0641234567" → "064 123 4567"
"telefon": formatirajTelefon, "telefon": formatirajTelefon,
// garancijaTekst prikazuje trajanje garancije (dani) na srpskom: 45 → „1 mesec i 15 dana"
"garancijaTekst": garancijaTekst,
// json serijalizuje vrednost u JSON za ugradnju u <script> (npr. Alpine x-data); // json serijalizuje vrednost u JSON za ugradnju u <script> (npr. Alpine x-data);
// template.JS sprečava da html/template dodatno escapuje rezultat // template.JS sprečava da html/template dodatno escapuje rezultat
"json": func(v any) (template.JS, error) { "json": func(v any) (template.JS, error) {
+7 -7
View File
@@ -49,7 +49,7 @@ type PodaciPodesavanja struct {
BackupIntervalSati string BackupIntervalSati string
BackupBrojKopija string BackupBrojKopija string
KalkulacijaMarza string KalkulacijaMarza string
ServisGarancijaMeseci string ServisGarancijaDana string
PredracunRokDana string PredracunRokDana string
PredvidjenRokDana string PredvidjenRokDana string
LoginPozadina string LoginPozadina string
@@ -366,15 +366,15 @@ func (h *Handler) SacuvajPodesavanja(w http.ResponseWriter, r *http.Request) {
} }
} }
// podrazumevani rok garancije za servis (meseci, 0120) // podrazumevana garancija za servis (dani, 03650)
if v := strings.TrimSpace(r.FormValue("servis_garancija_meseci")); v != "" { if v := strings.TrimSpace(r.FormValue("servis_garancija_dana")); v != "" {
n, err := strconv.Atoi(v) n, err := strconv.Atoi(v)
if err != nil || n < 0 || n > 120 { if err != nil || n < 0 || n > 3650 {
middleware.SetFlash(w, r, h.DB, "greska", "Rok garancije mora biti broj između 0 i 120 meseci.") middleware.SetFlash(w, r, h.DB, "greska", "Garancija mora biti broj između 0 i 3650 dana.")
http.Redirect(w, r, sledeci, http.StatusSeeOther) http.Redirect(w, r, sledeci, http.StatusSeeOther)
return return
} }
if err := ntechsqlite.SacuvajPodesavanje(r.Context(), h.DB, "servis_garancija_meseci", strconv.Itoa(n)); err != nil { if err := ntechsqlite.SacuvajPodesavanje(r.Context(), h.DB, "servis_garancija_dana", strconv.Itoa(n)); err != nil {
http.Error(w, "Greška pri čuvanju podešavanja", http.StatusInternalServerError) http.Error(w, "Greška pri čuvanju podešavanja", http.StatusInternalServerError)
return return
} }
@@ -779,7 +779,7 @@ func (h *Handler) napuniPodaciPodesavanja(r *http.Request, naslov string) (Podac
BackupIntervalSati: vrednostIliDefault(podesavanja, "backup_interval_sati", "24"), BackupIntervalSati: vrednostIliDefault(podesavanja, "backup_interval_sati", "24"),
BackupBrojKopija: vrednostIliDefault(podesavanja, "backup_broj_kopija", "7"), BackupBrojKopija: vrednostIliDefault(podesavanja, "backup_broj_kopija", "7"),
KalkulacijaMarza: vrednostIliDefault(podesavanja, "kalkulacija_marza", "20"), KalkulacijaMarza: vrednostIliDefault(podesavanja, "kalkulacija_marza", "20"),
ServisGarancijaMeseci: vrednostIliDefault(podesavanja, "servis_garancija_meseci", "2"), ServisGarancijaDana: vrednostIliDefault(podesavanja, "servis_garancija_dana", "60"),
PredracunRokDana: vrednostIliDefault(podesavanja, "predracun_rok_dana", "7"), PredracunRokDana: vrednostIliDefault(podesavanja, "predracun_rok_dana", "7"),
PredvidjenRokDana: vrednostIliDefault(podesavanja, "predvidjen_rok_dana", "15"), PredvidjenRokDana: vrednostIliDefault(podesavanja, "predvidjen_rok_dana", "15"),
}, nil }, nil
+105 -15
View File
@@ -3,6 +3,7 @@ package handler
import ( import (
"context" "context"
"encoding/base64" "encoding/base64"
"fmt"
"log/slog" "log/slog"
"net/http" "net/http"
"strconv" "strconv"
@@ -148,7 +149,7 @@ func (h *Handler) NoviNalog(w http.ResponseWriter, r *http.Request) {
tehnicari, err := h.KorisniciRepo.Lista(r.Context()) tehnicari, err := h.KorisniciRepo.Lista(r.Context())
if err != nil { if err != nil {
http.Error(w, "Greška pri učitavanju tehničara", http.StatusInternalServerError) http.Error(w, "Greška pri učitavanju servisera", http.StatusInternalServerError)
return return
} }
@@ -293,7 +294,7 @@ func (h *Handler) IzmeniNalog(w http.ResponseWriter, r *http.Request) {
tehnicari, err := h.KorisniciRepo.Lista(r.Context()) tehnicari, err := h.KorisniciRepo.Lista(r.Context())
if err != nil { if err != nil {
http.Error(w, "Greška pri učitavanju tehničara", http.StatusInternalServerError) http.Error(w, "Greška pri učitavanju servisera", http.StatusInternalServerError)
return return
} }
@@ -381,6 +382,8 @@ func (h *Handler) SacuvajIzmenaNaloga(w http.ResponseWriter, r *http.Request) {
nalog.DatumZavrsetka = stari.DatumZavrsetka nalog.DatumZavrsetka = stari.DatumZavrsetka
nalog.PredvidjenDatum = stari.PredvidjenDatum nalog.PredvidjenDatum = stari.PredvidjenDatum
nalog.DatumPrijema = stari.DatumPrijema nalog.DatumPrijema = stari.DatumPrijema
nalog.GarancijaDo = stari.GarancijaDo // garancija se uređuje u detaljima, ne u formi
nalog.GarancijaDana = stari.GarancijaDana // isto — trajanje garancije se ne dira u formi
} }
// garancija ne sme da bude pre datuma prijema // garancija ne sme da bude pre datuma prijema
@@ -537,12 +540,9 @@ func (h *Handler) DetaljiNaloga(w http.ResponseWriter, r *http.Request) {
nalog.PredvidjenDatum = defaultPredvidjenDatum(nalog.DatumPrijema, podesavanja) nalog.PredvidjenDatum = defaultPredvidjenDatum(nalog.DatumPrijema, podesavanja)
} }
// podrazumevana garancija (za dugme „Podrazumevano" u popup-u) i stanje „bez garancije" iz baze // podrazumevano trajanje garancije u danima (dugme „Podrazumevano") i stanje „bez garancije" iz baze
garancijaDefault := "" garancijaDefault := strconv.Itoa(defaultGarancijaDana(podesavanja))
if d := defaultGarancija(nalog.DatumPrijema, podesavanja); d != nil { bezGarancije := nalog.GarancijaDana == nil || *nalog.GarancijaDana <= 0
garancijaDefault = d.Format("2006-01-02")
}
bezGarancije := nalog.GarancijaDo == nil
klijentNaziv := "" klijentNaziv := ""
if nalog.KlijentID != nil { if nalog.KlijentID != nil {
@@ -560,10 +560,10 @@ func (h *Handler) DetaljiNaloga(w http.ResponseWriter, r *http.Request) {
} }
} }
// lista tehničara za izbor u popup-u „Promeni tehničara" // lista servisera za izbor u popup-u „Promeni servisera"
tehnicari, err := h.KorisniciRepo.Lista(r.Context()) tehnicari, err := h.KorisniciRepo.Lista(r.Context())
if err != nil { if err != nil {
slog.Error("greška pri učitavanju tehničara", "error", err) slog.Error("greška pri učitavanju servisera", "error", err)
} }
delovi, err := h.deloviSaPotrazivanima(r.Context(), id) delovi, err := h.deloviSaPotrazivanima(r.Context(), id)
@@ -905,7 +905,7 @@ func parseFormuNaloga(r *http.Request) (model.ServisniNalog, string) {
} }
} }
// opcioni tehničar // opcioni serviser
if tidStr := r.FormValue("tehnicar_id"); tidStr != "" { if tidStr := r.FormValue("tehnicar_id"); tidStr != "" {
if tid, err := strconv.ParseInt(tidStr, 10, 64); err == nil { if tid, err := strconv.ParseInt(tidStr, 10, 64); err == nil {
nalog.TehnicarID = &tid nalog.TehnicarID = &tid
@@ -1021,6 +1021,60 @@ func defaultPredvidjenDatum(datumPrijema time.Time, podesavanja map[string]strin
return &t return &t
} }
// srpskiPlural vraća odgovarajući oblik reči za dati broj (1 / 24 / 5+).
func srpskiPlural(n int, jedan, par, mnogo string) string {
if n < 0 {
n = -n
}
d, dd := n%10, n%100
switch {
case d == 1 && dd != 11:
return jedan
case d >= 2 && d <= 4 && (dd < 12 || dd > 14):
return par
default:
return mnogo
}
}
// garancijaUTekst formatira trajanje garancije (u danima) na srpskom:
// 20 → „20 dana", 45 → „1 mesec i 15 dana", 60 → „2 meseca".
func garancijaUTekst(dana int) string {
if dana <= 0 {
return "bez garancije"
}
m := dana / 30
d := dana % 30
mesecRec := srpskiPlural(m, "mesec", "meseca", "meseci")
danRec := srpskiPlural(d, "dan", "dana", "dana")
switch {
case m > 0 && d > 0:
return fmt.Sprintf("%d %s i %d %s", m, mesecRec, d, danRec)
case m > 0:
return fmt.Sprintf("%d %s", m, mesecRec)
default:
return fmt.Sprintf("%d %s", d, danRec)
}
}
// garancijaTekst je šablonski helper: trajanje garancije u danima → tekst, ili „—" za prazno.
func garancijaTekst(dana *int) string {
if dana == nil || *dana <= 0 {
return "—"
}
return garancijaUTekst(*dana)
}
// defaultGarancijaDana vraća podrazumevano trajanje garancije u danima iz podešavanja
// (servis_garancija_dana, podrazumevano 60).
func defaultGarancijaDana(podesavanja map[string]string) int {
dana, err := strconv.Atoi(vrednostIliDefault(podesavanja, "servis_garancija_dana", "60"))
if err != nil || dana < 0 {
return 60
}
return dana
}
// garancijaPrePrijema vraća true ako je datum garancije raniji od datuma prijema (po danu). // garancijaPrePrijema vraća true ako je datum garancije raniji od datuma prijema (po danu).
// Garancija ne sme da bude pre prijema uređaja. // Garancija ne sme da bude pre prijema uređaja.
func garancijaPrePrijema(garancija, prijem time.Time) bool { func garancijaPrePrijema(garancija, prijem time.Time) bool {
@@ -1483,6 +1537,42 @@ func (h *Handler) AzurirajGaranciju(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10), http.StatusSeeOther) http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10), http.StatusSeeOther)
} }
// AzurirajGarancijaDana postavlja trajanje garancije u danima (od završetka radova);
// „bez garancije" ili prazno/0 → bez garancije.
func (h *Handler) AzurirajGarancijaDana(w http.ResponseWriter, r *http.Request) {
if _, ok := h.zahtevajDozvolu(w, r, "servis.izmeni"); !ok {
return
}
id, err := parseID(chi.URLParam(r, "id"))
if err != nil {
http.Error(w, "Neispravan ID naloga", http.StatusBadRequest)
return
}
if err := r.ParseForm(); err != nil {
http.Error(w, "Greška pri čitanju forme", http.StatusBadRequest)
return
}
var dana *int
if r.FormValue("bez_garancije") != "1" {
if s := strings.TrimSpace(r.FormValue("garancija_dana")); s != "" {
n, err := strconv.Atoi(s)
if err != nil || n < 0 {
http.Error(w, "Garancija mora biti ceo broj dana.", http.StatusBadRequest)
return
}
if n > 0 {
dana = &n
}
}
}
if err := h.ServisRepo.AzurirajGarancijaDana(r.Context(), id, dana); err != nil {
slog.Error("greška pri ažuriranju garancije (dana)", "id", id, "error", err)
http.Error(w, "Greška pri ažuriranju garancije", http.StatusInternalServerError)
return
}
http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10), http.StatusSeeOther)
}
// AzurirajPredvidjenDatum postavlja ručni override predviđenog datuma popravke; // AzurirajPredvidjenDatum postavlja ručni override predviđenog datuma popravke;
// prazno polje vraća nalog na izvedeni default (prijem + rok iz podešavanja). // prazno polje vraća nalog na izvedeni default (prijem + rok iz podešavanja).
func (h *Handler) AzurirajPredvidjenDatum(w http.ResponseWriter, r *http.Request) { func (h *Handler) AzurirajPredvidjenDatum(w http.ResponseWriter, r *http.Request) {
@@ -1515,7 +1605,7 @@ func (h *Handler) AzurirajPredvidjenDatum(w http.ResponseWriter, r *http.Request
http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10), http.StatusSeeOther) http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10), http.StatusSeeOther)
} }
// AzurirajTehnicar menja dodeljenog tehničara na nalogu; prazna vrednost → nedodeljen. // AzurirajTehnicar menja dodeljenog servisera na nalogu; prazna vrednost → nedodeljen.
func (h *Handler) AzurirajTehnicar(w http.ResponseWriter, r *http.Request) { func (h *Handler) AzurirajTehnicar(w http.ResponseWriter, r *http.Request) {
if _, ok := h.zahtevajDozvolu(w, r, "servis.izmeni"); !ok { if _, ok := h.zahtevajDozvolu(w, r, "servis.izmeni"); !ok {
return return
@@ -1533,14 +1623,14 @@ func (h *Handler) AzurirajTehnicar(w http.ResponseWriter, r *http.Request) {
if t := strings.TrimSpace(r.FormValue("tehnicar_id")); t != "" { if t := strings.TrimSpace(r.FormValue("tehnicar_id")); t != "" {
v, err := strconv.ParseInt(t, 10, 64) v, err := strconv.ParseInt(t, 10, 64)
if err != nil { if err != nil {
http.Error(w, "Neispravan tehničar", http.StatusBadRequest) http.Error(w, "Neispravan serviser", http.StatusBadRequest)
return return
} }
tehnicarID = &v tehnicarID = &v
} }
if err := h.ServisRepo.AzurirajTehnicar(r.Context(), id, tehnicarID); err != nil { if err := h.ServisRepo.AzurirajTehnicar(r.Context(), id, tehnicarID); err != nil {
slog.Error("greška pri ažuriranju tehničara", "id", id, "error", err) slog.Error("greška pri ažuriranju servisera", "id", id, "error", err)
http.Error(w, "Greška pri ažuriranju tehničara", http.StatusInternalServerError) http.Error(w, "Greška pri ažuriranju servisera", http.StatusInternalServerError)
return return
} }
http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10), http.StatusSeeOther) http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10), http.StatusSeeOther)
+1
View File
@@ -40,6 +40,7 @@ type ServisniNalog struct {
Avans *float64 Avans *float64
Napomena string Napomena string
GarancijaDo *time.Time GarancijaDo *time.Time
GarancijaDana *int // trajanje garancije u danima (od završetka); nil/0 = bez garancije
DatumPrijema time.Time DatumPrijema time.Time
PredvidjenDatum *time.Time PredvidjenDatum *time.Time
DatumZavrsetka *time.Time DatumZavrsetka *time.Time
+4
View File
@@ -0,0 +1,4 @@
-- garancija_dana: trajanje garancije u danima (računato od datuma završetka radova).
-- Unosi se dok je uređaj u radu; pravi datum (garancija_do) se popunjava kad nalog
-- pređe u „Završeno" (datum_zavrsetka + garancija_dana). NULL ili 0 = bez garancije.
ALTER TABLE servisni_nalozi ADD COLUMN garancija_dana INTEGER;
@@ -22,14 +22,15 @@
<div class="kolona" style="gap:16px;"> <div class="kolona" style="gap:16px;">
<div style="max-width:280px;"> <div style="max-width:280px;">
<label for="servis_garancija_meseci" class="polje-labela"> <label for="servis_garancija_dana" class="polje-labela">
Podrazumevani rok garancije (meseci) Podrazumevana garancija (dana)
</label> </label>
<input type="number" id="servis_garancija_meseci" name="servis_garancija_meseci" <input type="number" id="servis_garancija_dana" name="servis_garancija_dana"
min="0" max="120" value="{{.ServisGarancijaMeseci}}" min="0" max="3650" value="{{.ServisGarancijaDana}}"
style="width:100%;"> style="width:100%;">
<div class="pomocni-tekst" style="margin-top:6px;"> <div class="pomocni-tekst" style="margin-top:6px;">
Automatski se upisuje pri prijemu uređaja. Vrednost 0 znači bez garancije. Trajanje garancije u danima (računato od završetka radova). Dugme „Podrazumevano"
na nalogu koristi ovu vrednost. 0 znači bez garancije.
</div> </div>
</div> </div>
+15 -15
View File
@@ -98,35 +98,35 @@
</div> </div>
</div> </div>
<div> <div>
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Garancija do</div> <div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Garancija</div>
<div style="font-size:14px;{{if .Nalog.GarancijaDo}}color:#16a34a;font-weight:500;{{else}}color:var(--tekst-sporedni);{{end}}"> <div style="font-size:14px;{{if not .BezGarancije}}color:#16a34a;font-weight:500;{{else}}color:var(--tekst-sporedni);{{end}}">
{{if .Nalog.GarancijaDo}}{{.Nalog.GarancijaDo.Format "02.01.2006."}}{{else}}—{{end}} {{garancijaTekst .Nalog.GarancijaDana}}
{{if index .Dozvole "servis.izmeni"}} {{if index .Dozvole "servis.izmeni"}}
<button type="button" class="izmeni-polje" onclick="ntechOtvoriModal(this)">Promeni</button> <button type="button" class="izmeni-polje" onclick="ntechOtvoriModal(this)">Promeni</button>
<dialog class="premesti-modal" onclick="if(event.target===this)this.close()"> <dialog class="premesti-modal" onclick="if(event.target===this)this.close()">
<form method="dialog" class="premesti-zaglavlje"> <form method="dialog" class="premesti-zaglavlje">
<h3>Garancija do</h3> <h3>Garancija</h3>
<button type="submit" class="premesti-zatvori" aria-label="Zatvori">&times;</button> <button type="submit" class="premesti-zatvori" aria-label="Zatvori">&times;</button>
</form> </form>
<form method="POST" action="/servis/{{.Nalog.ID}}/garancija" data-refresh="#nalog-zaglavlje" style="display:flex;flex-direction:column;gap:12px;padding:16px;"> <form method="POST" action="/servis/{{.Nalog.ID}}/garancija-dana" data-refresh="#nalog-zaglavlje" style="display:flex;flex-direction:column;gap:12px;padding:16px;">
<input type="hidden" name="_csrf" value="{{.CsrfToken}}"> <input type="hidden" name="_csrf" value="{{.CsrfToken}}">
<div> <div>
<label class="polje-labela">Garancija do</label> <label class="polje-labela">Trajanje garancije (dana)</label>
<div style="display:flex;align-items:center;gap:10px;"> <div style="display:flex;align-items:center;gap:10px;">
<input type="date" name="garancija_do" <input type="number" name="garancija_dana" min="0" step="1"
value="{{if .Nalog.GarancijaDo}}{{.Nalog.GarancijaDo.Format "2006-01-02"}}{{else}}{{.GarancijaDefault}}{{end}}" value="{{if .BezGarancije}}{{.GarancijaDefault}}{{else}}{{.Nalog.GarancijaDana}}{{end}}"
min="{{.Nalog.DatumPrijema.Format "2006-01-02"}}"
{{if .BezGarancije}}disabled{{end}} {{if .BezGarancije}}disabled{{end}}
style="flex:1;min-width:0;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;{{if .BezGarancije}}opacity:.5;{{end}}"> style="flex:1;min-width:0;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;{{if .BezGarancije}}opacity:.5;{{end}}">
<label class="tip-opcija{{if .BezGarancije}} tip-opcija-akt{{end}}" style="white-space:nowrap;"> <label class="tip-opcija{{if .BezGarancije}} tip-opcija-akt{{end}}" style="white-space:nowrap;">
<input type="checkbox" name="bez_garancije" value="1"{{if .BezGarancije}} checked{{end}} style="margin:0;width:auto;" <input type="checkbox" name="bez_garancije" value="1"{{if .BezGarancije}} checked{{end}} style="margin:0;width:auto;"
onchange="var d=this.closest('div').querySelector('[name=garancija_do]'); d.disabled=this.checked; d.style.opacity=this.checked?'.5':''; this.closest('label').classList.toggle('tip-opcija-akt',this.checked);"> onchange="var d=this.closest('div').querySelector('[name=garancija_dana]'); d.disabled=this.checked; d.style.opacity=this.checked?'.5':''; this.closest('label').classList.toggle('tip-opcija-akt',this.checked);">
Bez garancije Bez garancije
</label> </label>
</div> </div>
<div class="pomocni-tekst" style="margin-top:6px;">Računa se od datuma završetka radova.</div>
</div> </div>
<button type="button" class="btn-sekundarno" style="align-self:flex-start;" <button type="button" class="btn-sekundarno" style="align-self:flex-start;"
onclick="var f=this.closest('form'); var d=f.querySelector('[name=garancija_do]'); d.disabled=false; d.style.opacity=''; d.value='{{.GarancijaDefault}}'; var c=f.querySelector('[name=bez_garancije]'); c.checked=false; c.closest('label').classList.remove('tip-opcija-akt'); f.requestSubmit();">Podrazumevano</button> onclick="var f=this.closest('form'); var d=f.querySelector('[name=garancija_dana]'); d.disabled=false; d.style.opacity=''; d.value='{{.GarancijaDefault}}'; var c=f.querySelector('[name=bez_garancije]'); c.checked=false; c.closest('label').classList.remove('tip-opcija-akt'); f.requestSubmit();">Podrazumevano</button>
</form> </form>
</dialog> </dialog>
{{end}} {{end}}
@@ -139,20 +139,20 @@
</div> </div>
</div> </div>
<div> <div>
<div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Tehničar</div> <div style="font-size:12px;color:var(--tekst-sporedni);margin-bottom:4px;">Serviser</div>
<div style="font-size:14px;font-weight:500;color:var(--tekst-glavni);"> <div style="font-size:14px;font-weight:500;color:var(--tekst-glavni);">
{{if .TehnicarNaziv}}{{.TehnicarNaziv}}{{else}}<span style="color:var(--tekst-sporedni);font-weight:normal;">— nedodeljen —</span>{{end}} {{if .TehnicarNaziv}}{{.TehnicarNaziv}}{{else}}<span style="color:var(--tekst-sporedni);font-weight:normal;">— nedodeljen —</span>{{end}}
{{if index .Dozvole "servis.izmeni"}} {{if index .Dozvole "servis.izmeni"}}
<button type="button" class="izmeni-polje" onclick="ntechOtvoriModal(this)">Promeni</button> <button type="button" class="izmeni-polje" onclick="ntechOtvoriModal(this)">Promeni</button>
<dialog class="premesti-modal" onclick="if(event.target===this)this.close()"> <dialog class="premesti-modal" onclick="if(event.target===this)this.close()">
<form method="dialog" class="premesti-zaglavlje"> <form method="dialog" class="premesti-zaglavlje">
<h3>Tehničar</h3> <h3>Serviser</h3>
<button type="submit" class="premesti-zatvori" aria-label="Zatvori">&times;</button> <button type="submit" class="premesti-zatvori" aria-label="Zatvori">&times;</button>
</form> </form>
<form method="POST" action="/servis/{{.Nalog.ID}}/tehnicar" data-refresh="#nalog-zaglavlje" style="display:flex;flex-direction:column;gap:12px;padding:16px;"> <form method="POST" action="/servis/{{.Nalog.ID}}/tehnicar" data-refresh="#nalog-zaglavlje" style="display:flex;flex-direction:column;gap:12px;padding:16px;">
<input type="hidden" name="_csrf" value="{{.CsrfToken}}"> <input type="hidden" name="_csrf" value="{{.CsrfToken}}">
<div> <div>
<label class="polje-labela">Tehničar</label> <label class="polje-labela">Serviser</label>
<select name="tehnicar_id" <select name="tehnicar_id"
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;"> 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;">
<option value="">— nedodeljen —</option> <option value="">— nedodeljen —</option>
@@ -553,7 +553,7 @@
} }
})(); })();
// Popup polja (predviđen datum, garancija, tehničar): nema dugmeta „Sačuvaj" — // Popup polja (predviđen datum, garancija, serviser): nema dugmeta „Sačuvaj" —
// snima se na zatvaranje modala, i to samo ako je vrednost promenjena. // snima se na zatvaranje modala, i to samo ako je vrednost promenjena.
// Početno stanje forme pamtimo pri otvaranju; na „close" upoređujemo i, ako se // Početno stanje forme pamtimo pri otvaranju; na „close" upoređujemo i, ako se
// razlikuje, šaljemo formu (ntech AJAX presretač osveži #nalog-zaglavlje). // razlikuje, šaljemo formu (ntech AJAX presretač osveži #nalog-zaglavlje).
+5 -26
View File
@@ -100,11 +100,11 @@
<!-- ===================== KARTICA: TEHNIČAR (izmena) / KLIJENT I TEHNIČAR (novi) ===================== --> <!-- ===================== KARTICA: TEHNIČAR (izmena) / KLIJENT I TEHNIČAR (novi) ===================== -->
{{if .Izmena}} {{if .Izmena}}
<!-- pri izmeni klijent se ne menja (vezan pri prijemu) — ostaje samo tehničar --> <!-- pri izmeni klijent se ne menja (vezan pri prijemu) — ostaje samo serviser -->
<div class="kartica forma-kartica animiraj" style="margin-bottom:20px;"> <div class="kartica forma-kartica animiraj" style="margin-bottom:20px;">
<div class="sekcija-naslov">Tehničar</div> <div class="sekcija-naslov">Serviser</div>
<div> <div>
<label class="polje-labela">Tehničar</label> <label class="polje-labela">Serviser</label>
<select name="tehnicar_id" style="width:100%;"> <select name="tehnicar_id" style="width:100%;">
<option value="">— nedodeljen —</option> <option value="">— nedodeljen —</option>
{{range .Tehnicari}} {{range .Tehnicari}}
@@ -252,9 +252,9 @@
</template> </template>
</div> </div>
<!-- tehničar --> <!-- serviser -->
<div> <div>
<label class="polje-labela">Tehničar</label> <label class="polje-labela">Serviser</label>
<select name="tehnicar_id" style="width:100%;"> <select name="tehnicar_id" style="width:100%;">
<option value="">— nedodeljen —</option> <option value="">— nedodeljen —</option>
{{range .Tehnicari}} {{range .Tehnicari}}
@@ -280,27 +280,6 @@
value="{{if .Nalog.Avans}}{{.Nalog.Avans}}{{end}}" value="{{if .Nalog.Avans}}{{.Nalog.Avans}}{{end}}"
placeholder="0" style="width:100%;"> placeholder="0" style="width:100%;">
</div> </div>
{{if .Izmena}}
<!-- garancija — isti izgled kao popup „Garancija do" u detaljima naloga;
„Bez garancije" isključuje datum, „Podrazumevano" vraća prijem + meseci -->
<div>
<label class="polje-labela">Garancija do</label>
<div style="display:flex;align-items:center;gap:10px;">
<input type="date" name="garancija_do"
value="{{if .Nalog.GarancijaDo}}{{.Nalog.GarancijaDo.Format "2006-01-02"}}{{else}}{{.GarancijaDefault}}{{end}}"
min="{{.Nalog.DatumPrijema.Format "2006-01-02"}}"
{{if .BezGarancije}}disabled{{end}}
style="flex:1;min-width:0;{{if .BezGarancije}}opacity:.5;{{end}}">
<label class="tip-opcija{{if .BezGarancije}} tip-opcija-akt{{end}}" style="white-space:nowrap;">
<input type="checkbox" name="bez_garancije" value="1"{{if .BezGarancije}} checked{{end}} style="margin:0;width:auto;"
onchange="var d=this.closest('div').querySelector('[name=garancija_do]'); d.disabled=this.checked; d.style.opacity=this.checked?'.5':''; this.closest('label').classList.toggle('tip-opcija-akt',this.checked);">
Bez garancije
</label>
</div>
<button type="button" class="btn-sekundarno" style="margin-top:8px;"
onclick="var f=this.closest('form'); var d=f.querySelector('[name=garancija_do]'); d.disabled=false; d.style.opacity=''; d.value='{{.GarancijaDefault}}'; var c=f.querySelector('[name=bez_garancije]'); c.checked=false; c.closest('label').classList.remove('tip-opcija-akt'); d.dispatchEvent(new Event('change',{bubbles:true}));">Podrazumevano</button>
</div>
{{end}}
</div> </div>
<div style="margin-top:16px;"> <div style="margin-top:16px;">
@@ -146,7 +146,7 @@
</div> </div>
{{if .TehnicarNaziv}} {{if .TehnicarNaziv}}
<div> <div>
<div class="polje-labela">Tehničar</div> <div class="polje-labela">Serviser</div>
<div class="polje-vrednost">{{.TehnicarNaziv}}</div> <div class="polje-vrednost">{{.TehnicarNaziv}}</div>
</div> </div>
{{end}} {{end}}
@@ -244,7 +244,7 @@
<!-- potpisi --> <!-- potpisi -->
<div class="potpisi"> <div class="potpisi">
<div> <div>
<div class="potpis-linija">Predao (tehničar / firma)</div> <div class="potpis-linija">Predao (serviser / firma)</div>
</div> </div>
<div> <div>
<div class="potpis-linija">Preuzeo (klijent)</div> <div class="potpis-linija">Preuzeo (klijent)</div>
+1 -1
View File
@@ -143,7 +143,7 @@
</div> </div>
{{if .TehnicarNaziv}} {{if .TehnicarNaziv}}
<div> <div>
<div class="polje-labela">Tehničar</div> <div class="polje-labela">Serviser</div>
<div class="polje-vrednost">{{.TehnicarNaziv}}</div> <div class="polje-vrednost">{{.TehnicarNaziv}}</div>
</div> </div>
{{end}} {{end}}
+2 -2
View File
@@ -122,7 +122,7 @@
</div> </div>
{{if .TehnicarNaziv}} {{if .TehnicarNaziv}}
<div> <div>
<div class="polje-labela">Tehničar</div> <div class="polje-labela">Serviser</div>
<div class="polje-vrednost">{{.TehnicarNaziv}}</div> <div class="polje-vrednost">{{.TehnicarNaziv}}</div>
</div> </div>
{{end}} {{end}}
@@ -249,7 +249,7 @@
<div class="potpis-linija">Predao klijent</div> <div class="potpis-linija">Predao klijent</div>
</div> </div>
<div> <div>
<div class="potpis-linija">Primio tehničar</div> <div class="potpis-linija">Primio serviser</div>
</div> </div>
</div> </div>