Servis: 'Primljeno' iznos se sad stvarno šalje na fiskalni račun

Input 'Primljeno (din)' u modalu za preuzimanje je bio VAN <form> koji se
submituje — koristio se samo za JS prikaz kusura na ekranu, ali server ga
nikad nije primao. Fiskalni račun je uvek prikazivao Gotovina=dug (posle
avansa), Povraćaj=0,00, bez obzira šta je kupac stvarno predao.

Premešten input u POST formu; fiskalizujServis sad prima primljeno i šalje
max(dug, primljeno) kao Payment.Amount — PFR sam računa kusur, isto kao kod
Prodaje (fiskal.NapraviZahtev).

NAPOMENA: kad nalog ima avans, PFR i dalje računa povraćaj protiv PUNOG iznosa
stavki (ne umanjenog za avans), jer se avans nikad ne fiskalizuje kao posebna
transakcija — PFR ga ne 'vidi'. Za tačan kusur i u tom slučaju bi avans morao
biti fiskalizovan kao Advance tip transakcije pri uplati; to je veći, zaseban
zahvat.
This commit is contained in:
2026-07-02 12:21:39 +02:00
parent 1027cfa526
commit 5b39dfd222
2 changed files with 25 additions and 14 deletions
+16 -5
View File
@@ -2307,6 +2307,9 @@ func (h *Handler) PromeniStatus(w http.ResponseWriter, r *http.Request) {
if nacin == "" { if nacin == "" {
nacin = "Gotovina" nacin = "Gotovina"
} }
// primljeno je iznos koji je klijent stvarno predao (za tačan kusur na fiskalnom
// računu, isto kao kod Prodaje — v. fiskal.NapraviZahtev) — 0 ako nije unet
primljeno, _ := strconv.ParseFloat(strings.TrimSpace(r.FormValue("primljeno")), 64)
iznosStr := strings.TrimSpace(r.FormValue("naplaceno")) iznosStr := strings.TrimSpace(r.FormValue("naplaceno"))
iznos := 0.0 iznos := 0.0
if iznosStr != "" { if iznosStr != "" {
@@ -2346,7 +2349,7 @@ func (h *Handler) PromeniStatus(w http.ResponseWriter, r *http.Request) {
if fr, _ := h.FiskalRepo.DohvatiPoServisu(r.Context(), id); fr == nil { if fr, _ := h.FiskalRepo.DohvatiPoServisu(r.Context(), id); fr == nil {
klijent := h.fiskalKlijent() klijent := h.fiskalKlijent()
if klijent != nil && iznos > 0 { if klijent != nil && iznos > 0 {
h.fiskalizujServis(r.Context(), id, klijent, nacin, iznos) h.fiskalizujServis(r.Context(), id, klijent, nacin, iznos, primljeno)
} }
} }
} }
@@ -2403,8 +2406,11 @@ func (h *Handler) PromeniStatus(w http.ResponseWriter, r *http.Request) {
} }
// fiskalizujServis šalje fiskalni zahtev za servisni nalog pri preuzimanju. // fiskalizujServis šalje fiskalni zahtev za servisni nalog pri preuzimanju.
// Best-effort: greške se loguju, ne zaustavljaju promenu statusa. // Best-effort: greške se loguju, ne zaustavljaju promenu statusa. primljeno je
func (h *Handler) fiskalizujServis(ctx context.Context, servisID int64, klijent *fiskal.Klijent, nacinPlacanja string, iznos float64) { // iznos koji je klijent stvarno predao — ako je veći od iznos (dug posle avansa),
// na računu se iskazuje stvarno primljen iznos i PFR sam izračunava povraćaj
// (isto kao fiskal.NapraviZahtev za Prodaju); 0 ili manje od duga → bez povraćaja.
func (h *Handler) fiskalizujServis(ctx context.Context, servisID int64, klijent *fiskal.Klijent, nacinPlacanja string, iznos, primljeno float64) {
nalog, err := h.ServisRepo.DohvatiID(ctx, servisID) nalog, err := h.ServisRepo.DohvatiID(ctx, servisID)
if err != nil { if err != nil {
slog.Error("fiskalizujServis: nije pronađen nalog", "id", servisID, "error", err) slog.Error("fiskalizujServis: nije pronađen nalog", "id", servisID, "error", err)
@@ -2437,12 +2443,17 @@ func (h *Handler) fiskalizujServis(ctx context.Context, servisID int64, klijent
kasir := h.imeKasira(ctx) kasir := h.imeKasira(ctx)
iznosPlacanja := iznos
if primljeno > iznos {
iznosPlacanja = primljeno
}
zahtev := fiskal.InvoiceRequest{ zahtev := fiskal.InvoiceRequest{
InvoiceRequest: fiskal.InvoiceRequestBody{ InvoiceRequest: fiskal.InvoiceRequestBody{
InvoiceType: "Normal", InvoiceType: "Normal",
TransactionType: "Sale", TransactionType: "Sale",
Payment: []fiskal.PaymentItem{ Payment: []fiskal.PaymentItem{
{Amount: iznos, PaymentType: fiskal.TipPlacanja(nacinPlacanja)}, {Amount: iznosPlacanja, PaymentType: fiskal.TipPlacanja(nacinPlacanja)},
}, },
Items: items, Items: items,
Cashier: kasir, Cashier: kasir,
@@ -2639,7 +2650,7 @@ func (h *Handler) RetryFiskalizacija(w http.ResponseWriter, r *http.Request) {
return return
} }
h.fiskalizujServis(r.Context(), id, klijent, nalog.NacinPlacanja, nalog.Naplaceno) h.fiskalizujServis(r.Context(), id, klijent, nalog.NacinPlacanja, nalog.Naplaceno, 0)
// provjeri da li je uspelo // provjeri da li je uspelo
if fr, _ := h.FiskalRepo.DohvatiPoServisu(r.Context(), id); fr != nil { if fr, _ := h.FiskalRepo.DohvatiPoServisu(r.Context(), id); fr != nil {
+9 -9
View File
@@ -130,19 +130,19 @@
<span style="font-size:16px;font-weight:600;color:var(--tekst-glavni);">Za naplatu</span> <span style="font-size:16px;font-weight:600;color:var(--tekst-glavni);">Za naplatu</span>
<span style="font-size:18px;font-weight:600;color:var(--tekst-glavni);" id="za-naplatu-iznos">{{if index .Moduli "pdv"}}{{dinari .PreostaloSveSaPdv}}{{else}}{{dinari .PreostaloSve}}{{end}} din</span> <span style="font-size:18px;font-weight:600;color:var(--tekst-glavni);" id="za-naplatu-iznos">{{if index .Moduli "pdv"}}{{dinari .PreostaloSveSaPdv}}{{else}}{{dinari .PreostaloSve}}{{end}} din</span>
</div> </div>
<div style="margin-top:10px;padding-top:10px;border-top:0.5px solid var(--ivica);">
<label class="polje-labela">Primljeno (din)</label>
<input type="number" id="primljeno" name="primljeno" min="0" step="0.01" placeholder="Unesite primljeni iznos" style="width:100%;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:14px;box-sizing:border-box;" oninput="ntechPrimljenoUpdate(this)">
</div>
<div id="kusur-blok" style="display:none;justify-content:space-between;align-items:center;padding:8px 12px;background:color-mix(in srgb,var(--uspeh) 10%,transparent);border:0.5px solid var(--uspeh);border-radius:8px;">
<span style="color:var(--uspeh);font-weight:500;">Kusur (vratiti)</span>
<span id="kusur" style="font-size:16px;font-weight:600;color:var(--uspeh);">0,00 din</span>
</div>
<form method="POST" action="/servis/{{.Nalog.ID}}/status" data-refresh="#sadrzaj" style="margin-top:8px;"> <form method="POST" action="/servis/{{.Nalog.ID}}/status" data-refresh="#sadrzaj" style="margin-top:8px;">
<input type="hidden" name="_csrf" value="{{.CsrfToken}}"> <input type="hidden" name="_csrf" value="{{.CsrfToken}}">
<input type="hidden" name="status" value="Preuzeto"> <input type="hidden" name="status" value="Preuzeto">
<input type="hidden" name="naplaceno" value="{{if index .Moduli "pdv"}}{{printf "%.2f" .PreostaloSveSaPdv}}{{else}}{{printf "%.2f" .PreostaloSve}}{{end}}"> <input type="hidden" name="naplaceno" value="{{if index .Moduli "pdv"}}{{printf "%.2f" .PreostaloSveSaPdv}}{{else}}{{printf "%.2f" .PreostaloSve}}{{end}}">
<div style="margin-bottom:10px;"> <div style="margin-top:10px;padding-top:10px;border-top:0.5px solid var(--ivica);">
<label class="polje-labela">Primljeno (din)</label>
<input type="number" id="primljeno" name="primljeno" min="0" step="0.01" placeholder="Unesite primljeni iznos" style="width:100%;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:14px;box-sizing:border-box;" oninput="ntechPrimljenoUpdate(this)">
</div>
<div id="kusur-blok" style="display:none;justify-content:space-between;align-items:center;padding:8px 12px;background:color-mix(in srgb,var(--uspeh) 10%,transparent);border:0.5px solid var(--uspeh);border-radius:8px;margin-top:10px;">
<span style="color:var(--uspeh);font-weight:500;">Kusur (vratiti)</span>
<span id="kusur" style="font-size:16px;font-weight:600;color:var(--uspeh);">0,00 din</span>
</div>
<div style="margin-bottom:10px;margin-top:10px;">
<label class="polje-labela">Način plaćanja</label> <label class="polje-labela">Način plaćanja</label>
<select name="nacin_placanja" id="nacin-placanja-sel" style="width:100%;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:13px;" onchange="ntechNacinPlacanjaBroj(this)"> <select name="nacin_placanja" id="nacin-placanja-sel" style="width:100%;padding:8px 12px;background:var(--pozadina);border:0.5px solid var(--ivica);border-radius:8px;color:var(--tekst-glavni);font-size:13px;" onchange="ntechNacinPlacanjaBroj(this)">
<option value="Gotovina">Gotovina</option> <option value="Gotovina">Gotovina</option>