320 lines
9.4 KiB
Go
320 lines
9.4 KiB
Go
package handler
|
|
|
|
import (
|
|
"errors"
|
|
"log/slog"
|
|
"net/http"
|
|
"strconv"
|
|
|
|
"ntech/internal/db"
|
|
"ntech/internal/db/sqlite"
|
|
"ntech/internal/model"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
// PodaciMagacina su podaci za stranicu magacina
|
|
type PodaciMagacina struct {
|
|
model.PodaciStranice
|
|
Artikli []model.ArtikalSaKategorijom
|
|
Kategorije []model.Kategorija
|
|
Filter db.ArtikalFilter
|
|
KategorijaIDStr string
|
|
Sacuvano bool
|
|
Obrisan bool
|
|
Arhiviran bool
|
|
Vracen bool
|
|
Greska bool
|
|
PrikazArhivirani bool // true → lista prikazuje arhivirane umesto aktivnih
|
|
Premesten bool
|
|
StranicaBr int
|
|
UkupnoStranica int
|
|
UkupnoArtikala int
|
|
StranicaPrev int
|
|
StranicaNext int
|
|
StranicaQueryUrl string // čuva filtere za linkove paginacije
|
|
}
|
|
|
|
// Magacin renderuje listu artikala
|
|
func (h *Handler) Magacin(w http.ResponseWriter, r *http.Request) {
|
|
podesavanja, err := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
|
|
if err != nil {
|
|
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
filter := db.ArtikalFilter{
|
|
Pretraga: r.URL.Query().Get("pretraga"),
|
|
SamoKriticni: r.URL.Query().Get("kriticni") == "1",
|
|
Arhivirani: r.URL.Query().Get("arhivirani") == "1",
|
|
}
|
|
|
|
katIDStr := ""
|
|
if katID := r.URL.Query().Get("kategorija"); katID != "" {
|
|
id, err := strconv.ParseInt(katID, 10, 64)
|
|
if err == nil {
|
|
filter.KategorijaID = &id
|
|
katIDStr = katID
|
|
}
|
|
}
|
|
|
|
const pageSize = 50
|
|
stranicaBr := 1
|
|
if p := r.URL.Query().Get("stranica"); p != "" {
|
|
if v, err := strconv.Atoi(p); err == nil && v > 0 {
|
|
stranicaBr = v
|
|
}
|
|
}
|
|
filter.Limit = pageSize
|
|
filter.Offset = (stranicaBr - 1) * pageSize
|
|
|
|
artikli, err := h.Artikli.Lista(r.Context(), filter)
|
|
if err != nil {
|
|
http.Error(w, "Greška pri učitavanju artikala", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
ukupno, err := h.Artikli.PrebrojiPoFilteru(r.Context(), filter)
|
|
if err != nil {
|
|
http.Error(w, "Greška pri učitavanju artikala", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
ukupnoStranica := (ukupno + pageSize - 1) / pageSize
|
|
|
|
kategorije, err := h.KategorijeRepo.Lista(r.Context())
|
|
if err != nil {
|
|
http.Error(w, "Greška pri učitavanju kategorija", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
ps := h.popuniPodaciStranice(r, podesavanja)
|
|
ps.Stranica = "magacin"
|
|
ps.NaslovStranice = "Magacin"
|
|
|
|
// izgradi query string za paginaciju (čuva filtere)
|
|
queryDelići := ""
|
|
if v := filter.Pretraga; v != "" {
|
|
queryDelići += "&pretraga=" + v
|
|
}
|
|
if katIDStr != "" {
|
|
queryDelići += "&kategorija=" + katIDStr
|
|
}
|
|
if filter.SamoKriticni {
|
|
queryDelići += "&kriticni=1"
|
|
}
|
|
if filter.Arhivirani {
|
|
queryDelići += "&arhivirani=1"
|
|
}
|
|
|
|
stranicaPrev := stranicaBr - 1
|
|
if stranicaPrev < 1 {
|
|
stranicaPrev = 1
|
|
}
|
|
stranicaNext := stranicaBr + 1
|
|
if stranicaNext > ukupnoStranica {
|
|
stranicaNext = ukupnoStranica
|
|
}
|
|
|
|
podaci := PodaciMagacina{
|
|
PodaciStranice: ps,
|
|
Artikli: artikli,
|
|
Kategorije: kategorije,
|
|
Filter: filter,
|
|
KategorijaIDStr: katIDStr,
|
|
Sacuvano: r.URL.Query().Get("sacuvano") == "1",
|
|
Obrisan: r.URL.Query().Get("obrisan") == "1",
|
|
Arhiviran: r.URL.Query().Get("arhiviran") == "1",
|
|
Vracen: r.URL.Query().Get("vracen") == "1",
|
|
Greska: r.URL.Query().Get("greska") == "1",
|
|
PrikazArhivirani: filter.Arhivirani,
|
|
Premesten: r.URL.Query().Get("premesten") == "1",
|
|
StranicaBr: stranicaBr,
|
|
UkupnoStranica: ukupnoStranica,
|
|
UkupnoArtikala: ukupno,
|
|
StranicaPrev: stranicaPrev,
|
|
StranicaNext: stranicaNext,
|
|
StranicaQueryUrl: queryDelići,
|
|
}
|
|
|
|
h.renderujTemplate(w, "magacin", podaci)
|
|
}
|
|
|
|
// PremestiArtikal menja kategoriju artikla (premeštanje u drugu kategoriju).
|
|
// Prazno polje kategorija_id znači premeštanje u "bez kategorije".
|
|
func (h *Handler) PremestiArtikal(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := h.zahtevajDozvolu(w, r, "artikal.premesti"); !ok {
|
|
return
|
|
}
|
|
id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
|
|
if err != nil {
|
|
http.Error(w, "Neispravan ID artikla", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
var kategorijaID *int64
|
|
if v := r.FormValue("kategorija_id"); v != "" {
|
|
kid, err := strconv.ParseInt(v, 10, 64)
|
|
if err != nil {
|
|
http.Error(w, "Neispravna kategorija", http.StatusBadRequest)
|
|
return
|
|
}
|
|
kategorijaID = &kid
|
|
}
|
|
|
|
if err := h.Artikli.PremestiKategoriju(r.Context(), id, kategorijaID); err != nil {
|
|
http.Error(w, "Greška pri premeštanju artikla", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, "/magacin?premesten=1", http.StatusSeeOther)
|
|
}
|
|
|
|
// ObrisiArtikal briše artikal po ID-u
|
|
func (h *Handler) ObrisiArtikal(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := h.zahtevajDozvolu(w, r, "artikal.obrisi"); !ok {
|
|
return
|
|
}
|
|
idStr := chi.URLParam(r, "id")
|
|
id, err := strconv.ParseInt(idStr, 10, 64)
|
|
if err != nil {
|
|
http.Error(w, "Neispravan ID artikla", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := h.Artikli.Obrisi(r.Context(), id); err != nil {
|
|
// artikal je u prometu — ne brišemo ga, već ga arhiviramo
|
|
if errors.Is(err, db.ErrArtikalUUpotrebi) {
|
|
if err := h.Artikli.Arhiviraj(r.Context(), id); err != nil {
|
|
http.Redirect(w, r, "/magacin?greska=1", http.StatusSeeOther)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/magacin?arhiviran=1", http.StatusSeeOther)
|
|
return
|
|
}
|
|
http.Redirect(w, r, "/magacin?greska=1", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, "/magacin?obrisan=1", http.StatusSeeOther)
|
|
}
|
|
|
|
// VratiArtikal poništava arhiviranje i vraća artikal u aktivnu listu
|
|
func (h *Handler) VratiArtikal(w http.ResponseWriter, r *http.Request) {
|
|
if _, ok := h.zahtevajDozvolu(w, r, "artikal.obrisi"); !ok {
|
|
return
|
|
}
|
|
id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64)
|
|
if err != nil {
|
|
http.Error(w, "Neispravan ID artikla", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
if err := h.Artikli.Vrati(r.Context(), id); err != nil {
|
|
http.Redirect(w, r, "/magacin?arhivirani=1&greska=1", http.StatusSeeOther)
|
|
return
|
|
}
|
|
|
|
http.Redirect(w, r, "/magacin?arhivirani=1&vracen=1", http.StatusSeeOther)
|
|
}
|
|
|
|
// PodaciMagacinskeKartice su podaci za karticu jednog artikla
|
|
type PodaciMagacinskeKartice struct {
|
|
model.PodaciStranice
|
|
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
|
|
func (h *Handler) MagacinskaKartica(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
|
|
}
|
|
|
|
artikal, err := h.Artikli.DohvatiID(r.Context(), id)
|
|
if err != nil {
|
|
http.Error(w, "Artikal nije pronađen", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
promene, err := h.MagacinskePromeneRepo.Lista(r.Context(), &id, 0)
|
|
if err != nil {
|
|
http.Error(w, "Greška pri učitavanju promena", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
podesavanja, err := sqlite.DohvatiSvaPodesavanja(r.Context(), h.DB)
|
|
if err != nil {
|
|
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
|
|
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,
|
|
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)
|
|
}
|