diff --git a/cmd/ntech/main.go b/cmd/ntech/main.go index 922ba93..31efa12 100644 --- a/cmd/ntech/main.go +++ b/cmd/ntech/main.go @@ -159,7 +159,7 @@ func main() { // čuva odabrani FS (disk ili embed) za hot-reload u razvoju i za keš u produkciji h.TemplatesFS = templFS - if ntechEnv := os.Getenv("NTECH_ENV"); ntechEnv == "production" || ntechEnv == "demo" { + if ntechEnv := os.Getenv("NTECH_ENV"); ntechEnv == "production" { kes, err := handler.KreirajKes(templFS) if err != nil { slog.Error("Greška pri kreiranju keša šablona", "error", err) diff --git a/internal/db/sqlite/artikal.go b/internal/db/sqlite/artikal.go index 48b76fd..0bcc94b 100644 --- a/internal/db/sqlite/artikal.go +++ b/internal/db/sqlite/artikal.go @@ -30,7 +30,7 @@ func (r *ArtikalRepo) Lista(ctx context.Context, filter db.ArtikalFilter) ([]mod SELECT a.id, a.kategorija_id, a.sifra, a.barkod, a.naziv, a.opis, a.tip, a.jedinica_mere, a.kolicina, a.kolicina_min, a.lokacija, - a.nabavna_cena, a.prodajna_cena, a.pdv_stopa, a.marza, a.napomena, a.datum_unosa, a.arhiviran, + a.nabavna_cena, a.prodajna_cena, a.pdv_stopa, a.cena_sa_pdv, a.marza, a.napomena, a.datum_unosa, a.arhiviran, COALESCE(k.naziv, '') as kategorija_naziv, k.marza as kategorija_marza FROM artikli a LEFT JOIN kategorije k ON a.kategorija_id = k.id @@ -93,7 +93,7 @@ func (r *ArtikalRepo) Lista(ctx context.Context, filter db.ArtikalFilter) ([]mod err := redovi.Scan( &a.ID, &kategorijaID, &sifra, &barkod, &a.Naziv, &a.Opis, &a.Tip, &a.JedinicaMere, &a.Kolicina, &a.KolicinMin, &a.Lokacija, - &a.NabavnaCena, &a.ProdajnaCena, &a.PdvStopa, &marza, &a.Napomena, &a.DatumUnosa, &arhiviran, + &a.NabavnaCena, &a.ProdajnaCena, &a.PdvStopa, &a.CenaSaPdv, &marza, &a.Napomena, &a.DatumUnosa, &arhiviran, &a.KategorijaNaziv, &katMarza, ) if err != nil { @@ -136,11 +136,11 @@ func (r *ArtikalRepo) DohvatiID(ctx context.Context, id int64) (*model.Artikal, err := r.db.QueryRowContext(ctx, ` SELECT id, kategorija_id, sifra, barkod, naziv, opis, tip, jedinica_mere, kolicina, kolicina_min, - lokacija, nabavna_cena, prodajna_cena, pdv_stopa, marza, napomena, datum_unosa, arhiviran + lokacija, nabavna_cena, prodajna_cena, pdv_stopa, cena_sa_pdv, marza, napomena, datum_unosa, arhiviran FROM artikli WHERE id = ?`, id).Scan( &a.ID, &kategorijaID, &sifra, &barkod, &a.Naziv, &a.Opis, &a.Tip, &a.JedinicaMere, &a.Kolicina, &a.KolicinMin, &a.Lokacija, - &a.NabavnaCena, &a.ProdajnaCena, &a.PdvStopa, &marza, &a.Napomena, &a.DatumUnosa, &arhiviran, + &a.NabavnaCena, &a.ProdajnaCena, &a.PdvStopa, &a.CenaSaPdv, &marza, &a.Napomena, &a.DatumUnosa, &arhiviran, ) if err != nil { return nil, fmt.Errorf("ntech: ArtikalRepo.DohvatiID: %w", err) @@ -176,10 +176,10 @@ func (r *ArtikalRepo) Kreiraj(ctx context.Context, a *model.Artikal) (int64, err rezultat, err := r.db.ExecContext(ctx, ` INSERT INTO artikli (kategorija_id, sifra, barkod, naziv, opis, tip, jedinica_mere, kolicina, kolicina_min, lokacija, - nabavna_cena, prodajna_cena, pdv_stopa, marza, napomena) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + nabavna_cena, prodajna_cena, pdv_stopa, cena_sa_pdv, marza, napomena) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, a.KategorijaID, sifra, barkod, a.Naziv, a.Opis, a.Tip, a.JedinicaMere, a.Kolicina, a.KolicinMin, - a.Lokacija, a.NabavnaCena, a.ProdajnaCena, a.PdvStopa, a.Marza, a.Napomena, + a.Lokacija, a.NabavnaCena, a.ProdajnaCena, a.PdvStopa, a.CenaSaPdv, a.Marza, a.Napomena, ) if err != nil { return 0, fmt.Errorf("ntech: ArtikalRepo.Kreiraj: %w", err) @@ -207,11 +207,11 @@ func (r *ArtikalRepo) Izmeni(ctx context.Context, a *model.Artikal) error { UPDATE artikli SET kategorija_id = ?, sifra = ?, barkod = ?, naziv = ?, opis = ?, tip = ?, jedinica_mere = ?, kolicina = ?, kolicina_min = ?, lokacija = ?, - nabavna_cena = ?, prodajna_cena = ?, pdv_stopa = ?, marza = ?, napomena = ? - WHERE id = ?`, + nabavna_cena = ?, prodajna_cena = ?, pdv_stopa = ?, cena_sa_pdv = ?, marza = ?, napomena = ? + WHERE id = ?`, a.KategorijaID, sifra, barkod, a.Naziv, a.Opis, a.Tip, a.JedinicaMere, a.Kolicina, a.KolicinMin, a.Lokacija, - a.NabavnaCena, a.ProdajnaCena, a.PdvStopa, a.Marza, a.Napomena, a.ID, + a.NabavnaCena, a.ProdajnaCena, a.PdvStopa, a.CenaSaPdv, a.Marza, a.Napomena, a.ID, ) if err != nil { return fmt.Errorf("ntech: ArtikalRepo.Izmeni: %w", err) diff --git a/internal/handler/magacin_forma.go b/internal/handler/magacin_forma.go index 0fef929..8293fe0 100644 --- a/internal/handler/magacin_forma.go +++ b/internal/handler/magacin_forma.go @@ -62,7 +62,7 @@ func (h *Handler) NoviArtikal(w http.ResponseWriter, r *http.Request) { PodaciStranice: ps, Kategorije: kategorije, Dobavljaci: dobavljaci, - Artikal: model.Artikal{Sifra: predlogSifre, Tip: tip, JedinicaMere: "kom"}, + Artikal: model.Artikal{Sifra: predlogSifre, Tip: tip, JedinicaMere: "kom", PdvStopa: 20.0}, Izmena: false, }) } @@ -389,6 +389,15 @@ func parseFormuArtikla(r *http.Request) (model.Artikal, string) { artikal.NabavnaCena = v } + // PDV stopa — podrazumevano 20% + pdvStopa := 20.0 + if p := r.FormValue("pdv_stopa"); p != "" { + if v, err := strconv.ParseFloat(p, 64); err == nil && v >= 0 { + pdvStopa = v + } + } + artikal.PdvStopa = pdvStopa + if c := r.FormValue("prodajna_cena"); c != "" { v, err := strconv.ParseFloat(c, 64) if err != nil || v < 0 { @@ -413,6 +422,21 @@ func parseFormuArtikla(r *http.Request) (model.Artikal, string) { } } + // kalkulacija cena_sa_pdv na osnovu unetog polja + if c := r.FormValue("cena_sa_pdv"); c != "" { + // korisnik je uneo bruto cenu → izračunaj neto + if v, err := strconv.ParseFloat(c, 64); err == nil && v >= 0 { + artikal.CenaSaPdv = v + // ako prodajna_cena nije uneta ručno, izračunaj je + if r.FormValue("prodajna_cena") == "" { + artikal.ProdajnaCena = v / (1 + artikal.PdvStopa/100) + } + } + } else { + // korisnik je uneo neto cenu → izračunaj bruto + artikal.CenaSaPdv = artikal.ProdajnaCena * (1 + artikal.PdvStopa/100) + } + return artikal, "" } diff --git a/internal/model/artikal.go b/internal/model/artikal.go index f0591b1..3b86cc8 100644 --- a/internal/model/artikal.go +++ b/internal/model/artikal.go @@ -25,6 +25,7 @@ type Artikal struct { NabavnaCena float64 ProdajnaCena float64 PdvStopa float64 + CenaSaPdv float64 // prodajna cena sa PDV-om (bruto); = ProdajnaCena * (1 + PdvStopa/100) Marza *float64 // podrazumevana marža (%) za kalkulaciju; NULL = nije postavljeno Napomena string DatumUnosa time.Time @@ -37,14 +38,14 @@ func (a Artikal) PratiLager() bool { return a.Tip == TipProizvod || a.Tip == "" } -// CenaBezPdv izračunava prodajnu cenu bez PDV-a +// CenaBezPdv vraća prodajnu cenu bez PDV-a (neto) func (a Artikal) CenaBezPdv() float64 { - return a.ProdajnaCena / (1 + a.PdvStopa/100) + return a.ProdajnaCena } // PdvIznos izračunava iznos PDV-a za jednu jedinicu func (a Artikal) PdvIznos() float64 { - return a.ProdajnaCena - a.CenaBezPdv() + return a.CenaSaPdv - a.ProdajnaCena } // Kategorija predstavlja kategoriju artikala diff --git a/internal/model/model_test.go b/internal/model/model_test.go index a8693fe..d47bdb4 100644 --- a/internal/model/model_test.go +++ b/internal/model/model_test.go @@ -15,19 +15,20 @@ func ptr(v float64) *float64 { return &v } func TestArtikalCenaBezPdvIPdvIznos(t *testing.T) { testovi := []struct { naziv string - prodajna float64 + prodajna float64 // neto + cenaSaPdv float64 // bruto stopa float64 ocekBezPdv float64 ocekPdvIznos float64 }{ - {"stopa 20%", 120, 20, 100, 20}, - {"stopa 0%", 100, 0, 100, 0}, - {"stopa 10%", 110, 10, 100, 10}, - {"nula cena", 0, 20, 0, 0}, + {"stopa 20%", 100, 120, 20, 100, 20}, + {"stopa 0%", 100, 100, 0, 100, 0}, + {"stopa 10%", 100, 110, 10, 100, 10}, + {"nula cena", 0, 0, 20, 0, 0}, } for _, tt := range testovi { t.Run(tt.naziv, func(t *testing.T) { - a := Artikal{ProdajnaCena: tt.prodajna, PdvStopa: tt.stopa} + a := Artikal{ProdajnaCena: tt.prodajna, CenaSaPdv: tt.cenaSaPdv, PdvStopa: tt.stopa} if got := a.CenaBezPdv(); !jednako(got, tt.ocekBezPdv) { t.Errorf("CenaBezPdv() = %v, očekivano %v", got, tt.ocekBezPdv) } diff --git a/migrations/087_artikal_cena_sa_pdv.sql b/migrations/087_artikal_cena_sa_pdv.sql new file mode 100644 index 0000000..d13d385 --- /dev/null +++ b/migrations/087_artikal_cena_sa_pdv.sql @@ -0,0 +1,4 @@ +ALTER TABLE artikli ADD COLUMN cena_sa_pdv REAL NOT NULL DEFAULT 0; + +UPDATE artikli +SET cena_sa_pdv = ROUND(prodajna_cena * (1 + pdv_stopa / 100), 2); diff --git a/web/templates/stranice/magacin.html b/web/templates/stranice/magacin.html index 49a4320..de4423a 100644 --- a/web/templates/stranice/magacin.html +++ b/web/templates/stranice/magacin.html @@ -80,6 +80,7 @@