Servis: storno naloga (kao Prodaja/Nabavka) — KIR/KPO storno stavke, fiskalni refund

Do sada je jedini način da se poništi naplaćen/preuzet servisni nalog bio
fizičko brisanje (ObrisiNalog), koje ostavlja KIR/KPO/fiskalne zapise sa mrtvim
izvor_id — prihod ostaje prijavljen za nalog koji više ne postoji.

Migracija 100: servisni_nalozi dobija stornirano/razlog_storniranja (kao
prodajni_nalozi/nabavke). ServisRepo.Storno vraća ugrađene delove na stanje
ali NE briše nalog. Novi handler StornoNaloga (dozvola servis.storno):
- KIR/KPO original ostaje, dograđuje se storno stavka (model.KirStorno/KpoStorno)
- fiskalni refund (best-effort) preko refundujServis — Refund zahtev sa
  ReferentDocumentNumber ka originalnom PFR broju, isti obrazac kao stornirajProdaju
- ObrisiNalog sad odbija brisanje ako nalog već ima fiskalni/KIR/KPO zapis i
  upućuje na Storno

stornoBezRefunda (dashboard upozorenje) prošireno da prati i servisne stornose
bez uspešnog fiskalnog refunda, ne samo prodaju.
This commit is contained in:
2026-07-02 11:30:55 +02:00
parent 3c61063bbb
commit f74cb04024
9 changed files with 392 additions and 41 deletions
+15 -9
View File
@@ -118,7 +118,7 @@ func (h *Handler) kpoKandidatiServisa(ctx context.Context) (naplaceni []model.Se
}
naplaceni = make([]model.ServisniNalogSaKlijentom, 0, len(servisNalozi))
for _, sn := range servisNalozi {
if sn.Status == model.StatusPreuzeto && sn.Naplaceno != 0 {
if sn.Status == model.StatusPreuzeto && sn.Naplaceno != 0 && !sn.Stornirano {
naplaceni = append(naplaceni, sn)
} else {
nenaplaceniBroj++
@@ -211,17 +211,23 @@ func (h *Handler) PraznineKnjigovodstva(ctx context.Context) (model.PrazninaKnji
return p, nil
}
// stornoBezRefunda broji stornirane prodajne naloge čiji je originalni fiskalni
// račun i dalje neoznačen kao storniran — znači da je storno u bazi prošao, ali
// fiskalni refund ka ESIR/L-PFR uređaju NIJE uspeo (best-effort poziv je pao).
// Servis nema storno funkciju — obuhvaćena je samo prodaja.
// stornoBezRefunda broji stornirane prodajne i servisne naloge čiji je originalni
// fiskalni račun i dalje neoznačen kao storniran — znači da je storno u bazi
// prošao, ali fiskalni refund ka ESIR/L-PFR uređaju NIJE uspeo (best-effort poziv
// je pao).
func (h *Handler) stornoBezRefunda(ctx context.Context) (int, error) {
var n int
err := h.DB.QueryRowContext(ctx, `
SELECT COUNT(*) FROM prodajni_nalozi pn
JOIN fiskalni_racuni fr ON fr.prodaja_id = pn.id
AND fr.tip_transakcije = 'Sale' AND fr.storniran = 0
WHERE pn.stornirano = 1
SELECT
(SELECT COUNT(*) FROM prodajni_nalozi pn
JOIN fiskalni_racuni fr ON fr.prodaja_id = pn.id
AND fr.tip_transakcije = 'Sale' AND fr.storniran = 0
WHERE pn.stornirano = 1)
+
(SELECT COUNT(*) FROM servisni_nalozi sn
JOIN fiskalni_racuni fr ON fr.servis_id = sn.id
AND fr.tip_transakcije = 'Sale' AND fr.storniran = 0
WHERE sn.stornirano = 1)
`).Scan(&n)
return n, err
}