- Boja tačke na kritičnim zalihama: crvena kad je količina upola manja od minimalne
- Uklonjen preload pozadinske slike (nepotreban, izazivao warning u konzoli)
- @view-transition umotan u @supports da ne izaziva warning u browserima koji ne podržavaju
- ručni unos prodajne cene preračunava maržu (izracunajMarzu): obrnuta formula
marža = (prodajna / (nabavna × (1+pdv/100)) − 1) × 100
- kalkulacija poštuje da li je firma PDV obveznik: ako nije, prodajna se ne
uvećava za PDV (pdvStopa = 0); PodaciFormeNabavke.PdvObveznik → JS _ntechPdvObveznik
- proširena kolona „Marža %" da se vidi cela vrednost
- migracija 048: kolona uvoz na pdv_kpr (0=domaća nabavka, 1=uvoz)
- model PdvKpr.Uvoz; MapirajPPPDV(kir, kprDomace, kprUvoz) rutira uvoz u 006/106,
domaće u 008/108; test ažuriran + uvozni scenario
- repo: KPR Lista/DohvatiID/Kreiraj čitaju i pišu uvoz
- obračun: KPR se razdvaja na domaće/uvozne; obaveza ostaje na ukupnom KPR-u
- KPR forma: kvačica „Uvoz (JCI)"; lista: oznaka UVOZ uz broj dokumenta
- forma nabavke: sekcija „Zavisni troškovi" (slobodne stavke naziv+iznos,
dodavanje/uklanjanje) + izbor metoda raspodele (po vrednosti / po količini)
- JS: kalkNabavna (kalkulativna nabavna po stavci, prati server) i preracunajSve —
promena troška/metoda/količine/cene preračunava prodajne svih stavki
- detalji nabavke: prikaz zavisnih troškova, metoda raspodele i ukupnog iznosa
- handler DetaljiNabavke dohvata troškove (DohvatiTroskove)
Po stavci nabavke: marža% (predpopunjena globalnom iz podešavanja,
kalkulacija_marza) i prodajna cena = nabavna × (1+marža/100) × (1+PDV/100),
živo računato u Alpine, izmenjivo. Na čuvanje se ažurira nabavna+prodajna
cena artikla (ArtikalRepo.AzurirajCene) i upiše nivelacija 'kalkulacija';
prazna/nulta prodajna se preskače. ArtikliJSON nosi pdv_stopa. Postavka
podrazumevane marže u Podešavanja → Sistem.
Tabela nivelacije (migr 045) beleži svaku promenu prodajne cene:
artikal, stara→nova cena, razlog, izvor, korisnik, datum. Dva okidača:
posebna akcija „Promeni cenu" (modal, izvor 'rucno') i auto-trag pri
izmeni artikla (izvor 'izmena'). PromeniCenu je transakciono (update
cene + upis zapisa). Pregled /nivelacije sa filterom perioda i razlikom
(+/− i %). Modal otvara svoj nextElementSibling — radi i na mobilnom
uprkos dupliranim id-jevima iz dva rasporeda.
model.MapirajPPPDV preslikava zbirove KIR/KPR na polja zvaničnog
obrasca PPPDV (001-005/103-105, 006-009/106-109, 110, povraćaj) u
celim dinarima; zbirovi se računaju iz zaokruženih polja. Uvoz
(006/106) i nadoknada poljoprivredniku (007/107) se ne prate → 0.
Sekcija PPPDV dodata na /pdv/obracun. Prikaz za popunjavanje, ne
elektronska predaja.
Interni obračun: izlazni (dugovani) PDV iz KIR i odbitni (prethodni)
PDV iz KPR po stopama, konačna obaveza za uplatu ili povraćaj/prenos.
PdvBezOdbitka se ne računa u odbitni PDV. Stranica /pdv/obracun
(podrazumevano tekući mesec), link u sidebaru. Brojčana podloga za
budući zvanični PPPDV/POPDV obrazac.
Dugmad otvaraju modale koji preko AJAX-a (X-Requested-With: fetch) čuvaju
novi zapis i vraćaju JSON; novi dobavljač se odmah ubacuje u listu i bira.
Modal artikla proširen na sva polja kao puna stranica. Centriranje modala
prebačeno u .modal-overlay klasu (x-show je brisao inline display:flex).
PDV se izvodi iz stope artikla po stavci (aproksimacija: nabavna cena
= osnovica bez PDV). Grupisanje po stopi (20→opšta, 10→posebna,
ostalo→oslobođena nabavka), broj dokumenta NAB-<id>, veza izvor/izvor_id.
Auto-zapisi se ne mogu ručno brisati u KPR; brisanje nabavke uklanja
vezani KPR zapis.
Kad se sačuva prodaja na klijenta (PDV obveznik), zapis se sam zavede u
KIR (model.KirIzProdaje grupiše stavke po stopi). Storno/brisanje prodaje
uklanja vezani KIR zapis (ObrisiPoIzvoru). Maloprodaja građanima (bez
klijenta) se preskače — ide preko fiskalizacije (Faza 3). Helper
modulUkljucen; auto-zapisi u UI nemaju ručno brisanje. Test.
KPR (handler, rute pod RequireModul("pdv"), UI sa sumama po stopama,
izbor dobavljača, datum plaćanja, PDV bez odbitka / oslobođena nabavka)
+ stavka u meniju. Dobavljači dobili PIB i mesto (migracija 043) jer KPR
traži PIB dobavljača za POPDV. Time je Faza 2a kompletna (KIR + KPR).
KIR forma nudi padajuću listu postojećih klijenata koja popunjava naziv,
PIB/JMBG i mesto kupca (uz ručni unos za kupce van baze). KIR i dalje
čuva kupca kao tekst, ne kao vezu. Klijenti dobili polje mesto (migracija
042), provučeno kroz model, repo, formu i handler.
Pregled knjige izdatih računa (filter perioda + sume po stopama), forma
unosa (server računa ukupno) i brisanje. Nove dozvole pdv.*; prvi put
montiran RequireModul("pdv") — KIR je vidljiv u meniju (.Moduli) i
dostupan samo PDV obveznicima, uz RBAC. Oznaka PIB/JMBG po broju cifara.
Tabele pdv_kir i pdv_kpr (iznosi po vrsti stope), modeli PdvKir/PdvKpr,
repozitorijumi sa filterom perioda i integracioni test (datum round-trip,
nullable datum plaćanja).
Handleri (prikaz, dodaj, izmeni, arhiviraj/vrati) sa validacijom i flash
porukama; rute pod /admin/podesavanja/pdv-stope (dozvole podesavanja.*);
stranica pdv_stope registrovana u kes.go i dodata u meni Podešavanja.
Šifarnik je opšti (bez RequireModul) jer ga koristi i kalkulacija.
Tabela pdv_stope (seed 20/10/0%), model PdvStopa, PdvStopaRepository
+ SQLite implementacija (arhiviranje umesto brisanja) i integracioni
test (migracija + CRUD round-trip).
config.SviModuli + PodaciStranice.Moduli (uslovni meni, analogno Dozvole)
i middleware.RequireModul (zaštita ruta, analogno RequireDozvola). Sloj
iznad RBAC-a: zahtev mora proći i „modul uključen" i „korisnik sme".
Dopunjen test (TestSviModuli). Time je Faza 0 kompletna.
Dodata kartica „Pravni i poreski status" na Podešavanja → Opšte:
pravni oblik, režim rada, PDV obveznik, fiskalizacija. Čuva se u
postojećoj key-value tabeli podesavanja (bez migracije). Fiskalizacija
se zasivi i forsira na „Ne" u režimu „samo evidencija".
HTML nije imao Cache-Control, pa je browser servirao staru keširanu stranicu
sa starim AssetV tokenom i posle deploya — stari CSS/JS. Dodato no-cache na
renderujTemplate i renderujStandalone: browser revalidira HTML, dobija svež
AssetV token koji povlači svež CSS/JS. Statika ostaje immutable + ?v=verzija.
dashboard.go i izvestaji.go više ne sadrže direktan SQL — svih 12 upita
prebačeno u novi IzvestajRepository (internal/db/sqlite/izvestaj.go). Repo vraća
sirove redove (model.*Red tipovi), a handleri zadržavaju prezentaciju
(formatiranje datuma, boje tačaka, rang, sklapanje niza 12 meseci). Žičenje kroz
Handler.IzvestajRepo (+ reinicijalizuj).
Dobici: testabilnost (dodati integracioni testovi izvestaj_test.go) i put ka
Postgres-u bez prepravke handlera. dashboard.prihod provera ostaje u handleru.
Van obima: middleware/flash.go i backup VACUUM INTO (ne pripadaju repo sloju).
Uveden podrazumevani slog logger u main.go (podesiLog): JSON u produkciji,
tekst u razvoju, nivo Info. Svih ~70 poziva log.Printf/Println/Fatalf zamenjeno
slog.Error/Warn/Info: greška se prosleđuje kao atribut "error", informativne
vrednosti kao imenovani atributi (port, broj, putanja...), Fatalf -> Error +
os.Exit(1). Upozorenja (inicijalizacija/čišćenje dozvola, migracija kolone) idu
kao slog.Warn.
Auth log (internal/auth/log.go) namerno NIJE diran — ostaje zaseban *log.Logger
u fail2ban formatu. (slog.SetDefault usput preusmerava i standardni log paket.)
Auto-backup i čišćenje sesija/pokušaja su koristili originalni db handle iz
main.go, koji posle obnove backupa (VratiBackup) ostaje zatvoren — gorutine bi
prestale da rade do restarta. Sada rade preko novog helpera h.SaBazom, koji pod
deljenim zaključavanjem prosleđuje trenutnu h.DB, pa vide zamenjenu konekciju.
Gorutine su premeštene da startuju posle kreiranja h. time.Sleep je van
zaključavanja da ne odlaže obnovu.
Obnova backupa (VratiBackup) je menjala Handler.DB i repozitorijume bez
zaključavanja dok drugi zahtevi rade — data race i moguć upit nad zatvorenom
konekcijom. Uveden Handler.mu (sync.RWMutex): zahtevi drže deljeno zaključavanje
preko middleware-a ZakljucajCitanje, a sama zamena (close+copy+reopen+reinit)
uzima ekskluzivno zaključavanje u zasebnoj gorutini, pa sačeka da svi tekući
zahtevi završe (drain) pre zamene. Zasebna gorutina je nužna jer zahtev još
drži deljeno zaključavanje (inače deadlock).
Poznato ograničenje: pozadinske gorutine i dalje koriste stari db handle iz
main.go — zaseban slučaj za kasnije.
TOTP tajne se više ne čuvaju kao čist tekst u koloni korisnici.totp_tajna.
Uvedene auth.Sifruj/auth.Desifruj (AES-256-GCM) u internal/auth/kripto.go.
sqliteKorisniciRepo šifruje pri SacuvajTotpTajnu i dešifruje pri čitanju
(DohvatiPoImenu/PoID/Lista), pa ostatak programa i dalje vidi čistu tajnu.
Ključ je 32-bajtni NTECH_TOTP_KEY (base64), učitava se ili generiše pri
pokretanju (ucitajTotpKljuc) i upisuje u ntech.env van baze. Stare nešifrovane
tajne se tolerišu pri čitanju (fallback) i jednokratno šifruju pri startu
(ZasifrujPostojeceTotp). RequireAuth i Handler provode ključ do repo-a.
Dodati prvi testovi u repozitorijumu (internal/auth/kripto_test.go).
Uvedena `AssetV` promenljiva za cache-busting statičkih fajlova, koja se postavlja pri svakom pokretanju.
Kreiran pogled `klijent_prikaz` (migracija 038) za jedinstveno prikazivanje imena klijenta, čime se eliminiše ponavljanje COALESCE logike u upitima.
Izvučena `dodeliOpcijeKorisnika` funkcija u korisnici.go radi DRY.
Zamenjeni inline stilovi u HTML šablonima CSS klasama (`.polje-labela`, `.obavezno`, `.pomocni-tekst`, `.tabela`, `.kartica-tabela`, `.prazno-stanje`).
Dodat `width: 100%` na inpute i `resize: vertical` na textarea u main.css.
Bezbednost / dozvole:
- Nov RequireDozvola(proveri, akcija) middleware (po uzoru na RequireAdmin):
na odbijanje redirekt na /dashboard sa flash porukom umesto golog 403
- 10 "pregled" ruta (prodaja, servis, klijenti, dobavljači, nabavke) prešlo
na deklarativnu proveru na nivou rute
- Uklonjene inline pregled-provere iz handlera — provera je sad na jednom
mestu po ruti, vidljiva u ruteru
Backup podešavanja:
- Neispravan unos (van opsega ili ne-broj) više se ne preskače tiho;
prikazuje se jasna greška i ne prikazuje se lažno "sačuvano"
- Hvata se greška pri čuvanju u bazu (uklonjen progutani _ =)
Šabloni:
- "Premesti" dropdown izvučen u jedan {{define "premestiMeni"}} partial
(više nije dupliran u tabeli i mobilnoj kartici)
- Dodat dict helper u FuncMap saSidebar šablona radi prosleđivanja više
vrednosti partialu; standalone šabloni namerno ostaju bez izmene
Magacin:
- Dodato premeštanje artikla u drugu kategoriju (dugme + nativni
<details> meni, bez JS-a; radi na desktopu i mobilnom)
- Endpoint POST /magacin/premesti/{id} uz proveru dozvole artikal.premesti
Backup:
- Nova podešavanja: interval automatskog backupa i broj kopija (rotacija)
- Periodični backup uz onaj pri pokretanju; interval se čita iz baze
- Migracija 037_backup_podesavanja.sql
Dozvole (RBAC):
- Dodate kartice koje su nedostajale (dashboard.prihod, prodaja.storno,
podesavanja.login_pozadina, tema.lokalno) — popravljen i bug gde su se
gasile pri svakom čuvanju matrice
- Aktivirana kontrola pregleda za prodaju, servis, klijente i dobavljače
(provera u handlerima + skrivanje iz sidebara)
- Uklonjene mrtve/obmanjujuće dozvole iz matrice i sveAkcije (korisnici,
podsetnici, artikal.pregled, kategorija.izmeni, tema.globalno,
podesavanja.app_pozadina); sveAkcije 47 -> 34
- Čišćenje zastarelih redova (siročića) u tabeli dozvola pri startu
Ostalo:
- Statički fajlovi: embed celog web/static i ispravan MIME za .js/.css
- Keš šablona: dodat admin_dozvole (stranica Dozvole se nije otvarala)
- Sidebar accordion: radi i skupljen i proširen, međusobno isključiv
- Uklonjena globalna tema i pozadinska slika aplikacije (ostala samo lična pozadina po korisniku)
- Uklonjena animacija treperenja pozadine pri navigaciji; dodat sessionStorage za instant prikaz
- Dozvole premeštene iz sidebar-a u Podešavanja → Sistem; vidljive i adminu (samo Radnik kolona)
- Admin može menjati samo dozvole uloge Radnik, superadmin menja i Radnik i Admin
- Zatamnjivanje kartice NTech na stranici prijave — novi slider u Podešavanja → Izgled
- Upozorenje na dashboard-u (kritične zalihe) — popravljen kontrast boje