izbacivanje BUG.md iz praćenja, .gitignore ažuriran
This commit is contained in:
@@ -1,234 +0,0 @@
|
|||||||
# 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:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
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`:
|
|
||||||
```go
|
|
||||||
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):
|
|
||||||
|
|
||||||
```go
|
|
||||||
// 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`:
|
|
||||||
|
|
||||||
```sql
|
|
||||||
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:
|
|
||||||
|
|
||||||
```go
|
|
||||||
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**:
|
|
||||||
|
|
||||||
```css
|
|
||||||
.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:
|
|
||||||
|
|
||||||
```go
|
|
||||||
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`:
|
|
||||||
```go
|
|
||||||
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:
|
|
||||||
```sql
|
|
||||||
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`.
|
|
||||||
Reference in New Issue
Block a user