# Magacin — Plan izmena: Cena sa PDV-om Dodajemo polje **Cena sa PDV-om** i **PDV dropdown** u forme za Novi artikal i Izmeni artikal. --- ## Trenutno stanje | Kolona u DB | Šta čuva | Default | |---|---|---| | `prodajna_cena` | Prodajna cena (neto, bez PDV-a) | 0 | | `pdv_stopa` | PDV stopa u % | 20.0 | | ~~`cena_sa_pdv`~~ | ne postoji | — | **Model metode** (`internal/model/artikal.go`, linije 41–47): ```go // PAŽNJA: ove metode tretiraju ProdajnaCenu kao BRUTO — ne odgovaraju // korisnikovom modelu gde je prodajna_cena NETO. func (a Artikal) CenaBezPdv() float64 { return a.ProdajnaCena / (1 + a.PdvStopa/100) } func (a Artikal) PdvIznos() float64 { return a.ProdajnaCena - a.CenaBezPdv() } ``` Ove metode treba ispraviti posle ove izmene (videti §6). --- ## Šta dodajemo | Novo | Opis | |---|---| | Kolona `cena_sa_pdv` | Prodajna cena sa PDV-om (bruto). Čuva se u bazi radi brzog prikaza. | | Dropdown `pdv_stopa` | Izbor PDV stope u formi. Default: Opšta stopa 20%. | | JS dvosmerna kalkulacija | Prodajna ↔ sa PDV (automatsko popunjavanje u zavisnosti od toga šta je uneto) | **Formula:** ``` cena_sa_pdv = prodajna_cena * (1 + pdv_stopa / 100) prodajna_cena = cena_sa_pdv / (1 + pdv_stopa / 100) ``` --- ## Izmene po fajlovima ### 1. `migrations/087_artikal_cena_sa_pdv.sql` — NOVI FAJL ```sql 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); ``` Drugi red popunjava sve postojeće artikle — svaka prodajna cena se konvertuje u bruto. --- ### 2. `internal/model/artikal.go` **Linija 26** — posle `ProdajnaCena float64` dodati novo polje: ```go // DODATI: CenaSaPdv float64 // prodajna cena sa PDV-om (bruto); = ProdajnaCena * (1 + PdvStopa/100) ``` **Linije 40–47** — ispraviti metode: ```go // STARO: func (a Artikal) CenaBezPdv() float64 { return a.ProdajnaCena / (1 + a.PdvStopa/100) } func (a Artikal) PdvIznos() float64 { return a.ProdajnaCena - a.CenaBezPdv() } // NOVO: func (a Artikal) CenaBezPdv() float64 { return a.ProdajnaCena // prodajna_cena je već neto } func (a Artikal) PdvIznos() float64 { return a.CenaSaPdv - a.ProdajnaCena } ``` --- ### 3. `internal/db/sqlite/artikal.go` #### 3a. SELECT u `Lista` (linija ~33) — dodati `cena_sa_pdv` na kraj SELECT-a ```sql -- STARO (kraj SELECT-a): a.nabavna_cena, a.prodajna_cena, a.pdv_stopa, a.marza, a.napomena, -- NOVO: a.nabavna_cena, a.prodajna_cena, a.pdv_stopa, a.cena_sa_pdv, a.marza, a.napomena, ``` #### 3b. Scan u `Lista` (linija ~96) — dodati `&a.CenaSaPdv` iza `&a.PdvStopa`: ```go // STARO: &a.NabavnaCena, &a.ProdajnaCena, &a.PdvStopa, &marza, &a.Napomena, &a.DatumUnosa, &arhiviran, // NOVO: &a.NabavnaCena, &a.ProdajnaCena, &a.PdvStopa, &a.CenaSaPdv, &marza, &a.Napomena, &a.DatumUnosa, &arhiviran, ``` #### 3c. SELECT u `DohvatiID` (linija ~139–141) — isti princip: ```sql -- STARO: lokacija, nabavna_cena, prodajna_cena, pdv_stopa, marza, napomena, datum_unosa, arhiviran -- NOVO: lokacija, nabavna_cena, prodajna_cena, pdv_stopa, cena_sa_pdv, marza, napomena, datum_unosa, arhiviran ``` #### 3d. Scan u `DohvatiID` (linija ~143): ```go // STARO: &a.NabavnaCena, &a.ProdajnaCena, &a.PdvStopa, &marza, &a.Napomena, &a.DatumUnosa, &arhiviran, // NOVO: &a.NabavnaCena, &a.ProdajnaCena, &a.PdvStopa, &a.CenaSaPdv, &marza, &a.Napomena, &a.DatumUnosa, &arhiviran, ``` #### 3e. INSERT u `Kreiraj` (linije ~179–182) — dodati `cena_sa_pdv`: ```go // STARO: INSERT INTO artikli (..., nabavna_cena, prodajna_cena, pdv_stopa, marza, napomena) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ... a.Lokacija, a.NabavnaCena, a.ProdajnaCena, a.PdvStopa, a.Marza, a.Napomena, // NOVO: INSERT INTO artikli (..., nabavna_cena, prodajna_cena, pdv_stopa, cena_sa_pdv, marza, napomena) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) ... a.Lokacija, a.NabavnaCena, a.ProdajnaCena, a.PdvStopa, a.CenaSaPdv, a.Marza, a.Napomena, ``` #### 3f. UPDATE u `Izmeni` (linije ~210–214) — dodati `cena_sa_pdv = ?`: ```go // STARO: UPDATE artikli SET ..., nabavna_cena = ?, prodajna_cena = ?, pdv_stopa = ?, marza = ?, napomena = ? WHERE id = ? ... a.NabavnaCena, a.ProdajnaCena, a.PdvStopa, a.Marza, a.Napomena, a.ID, // NOVO: UPDATE artikli SET ..., nabavna_cena = ?, prodajna_cena = ?, pdv_stopa = ?, cena_sa_pdv = ?, marza = ?, napomena = ? WHERE id = ? ... a.NabavnaCena, a.ProdajnaCena, a.PdvStopa, a.CenaSaPdv, a.Marza, a.Napomena, a.ID, ``` --- ### 4. `internal/handler/magacin_forma.go` #### 4a. `parseFormuArtikla` — čitanje `pdv_stopa` iz forme (dodati iza linije 389, pre `prodajna_cena`): ```go // DODATI — čitanje PDV stope iz forme: 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 ``` #### 4b. `parseFormuArtikla` — kalkulacija `CenaSaPdv` (dodati na kraju, pre `return artikal, ""`): ```go // DODATI — izračunaj 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) } ``` #### 4c. Nivelacioni trag — linija ~278 Ovaj deo poredi `artikal.ProdajnaCena - staraCena`. Ništa ne menjamo — i dalje poredimo neto cene. Ostaje kako jeste. --- ### 5. `web/templates/stranice/magacin_forma.html` #### 5a. Blok cena (linije 106–122) — trenutno izgleda ovako: ```html
``` #### 5b. ZAMENA — novi blok cena sa PDV dropdown-om i trećim poljem: ```html
``` #### 5c. JavaScript blok (linije ~187–207) — dodati logiku za `cena_sa_pdv` i PDV stopa ```javascript // NOVI JS — zamenjuje stari blok kalkulacije cena: (function() { var nabavna = document.getElementById('nabavna-cena'); var prodajna = document.getElementById('prodajna-cena'); var saPdv = document.getElementById('cena-sa-pdv'); var pdvSel = document.getElementById('pdv-stopa'); 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() { var p = br(prodajna); if (p > 0) saPdv.value = (p * (1 + pdv() / 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() { var n = br(nabavna), p = br(prodajna); if (n > 0) marza.value = ((p / n - 1) * 100).toFixed(2); } // pri promeni PDV stope — preračunaj cena sa PDV iz prodajne function izPdvStope() { if (prodajna.value !== '') izProdajne(); } prodajna.addEventListener('input', izProdajne); saPdv.addEventListener('input', izSaPdv); nabavna.addEventListener('input', izMarzeKalc); marza.addEventListener('input', izMarzeKalc); pdvSel.addEventListener('change', izPdvStope); })(); ``` --- ### 6. Napomene — šta NE menjamo u ovoj fazi | Fajl | Zašto ne diramo | |---|---| | `internal/handler/prodaja.go` linija 74 | POS i dalje koristi `a.ProdajnaCena` kao neto cenu; korisnik vidi neto pri prodaji. Ako treba prikazati bruto → posebna izmena. | | `internal/fiskal/mapiranje.go` linija 66 | Koristi `s.CenaBezPdv` iz `model.StavkaProdaje` (ne iz `model.Artikal`) — to je poseban struct, nije zahvaćen. | | `internal/db/sqlite/izvestaj.go` | Koristi `ProdajnaCena` za vrednost zaliha — neto ostaje logična osnova za to. | | `magacin.html` (lista) | Prikazuje `ProdajnaCena` — može ostati neto, ili se doda kolona `CenaSaPdv`. Nije hitan. | --- ## Redosled implementacije 1. **Migracija** `087_artikal_cena_sa_pdv.sql` — nova kolona + popunjavanje postojećih 2. **Model** `internal/model/artikal.go` — novo polje + ispravka metoda 3. **SQLite repo** `internal/db/sqlite/artikal.go` — SELECT, Scan, INSERT, UPDATE (4 mesta) 4. **Handler parser** `internal/handler/magacin_forma.go` — čitanje `pdv_stopa` + kalkulacija `cena_sa_pdv` 5. **Šablon** `web/templates/stranice/magacin_forma.html` — PDV dropdown + treće polje + novi JS