Merge feature/prodaja-pdv-popust-fix → develop
This commit is contained in:
@@ -210,20 +210,21 @@ func (r *ProdajaRepo) Kreiraj(ctx context.Context, n *model.ProdajniNalog, stavk
|
||||
}
|
||||
}
|
||||
|
||||
// CenaPoKomadu je neto (bez PDV); PDV se dodaje naviše
|
||||
cenaBezPdv := s.CenaBezPdv
|
||||
pdvIznos := s.PdvIznos
|
||||
if cenaBezPdv == 0 {
|
||||
cenaBezPdv = s.CenaPoKomadu
|
||||
pdvIznos = cenaBezPdv * s.PdvStopa / 100
|
||||
}
|
||||
|
||||
// popust umanjuje cenu po komadu pre računanja ukupnog i PDV-a
|
||||
cenaPoslePopusta := s.CenaPoKomadu
|
||||
if s.PopustProcenat > 0 {
|
||||
cenaPoslePopusta = cenaPoslePopusta * (1 - s.PopustProcenat/100)
|
||||
}
|
||||
ukupnoStavke := float64(s.Kolicina) * cenaPoslePopusta
|
||||
|
||||
// CenaPoKomadu je neto (bez PDV); PDV se dodaje naviše.
|
||||
// cena_bez_pdv/pdv_iznos moraju biti diskontovani da ostanu konzistentni sa ukupno.
|
||||
cenaBezPdv := s.CenaBezPdv
|
||||
pdvIznos := s.PdvIznos
|
||||
if cenaBezPdv == 0 {
|
||||
cenaBezPdv = cenaPoslePopusta
|
||||
pdvIznos = cenaBezPdv * s.PdvStopa / 100
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
INSERT INTO stavke_prodaje
|
||||
(nalog_id, artikal_id, kolicina, cena_po_komadu, popust_procenat, ukupno, pdv_stopa, pdv_iznos, cena_bez_pdv)
|
||||
|
||||
@@ -81,7 +81,7 @@ func TestProdajaKreiraj_NedovoljnoStanja(t *testing.T) {
|
||||
}
|
||||
|
||||
// TestProdajaKreiraj_PdvAutoKalkulacija: kada CenaBezPdv=0 a PdvStopa>0,
|
||||
// cena_bez_pdv i pdv_iznos se automatski računaju iz bruto cene.
|
||||
// cena_bez_pdv i pdv_iznos se automatski računaju iz neto cene (CenaPoKomadu je neto).
|
||||
func TestProdajaKreiraj_PdvAutoKalkulacija(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
baza := testDB(t)
|
||||
@@ -92,11 +92,11 @@ func TestProdajaKreiraj_PdvAutoKalkulacija(t *testing.T) {
|
||||
Naziv: "Tastatura", Tip: model.TipProizvod, Kolicina: 10,
|
||||
})
|
||||
|
||||
// bruto cena 1200, PDV 20% → neto = 1200/1.2 = 1000, pdv_iznos = 200
|
||||
// neto cena 1000, PDV 20% → pdv_iznos = 200
|
||||
_, err := prodRepo.Kreiraj(ctx, &model.ProdajniNalog{
|
||||
BrojNaloga: "PR-PDV-001", Ukupno: 1200, NacinPlacanja: "gotovina", Datum: time.Now(),
|
||||
BrojNaloga: "PR-PDV-001", Ukupno: 1000, NacinPlacanja: "gotovina", Datum: time.Now(),
|
||||
}, []model.StavkaProdaje{
|
||||
{ArtikalID: artID, Kolicina: 1, CenaPoKomadu: 1200, PdvStopa: 20},
|
||||
{ArtikalID: artID, Kolicina: 1, CenaPoKomadu: 1000, PdvStopa: 20},
|
||||
// CenaBezPdv=0 namerno — treba da se auto-izračuna
|
||||
}, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -62,13 +62,14 @@ func NapraviZahtev(
|
||||
if s.PdvStopa > 0 {
|
||||
stopa = s.PdvStopa
|
||||
}
|
||||
// CenaPoKomadu je neto cena — konvertujemo u bruto; popust umanjuje neto cenu pre PDV-a
|
||||
// CenaBezPdv iz baze je već diskontovana (popust primenjen pri kreiranju naloga);
|
||||
// popust se ponovo primenjuje samo u fallback grani kad CenaBezPdv nije popunjena.
|
||||
netoCena := s.CenaBezPdv
|
||||
if netoCena == 0 {
|
||||
netoCena = s.CenaPoKomadu
|
||||
}
|
||||
if s.PopustProcenat > 0 {
|
||||
netoCena = math.Round(netoCena*(1-s.PopustProcenat/100)*100) / 100
|
||||
if s.PopustProcenat > 0 {
|
||||
netoCena = math.Round(netoCena*(1-s.PopustProcenat/100)*100) / 100
|
||||
}
|
||||
}
|
||||
brutoCena := BrutoCena(netoCena, stopa)
|
||||
brutoTotal := math.Round(brutoCena*float64(s.Kolicina)*100) / 100
|
||||
|
||||
@@ -120,7 +120,11 @@ func KirIzProdaje(nalog ProdajniNalog, stavke []StavkaProdaje, kupacNaziv, kupac
|
||||
IzvorID: &id,
|
||||
}
|
||||
for _, s := range stavke {
|
||||
k.DodajNeto(float64(s.Kolicina)*s.CenaPoKomadu, s.PdvStopa)
|
||||
cenaPoslePopusta := s.CenaPoKomadu
|
||||
if s.PopustProcenat > 0 {
|
||||
cenaPoslePopusta = cenaPoslePopusta * (1 - s.PopustProcenat/100)
|
||||
}
|
||||
k.DodajNeto(float64(s.Kolicina)*cenaPoslePopusta, s.PdvStopa)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
@@ -11,9 +11,9 @@ func blizu(a, b float64) bool { return math.Abs(a-b) < 0.01 }
|
||||
func TestKirIzProdaje(t *testing.T) {
|
||||
nalog := ProdajniNalog{ID: 5, BrojNaloga: "P-1", Datum: time.Date(2026, 6, 1, 0, 0, 0, 0, time.UTC)}
|
||||
stavke := []StavkaProdaje{
|
||||
// 20%: 2 × 120 = 240 (osnovica 200, PDV 40)
|
||||
// 20%: CenaPoKomadu je NETO → osnovica 2×120=240, PDV 240×20%=48
|
||||
{Kolicina: 2, CenaPoKomadu: 120, PdvStopa: 20},
|
||||
// 10%: 1 × 110 = 110 (osnovica 100, PDV 10)
|
||||
// 10%: osnovica 1×110=110, PDV 110×10%=11
|
||||
{Kolicina: 1, CenaPoKomadu: 110, PdvStopa: 10},
|
||||
// 0%: 1 × 50 = 50 (oslobođeno, bez PDV)
|
||||
{Kolicina: 1, CenaPoKomadu: 50, PdvStopa: 0},
|
||||
@@ -27,17 +27,17 @@ func TestKirIzProdaje(t *testing.T) {
|
||||
if k.BrojDokumenta != "P-1" || k.KupacNaziv != "Kupac doo" || k.KupacPib != "123456789" {
|
||||
t.Errorf("zaglavlje ne odgovara: %+v", k)
|
||||
}
|
||||
if !blizu(k.OsnovicaOpsta, 200) || !blizu(k.PdvOpsta, 40) {
|
||||
t.Errorf("opšta: osnovica=%v pdv=%v, očekivano 200/40", k.OsnovicaOpsta, k.PdvOpsta)
|
||||
if !blizu(k.OsnovicaOpsta, 240) || !blizu(k.PdvOpsta, 48) {
|
||||
t.Errorf("opšta: osnovica=%v pdv=%v, očekivano 240/48", k.OsnovicaOpsta, k.PdvOpsta)
|
||||
}
|
||||
if !blizu(k.OsnovicaPosebna, 100) || !blizu(k.PdvPosebna, 10) {
|
||||
t.Errorf("posebna: osnovica=%v pdv=%v, očekivano 100/10", k.OsnovicaPosebna, k.PdvPosebna)
|
||||
if !blizu(k.OsnovicaPosebna, 110) || !blizu(k.PdvPosebna, 11) {
|
||||
t.Errorf("posebna: osnovica=%v pdv=%v, očekivano 110/11", k.OsnovicaPosebna, k.PdvPosebna)
|
||||
}
|
||||
if !blizu(k.OslobodenSaPravom, 50) {
|
||||
t.Errorf("oslobođeno=%v, očekivano 50", k.OslobodenSaPravom)
|
||||
}
|
||||
if !blizu(k.Ukupno, 400) {
|
||||
t.Errorf("ukupno=%v, očekivano 400 (240+110+50)", k.Ukupno)
|
||||
if !blizu(k.Ukupno, 459) {
|
||||
t.Errorf("ukupno=%v, očekivano 459 (288+121+50 bruto)", k.Ukupno)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user