diff --git a/cmd/ntech/main.go b/cmd/ntech/main.go index d602bd5..99421aa 100644 --- a/cmd/ntech/main.go +++ b/cmd/ntech/main.go @@ -315,6 +315,7 @@ func main() { r.With(modul("pdv")).Get("/pdv/kpr/nova", h.NoviPdvKpr) r.With(modul("pdv"), doz("pdv.dodaj")).Post("/pdv/kpr/nova", h.SacuvajPdvKpr) r.With(modul("pdv"), doz("pdv.obrisi")).Post("/pdv/kpr/obrisi/{id}", h.ObrisiPdvKpr) + r.With(modul("pdv"), doz("pdv.dodaj")).Post("/pdv/kpr/backfill-nabavke", h.KprBackfillNabavke) r.With(modul("pdv")).Get("/pdv/obracun", h.PdvObracunStranica) r.Get("/magacin", h.Magacin) r.Get("/usluge", h.Usluge) diff --git a/internal/db/repository.go b/internal/db/repository.go index b688d23..502c264 100644 --- a/internal/db/repository.go +++ b/internal/db/repository.go @@ -78,6 +78,8 @@ type PdvKprRepository interface { Obrisi(ctx context.Context, id int64) error // ObrisiPoIzvoru briše zapise vezane za dati izvor (npr. pri brisanju nabavke) 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) } // NivelacijaRepository definiše operacije nad evidencijom promene prodajnih cena diff --git a/internal/db/sqlite/nabavka.go b/internal/db/sqlite/nabavka.go index bb33ce7..dae96cf 100644 --- a/internal/db/sqlite/nabavka.go +++ b/internal/db/sqlite/nabavka.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "time" "ntech/internal/model" ) @@ -24,6 +25,7 @@ func (r *NabavkaRepo) Lista(ctx context.Context) ([]model.NabavkaSaDetaljem, err SELECT n.id, n.dobavljac_id, n.napomena, n.ukupno, n.metod_raspodele, n.stornirano, n.razlog_storniranja, n.datum, + n.broj_racuna, n.datum_racuna, n.pdv_iznos, COALESCE(d.naziv, '') AS dobavljac_naziv FROM nabavke n LEFT JOIN dobavljaci d ON n.dobavljac_id = d.id @@ -37,11 +39,12 @@ func (r *NabavkaRepo) Lista(ctx context.Context) ([]model.NabavkaSaDetaljem, err for redovi.Next() { var n model.NabavkaSaDetaljem var dobavljacID sql.NullInt64 - var napomena, metod, razlogStorniranja sql.NullString + var napomena, metod, razlogStorniranja, brojRacuna, datumRacuna sql.NullString err := redovi.Scan( &n.ID, &dobavljacID, &napomena, &n.Ukupno, &metod, &n.Stornirano, &razlogStorniranja, &n.Datum, + &brojRacuna, &datumRacuna, &n.PdvIznos, &n.DobavljacNaziv, ) if err != nil { @@ -54,6 +57,12 @@ func (r *NabavkaRepo) Lista(ctx context.Context) ([]model.NabavkaSaDetaljem, err n.Napomena = napomena.String n.MetodRaspodele = metod.String n.RazlogStorniranja = razlogStorniranja.String + n.BrojRacuna = brojRacuna.String + if datumRacuna.Valid && datumRacuna.String != "" { + if t, e := time.Parse("2006-01-02", datumRacuna.String[:10]); e == nil { + n.DatumRacuna = &t + } + } rezultat = append(rezultat, n) } @@ -65,14 +74,16 @@ func (r *NabavkaRepo) Lista(ctx context.Context) ([]model.NabavkaSaDetaljem, err func (r *NabavkaRepo) DohvatiID(ctx context.Context, id int64) (*model.Nabavka, error) { var n model.Nabavka var dobavljacID sql.NullInt64 - var napomena, metod, razlogStorniranja sql.NullString + var napomena, metod, razlogStorniranja, brojRacuna, datumRacuna sql.NullString err := r.db.QueryRowContext(ctx, ` SELECT id, dobavljac_id, napomena, ukupno, metod_raspodele, - stornirano, razlog_storniranja, datum + stornirano, razlog_storniranja, datum, + broj_racuna, datum_racuna, pdv_iznos FROM nabavke WHERE id = ?`, id).Scan( &n.ID, &dobavljacID, &napomena, &n.Ukupno, &metod, &n.Stornirano, &razlogStorniranja, &n.Datum, + &brojRacuna, &datumRacuna, &n.PdvIznos, ) if err != nil { return nil, fmt.Errorf("ntech: NabavkaRepo.DohvatiID: %w", err) @@ -84,6 +95,12 @@ func (r *NabavkaRepo) DohvatiID(ctx context.Context, id int64) (*model.Nabavka, n.Napomena = napomena.String n.MetodRaspodele = metod.String n.RazlogStorniranja = razlogStorniranja.String + n.BrojRacuna = brojRacuna.String + if datumRacuna.Valid && datumRacuna.String != "" { + if t, e := time.Parse("2006-01-02", datumRacuna.String[:10]); e == nil { + n.DatumRacuna = &t + } + } return &n, nil } @@ -163,9 +180,10 @@ func (r *NabavkaRepo) Kreiraj(ctx context.Context, n *model.Nabavka, stavke []mo // upisujemo zaglavlje nabavke rezultat, err := tx.ExecContext(ctx, ` - INSERT INTO nabavke (dobavljac_id, napomena, ukupno, metod_raspodele) - VALUES (?, ?, ?, ?)`, + INSERT INTO nabavke (dobavljac_id, napomena, ukupno, metod_raspodele, broj_racuna, datum_racuna, pdv_iznos) + VALUES (?, ?, ?, ?, ?, ?, ?)`, nullInt64(n.DobavljacID), nullString(n.Napomena), ukupno, nullString(n.MetodRaspodele), + nullString(n.BrojRacuna), nullDateString(n.DatumRacuna), n.PdvIznos, ) if err != nil { return 0, fmt.Errorf("ntech: NabavkaRepo.Kreiraj: insert nabavka: %w", err) diff --git a/internal/db/sqlite/pdv_evidencija.go b/internal/db/sqlite/pdv_evidencija.go index f6a7ace..7e29538 100644 --- a/internal/db/sqlite/pdv_evidencija.go +++ b/internal/db/sqlite/pdv_evidencija.go @@ -292,3 +292,15 @@ func (r *PdvKprRepo) ObrisiPoIzvoru(ctx context.Context, izvor string, izvorID i } return nil } + +// PostojiZaIzvor vraća true ako postoji bar jedan KPR zapis za dati izvor i izvorID +func (r *PdvKprRepo) PostojiZaIzvor(ctx context.Context, izvor string, izvorID int64) (bool, error) { + var n int + err := r.db.QueryRowContext(ctx, + "SELECT COUNT(*) FROM pdv_kpr WHERE izvor = ? AND izvor_id = ?", izvor, izvorID, + ).Scan(&n) + if err != nil { + return false, fmt.Errorf("ntech: PdvKprRepo.PostojiZaIzvor: %w", err) + } + return n > 0, nil +} diff --git a/internal/db/sqlite/utils.go b/internal/db/sqlite/utils.go index 5ff9a3e..4f058ab 100644 --- a/internal/db/sqlite/utils.go +++ b/internal/db/sqlite/utils.go @@ -46,3 +46,12 @@ func nullTime(v *time.Time) sql.NullTime { } return sql.NullTime{Time: *v, Valid: true} } + +// nullDateString upisuje *time.Time kao "YYYY-MM-DD" string ili NULL — +// koristiti za DATE kolone gde sql.NullTime nije kompatibilan s driver-om +func nullDateString(v *time.Time) sql.NullString { + if v == nil { + return sql.NullString{} + } + return sql.NullString{String: v.Format("2006-01-02"), Valid: true} +} diff --git a/internal/handler/nabavka.go b/internal/handler/nabavka.go index 1706ea3..90f7ed0 100644 --- a/internal/handler/nabavka.go +++ b/internal/handler/nabavka.go @@ -7,6 +7,7 @@ import ( "net/http" "strconv" "strings" + "time" "ntech/internal/db" "ntech/internal/db/sqlite" @@ -32,6 +33,7 @@ type PodaciFormeNabavke struct { Kategorije []model.Kategorija // za dropdown u modalu novog artikla Marza string // podrazumevana marža (%) za kalkulaciju PdvObveznik bool // da li firma obračunava PDV (utiče na prodajnu cenu u kalkulaciji) + Danas string // format "2006-01-02" — default za datum računa Greska string } @@ -44,6 +46,7 @@ type PodaciDetaljiNabavke struct { UkupanTrosak float64 DobavljacNaziv string Sacuvano bool + Greska string } // artikalUJSON pretvara listu artikala u template.JS vrednost bezbednu za umetanje u