Prodaja: skener barkoda, prilagodjeni dropdown artikala, ispravka cena sa/bez PDV-a po rezimu
This commit is contained in:
@@ -197,7 +197,8 @@ func (r *ProdajaRepo) Kreiraj(ctx context.Context, n *model.ProdajniNalog, stavk
|
||||
}
|
||||
|
||||
// provera stanja, smanjenje i insert stavki
|
||||
for _, s := range stavke {
|
||||
for i := range stavke {
|
||||
s := stavke[i]
|
||||
var naziv, tip string
|
||||
var stanjePre int
|
||||
err := tx.QueryRowContext(ctx,
|
||||
@@ -230,14 +231,18 @@ func (r *ProdajaRepo) Kreiraj(ctx context.Context, n *model.ProdajniNalog, stavk
|
||||
}
|
||||
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.
|
||||
// CenaPoKomadu je cena za naplatu: kod punog PDV obveznika bruto (sa PDV-om,
|
||||
// PDV se izdvaja naniže), kod firme na evidenciji nema PDV-a (PdvStopa=0, pa je bez razlike).
|
||||
cenaBezPdv := s.CenaBezPdv
|
||||
pdvIznos := s.PdvIznos
|
||||
if cenaBezPdv == 0 {
|
||||
cenaBezPdv = cenaPoslePopusta
|
||||
pdvIznos = cenaBezPdv * s.PdvStopa / 100
|
||||
cenaBezPdv = cenaPoslePopusta / (1 + s.PdvStopa/100)
|
||||
pdvIznos = cenaPoslePopusta - cenaBezPdv
|
||||
}
|
||||
// vraćamo izračunate vrednosti u slajs pozivaoca (npr. za auto-upis u KIR posle Kreiraj)
|
||||
stavke[i].CenaBezPdv = cenaBezPdv
|
||||
stavke[i].PdvIznos = pdvIznos
|
||||
stavke[i].Ukupno = ukupnoStavke
|
||||
_, 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)
|
||||
@@ -443,9 +448,10 @@ func (r *ProdajaRepo) DnevniPrometMaloprodaje(ctx context.Context, datum string)
|
||||
return p, fmt.Errorf("ntech: ProdajaRepo.DnevniPrometMaloprodaje: count: %w", err)
|
||||
}
|
||||
|
||||
// stavke grupisane po PDV stopi
|
||||
// stavke grupisane po PDV stopi — osnovica i PDV se čitaju iz već izračunatih
|
||||
// cena_bez_pdv/pdv_iznos kolona (ukupno je bruto iznos za naplatu, ne osnovica)
|
||||
redovi, err := r.db.QueryContext(ctx, `
|
||||
SELECT s.pdv_stopa, SUM(s.ukupno), SUM(s.ukupno * s.pdv_stopa / 100.0)
|
||||
SELECT s.pdv_stopa, SUM(s.cena_bez_pdv * s.kolicina), SUM(s.pdv_iznos * s.kolicina)
|
||||
FROM stavke_prodaje s
|
||||
JOIN prodajni_nalozi p ON p.id = s.nalog_id
|
||||
WHERE p.klijent_id IS NULL AND p.stornirano = 0 AND DATE(p.datum) = ?
|
||||
|
||||
@@ -81,7 +81,8 @@ 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 neto cene (CenaPoKomadu je neto).
|
||||
// cena_bez_pdv i pdv_iznos se automatski izdvajaju naniže iz bruto cene
|
||||
// (CenaPoKomadu je kod punog PDV obveznika cena za naplatu, sa PDV-om).
|
||||
func TestProdajaKreiraj_PdvAutoKalkulacija(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
baza := testDB(t)
|
||||
@@ -92,13 +93,14 @@ func TestProdajaKreiraj_PdvAutoKalkulacija(t *testing.T) {
|
||||
Naziv: "Tastatura", Tip: model.TipProizvod, Kolicina: 10,
|
||||
})
|
||||
|
||||
// neto cena 1000, PDV 20% → pdv_iznos = 200
|
||||
_, err := prodRepo.Kreiraj(ctx, &model.ProdajniNalog{
|
||||
BrojNaloga: "PR-PDV-001", Ukupno: 1000, NacinPlacanja: "gotovina", Datum: time.Now(),
|
||||
}, []model.StavkaProdaje{
|
||||
{ArtikalID: artID, Kolicina: 1, CenaPoKomadu: 1000, PdvStopa: 20},
|
||||
// bruto cena 1200, PDV 20% → cena_bez_pdv = 1000, pdv_iznos = 200
|
||||
stavke := []model.StavkaProdaje{
|
||||
{ArtikalID: artID, Kolicina: 1, CenaPoKomadu: 1200, PdvStopa: 20},
|
||||
// CenaBezPdv=0 namerno — treba da se auto-izračuna
|
||||
}, nil)
|
||||
}
|
||||
_, err := prodRepo.Kreiraj(ctx, &model.ProdajniNalog{
|
||||
BrojNaloga: "PR-PDV-001", Ukupno: 1200, NacinPlacanja: "gotovina", Datum: time.Now(),
|
||||
}, stavke, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Kreiraj: %v", err)
|
||||
}
|
||||
|
||||
@@ -105,8 +105,9 @@ func (k *PdvKir) DodajNeto(osnovica, stopa float64) {
|
||||
}
|
||||
|
||||
// KirIzProdaje gradi KIR zapis iz prodaje: stavke se grupišu po PDV stopi
|
||||
// (20→opšta, 10→posebna, ostalo→oslobođeno). CenaPoKomadu je prodajna cena BEZ PDV
|
||||
// (neto), PDV se dodaje naviše — isti obrazac kao kod servisnih radova/delova.
|
||||
// (20→opšta, 10→posebna, ostalo→oslobođeno). Koristi već izračunatu CenaBezPdv
|
||||
// (neto, popust je već uračunat pri upisu stavke — v. ProdajaRepo.Kreiraj), jer je
|
||||
// CenaPoKomadu kod punog PDV obveznika bruto (cena za naplatu), ne neto.
|
||||
// DatumPrometa je datum same prodaje (nalog.Datum); DatumKnjizenja je datum kada se
|
||||
// ovaj KIR zapis stvarno upisuje u knjigu — za prodaju upisanu odmah to je isti trenutak,
|
||||
// ali za naknadni backfill starih naloga upis kasni za periodom pa se prosleđuje posebno.
|
||||
@@ -123,11 +124,7 @@ func KirIzProdaje(nalog ProdajniNalog, stavke []StavkaProdaje, kupacNaziv, kupac
|
||||
IzvorID: &id,
|
||||
}
|
||||
for _, s := range stavke {
|
||||
cenaPoslePopusta := s.CenaPoKomadu
|
||||
if s.PopustProcenat > 0 {
|
||||
cenaPoslePopusta = cenaPoslePopusta * (1 - s.PopustProcenat/100)
|
||||
}
|
||||
k.DodajNeto(float64(s.Kolicina)*cenaPoslePopusta, s.PdvStopa)
|
||||
k.DodajNeto(float64(s.Kolicina)*s.CenaBezPdv, s.PdvStopa)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
@@ -11,12 +11,12 @@ 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%: CenaPoKomadu je NETO → osnovica 2×120=240, PDV 240×20%=48
|
||||
{Kolicina: 2, CenaPoKomadu: 120, PdvStopa: 20},
|
||||
// 20%: CenaBezPdv je već izračunata neto vrednost (v. ProdajaRepo.Kreiraj) → osnovica 2×120=240, PDV 240×20%=48
|
||||
{Kolicina: 2, CenaBezPdv: 120, PdvStopa: 20},
|
||||
// 10%: osnovica 1×110=110, PDV 110×10%=11
|
||||
{Kolicina: 1, CenaPoKomadu: 110, PdvStopa: 10},
|
||||
{Kolicina: 1, CenaBezPdv: 110, PdvStopa: 10},
|
||||
// 0%: 1 × 50 = 50 (oslobođeno, bez PDV)
|
||||
{Kolicina: 1, CenaPoKomadu: 50, PdvStopa: 0},
|
||||
{Kolicina: 1, CenaBezPdv: 50, PdvStopa: 0},
|
||||
}
|
||||
|
||||
k := KirIzProdaje(nalog, stavke, "Kupac doo", "123456789", "Niš", nalog.Datum)
|
||||
|
||||
Reference in New Issue
Block a user