Servis: avans se fiskalizuje kao Advance/Sale, konačan račun ga zatvara

Avans se do sada nikad nije fiskalizovao — bio je samo interno polje. Zbog
toga je konačni fiskalni račun (pri preuzimanju) uvek prikazivao povraćaj
protiv PUNOG iznosa stavki, bez znanja da je avans već delimično plaćen.

Sad:
- avans se fiskalizuje kao Advance/Sale pri kreiranju/izmeni naloga
  (fiskalizujAvansServisa — fiskalizuje samo deltu ako se avans dopunjava)
- konačan Normal/Sale račun pri preuzimanju šalje advancePaid/advanceTax/
  advanceLastInvoiceNumber/advanceLastInvoiceDateTime — PFR sad zna za avans
  i tačno računa preostali dug i kusur
- ako avans premaši punu cenu, konačan račun se zatvara do pune cene, a
  razlika se automatski vraća posebnim Advance/Refund zahtevom
- servis_detalji modal prikazuje 'Avans premašuje cenu — vratiti klijentu X'
  umesto tihog svođenja duga na 0

Uzgred ispravljena dva mesta koja su koristila DohvatiPoServisu (samo
najnoviji zapis, bilo kog tipa) umesto DohvatiPoServisuITip(Normal,Sale):
- PromeniStatus je avansni račun pogrešno tretirao kao 'već fiskalizovano'
  i preskakao konačan račun kad god je nalog imao avans
- RetryFiskalizacija i FiskalGreska detekcija na servis_detalji stranici
  su imale isti problem — nalog sa avansom je izgledao fiskalizovan i
  dugme 'Pokušaj ponovo' se nije ni prikazivalo

PorezIzBrutoAvansa pretpostavlja opštu PDV stopu (20%) jer stavke u trenutku
uplate avansa još nisu poznate — nema opšteg rešenja bez pretpostavke.
This commit is contained in:
2026-07-02 12:52:04 +02:00
parent ca1c8c8241
commit fcda706edd
6 changed files with 282 additions and 18 deletions
+8 -2
View File
@@ -29,9 +29,15 @@ func NoviKlijent(baseURL, apiKey string) *Klijent {
}
}
// InvoiceRequest je omotač za Teron invoiceRequest JSON objekat.
// InvoiceRequest je omotač za Teron invoiceRequest JSON objekat. Polja Advance*
// su na vrhu (van invoiceRequest) — Teron ih tako očekuje za konačni račun koji
// zatvara prethodno izdat avansni račun (v. NapraviKonacniZahtevSaAvansom).
type InvoiceRequest struct {
InvoiceRequest InvoiceRequestBody `json:"invoiceRequest"`
InvoiceRequest InvoiceRequestBody `json:"invoiceRequest"`
AdvancePaid *float64 `json:"advancePaid,omitempty"`
AdvanceTax *float64 `json:"advanceTax,omitempty"`
AdvanceLastInvoiceNumber string `json:"advanceLastInvoiceNumber,omitempty"`
AdvanceLastInvoiceDateTime string `json:"advanceLastInvoiceDateTime,omitempty"`
}
// InvoiceRequestBody su podaci koje Teron očekuje unutar invoiceRequest polja.
+45
View File
@@ -142,3 +142,48 @@ func NapraviRefundZahtev(
zahtev.InvoiceRequest.ReferentDocumentNumber = referentBroj
return zahtev
}
// NazivAvansneStavke je naziv stavke na avansnim fiskalnim računima (isti naziv
// mora biti dosledan, jer ga PFR ne povezuje po ID-u već po sadržaju stavke).
const NazivAvansneStavke = "Аванс"
// StopaAvansa je pretpostavljena PDV stopa za avans — u trenutku uplate avansa
// stavke (radovi/delovi) obično još nisu poznate, pa se ne može tačno odrediti
// stvarna stopa. Koristi se opšta stopa (20%) kao razumna pretpostavka za
// srpsko tržište; ako firma posluje isključivo po posebnoj/nultoj stopi, ovo
// treba prilagoditi ručno (nema opšteg rešenja bez poznavanja stavki unapred).
const StopaAvansa = 20.0
// NapraviAvansZahtev gradi Advance/Sale zahtev za primljeni avans — jedna stavka
// "Аванс" u punom (bruto) iznosu.
func NapraviAvansZahtev(iznos float64, nacinPlacanja, kasir string) InvoiceRequest {
return InvoiceRequest{
InvoiceRequest: InvoiceRequestBody{
InvoiceType: "Advance",
TransactionType: "Sale",
Payment: []PaymentItem{
{Amount: iznos, PaymentType: TipPlacanja(nacinPlacanja)},
},
Items: []InvoiceItem{
{Name: NazivAvansneStavke, Labels: []string{OznakaPDV(StopaAvansa)}, TotalAmount: iznos, UnitPrice: iznos, Quantity: 1},
},
Cashier: kasir,
},
}
}
// NapraviAvansRefundZahtev gradi Advance/Refund zahtev za povraćaj dela ili celog
// avansa (npr. kad avans premaši konačnu cenu popravke). referentBroj je PfrBroj
// originalnog avansnog računa.
func NapraviAvansRefundZahtev(iznos float64, nacinPlacanja, kasir, referentBroj string) InvoiceRequest {
zahtev := NapraviAvansZahtev(iznos, nacinPlacanja, kasir)
zahtev.InvoiceRequest.TransactionType = "Refund"
zahtev.InvoiceRequest.ReferentDocumentNumber = referentBroj
return zahtev
}
// PorezIzBrutoAvansa izvlači poreski deo iz bruto iznosa avansa po StopaAvansa
// (npr. 500 din avansa po 20% → porez ≈ 83.33 din).
func PorezIzBrutoAvansa(bruto float64) float64 {
return math.Round(bruto*StopaAvansa/(100+StopaAvansa)*100) / 100
}