Cena sa PDV-om u magacinu + uslovno prikazivanje po modulu
- migrations/087: nova kolona cena_sa_pdv + popunjavanje postojećih - model.Artikal: polje CenaSaPdv, ispravljene CenaBezPdv/PdvIznos (neto) - sqlite/artikal.go: SELECT/Scan/INSERT/UPDATE za cena_sa_pdv (6 mesta) - magacin_forma.go: pdv_stopa iz forme, kalkulacija cena_sa_pdv, default PDV 20% - magacin.html: dinari format (25.999,00), kolona Sa PDV-om, uslovno po Moduli.pdv - magacin_forma.html: PDV dropdown + Cena sa PDV polje, uslovno po Moduli.pdv, optimizovan layout (Naziv/Jed. mere/Kategorija u 1 redu) - main.go: demo mod bez keširanja šablona (hot-reload) - model_test.go: ažurirani testovi za novo ponašanje CenaBezPdv/PdvIznos
This commit is contained in:
+1
-1
@@ -159,7 +159,7 @@ func main() {
|
|||||||
// čuva odabrani FS (disk ili embed) za hot-reload u razvoju i za keš u produkciji
|
// čuva odabrani FS (disk ili embed) za hot-reload u razvoju i za keš u produkciji
|
||||||
h.TemplatesFS = templFS
|
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)
|
kes, err := handler.KreirajKes(templFS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
slog.Error("Greška pri kreiranju keša šablona", "error", err)
|
slog.Error("Greška pri kreiranju keša šablona", "error", err)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ func (r *ArtikalRepo) Lista(ctx context.Context, filter db.ArtikalFilter) ([]mod
|
|||||||
SELECT
|
SELECT
|
||||||
a.id, a.kategorija_id, a.sifra, a.barkod, a.naziv, a.opis, a.tip, a.jedinica_mere,
|
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.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
|
COALESCE(k.naziv, '') as kategorija_naziv, k.marza as kategorija_marza
|
||||||
FROM artikli a
|
FROM artikli a
|
||||||
LEFT JOIN kategorije k ON a.kategorija_id = k.id
|
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(
|
err := redovi.Scan(
|
||||||
&a.ID, &kategorijaID, &sifra, &barkod, &a.Naziv, &a.Opis, &a.Tip, &a.JedinicaMere,
|
&a.ID, &kategorijaID, &sifra, &barkod, &a.Naziv, &a.Opis, &a.Tip, &a.JedinicaMere,
|
||||||
&a.Kolicina, &a.KolicinMin, &a.Lokacija,
|
&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,
|
&a.KategorijaNaziv, &katMarza,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -136,11 +136,11 @@ func (r *ArtikalRepo) DohvatiID(ctx context.Context, id int64) (*model.Artikal,
|
|||||||
|
|
||||||
err := r.db.QueryRowContext(ctx, `
|
err := r.db.QueryRowContext(ctx, `
|
||||||
SELECT id, kategorija_id, sifra, barkod, naziv, opis, tip, jedinica_mere, kolicina, kolicina_min,
|
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(
|
FROM artikli WHERE id = ?`, id).Scan(
|
||||||
&a.ID, &kategorijaID, &sifra, &barkod, &a.Naziv, &a.Opis, &a.Tip, &a.JedinicaMere,
|
&a.ID, &kategorijaID, &sifra, &barkod, &a.Naziv, &a.Opis, &a.Tip, &a.JedinicaMere,
|
||||||
&a.Kolicina, &a.KolicinMin, &a.Lokacija,
|
&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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ntech: ArtikalRepo.DohvatiID: %w", err)
|
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, `
|
rezultat, err := r.db.ExecContext(ctx, `
|
||||||
INSERT INTO artikli
|
INSERT INTO artikli
|
||||||
(kategorija_id, sifra, barkod, naziv, opis, tip, jedinica_mere, kolicina, kolicina_min, lokacija,
|
(kategorija_id, sifra, barkod, naziv, opis, tip, jedinica_mere, kolicina, kolicina_min, lokacija,
|
||||||
nabavna_cena, prodajna_cena, pdv_stopa, marza, napomena)
|
nabavna_cena, prodajna_cena, pdv_stopa, cena_sa_pdv, marza, napomena)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
a.KategorijaID, sifra, barkod, a.Naziv, a.Opis, a.Tip, a.JedinicaMere, a.Kolicina, a.KolicinMin,
|
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 {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("ntech: ArtikalRepo.Kreiraj: %w", err)
|
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
|
UPDATE artikli SET
|
||||||
kategorija_id = ?, sifra = ?, barkod = ?, naziv = ?, opis = ?, tip = ?, jedinica_mere = ?,
|
kategorija_id = ?, sifra = ?, barkod = ?, naziv = ?, opis = ?, tip = ?, jedinica_mere = ?,
|
||||||
kolicina = ?, kolicina_min = ?, lokacija = ?,
|
kolicina = ?, kolicina_min = ?, lokacija = ?,
|
||||||
nabavna_cena = ?, prodajna_cena = ?, pdv_stopa = ?, marza = ?, napomena = ?
|
nabavna_cena = ?, prodajna_cena = ?, pdv_stopa = ?, cena_sa_pdv = ?, marza = ?, napomena = ?
|
||||||
WHERE id = ?`,
|
WHERE id = ?`,
|
||||||
a.KategorijaID, sifra, barkod, a.Naziv, a.Opis, a.Tip, a.JedinicaMere,
|
a.KategorijaID, sifra, barkod, a.Naziv, a.Opis, a.Tip, a.JedinicaMere,
|
||||||
a.Kolicina, a.KolicinMin, a.Lokacija,
|
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 {
|
if err != nil {
|
||||||
return fmt.Errorf("ntech: ArtikalRepo.Izmeni: %w", err)
|
return fmt.Errorf("ntech: ArtikalRepo.Izmeni: %w", err)
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ func (h *Handler) NoviArtikal(w http.ResponseWriter, r *http.Request) {
|
|||||||
PodaciStranice: ps,
|
PodaciStranice: ps,
|
||||||
Kategorije: kategorije,
|
Kategorije: kategorije,
|
||||||
Dobavljaci: dobavljaci,
|
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,
|
Izmena: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -389,6 +389,15 @@ func parseFormuArtikla(r *http.Request) (model.Artikal, string) {
|
|||||||
artikal.NabavnaCena = v
|
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 != "" {
|
if c := r.FormValue("prodajna_cena"); c != "" {
|
||||||
v, err := strconv.ParseFloat(c, 64)
|
v, err := strconv.ParseFloat(c, 64)
|
||||||
if err != nil || v < 0 {
|
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, ""
|
return artikal, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type Artikal struct {
|
|||||||
NabavnaCena float64
|
NabavnaCena float64
|
||||||
ProdajnaCena float64
|
ProdajnaCena float64
|
||||||
PdvStopa 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
|
Marza *float64 // podrazumevana marža (%) za kalkulaciju; NULL = nije postavljeno
|
||||||
Napomena string
|
Napomena string
|
||||||
DatumUnosa time.Time
|
DatumUnosa time.Time
|
||||||
@@ -37,14 +38,14 @@ func (a Artikal) PratiLager() bool {
|
|||||||
return a.Tip == TipProizvod || a.Tip == ""
|
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 {
|
func (a Artikal) CenaBezPdv() float64 {
|
||||||
return a.ProdajnaCena / (1 + a.PdvStopa/100)
|
return a.ProdajnaCena
|
||||||
}
|
}
|
||||||
|
|
||||||
// PdvIznos izračunava iznos PDV-a za jednu jedinicu
|
// PdvIznos izračunava iznos PDV-a za jednu jedinicu
|
||||||
func (a Artikal) PdvIznos() float64 {
|
func (a Artikal) PdvIznos() float64 {
|
||||||
return a.ProdajnaCena - a.CenaBezPdv()
|
return a.CenaSaPdv - a.ProdajnaCena
|
||||||
}
|
}
|
||||||
|
|
||||||
// Kategorija predstavlja kategoriju artikala
|
// Kategorija predstavlja kategoriju artikala
|
||||||
|
|||||||
@@ -15,19 +15,20 @@ func ptr(v float64) *float64 { return &v }
|
|||||||
func TestArtikalCenaBezPdvIPdvIznos(t *testing.T) {
|
func TestArtikalCenaBezPdvIPdvIznos(t *testing.T) {
|
||||||
testovi := []struct {
|
testovi := []struct {
|
||||||
naziv string
|
naziv string
|
||||||
prodajna float64
|
prodajna float64 // neto
|
||||||
|
cenaSaPdv float64 // bruto
|
||||||
stopa float64
|
stopa float64
|
||||||
ocekBezPdv float64
|
ocekBezPdv float64
|
||||||
ocekPdvIznos float64
|
ocekPdvIznos float64
|
||||||
}{
|
}{
|
||||||
{"stopa 20%", 120, 20, 100, 20},
|
{"stopa 20%", 100, 120, 20, 100, 20},
|
||||||
{"stopa 0%", 100, 0, 100, 0},
|
{"stopa 0%", 100, 100, 0, 100, 0},
|
||||||
{"stopa 10%", 110, 10, 100, 10},
|
{"stopa 10%", 100, 110, 10, 100, 10},
|
||||||
{"nula cena", 0, 20, 0, 0},
|
{"nula cena", 0, 0, 20, 0, 0},
|
||||||
}
|
}
|
||||||
for _, tt := range testovi {
|
for _, tt := range testovi {
|
||||||
t.Run(tt.naziv, func(t *testing.T) {
|
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) {
|
if got := a.CenaBezPdv(); !jednako(got, tt.ocekBezPdv) {
|
||||||
t.Errorf("CenaBezPdv() = %v, očekivano %v", got, tt.ocekBezPdv)
|
t.Errorf("CenaBezPdv() = %v, očekivano %v", got, tt.ocekBezPdv)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -80,6 +80,7 @@
|
|||||||
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-jak);">Kategorija</th>
|
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-jak);">Kategorija</th>
|
||||||
<th style="padding:12px 16px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-jak);">Količina</th>
|
<th style="padding:12px 16px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-jak);">Količina</th>
|
||||||
<th style="padding:12px 16px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-jak);">Prodajna</th>
|
<th style="padding:12px 16px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-jak);">Prodajna</th>
|
||||||
|
{{if index .Moduli "pdv"}}<th style="padding:12px 16px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-jak);">Sa PDV-om</th>{{end}}
|
||||||
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-jak);">Lokacija</th>
|
<th style="padding:12px 16px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-jak);">Lokacija</th>
|
||||||
<th style="padding:12px 16px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-jak);">Akcije</th>
|
<th style="padding:12px 16px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-jak);">Akcije</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -101,7 +102,8 @@
|
|||||||
<span style="font-size:12px;color:var(--tekst-sporedni);">{{if eq .Tip "usluga"}}usluga{{else}}trošak{{end}}</span>
|
<span style="font-size:12px;color:var(--tekst-sporedni);">{{if eq .Tip "usluga"}}usluga{{else}}trošak{{end}}</span>
|
||||||
{{end}}
|
{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:12px 16px;text-align:right;font-size:14px;color:var(--tekst-glavni);">{{dinariCeli .ProdajnaCena}} din</td>
|
<td style="padding:12px 16px;text-align:right;font-size:14px;color:var(--tekst-glavni);">{{dinari .ProdajnaCena}} din</td>
|
||||||
|
{{if index $.Moduli "pdv"}}<td style="padding:12px 16px;text-align:right;font-size:14px;color:var(--tekst-glavni);">{{dinari .CenaSaPdv}} din</td>{{end}}
|
||||||
<td style="padding:12px 16px;font-size:13px;color:var(--tekst-sporedni);">
|
<td style="padding:12px 16px;font-size:13px;color:var(--tekst-sporedni);">
|
||||||
{{if .Lokacija}}{{.Lokacija}}{{else}}—{{end}}
|
{{if .Lokacija}}{{.Lokacija}}{{else}}—{{end}}
|
||||||
</td>
|
</td>
|
||||||
@@ -134,7 +136,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{{else}}
|
{{else}}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="7" style="padding:32px;text-align:center;font-size:14px;color:var(--tekst-sporedni);">
|
<td colspan="{{if index .Moduli "pdv"}}8{{else}}7{{end}}" style="padding:32px;text-align:center;font-size:14px;color:var(--tekst-sporedni);">
|
||||||
{{if or $.Filter.Pretraga $.KategorijaIDStr $.Filter.SamoKriticni}}Za datu pretragu nema stavki u bazi.{{else}}Nema stavki. <a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/magacin/novi?tip={{$.Tip}}" style="color:var(--sb-akcent);">Dodaj prvu.</a>{{end}}
|
{{if or $.Filter.Pretraga $.KategorijaIDStr $.Filter.SamoKriticni}}Za datu pretragu nema stavki u bazi.{{else}}Nema stavki. <a hx-boost="true" hx-target="#glavni-sadrzaj" hx-select="#glavni-sadrzaj" hx-swap="outerHTML" href="/magacin/novi?tip={{$.Tip}}" style="color:var(--sb-akcent);">Dodaj prvu.</a>{{end}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -192,8 +194,13 @@
|
|||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<div class="pomocni-tekst">
|
<div class="pomocni-tekst">
|
||||||
<span style="color:var(--tekst-glavni);font-weight:500;">Cena:</span> {{dinariCeli .ProdajnaCena}} din
|
<span style="color:var(--tekst-glavni);font-weight:500;">Cena:</span> {{dinari .ProdajnaCena}} din
|
||||||
</div>
|
</div>
|
||||||
|
{{if index $.Moduli "pdv"}}
|
||||||
|
<div class="pomocni-tekst">
|
||||||
|
<span style="color:var(--tekst-glavni);font-weight:500;">Sa PDV:</span> {{dinari .CenaSaPdv}} din
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
{{if .Lokacija}}
|
{{if .Lokacija}}
|
||||||
<div class="pomocni-tekst">
|
<div class="pomocni-tekst">
|
||||||
<span style="color:var(--tekst-glavni);font-weight:500;">Lokacija:</span> {{.Lokacija}}
|
<span style="color:var(--tekst-glavni);font-weight:500;">Lokacija:</span> {{.Lokacija}}
|
||||||
|
|||||||
@@ -43,44 +43,42 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- naziv -->
|
<!-- naziv / jedinica mere / kategorija — u 1 redu -->
|
||||||
<div>
|
|
||||||
<label class="polje-labela">
|
|
||||||
Naziv <span style="color:#dc2626;">*</span>
|
|
||||||
</label>
|
|
||||||
<input type="text" name="naziv" value="{{.Artikal.Naziv}}"
|
|
||||||
placeholder="npr. RAM DDR4 8GB Kingston"
|
|
||||||
style="width:100%;">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Magacin je isključivo za proizvode (prate lager); usluge i troškovi
|
<!-- Magacin je isključivo za proizvode (prate lager); usluge i troškovi
|
||||||
imaju svoje module, zato ovde tip više nije izbor -->
|
imaju svoje module, zato ovde tip više nije izbor -->
|
||||||
<input type="hidden" name="tip" value="proizvod">
|
<input type="hidden" name="tip" value="proizvod">
|
||||||
<!-- jedinica mere -->
|
<div style="display:grid;grid-template-columns:1fr 110px 1fr;gap:12px;">
|
||||||
<div>
|
<div>
|
||||||
<label class="polje-labela">Jedinica mere</label>
|
<label class="polje-labela">
|
||||||
{{$jm := .Artikal.JedinicaMere}}
|
Naziv <span style="color:#dc2626;">*</span>
|
||||||
<select name="jedinica_mere" style="width:100%;">
|
</label>
|
||||||
<option value="kom" {{if eq $jm "kom"}}selected{{end}}>kom</option>
|
<input type="text" name="naziv" value="{{.Artikal.Naziv}}"
|
||||||
<option value="sat" {{if eq $jm "sat"}}selected{{end}}>sat</option>
|
placeholder="npr. RAM DDR4 8GB Kingston"
|
||||||
<option value="set" {{if eq $jm "set"}}selected{{end}}>set</option>
|
style="width:100%;">
|
||||||
<option value="m" {{if eq $jm "m"}}selected{{end}}>m</option>
|
</div>
|
||||||
<option value="m2" {{if eq $jm "m2"}}selected{{end}}>m²</option>
|
<div>
|
||||||
<option value="l" {{if eq $jm "l"}}selected{{end}}>l</option>
|
<label class="polje-labela">Jed. mere</label>
|
||||||
<option value="kg" {{if eq $jm "kg"}}selected{{end}}>kg</option>
|
{{$jm := .Artikal.JedinicaMere}}
|
||||||
<option value="pak" {{if eq $jm "pak"}}selected{{end}}>pak</option>
|
<select name="jedinica_mere" style="width:100%;">
|
||||||
</select>
|
<option value="kom" {{if eq $jm "kom"}}selected{{end}}>kom</option>
|
||||||
</div>
|
<option value="sat" {{if eq $jm "sat"}}selected{{end}}>sat</option>
|
||||||
|
<option value="set" {{if eq $jm "set"}}selected{{end}}>set</option>
|
||||||
<!-- kategorija -->
|
<option value="m" {{if eq $jm "m"}}selected{{end}}>m</option>
|
||||||
<div>
|
<option value="m2" {{if eq $jm "m2"}}selected{{end}}>m²</option>
|
||||||
<label class="polje-labela">Kategorija</label>
|
<option value="l" {{if eq $jm "l"}}selected{{end}}>l</option>
|
||||||
<select name="kategorija_id" style="width:100%;">
|
<option value="kg" {{if eq $jm "kg"}}selected{{end}}>kg</option>
|
||||||
<option value="">— bez kategorije —</option>
|
<option value="pak" {{if eq $jm "pak"}}selected{{end}}>pak</option>
|
||||||
{{range .Kategorije}}
|
</select>
|
||||||
<option value="{{.ID}}" {{if eq (printf "%d" .ID) $.KategorijaIDStr}}selected{{end}}>{{.Naziv}}</option>
|
</div>
|
||||||
{{end}}
|
<div>
|
||||||
</select>
|
<label class="polje-labela">Kategorija</label>
|
||||||
|
<select name="kategorija_id" style="width:100%;">
|
||||||
|
<option value="">— bez kategorije —</option>
|
||||||
|
{{range .Kategorije}}
|
||||||
|
<option value="{{.ID}}" {{if eq (printf "%d" .ID) $.KategorijaIDStr}}selected{{end}}>{{.Naziv}}</option>
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- opis -->
|
<!-- opis -->
|
||||||
@@ -103,15 +101,38 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
|
<!-- PDV stopa -->
|
||||||
|
{{if index .Moduli "pdv"}}
|
||||||
|
<div>
|
||||||
|
<label class="polje-labela">PDV stopa</label>
|
||||||
|
<select name="pdv_stopa" id="pdv-stopa"
|
||||||
|
style="width:100%;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:14px;">
|
||||||
|
<option value="20" {{if eq .Artikal.PdvStopa 20.0}}selected{{end}}>Opšta stopa — 20%</option>
|
||||||
|
<option value="10" {{if eq .Artikal.PdvStopa 10.0}}selected{{end}}>Posebna stopa — 10%</option>
|
||||||
|
<option value="0" {{if eq .Artikal.PdvStopa 0.0}}selected{{end}}>Oslobođeno — 0%</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
||||||
|
<!-- cene: nabavna / prodajna / sa PDV -->
|
||||||
|
<div style="display:grid;grid-template-columns:1fr 1fr{{if index .Moduli "pdv"}} 1fr{{end}};gap:12px;">
|
||||||
<div>
|
<div>
|
||||||
<label class="polje-labela">Nabavna cena (din)</label>
|
<label class="polje-labela">Nabavna cena (din)</label>
|
||||||
<input type="number" name="nabavna_cena" value="{{.Artikal.NabavnaCena}}" min="0" step="0.01" style="width:100%;">
|
<input type="number" id="nabavna-cena" name="nabavna_cena"
|
||||||
|
value="{{printf "%.2f" .Artikal.NabavnaCena}}" min="0" step="0.01" style="width:100%;">
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label class="polje-labela">Prodajna cena (din)</label>
|
<label class="polje-labela">Prodajna cena (din)</label>
|
||||||
<input type="number" name="prodajna_cena" value="{{.Artikal.ProdajnaCena}}" min="0" step="0.01" style="width:100%;">
|
<input type="number" id="prodajna-cena" name="prodajna_cena"
|
||||||
|
value="{{printf "%.2f" .Artikal.ProdajnaCena}}" min="0" step="0.01" style="width:100%;">
|
||||||
</div>
|
</div>
|
||||||
|
{{if index .Moduli "pdv"}}
|
||||||
|
<div>
|
||||||
|
<label class="polje-labela">Cena sa PDV-om (din)</label>
|
||||||
|
<input type="number" id="cena-sa-pdv" name="cena_sa_pdv"
|
||||||
|
value="{{printf "%.2f" .Artikal.CenaSaPdv}}" min="0" step="0.01" style="width:100%;">
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- marža za kalkulaciju; prazno = nasleđuje maržu kategorije ili globalnu -->
|
<!-- marža za kalkulaciju; prazno = nasleđuje maržu kategorije ili globalnu -->
|
||||||
@@ -184,26 +205,66 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// dvosmerno povezivanje: nabavna + marža → prodajna, i prodajna → marža
|
// dvosmerno povezivanje: nabavna + marža → prodajna, prodajna ↔ cena sa PDV
|
||||||
var nabavna = document.querySelector('[name="nabavna_cena"]');
|
var nabavna = document.getElementById('nabavna-cena');
|
||||||
var prodajna = document.querySelector('[name="prodajna_cena"]');
|
var prodajna = document.getElementById('prodajna-cena');
|
||||||
var marza = document.querySelector('[name="marza"]');
|
var saPdv = document.getElementById('cena-sa-pdv');
|
||||||
function broj(el) { return parseFloat(el.value) || 0; }
|
var pdvSel = document.getElementById('pdv-stopa');
|
||||||
// postavljanje .value programski ne okida 'input', pa nema beskonačne petlje
|
var marza = document.querySelector('[name="marza"]');
|
||||||
|
|
||||||
|
function pdv() { return parseFloat(pdvSel.value) || 0; }
|
||||||
|
function br(el) { return parseFloat(el.value) || 0; }
|
||||||
|
|
||||||
|
// prodajna → cena sa PDV
|
||||||
function izProdajne() {
|
function izProdajne() {
|
||||||
var n = broj(nabavna);
|
var p = br(prodajna);
|
||||||
if (n <= 0) return;
|
if (p > 0) saPdv.value = (p * (1 + pdv() / 100)).toFixed(2);
|
||||||
prodajna.value = (n * (1 + broj(marza) / 100)).toFixed(2);
|
izMarze();
|
||||||
}
|
}
|
||||||
|
// cena sa PDV → prodajna
|
||||||
|
function izSaPdv() {
|
||||||
|
var s = br(saPdv);
|
||||||
|
if (s > 0) prodajna.value = (s / (1 + pdv() / 100)).toFixed(2);
|
||||||
|
izMarze();
|
||||||
|
}
|
||||||
|
// nabavna + marža → prodajna (i onda prodajna → sa PDV)
|
||||||
|
function izMarzeKalc() {
|
||||||
|
var n = br(nabavna), m = br(marza);
|
||||||
|
if (n > 0 && marza.value !== '') {
|
||||||
|
prodajna.value = (n * (1 + m / 100)).toFixed(2);
|
||||||
|
izProdajne();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// prodajna + nabavna → marža
|
||||||
function izMarze() {
|
function izMarze() {
|
||||||
var n = broj(nabavna), p = broj(prodajna);
|
var n = br(nabavna), p = br(prodajna);
|
||||||
if (n <= 0) { marza.value = ''; return; }
|
if (n > 0) marza.value = ((p / n - 1) * 100).toFixed(2);
|
||||||
marza.value = ((p / n - 1) * 100).toFixed(2);
|
|
||||||
}
|
}
|
||||||
if (nabavna && prodajna && marza) {
|
// pri promeni PDV stope — preračunaj cena sa PDV iz prodajne
|
||||||
marza.addEventListener('input', izProdajne);
|
function izPdvStope() {
|
||||||
nabavna.addEventListener('input', izProdajne);
|
if (prodajna.value !== '') izProdajne();
|
||||||
prodajna.addEventListener('input', izMarze);
|
}
|
||||||
|
|
||||||
|
if (nabavna && prodajna && saPdv && marza && pdvSel) {
|
||||||
|
prodajna.addEventListener('input', izProdajne);
|
||||||
|
saPdv.addEventListener('input', izSaPdv);
|
||||||
|
nabavna.addEventListener('input', izMarzeKalc);
|
||||||
|
marza.addEventListener('input', izMarzeKalc);
|
||||||
|
pdvSel.addEventListener('change', izPdvStope);
|
||||||
|
} else if (nabavna && prodajna && marza) {
|
||||||
|
// bez PDV modula — samo osnovna kalkulacija nabavna+marža↔prodajna
|
||||||
|
marza.addEventListener('input', function() {
|
||||||
|
var n = br(nabavna);
|
||||||
|
if (n > 0) prodajna.value = (n * (1 + br(marza) / 100)).toFixed(2);
|
||||||
|
});
|
||||||
|
nabavna.addEventListener('input', function() {
|
||||||
|
var n = br(nabavna);
|
||||||
|
if (n > 0 && marza.value !== '') prodajna.value = (n * (1 + br(marza) / 100)).toFixed(2);
|
||||||
|
});
|
||||||
|
prodajna.addEventListener('input', function() {
|
||||||
|
var n = br(nabavna);
|
||||||
|
if (n > 0) marza.value = ((br(prodajna) / n - 1) * 100).toFixed(2);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user