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:
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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, 0–120)
|
// podrazumevana garancija za servis (dani, 0–3650)
|
||||||
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
@@ -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 / 2–4 / 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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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">×</button>
|
<button type="submit" class="premesti-zatvori" aria-label="Zatvori">×</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">×</button>
|
<button type="submit" class="premesti-zatvori" aria-label="Zatvori">×</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).
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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}}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user