From 803e1f63412992b433c526c4bcee3bf82d44343c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dalibor=20Markovi=C4=87?= Date: Sun, 14 Jun 2026 16:23:11 +0200 Subject: [PATCH] =?UTF-8?q?feat(nabavka):=20UI=20zavisnih=20tro=C5=A1kova?= =?UTF-8?q?=20=E2=80=94=20forma=20i=20prikaz=20u=20detaljima=20(Faza=20C,?= =?UTF-8?q?=20celina=202)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - forma nabavke: sekcija „Zavisni troškovi" (slobodne stavke naziv+iznos, dodavanje/uklanjanje) + izbor metoda raspodele (po vrednosti / po količini) - JS: kalkNabavna (kalkulativna nabavna po stavci, prati server) i preracunajSve — promena troška/metoda/količine/cene preračunava prodajne svih stavki - detalji nabavke: prikaz zavisnih troškova, metoda raspodele i ukupnog iznosa - handler DetaljiNabavke dohvata troškove (DohvatiTroskove) --- internal/handler/nabavka.go | 14 ++++++ web/static/js/ntech.js | 43 +++++++++++++++-- web/templates/stranice/nabavka_detalji.html | 24 ++++++++++ web/templates/stranice/nabavka_forma.html | 51 +++++++++++++++++++-- 4 files changed, 126 insertions(+), 6 deletions(-) diff --git a/internal/handler/nabavka.go b/internal/handler/nabavka.go index ad5e367..969f179 100644 --- a/internal/handler/nabavka.go +++ b/internal/handler/nabavka.go @@ -39,6 +39,8 @@ type PodaciDetaljiNabavke struct { model.PodaciStranice Nabavka model.Nabavka Stavke []model.StavkaSaArtiklom + Troskovi []model.NabavkaTrosak + UkupanTrosak float64 DobavljacNaziv string } @@ -259,6 +261,12 @@ func (h *Handler) DetaljiNabavke(w http.ResponseWriter, r *http.Request) { return } + troskovi, err := h.NabavkeRepo.DohvatiTroskove(r.Context(), id) + if err != nil { + http.Error(w, "Greška pri učitavanju troškova", 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) @@ -277,10 +285,16 @@ func (h *Handler) DetaljiNabavke(w http.ResponseWriter, r *http.Request) { ps := h.popuniPodaciStranice(r, podesavanja) ps.Stranica = "nabavke" ps.NaslovStranice = "Detalji nabavke" + var ukupanTrosak float64 + for _, t := range troskovi { + ukupanTrosak += t.Iznos + } podaci := PodaciDetaljiNabavke{ PodaciStranice: ps, Nabavka: *nabavka, Stavke: stavke, + Troskovi: troskovi, + UkupanTrosak: ukupanTrosak, DobavljacNaziv: dobavljacNaziv, } diff --git a/web/static/js/ntech.js b/web/static/js/ntech.js index 8ff81ef..93f0394 100644 --- a/web/static/js/ntech.js +++ b/web/static/js/ntech.js @@ -189,6 +189,8 @@ document.addEventListener('alpine:init', () => { stavke: [{artikal_id: '', kolicina: 1, cena: 0, marza: 0, prodajna: 0}], artikliOpcije: [], marzaDefault: 0, + troskovi: [], // zavisni troškovi {naziv, iznos} + metodRaspodele: 'vrednost', // 'vrednost' ili 'kolicina' isMobile: false, modal: false, modalUcitavanje: false, @@ -222,6 +224,7 @@ document.addEventListener('alpine:init', () => { }, dodajStavku() { this.stavke.push({artikal_id: '', kolicina: 1, cena: 0, marza: this.marzaDefault, prodajna: 0}) + this.preracunajSve() }, // PDV stopa izabranog artikla (iz JSON liste) — za obračun prodajne cene pdvStopa(artikalId) { @@ -238,15 +241,49 @@ document.addEventListener('alpine:init', () => { } this.izracunajProdajnu(s) }, - // prodajna (sa PDV) = nabavna × (1 + marža/100) × (1 + pdvStopa/100), zaokruženo na 2 decimale - izracunajProdajnu(s) { + // ukupan zavisni trošak nabavke + ukupanTrosak() { + return this.troskovi.reduce((z, t) => z + (parseFloat(t.iznos) || 0), 0) + }, + // osnovica raspodele po izabranom metodu (zbir po svim stavkama) + osnovicaRaspodele() { + return this.stavke.reduce((z, s) => { + const kol = parseFloat(s.kolicina) || 0 + return z + (this.metodRaspodele === 'kolicina' ? kol : kol * (parseFloat(s.cena) || 0)) + }, 0) + }, + // kalkulativna nabavna cena po komadu (fakturna + raspodeljeni trošak) — isto kao server + kalkNabavna(s) { const cena = parseFloat(s.cena) || 0 + const kol = parseFloat(s.kolicina) || 0 + const trosak = this.ukupanTrosak() + const osn = this.osnovicaRaspodele() + if (trosak <= 0 || osn <= 0 || kol <= 0) return cena + const baza = this.metodRaspodele === 'kolicina' ? kol : kol * cena + const trosakPoKomadu = (trosak * (baza / osn)) / kol + return Math.round((cena + trosakPoKomadu) * 100) / 100 + }, + // prodajna (sa PDV) = kalkulativna nabavna × (1 + marža/100) × (1 + pdvStopa/100) + izracunajProdajnu(s) { + const nabavna = this.kalkNabavna(s) const marza = parseFloat(s.marza) || 0 const pdv = this.pdvStopa(s.artikal_id) - s.prodajna = Math.round(cena * (1 + marza / 100) * (1 + pdv / 100) * 100) / 100 + s.prodajna = Math.round(nabavna * (1 + marza / 100) * (1 + pdv / 100) * 100) / 100 + }, + // raspodela zavisi od svih stavki — promena troška/metoda/količine/cene preračunava sve + preracunajSve() { + this.stavke.forEach(s => this.izracunajProdajnu(s)) + }, + dodajTrosak() { + this.troskovi.push({naziv: '', iznos: 0}) + }, + ukloniTrosak(i) { + this.troskovi.splice(i, 1) + this.preracunajSve() }, ukloniStavku(i) { if (this.stavke.length > 1) this.stavke.splice(i, 1) + this.preracunajSve() }, ukupnoStavke(s) { return (parseFloat(s.kolicina) * parseFloat(s.cena) || 0).toFixed(2) diff --git a/web/templates/stranice/nabavka_detalji.html b/web/templates/stranice/nabavka_detalji.html index 3f44201..8e89f25 100644 --- a/web/templates/stranice/nabavka_detalji.html +++ b/web/templates/stranice/nabavka_detalji.html @@ -122,6 +122,30 @@ + + {{if .Troskovi}} +
+
+ Zavisni troškovi + + Raspodela: {{if eq .Nabavka.MetodRaspodele "kolicina"}}po količini{{else}}po vrednosti stavke{{end}} + +
+
+ {{range .Troskovi}} +
+ {{.Naziv}} + {{printf "%.2f" .Iznos}} din +
+ {{end}} +
+ Ukupno troškovi: + {{printf "%.2f" .UkupanTrosak}} din +
+
+
+ {{end}} +
diff --git a/web/templates/stranice/nabavka_forma.html b/web/templates/stranice/nabavka_forma.html index c1e8961..55305a8 100644 --- a/web/templates/stranice/nabavka_forma.html +++ b/web/templates/stranice/nabavka_forma.html @@ -107,11 +107,12 @@ @@ -176,11 +177,11 @@
- +
- +
@@ -205,6 +206,50 @@
+ +
+
+ Zavisni troškovi + +
+ + + +
+
Odustani