diff --git a/cmd/ntech/main.go b/cmd/ntech/main.go index 8eb62b1..62b9121 100644 --- a/cmd/ntech/main.go +++ b/cmd/ntech/main.go @@ -309,6 +309,8 @@ func main() { r.With(doz("artikal.izmeni")).Post("/magacin/izmeni/{id}", h.SacuvajIzmenuArtikla) r.With(doz("artikal.obrisi")).Get("/magacin/obrisi/{id}", h.ObrisiArtikal) r.With(doz("artikal.obrisi")).Get("/magacin/vrati/{id}", h.VratiArtikal) + r.With(doz("artikal.izmeni")).Post("/magacin/kartica/{id}/dobavljac/dodaj", h.DodajDobavljacaArtiklu) + r.With(doz("artikal.izmeni")).Post("/magacin/kartica/{id}/dobavljac/obrisi", h.ObrisiDobavljacaArtikla) r.With(doz("artikal.premesti")).Post("/magacin/premesti/{id}", h.PremestiArtikal) r.With(doz("artikal.izmeni")).Post("/magacin/promeni-cenu/{id}", h.PromeniCenuArtikla) r.With(doz("artikal.izmeni")).Get("/nivelacije", h.Nivelacije) diff --git a/internal/db/repository.go b/internal/db/repository.go index d89d552..9036017 100644 --- a/internal/db/repository.go +++ b/internal/db/repository.go @@ -37,6 +37,8 @@ type ArtikalRepository interface { PostaviDobavljaceArtikla(ctx context.Context, artikalID int64, dobavljaciID []int64) error // PoveziDobavljaca dodaje vezu artikal–dobavljač ako ne postoji (auto pri nabavci) PoveziDobavljaca(ctx context.Context, artikalID, dobavljacID int64) error + // OdveziDobavljaca uklanja vezu artikal–dobavljač + OdveziDobavljaca(ctx context.Context, artikalID, dobavljacID int64) error // SveDobavljaceArtikala vraća mapu artikal_id → lista dobavljac_id (za filter u nabavci) SveDobavljaceArtikala(ctx context.Context) (map[int64][]int64, error) } diff --git a/internal/db/sqlite/artikal.go b/internal/db/sqlite/artikal.go index 2f690b3..fabf743 100644 --- a/internal/db/sqlite/artikal.go +++ b/internal/db/sqlite/artikal.go @@ -362,6 +362,16 @@ func (r *ArtikalRepo) PoveziDobavljaca(ctx context.Context, artikalID, dobavljac return nil } +// OdveziDobavljaca uklanja vezu artikal–dobavljač +func (r *ArtikalRepo) OdveziDobavljaca(ctx context.Context, artikalID, dobavljacID int64) error { + _, err := r.db.ExecContext(ctx, + "DELETE FROM artikal_dobavljac WHERE artikal_id = ? AND dobavljac_id = ?", artikalID, dobavljacID) + if err != nil { + return fmt.Errorf("ntech: ArtikalRepo.OdveziDobavljaca: %w", err) + } + return nil +} + // SveDobavljaceArtikala vraća mapu artikal_id → lista dobavljac_id func (r *ArtikalRepo) SveDobavljaceArtikala(ctx context.Context) (map[int64][]int64, error) { redovi, err := r.db.QueryContext(ctx, diff --git a/internal/handler/kes.go b/internal/handler/kes.go index 62e6464..ab6ff9d 100644 --- a/internal/handler/kes.go +++ b/internal/handler/kes.go @@ -7,6 +7,7 @@ import ( "log/slog" "math" "net/http" + "strings" ) var bazniSabloni = []string{ @@ -62,6 +63,8 @@ var sablonskeFunkcije = template.FuncMap{ "dinariCeli": func(v float64) string { return formatirajDinare(v, 0) }, + // telefon formatira srpski broj telefona radi lakšeg čitanja: "0641234567" → "064 123 4567" + "telefon": formatirajTelefon, // statusPre vraća true ako je `a` pre `b` u redosledu statusa "statusPre": func(a, b string, statusi []string) bool { ia, ib := -1, -1 @@ -219,3 +222,60 @@ func formatirajDinare(v float64, decimale int) string { } return rezultat } + +// formatirajTelefon formatira srpski broj telefona radi lakšeg čitanja: +// pozivni broj odvojen kosom crtom, ostatak grupisan crticom. +// Primeri: "0641234567" → "064/123-4567", "+381641234567" → "+381 64/123-4567". +// Ako format nije prepoznat, vraća original. +func formatirajTelefon(s string) string { + s = strings.TrimSpace(s) + if s == "" { + return "" + } + + // izdvoj cifre i zapamti da li je međunarodni (+) + medjunarodni := strings.HasPrefix(s, "+") || strings.HasPrefix(s, "00") + var cifre []rune + for _, c := range s { + if c >= '0' && c <= '9' { + cifre = append(cifre, c) + } + } + d := string(cifre) + + // međunarodni srpski prefiks (381): "+381 64/123-4567" + if medjunarodni { + d = strings.TrimPrefix(d, "00") + if strings.HasPrefix(d, "381") { + ostatak := d[3:] // bez vodeće nule, npr. "641234567" + if len(ostatak) < 7 || len(ostatak) > 9 { + return s + } + return "+381 " + ostatak[:2] + "/" + grupisiTelefon(ostatak[2:]) + } + return s // strani broj — ne diramo + } + + // lokalni format: očekujemo vodeću nulu i 8–10 cifara ukupno + if !strings.HasPrefix(d, "0") || len(d) < 8 || len(d) > 10 { + return s + } + // pozivni (3 cifre, npr. 064/011) "/" ostatak grupisan crticom + return d[:3] + "/" + grupisiTelefon(d[3:]) +} + +// grupisiTelefon deli niz cifara u grupe od po 3 crticom (poslednja može 4) — "1234567" → "123-4567" +func grupisiTelefon(d string) string { + if len(d) <= 4 { + return d + } + var delovi []string + delovi = append(delovi, d[:3]) + ostatak := d[3:] + for len(ostatak) > 4 { + delovi = append(delovi, ostatak[:3]) + ostatak = ostatak[3:] + } + delovi = append(delovi, ostatak) + return strings.Join(delovi, "-") +} diff --git a/internal/handler/magacin.go b/internal/handler/magacin.go index 1776fb5..e8a7e52 100644 --- a/internal/handler/magacin.go +++ b/internal/handler/magacin.go @@ -2,6 +2,7 @@ package handler import ( "errors" + "log/slog" "net/http" "strconv" @@ -220,8 +221,10 @@ func (h *Handler) VratiArtikal(w http.ResponseWriter, r *http.Request) { // PodaciMagacinskeKartice su podaci za karticu jednog artikla type PodaciMagacinskeKartice struct { model.PodaciStranice - Artikal model.Artikal - Promene []model.MagacinskaPromenaSaDetaljem + Artikal model.Artikal + Promene []model.MagacinskaPromenaSaDetaljem + Dobavljaci []model.Dobavljac // dobavljači vezani za artikal + DostupniDobavljaci []model.Dobavljac // dobavljači koji još nisu vezani (za dodavanje) } // MagacinskaKartica prikazuje sve promene stanja za jedan artikal @@ -250,13 +253,67 @@ func (h *Handler) MagacinskaKartica(w http.ResponseWriter, r *http.Request) { return } + // dobavljači: vezani za artikal i oni koji još nisu vezani (za padajući izbor) + sviDobavljaci, _ := h.DobavljaciRepo.Lista(r.Context(), "") + vezaniIDs, _ := h.Artikli.DobavljaciArtikla(r.Context(), id) + vezanSet := map[int64]bool{} + for _, did := range vezaniIDs { + vezanSet[did] = true + } + var vezani, dostupni []model.Dobavljac + for _, d := range sviDobavljaci { + if vezanSet[d.ID] { + vezani = append(vezani, d) + } else { + dostupni = append(dostupni, d) + } + } + ps := h.popuniPodaciStranice(r, podesavanja) ps.Stranica = "magacin" ps.NaslovStranice = "Kartica: " + artikal.Naziv h.renderujTemplate(w, "magacin_kartica", PodaciMagacinskeKartice{ - PodaciStranice: ps, - Artikal: *artikal, - Promene: promene, + PodaciStranice: ps, + Artikal: *artikal, + Promene: promene, + Dobavljaci: vezani, + DostupniDobavljaci: dostupni, }) } + +// DodajDobavljacaArtiklu veže izabranog dobavljača za artikal +func (h *Handler) DodajDobavljacaArtiklu(w http.ResponseWriter, r *http.Request) { + id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) + if err != nil { + http.Error(w, "Neispravan ID artikla", http.StatusBadRequest) + return + } + dobID, err := strconv.ParseInt(r.FormValue("dobavljac_id"), 10, 64) + if err != nil { + http.Redirect(w, r, "/magacin/kartica/"+chi.URLParam(r, "id"), http.StatusSeeOther) + return + } + if e := h.Artikli.PoveziDobavljaca(r.Context(), id, dobID); e != nil { + slog.Error("vezivanje dobavljača nije uspelo", "artikal_id", id, "error", e) + } + http.Redirect(w, r, "/magacin/kartica/"+chi.URLParam(r, "id"), http.StatusSeeOther) +} + +// ObrisiDobavljacaArtikla uklanja vezu dobavljača sa artiklom +func (h *Handler) ObrisiDobavljacaArtikla(w http.ResponseWriter, r *http.Request) { + id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) + if err != nil { + http.Error(w, "Neispravan ID artikla", http.StatusBadRequest) + return + } + dobID, err := strconv.ParseInt(r.FormValue("dobavljac_id"), 10, 64) + if err != nil { + http.Redirect(w, r, "/magacin/kartica/"+chi.URLParam(r, "id"), http.StatusSeeOther) + return + } + if e := h.Artikli.OdveziDobavljaca(r.Context(), id, dobID); e != nil { + slog.Error("uklanjanje dobavljača nije uspelo", "artikal_id", id, "error", e) + } + http.Redirect(w, r, "/magacin/kartica/"+chi.URLParam(r, "id"), http.StatusSeeOther) +} diff --git a/web/templates/stranice/dobavljaci.html b/web/templates/stranice/dobavljaci.html index 6dc9541..303718b 100644 --- a/web/templates/stranice/dobavljaci.html +++ b/web/templates/stranice/dobavljaci.html @@ -48,7 +48,7 @@ {{if .KontaktOsoba}}{{.KontaktOsoba}}{{else}}—{{end}}