From 7eb472b9e63b4e6dfdb9ea6f42b1c5bb279e406d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20Markovi=C4=87?= Date: Sun, 21 Jun 2026 01:34:15 +0200 Subject: [PATCH] =?UTF-8?q?Kartica=20artikla:=20prikaz=20vezanih=20dobavlj?= =?UTF-8?q?a=C4=8Da=20sa=20dodavanjem/uklanjanjem;=20helper=20telefon=20(0?= =?UTF-8?q?64/123-4567);=20CLAUDE.md=20frontend=20i=20formatiranje=20sekci?= =?UTF-8?q?je?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/ntech/main.go | 2 + internal/db/repository.go | 2 + internal/db/sqlite/artikal.go | 10 +++ internal/handler/kes.go | 60 +++++++++++++++++ internal/handler/magacin.go | 67 +++++++++++++++++-- web/templates/stranice/dobavljaci.html | 4 +- web/templates/stranice/klijenti.html | 4 +- web/templates/stranice/magacin_kartica.html | 49 ++++++++++++++ web/templates/stranice/prodaja_stampa.html | 2 +- web/templates/stranice/servis_otpremnica.html | 6 +- web/templates/stranice/servis_predracun.html | 6 +- web/templates/stranice/servis_stampa.html | 2 +- .../stranice/servis_status_javni.html | 2 +- 13 files changed, 198 insertions(+), 18 deletions(-) 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}} - {{if .Telefon}}{{.Telefon}}{{else}}—{{end}} + {{if .Telefon}}{{telefon .Telefon}}{{else}}—{{end}} {{if .Email}}{{.Email}}{{else}}—{{end}} @@ -109,7 +109,7 @@ {{end}} {{if .Telefon}}
- Telefon: {{.Telefon}} + Telefon: {{telefon .Telefon}}
{{end}} {{if .Email}} diff --git a/web/templates/stranice/klijenti.html b/web/templates/stranice/klijenti.html index 42c22c6..f00ec84 100644 --- a/web/templates/stranice/klijenti.html +++ b/web/templates/stranice/klijenti.html @@ -73,7 +73,7 @@ {{end}} - {{if .Telefon}}{{.Telefon}}{{else}}—{{end}} + {{if .Telefon}}{{telefon .Telefon}}{{else}}—{{end}} {{if .Email}}{{.Email}}{{else}}—{{end}} @@ -141,7 +141,7 @@
{{if .Telefon}}
- Telefon: {{.Telefon}} + Telefon: {{telefon .Telefon}}
{{end}} {{if .Email}} diff --git a/web/templates/stranice/magacin_kartica.html b/web/templates/stranice/magacin_kartica.html index c56eb30..0c63d0a 100644 --- a/web/templates/stranice/magacin_kartica.html +++ b/web/templates/stranice/magacin_kartica.html @@ -39,6 +39,55 @@
+ +
+
+ Dobavljači + {{len .Dobavljaci}} +
+ + {{if .Dobavljaci}} +
+ {{range .Dobavljaci}} +
+
+
{{.Naziv}}
+ {{if or .Telefon .KontaktOsoba}} +
+ {{if .KontaktOsoba}}{{.KontaktOsoba}}{{end}}{{if and .KontaktOsoba .Telefon}} · {{end}}{{if .Telefon}}{{telefon .Telefon}}{{end}} +
+ {{end}} +
+ {{if index $.Dozvole "artikal.izmeni"}} +
+ + +
+ {{end}} +
+ {{end}} +
+ {{else}} +
Nijedan dobavljač nije vezan za ovaj artikal.
+ {{end}} + + {{if index .Dozvole "artikal.izmeni"}} + {{if .DostupniDobavljaci}} +
+ + +
+ {{else}} +
Svi dobavljači su već vezani za ovaj artikal.
+ {{end}} + {{end}} +
+
diff --git a/web/templates/stranice/prodaja_stampa.html b/web/templates/stranice/prodaja_stampa.html index 993f058..ebd0321 100644 --- a/web/templates/stranice/prodaja_stampa.html +++ b/web/templates/stranice/prodaja_stampa.html @@ -46,7 +46,7 @@ {{end}} {{if .Adresa}}
{{.Adresa}}
{{end}} {{if .Telefon}} -
{{.Telefon}}
+
{{telefon .Telefon}}
{{end}} {{if .PIB}}
PIB: {{.PIB}}
{{end}} diff --git a/web/templates/stranice/servis_otpremnica.html b/web/templates/stranice/servis_otpremnica.html index 0c4e62f..719e327 100644 --- a/web/templates/stranice/servis_otpremnica.html +++ b/web/templates/stranice/servis_otpremnica.html @@ -84,7 +84,7 @@
{{if .Podnazlov}}{{.Podnazlov}}
{{end}} {{if .Adresa}}{{.Adresa}}
{{end}} - {{if .Telefon}}Tel: {{.Telefon}}
{{end}} + {{if .Telefon}}Tel: {{telefon .Telefon}}
{{end}} {{if .PIB}}PIB: {{.PIB}}{{end}}
@@ -112,7 +112,7 @@
{{if .NazivFirme}}{{.NazivFirme}}{{else}}—{{end}}
{{if .Adresa}}{{.Adresa}}
{{end}} - {{if .Telefon}}{{.Telefon}}{{end}} + {{if .Telefon}}{{telefon .Telefon}}{{end}}
@@ -121,7 +121,7 @@
{{.KlijentNaziv}}
{{if .Klijent}} - {{if .Klijent.Telefon}}Tel: {{.Klijent.Telefon}}
{{end}} + {{if .Klijent.Telefon}}Tel: {{telefon .Klijent.Telefon}}
{{end}} {{if .Klijent.Email}}{{.Klijent.Email}}
{{end}} {{if .Klijent.Mesto}}{{.Klijent.Mesto}}{{end}} {{end}} diff --git a/web/templates/stranice/servis_predracun.html b/web/templates/stranice/servis_predracun.html index a394abe..579cdd1 100644 --- a/web/templates/stranice/servis_predracun.html +++ b/web/templates/stranice/servis_predracun.html @@ -83,7 +83,7 @@
{{if .Podnazlov}}{{.Podnazlov}}
{{end}} {{if .Adresa}}{{.Adresa}}
{{end}} - {{if .Telefon}}Tel: {{.Telefon}}
{{end}} + {{if .Telefon}}Tel: {{telefon .Telefon}}
{{end}} {{if .PIB}}PIB: {{.PIB}}{{end}}
@@ -109,7 +109,7 @@
{{if .NazivFirme}}{{.NazivFirme}}{{else}}—{{end}}
{{if .Adresa}}{{.Adresa}}
{{end}} - {{if .Telefon}}{{.Telefon}}{{end}} + {{if .Telefon}}{{telefon .Telefon}}{{end}}
@@ -118,7 +118,7 @@
{{.KlijentNaziv}}
{{if .Klijent}} - {{if .Klijent.Telefon}}Tel: {{.Klijent.Telefon}}
{{end}} + {{if .Klijent.Telefon}}Tel: {{telefon .Klijent.Telefon}}
{{end}} {{if .Klijent.Email}}{{.Klijent.Email}}
{{end}} {{if .Klijent.Mesto}}{{.Klijent.Mesto}}{{end}} {{end}} diff --git a/web/templates/stranice/servis_stampa.html b/web/templates/stranice/servis_stampa.html index 5b2d11c..4e22b6f 100644 --- a/web/templates/stranice/servis_stampa.html +++ b/web/templates/stranice/servis_stampa.html @@ -76,7 +76,7 @@
{{if .Podnazlov}}{{.Podnazlov}}
{{end}} {{if .Adresa}}{{.Adresa}}
{{end}} - {{if .Telefon}}Tel: {{.Telefon}}
{{end}} + {{if .Telefon}}Tel: {{telefon .Telefon}}
{{end}} {{if .PIB}}PIB: {{.PIB}}{{end}}
diff --git a/web/templates/stranice/servis_status_javni.html b/web/templates/stranice/servis_status_javni.html index c39f643..fb1ace0 100644 --- a/web/templates/stranice/servis_status_javni.html +++ b/web/templates/stranice/servis_status_javni.html @@ -198,7 +198,7 @@ {{if .Telefon}}
Kontakt
- {{.Telefon}} + {{telefon .Telefon}} {{if .Adresa}}
{{.Adresa}}
{{end}}
{{end}}