Skip to content

Latest commit

 

History

History
234 lines (150 loc) · 10.7 KB

File metadata and controls

234 lines (150 loc) · 10.7 KB

Bug lista — Dashboard "Prihod ovog meseca"

Istraživanje logike PrihodTekuciMesec i sve komponente koje utiču na prikazanu cifru.


BUG-01 — Stornirani prodajni nalozi ulaze u prihod ⚠️ KRITIČAN

Fajl: internal/db/sqlite/izvestaj.go:39–48

Problem: SQL upit koji računa prihod ne filtrira stornirane naloge:

SELECT SUM(ukupno) FROM prodajni_nalozi
WHERE substr(datum, 1, 7) = strftime('%Y-%m', 'now', 'localtime')
-- ❌ nema: AND stornirano = 0

Kolona stornirano INTEGER NOT NULL DEFAULT 0 postoji od migracije 035_pos_faza1.sql. Kada se nalog stornira, stornirano = 1 i magacin se vraća, ali iznos ostaje u prihodu.

Posledica: Svaki stornirani nalog uvećava prikazani mesečni prihod za iznos koji nije stvarno naplaćen. Na kraju meseca razlika može biti značajna ako ima više storniranja.

Isti propust na još tri mesta u istom fajlu:

  • MesecniPrihodProdaja (l.146) — grafikon prihoda po mesecima
  • PoslednjeProdaje (l.104) — lista poslednjih prodaja na dashboardu
  • TopKlijenti (l.220) — rang lista kupaca po vrednosti

Ispravka: Dodati AND stornirano = 0 u sve četiri SQL SELECT-e.


BUG-02 — Mešanje PDV-a: prodaja bruto, servis neto ⚠️ KRITIČAN

Fajl: internal/db/sqlite/izvestaj.go:39–48

Problem: Zbir spaja dve veličine koje nisu uporedive:

Izvor Kolona PDV
prodajni_nalozi.ukupno kolicina × cena_po_komadu SA PDV-om (bruto)
servisni_nalozi.cena_konacna dijagnostika + radovi + delovi BEZ PDV-a (neto)

Prodajna cena se unosi kao maloprodajna (bruto) cena — to potvrđuje handler/prodaja.go:188–191:

ukupno += float64(s.Kolicina) * s.CenaPoKomadu  // bruto
nalog.Ukupno = ukupno

cena_konacna za servis se auto-računa koristeći rad.Ukupno() i deo.Ukupno() (model servis.go:77,127) koji vraćaju kolicina × cena_komada — neto cenu bez PDV-a.

Posledica: Prikazana cifra nije ni ukupan prihod sa PDV-om ni ukupan neto prihod. Za firme sa PDV-om (stopa 20%) to može biti razlika od 20% na delu koji dolazi iz servisa.

Ispravka: Odlučiti se za jedinstven standard (preporučeno: sa PDV-om) i koristiti cena_sa_pdv kolone / UkupnoSaPdv() metode konzistentno.


BUG-03 — Delovi servisa ne ulaze u prihod kada je cena ručno uneta ⚠️ VAŽAN

Fajl: internal/db/sqlite/izvestaj.go:44–48, internal/handler/servis.go:1498–1500

Problem: Šablon servisa tretira cena_konacna kao cenu rada (bez delova):

// servis.go:1498–1500
} else if nalog.CenaKonacna != nil {
    ukupnoSve = *nalog.CenaKonacna + ukupnoDelovi   // delovi se DODAJU na cenu_konacnu

Kada korisnik ručno unese cena_konacna (polje "Cena rada"), u bazu ide samo vrednost rada — delovi su odvojeni. SQL za prihod uzima samo cena_konacna:

SELECT SUM(cena_konacna) FROM servisni_nalozi WHERE status = 'Preuzeto' ...

Posledica: Vrednost svih ugrađenih delova na servisnim nalozima sa ručno unetom cena_konacna ne ulazi u prikazani prihod. Prihod je manji od stvarnog.

Napomena: Kada je status prelaz na "Preuzeto" i cena_konacna je bila NULL, auto-izračun (servis.go:2041–2048) uključuje delove u cena_konacna. Dakle ponašanje zavisi od toga da li je korisnik uneo cenu ručno ili je sistem auto-izračunao — nedoslednost u definiciji polja.


BUG-04 — Duplo računanje delova u naplaćenom iznosu pri auto-izračunu ⚠️ VAŽAN

Fajl: internal/handler/servis.go:2034–2083

Problem: Kada servisni nalog nema unesenu cena_konacna i prelazi u status "Preuzeto", dešava se:

  1. Auto-izračun (l.2037–2049): cena_konacna = dijagnostika + radovi + **delovi** → snima u DB
  2. Izračun naplaćenog iznosa (l.2064–2073): čita novu cena_konacna iz DB, pa dodaje ukupnoDelovi još jednom:
nalog.CenaKonacna = &ukupno  // ukupno već sadrži delove (l.2049)
// ...
nalog, _ = h.ServisRepo.DohvatiID(...)  // čita iz DB — cena_konacna uključuje delove
iznos = *nalog.CenaKonacna + ukupnoDelovi  // ← delovi se broje DVAPUT

Posledica: Iznos koji se fiskalizuje i beleži kao naplaćen veći je od stvarno dugovanog (delovi duplo). Ovo je greška u fiskalnom računu.

Reprodukcija: Napraviti servisni nalog, dodati deo (npr. 1.000 din), ne unositi cenu rada, promeniti status u "Preuzeto" bez popunjavanja forme. Naplaćeni iznos biće 2.000 umesto 1.000.


BUG-05 — CSS skriva cifru prihoda — UX propust

Fajl: web/templates/stranice/dashboard.html:16–23

Problem: Cifra prihoda ima opacity: 0 po defaultu i prikazuje se tek posle 1 sekunde hover-a:

.prihod-cifra {
    opacity: 0;
    transition: opacity 0.3s ease;
}
.dash-stat:hover .prihod-cifra {
    opacity: 1;
    transition-delay: 1s;  /* ← 1 sekunda čekanja */
}

Labela "Prihod ovog meseca" je vidljiva, ali cifra nije. Na mobilnim uređajima (bez hover) cifra je trajno nevidljiva.

Posledica: Korisnik ne može pročitati prihod na mobilnom uređaju. Na desktopu mora da zna da treba da čeka sekund na kartici.


Sažetak

# Opis Ozbiljnost Fajl
BUG-01 Stornirani nalozi u prihodu Kritičan izvestaj.go:41–43
BUG-02 Prodaja SA PDV / servis BEZ PDV Kritičan izvestaj.go:39–48
BUG-03 Delovi servisa izostaju kod ručne cene Važan izvestaj.go:44–48
BUG-04 Duplo računanje delova u naplaćenom iznosu Važan servis.go:2034–2083
BUG-05 CSS skriva cifru na mobilnom UX dashboard.html:16–23

BUG-01 do BUG-05 su ISPRAVLJENI u ovoj sesiji.


Novi nalazi — pregled koda 2026-06-27

Drugi krug pregleda, posle ispravki BUG-01..05.

BUG-06 do BUG-10 su ISPRAVLJENI i pokriveni testovima (2026-06-27).

# Ispravka Test
06/07 PdvKir.DodajNeto — neto osnovica + PDV po stvarnoj stopi; servis KIR ga koristi TestPdvKirDodajNeto
08 TopKlijenti servis: naplaceno+avans WHERE status='Preuzeto' TestTopKlijenti_SamoPreuzetiNalozi
09 kljuceviMeseci sidri na 1. u mesecu TestKljuceviMeseci
10 prihod servisa = SUM(naplaceno + COALESCE(avans,0)), filter (naplaceno>0 OR avans>0) TestPrihodTekuciMesec_ServisSaAvansom, ...PotpunoAvansiran, ...GarancijaNeUlazi

BUG-06 — Servis KIR: neto cena tretirana kao bruto ⚠️ KRITIČAN (poreski)

Fajl: internal/handler/servis.go:2114–2133 (auto-upis u KIR pri prelasku u „Preuzeto")

Problem: Kod računa osnovicu i PDV deljenjem sa 1.2, tretirajući r.Ukupno() kao bruto:

osnovica := r.Ukupno() / 1.2 // 20% PDV
pdv := r.Ukupno() - osnovica
kir.Ukupno += r.Ukupno()

Ali cena_komada rada/dela je NETO (bez PDV-a) — potvrđeno u servisni_radovi.go:40:

rad.CenaSaPdv = rad.CenaKomada * (1 + rad.PdvStopa/100)

Dakle r.Ukupno() = kolicina × cena_komada je već osnovica (neto). Deljenjem neto vrednosti sa 1.2 dobija se premala osnovica, a PDV se računa na pogrešnu (umanjenu) bazu. kir.Ukupno je zapravo neto, iako kolona „Ukupno" treba da bude bruto sa PDV-om.

Primer (rad 1000 din neto, 20%):

  • Tačno: osnovica 1000, PDV 200, ukupno 1200
  • Kod daje: osnovica 833.33, PDV 166.67, ukupno 1000

Poređenje: KirIzProdaje (model/pdv_evidencija.go:98–116) to radi ispravno — ali tamo je cena_po_komadu bruto, pa je deljenje opravdano. Servis je samo prekopirao obrazac bez korekcije za neto cenu.

Ispravka: Za servis koristiti r.UkupnoSaPdv() kao bruto, ili direktno: osnovica = r.Ukupno(), pdv = r.Ukupno() * stopa/100.


BUG-07 — Servis KIR: hardkodovana stopa 20%, ignoriše PdvStopa ⚠️ VAŽAN (poreski)

Fajl: internal/handler/servis.go:2118, 2128

Problem: Stopa je fiksirana na / 1.2 (20%) iako i rad i deo imaju polje PdvStopa. Sve stavke se sabiraju u OsnovicaOpsta/PdvOpsta, bez razvrstavanja na opštu (20%), posebnu (10%) i oslobođen promet.

KirIzProdaje to radi ispravno preko switch s.PdvStopa { case 20 … case 10 … default … }. Servisni KIR ne. Usluga/deo sa 10% ili 0% PDV biće pogrešno evidentiran.

Ispravka: Razvrstati po rad.PdvStopa / deo.PdvStopa kao u KirIzProdaje.


BUG-08 — TopKlijenti: servis bez statusa + neto cena, nedosledno ⚠️ SREDNJI

Fajl: internal/db/sqlite/izvestaj.go:228–231

Problem: Podupit za servis i dalje koristi:

SELECT klijent_id, SUM(cena_konacna) … FROM servisni_nalozi
WHERE cena_konacna IS NOT NULL GROUP BY klijent_id
  • Nema filtera statusa → broji i naloge koji još nisu preuzeti („U popravci", „Čeka delove"), pa i one koji nikad neće biti naplaćeni.
  • Koristi neto cena_konacna (bez delova, posle BUG-04 fixa), dok dashboard i mesečni grafikon sada koriste bruto naplaceno.

Posledica: isti klijent ima različitu „ukupnu vrednost" na različitim ekranima.

Ispravka: Uskladiti sa ostatkom: SUM(naplaceno) WHERE status='Preuzeto' AND naplaceno > 0. (Prodajni podupit je već usklađen sa stornirano = 0.)


BUG-09 — Izveštaji: 12-mesečni grafikon preskače mesece na kraju meseca ⚠️ LATENTAN

Fajl: internal/handler/izvestaji.go:117–119

Problem: Petlja gradi ključeve meseci preko sada.AddDate(0, -i, 0). Go-ov AddDate normalizuje prelivanje dana: npr. 31. mart − 1 mesec = „31. februar" → 3. mart. Na 29/30/31. u mesecu neki mesec se duplira, a drugi (npr. februar) dobije ključ koji se nikad ne generiše → prikaže 0.

Ne manifestuje se danas (27.), ali se javlja svakog 29–31. u mesecu.

Ispravka: Računati ključ preko prvog u mesecu, npr. time.Date(god, mesec, 1, …) sa ručnim oduzimanjem meseci, ili AddDate nad BeginningOfMonth.


BUG-10 — Avans se ne uračunava u prihod (regresija od fixa BUG-02/03) ⚠️ VAŽAN

Fajl: internal/db/sqlite/izvestaj.go:46–49 (i MesecniPrihodServis)

Problem: Prihod od servisa sada je SUM(naplaceno). Ali naplaceno je iznos naplaćen pri preuzimanju = (cena_konacna + delovi) − avans (servis.go:2069–2073). Avans je naplaćen ranije i nigde se ne evidentira kao zaseban prihod.

Dve posledice:

  1. Za naloge sa avansom, deo prihoda pokriven avansom nedostaje iz „prihoda meseca". Stari kod (cena_konacna) je taj deo uključivao.
  2. Filter naplaceno > 0 (dodat da izbaci garancijske popravke) potpuno izbacuje naloge gde je avans pokrio ceo iznos (naplaceno = 0) — iako su plaćeni.

Napomena: Ovo je svesni kompromis mog prethodnog fixa (naplaceno je gotovinski tačan za naloge bez avansa). Treba odlučiti definiciju prihoda. Ako je cilj ukupan prihod: SUM(naplaceno + COALESCE(avans, 0)) za status='Preuzeto', uz uslov (naplaceno > 0 OR avans > 0) umesto samo naplaceno > 0.