diff --git a/cmd/ntech/main.go b/cmd/ntech/main.go index 99421aa..1ee418e 100644 --- a/cmd/ntech/main.go +++ b/cmd/ntech/main.go @@ -311,6 +311,7 @@ func main() { r.With(modul("pdv")).Get("/pdv/kir/nova", h.NoviPdvKir) r.With(modul("pdv"), doz("pdv.dodaj")).Post("/pdv/kir/nova", h.SacuvajPdvKir) r.With(modul("pdv"), doz("pdv.obrisi")).Post("/pdv/kir/obrisi/{id}", h.ObrisiPdvKir) + r.With(modul("pdv"), doz("pdv.dodaj")).Post("/pdv/kir/backfill-prodaje", h.KirBackfillProdaje) r.With(modul("pdv")).Get("/pdv/kpr", h.PdvKpr) r.With(modul("pdv")).Get("/pdv/kpr/nova", h.NoviPdvKpr) r.With(modul("pdv"), doz("pdv.dodaj")).Post("/pdv/kpr/nova", h.SacuvajPdvKpr) diff --git a/internal/db/repository.go b/internal/db/repository.go index 502c264..d2e5e29 100644 --- a/internal/db/repository.go +++ b/internal/db/repository.go @@ -68,6 +68,8 @@ type PdvKirRepository interface { Obrisi(ctx context.Context, id int64) error // ObrisiPoIzvoru briše zapise vezane za dati izvor (npr. pri stornu prodaje) ObrisiPoIzvoru(ctx context.Context, izvor string, izvorID int64) error + // PostojiZaIzvor vraća true ako postoji bar jedan zapis za dati izvor i izvorID + PostojiZaIzvor(ctx context.Context, izvor string, izvorID int64) (bool, error) } // PdvKprRepository definiše operacije nad knjigom primljenih računa (KPR) diff --git a/internal/db/sqlite/pdv_evidencija.go b/internal/db/sqlite/pdv_evidencija.go index 7e29538..61652b5 100644 --- a/internal/db/sqlite/pdv_evidencija.go +++ b/internal/db/sqlite/pdv_evidencija.go @@ -136,6 +136,18 @@ func (r *PdvKirRepo) ObrisiPoIzvoru(ctx context.Context, izvor string, izvorID i return nil } +// PostojiZaIzvor vraća true ako postoji bar jedan KIR zapis za dati izvor i izvorID +func (r *PdvKirRepo) PostojiZaIzvor(ctx context.Context, izvor string, izvorID int64) (bool, error) { + var n int + err := r.db.QueryRowContext(ctx, + "SELECT COUNT(*) FROM pdv_kir WHERE izvor = ? AND izvor_id = ?", izvor, izvorID, + ).Scan(&n) + if err != nil { + return false, fmt.Errorf("ntech: PdvKirRepo.PostojiZaIzvor: %w", err) + } + return n > 0, nil +} + // izvorIliRucno vraća izvor ili podrazumevano „rucno" (izvor kolona je NOT NULL) func izvorIliRucno(izvor string) string { if izvor == "" { diff --git a/internal/handler/pdv_kir.go b/internal/handler/pdv_kir.go index 6d63954..790f358 100644 --- a/internal/handler/pdv_kir.go +++ b/internal/handler/pdv_kir.go @@ -1,6 +1,8 @@ package handler import ( + "fmt" + "log/slog" "net/http" "strconv" "strings" @@ -188,3 +190,58 @@ func (h *Handler) ObrisiPdvKir(w http.ResponseWriter, r *http.Request) { } http.Redirect(w, r, "/pdv/kir?obrisan=1", http.StatusSeeOther) } + +// KirBackfillProdaje kreira KIR zapise za sve B2B prodaje koje ih nemaju. +// Namenjen za jednokratno popunjavanje starih podataka. +func (h *Handler) KirBackfillProdaje(w http.ResponseWriter, r *http.Request) { + if _, ok := h.zahtevajDozvolu(w, r, "pdv.dodaj"); !ok { + return + } + ctx := r.Context() + + nalozi, err := h.ProdajaRepo.Lista(ctx, "") + if err != nil { + http.Error(w, "Greška pri učitavanju prodaje", http.StatusInternalServerError) + return + } + + var kreirano, preskoceno int + for _, nd := range nalozi { + if nd.KlijentID == nil { + preskoceno++ + continue + } + postoji, _ := h.PdvKirRepo.PostojiZaIzvor(ctx, "prodaja", nd.ID) + if postoji { + preskoceno++ + continue + } + stavke, err := h.ProdajaRepo.DohvatiStavke(ctx, nd.ID) + if err != nil { + slog.Error("backfill KIR: greška pri čitanju stavki", "prodaja_id", nd.ID, "error", err) + continue + } + var ss []model.StavkaProdaje + for _, s := range stavke { + ss = append(ss, s.StavkaProdaje) + } + klijent, err := h.KlijentiRepo.DohvatiID(ctx, *nd.KlijentID) + if err != nil { + slog.Error("backfill KIR: greška pri čitanju klijenta", "prodaja_id", nd.ID, "error", err) + continue + } + pib := klijent.PIB + if klijent.Tip != "pravno" { + pib = klijent.JMBG + } + kir := model.KirIzProdaje(nd.ProdajniNalog, ss, klijent.PunoIme(), pib, klijent.Mesto) + if _, e := h.PdvKirRepo.Kreiraj(ctx, &kir); e != nil { + slog.Error("backfill KIR: greška pri kreiranju zapisa", "prodaja_id", nd.ID, "error", e) + continue + } + kreirano++ + } + + middleware.SetFlash(w, r, h.DB, "uspeh", fmt.Sprintf("Backfill završen: %d novih KIR zapisa, %d preskočeno.", kreirano, preskoceno)) + http.Redirect(w, r, "/pdv/kir", http.StatusSeeOther) +} diff --git a/web/templates/stranice/pdv_kir.html b/web/templates/stranice/pdv_kir.html index e4959a3..7b4f84b 100644 --- a/web/templates/stranice/pdv_kir.html +++ b/web/templates/stranice/pdv_kir.html @@ -20,7 +20,18 @@ {{if or .Od .Do}}Poništi filter{{end}} - + Nov izlazni račun +
+ {{if index .Dozvole "pdv.dodaj"}} +
+ + +
+ {{end}} + + Nov izlazni račun +