diff --git a/cmd/ntech/main.go b/cmd/ntech/main.go index 6d46b42..3ef453d 100644 --- a/cmd/ntech/main.go +++ b/cmd/ntech/main.go @@ -360,6 +360,7 @@ func main() { 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}/garancija", h.AzurirajGaranciju) + r.With(doz("servis.izmeni")).Post("/servis/{id}/napomena-klijentu", h.SacuvajNapomenuKlijentu) r.With(doz("servis.izmeni")).Post("/servis/{id}/delovi", h.DodajDeloNalogu) r.With(doz("servis.izmeni")).Post("/servis/{id}/delovi/{deo_id}/obrisi", h.ObrisiDeloNaloga) r.With(doz("servis.izmeni")).Post("/servis/{id}/radovi", h.DodajRadNalogu) diff --git a/internal/db/repository.go b/internal/db/repository.go index 7f1c31a..144e48a 100644 --- a/internal/db/repository.go +++ b/internal/db/repository.go @@ -192,6 +192,7 @@ type ServisRepository interface { Izmeni(ctx context.Context, n *model.ServisniNalog) error AzurirajStatus(ctx context.Context, id int64, status string) error AzurirajGaranciju(ctx context.Context, id int64, garancijaDo *time.Time) error + AzurirajNapomenuKlijentu(ctx context.Context, id int64, tekst string) error Obrisi(ctx context.Context, id int64, korisnikID *int64) error SledeciBroj(ctx context.Context) (string, error) } diff --git a/internal/db/sqlite/servis.go b/internal/db/sqlite/servis.go index 85a210c..6254fee 100644 --- a/internal/db/sqlite/servis.go +++ b/internal/db/sqlite/servis.go @@ -56,9 +56,9 @@ func (r *ServisRepo) Lista(ctx context.Context, pretraga, status string) ([]mode upit := ` SELECT sn.id, sn.klijent_id, sn.tehnicar_id, sn.broj_naloga, sn.uredjaj, sn.serijski_broj, - sn.opis_kvara, 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.ostecenja, sn.pin_uredjaja, sn.pribor, sn.javni_token, + sn.ostecenja, sn.pin_uredjaja, sn.pribor, sn.napomena_klijentu, sn.javni_token, COALESCE(kp.naziv, '') AS klijent_naziv FROM servisni_nalozi sn LEFT JOIN klijent_prikaz kp ON kp.id = sn.klijent_id @@ -103,9 +103,9 @@ func (r *ServisRepo) DohvatiID(ctx context.Context, id int64) (*model.ServisniNa red := r.db.QueryRowContext(ctx, ` SELECT id, klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj, - opis_kvara, 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, - ostecenja, pin_uredjaja, pribor, javni_token + ostecenja, pin_uredjaja, pribor, napomena_klijentu, javni_token FROM servisni_nalozi WHERE id = ?`, id) var n model.ServisniNalog @@ -126,12 +126,12 @@ func (r *ServisRepo) Kreiraj(ctx context.Context, n *model.ServisniNalog) (int64 rezultat, err := r.db.ExecContext(ctx, ` INSERT INTO servisni_nalozi - (klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj, opis_kvara, + (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, ostecenja, pin_uredjaja, pribor, datum_prijema, javni_token) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, nullInt64(n.KlijentID), nullInt64(n.TehnicarID), n.BrojNaloga, n.Uredjaj, - nullString(n.SerijskiBroj), n.OpisKvara, n.Status, + nullString(n.SerijskiBroj), n.OpisKvara, nullString(n.TrazeneNadogradnje), n.Status, nullFloat64(n.CenaOd), nullFloat64(n.CenaDo), nullFloat64(n.CenaKonacna), nullFloat64(n.Avans), nullString(n.Napomena), nullTime(n.GarancijaDo), nullTime(n.DatumZavrsetka), @@ -155,9 +155,9 @@ func (r *ServisRepo) DohvatiJavniToken(ctx context.Context, token string) (*mode red := r.db.QueryRowContext(ctx, ` SELECT id, klijent_id, tehnicar_id, broj_naloga, uredjaj, serijski_broj, - opis_kvara, 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, - ostecenja, pin_uredjaja, pribor, javni_token + ostecenja, pin_uredjaja, pribor, napomena_klijentu, javni_token FROM servisni_nalozi WHERE javni_token = ?`, token) var n model.ServisniNalog @@ -171,12 +171,12 @@ func (r *ServisRepo) DohvatiJavniToken(ctx context.Context, token string) (*mode func (r *ServisRepo) Izmeni(ctx context.Context, n *model.ServisniNalog) error { _, err := r.db.ExecContext(ctx, ` UPDATE servisni_nalozi SET - klijent_id = ?, tehnicar_id = ?, uredjaj = ?, serijski_broj = ?, opis_kvara = ?, + klijent_id = ?, tehnicar_id = ?, uredjaj = ?, serijski_broj = ?, opis_kvara = ?, trazene_nadogradnje = ?, status = ?, cena_od = ?, cena_do = ?, cena_konacna = ?, avans = ?, napomena = ?, garancija_do = ?, datum_zavrsetka = ?, ostecenja = ?, pin_uredjaja = ?, pribor = ? WHERE id = ?`, - nullInt64(n.KlijentID), nullInt64(n.TehnicarID), n.Uredjaj, nullString(n.SerijskiBroj), n.OpisKvara, + nullInt64(n.KlijentID), nullInt64(n.TehnicarID), n.Uredjaj, nullString(n.SerijskiBroj), n.OpisKvara, nullString(n.TrazeneNadogradnje), n.Status, nullFloat64(n.CenaOd), nullFloat64(n.CenaDo), nullFloat64(n.CenaKonacna), nullFloat64(n.Avans), nullString(n.Napomena), nullTime(n.GarancijaDo), nullTime(n.DatumZavrsetka), nullString(n.Ostecenja), nullString(n.PinUredjaja), nullString(n.Pribor), @@ -229,6 +229,17 @@ func (r *ServisRepo) AzurirajGaranciju(ctx context.Context, id int64, garancijaD return nil } +// AzurirajNapomenuKlijentu postavlja tekst napomene namenjene klijentu na nalogu +func (r *ServisRepo) AzurirajNapomenuKlijentu(ctx context.Context, id int64, tekst string) error { + _, err := r.db.ExecContext(ctx, + "UPDATE servisni_nalozi SET napomena_klijentu = ? WHERE id = ?", tekst, id, + ) + if err != nil { + return fmt.Errorf("ntech: ServisRepo.AzurirajNapomenuKlijentu: %w", err) + } + return nil +} + // Obrisi briše servisni nalog po ID-u // Obrisi briše servisni nalog i vraća ugrađene delove na stanje u magacinu (u transakciji) func (r *ServisRepo) Obrisi(ctx context.Context, id int64, korisnikID *int64) error { @@ -310,15 +321,15 @@ func (r *ServisRepo) Obrisi(ctx context.Context, id int64, korisnikID *int64) er // klijentNaziv je opcioni pokazivač, nil kada se čita bez JOIN-a func scanNalog(scan func(...any) error, n *model.ServisniNalog, klijentNaziv *string) error { var klijentID, tehnicarID sql.NullInt64 - var serijskiBroj, napomena, ostecenja, pinUredjaja, pribor, javniToken sql.NullString + var serijskiBroj, napomena, ostecenja, pinUredjaja, pribor, napomenaKlijentu, javniToken sql.NullString var cenaOd, cenaDo, cenaKonacna, avans sql.NullFloat64 var garancijaDo, datumZavrsetka sql.NullTime args := []any{ &n.ID, &klijentID, &tehnicarID, &n.BrojNaloga, &n.Uredjaj, &serijskiBroj, - &n.OpisKvara, &n.Status, &cenaOd, &cenaDo, &cenaKonacna, + &n.OpisKvara, &n.TrazeneNadogradnje, &n.Status, &cenaOd, &cenaDo, &cenaKonacna, &avans, &napomena, &garancijaDo, &n.DatumPrijema, &datumZavrsetka, - &ostecenja, &pinUredjaja, &pribor, &javniToken, + &ostecenja, &pinUredjaja, &pribor, &napomenaKlijentu, &javniToken, } if klijentNaziv != nil { @@ -342,6 +353,7 @@ func scanNalog(scan func(...any) error, n *model.ServisniNalog, klijentNaziv *st n.Ostecenja = ostecenja.String n.PinUredjaja = pinUredjaja.String n.Pribor = pribor.String + n.NapomenaKlijentu = napomenaKlijentu.String if cenaOd.Valid { v := cenaOd.Float64 n.CenaOd = &v diff --git a/internal/handler/servis.go b/internal/handler/servis.go index 785f459..a867cd6 100644 --- a/internal/handler/servis.go +++ b/internal/handler/servis.go @@ -772,16 +772,17 @@ func parseFormuNaloga(r *http.Request) (model.ServisniNalog, string) { } nalog := model.ServisniNalog{ - BrojNaloga: strings.TrimSpace(r.FormValue("broj_naloga")), - Uredjaj: uredjaj, - SerijskiBroj: strings.TrimSpace(r.FormValue("serijski_broj")), - OpisKvara: opisKvara, - Status: r.FormValue("status"), - Napomena: strings.TrimSpace(r.FormValue("napomena")), - Ostecenja: strings.TrimSpace(r.FormValue("ostecenja")), - PinUredjaja: strings.TrimSpace(r.FormValue("pin_uredjaja")), - Pribor: strings.TrimSpace(r.FormValue("pribor")), - DatumPrijema: time.Now(), + BrojNaloga: strings.TrimSpace(r.FormValue("broj_naloga")), + Uredjaj: uredjaj, + SerijskiBroj: strings.TrimSpace(r.FormValue("serijski_broj")), + OpisKvara: opisKvara, + TrazeneNadogradnje: strings.TrimSpace(r.FormValue("trazene_nadogradnje")), + Status: r.FormValue("status"), + Napomena: strings.TrimSpace(r.FormValue("napomena")), + Ostecenja: strings.TrimSpace(r.FormValue("ostecenja")), + PinUredjaja: strings.TrimSpace(r.FormValue("pin_uredjaja")), + Pribor: strings.TrimSpace(r.FormValue("pribor")), + DatumPrijema: time.Now(), } // datum prijema — korisnik može da unese drugi datum (npr. retroaktivno) @@ -1364,6 +1365,29 @@ func (h *Handler) AzurirajGaranciju(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10), http.StatusSeeOther) } +// SacuvajNapomenuKlijentu ažurira napomenu namenjenu klijentu na nalogu +func (h *Handler) SacuvajNapomenuKlijentu(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 + } + tekst := strings.TrimSpace(r.FormValue("napomena_klijentu")) + if err := h.ServisRepo.AzurirajNapomenuKlijentu(r.Context(), id, tekst); err != nil { + slog.Error("greška pri ažuriranju napomene klijentu", "id", id, "error", err) + http.Error(w, "Greška pri ažuriranju napomene", http.StatusInternalServerError) + return + } + http.Redirect(w, r, "/servis/"+strconv.FormatInt(id, 10), http.StatusSeeOther) +} + // PodaciJavnogStatusa su podaci za javnu status stranicu servisnog naloga type PodaciJavnogStatusa struct { Nalog model.ServisniNalog diff --git a/internal/model/servis.go b/internal/model/servis.go index 184cd08..1b6217b 100644 --- a/internal/model/servis.go +++ b/internal/model/servis.go @@ -25,26 +25,28 @@ var SviStatusi = []string{ // ServisniNalog predstavlja jedan servisni nalog type ServisniNalog struct { - ID int64 - KlijentID *int64 - TehnicarID *int64 - BrojNaloga string - Uredjaj string - SerijskiBroj string - OpisKvara string - Status string - CenaOd *float64 - CenaDo *float64 - CenaKonacna *float64 - Avans *float64 - Napomena string - GarancijaDo *time.Time - DatumPrijema time.Time - DatumZavrsetka *time.Time - Ostecenja string - PinUredjaja string - Pribor string - JavniToken string + ID int64 + KlijentID *int64 + TehnicarID *int64 + BrojNaloga string + Uredjaj string + SerijskiBroj string + OpisKvara string + TrazeneNadogradnje string + Status string + CenaOd *float64 + CenaDo *float64 + CenaKonacna *float64 + Avans *float64 + Napomena string + GarancijaDo *time.Time + DatumPrijema time.Time + DatumZavrsetka *time.Time + Ostecenja string + PinUredjaja string + Pribor string + NapomenaKlijentu string + JavniToken string } // ServisniDeo predstavlja jedan artikal ugrađen u servisni nalog diff --git a/migrations/069_servis_trazene_nadogradnje.sql b/migrations/069_servis_trazene_nadogradnje.sql new file mode 100644 index 0000000..85be91a --- /dev/null +++ b/migrations/069_servis_trazene_nadogradnje.sql @@ -0,0 +1,3 @@ +-- tražene nadogradnje: ono što klijent želi da se dogradi/poboljša, +-- odvojeno od opis_kvara (ono što ne radi) +ALTER TABLE servisni_nalozi ADD COLUMN trazene_nadogradnje TEXT NOT NULL DEFAULT ''; diff --git a/migrations/070_servis_napomena_klijentu.sql b/migrations/070_servis_napomena_klijentu.sql new file mode 100644 index 0000000..831625e --- /dev/null +++ b/migrations/070_servis_napomena_klijentu.sql @@ -0,0 +1,3 @@ +-- napomena klijentu: poruka koju serviser ostavlja klijentu (vidljiva na nalogu), +-- odvojeno od interne napomene +ALTER TABLE servisni_nalozi ADD COLUMN napomena_klijentu TEXT NOT NULL DEFAULT ''; diff --git a/web/templates/stranice/servis_detalji.html b/web/templates/stranice/servis_detalji.html index 0923e85..d26f4a9 100644 --- a/web/templates/stranice/servis_detalji.html +++ b/web/templates/stranice/servis_detalji.html @@ -135,6 +135,12 @@