package sqlite import ( "context" "database/sql" "fmt" "ntech/internal/model" ) // FiskalRepo je SQLite implementacija FiskalRepository interfejsa type FiskalRepo struct { db *sql.DB } // NoviFiskalRepo kreira novi FiskalRepo func NoviFiskalRepo(db *sql.DB) *FiskalRepo { return &FiskalRepo{db: db} } // Kreiraj dodaje novi fiskalni račun u bazu. Vraća ID kreiranog reda. // Koristi prodaja_id (za prodaju) ILI servis_id (za servis) — ne oba. func (r *FiskalRepo) Kreiraj(ctx context.Context, fr *model.FiskalniRacun) (int64, error) { var prodajaID, servisID any if fr.ProdajaID > 0 { prodajaID = fr.ProdajaID } if fr.ServisID > 0 { servisID = fr.ServisID } rez, err := r.db.ExecContext(ctx, ` INSERT INTO fiskalni_racuni ( prodaja_id, servis_id, tip_racuna, tip_transakcije, pfr_broj, pfr_vreme, brojac, ekstenzija_brojaca, url_verifikacija, qr_kod, poreske_stavke, ukupno_za_naplatu, ukupan_porez, sirovi_odgovor, potpisao, zatrazio, poruka ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) `, prodajaID, servisID, fr.TipRacuna, fr.TipTransakcije, fr.PfrBroj, fr.PfrVreme, fr.Brojac, fr.EkstenzijaBrojaca, fr.UrlVerifikacija, fr.QRKod, fr.PoreskeStavke, fr.UkupnoZaNaplatu, fr.UkupanPorez, fr.SiroviOdgovor, fr.Potpisao, fr.Zatrazio, fr.Poruka, ) if err != nil { return 0, fmt.Errorf("ntech: FiskalRepo.Kreiraj: %w", err) } id, err := rez.LastInsertId() if err != nil { return 0, fmt.Errorf("ntech: FiskalRepo.Kreiraj.LastInsertId: %w", err) } return id, nil } // DohvatiPoProdaji vraća fiskalni račun za dati prodajni nalog. // Vraća nil bez greške ako račun ne postoji. func (r *FiskalRepo) DohvatiPoProdaji(ctx context.Context, prodajaID int64) (*model.FiskalniRacun, error) { fr := &model.FiskalniRacun{} var storniran int var sID sql.NullInt64 err := r.db.QueryRowContext(ctx, ` SELECT id, prodaja_id, servis_id, tip_racuna, tip_transakcije, pfr_broj, pfr_vreme, brojac, ekstenzija_brojaca, url_verifikacija, qr_kod, poreske_stavke, ukupno_za_naplatu, ukupan_porez, sirovi_odgovor, potpisao, zatrazio, poruka, storniran, vreme_kreiranja FROM fiskalni_racuni WHERE prodaja_id = ? `, prodajaID).Scan( &fr.ID, &fr.ProdajaID, &sID, &fr.TipRacuna, &fr.TipTransakcije, &fr.PfrBroj, &fr.PfrVreme, &fr.Brojac, &fr.EkstenzijaBrojaca, &fr.UrlVerifikacija, &fr.QRKod, &fr.PoreskeStavke, &fr.UkupnoZaNaplatu, &fr.UkupanPorez, &fr.SiroviOdgovor, &fr.Potpisao, &fr.Zatrazio, &fr.Poruka, &storniran, &fr.VremeKreiranja, ) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, fmt.Errorf("ntech: FiskalRepo.DohvatiPoProdaji: %w", err) } if sID.Valid { fr.ServisID = sID.Int64 } fr.Storniran = storniran != 0 return fr, nil } // DohvatiPoServisu vraća fiskalni račun za dati servisni nalog. // Vraća nil bez greške ako račun ne postoji. func (r *FiskalRepo) DohvatiPoServisu(ctx context.Context, servisID int64) (*model.FiskalniRacun, error) { fr := &model.FiskalniRacun{} var storniran int var sID, pID sql.NullInt64 err := r.db.QueryRowContext(ctx, ` SELECT id, prodaja_id, servis_id, tip_racuna, tip_transakcije, pfr_broj, pfr_vreme, brojac, ekstenzija_brojaca, url_verifikacija, qr_kod, poreske_stavke, ukupno_za_naplatu, ukupan_porez, sirovi_odgovor, potpisao, zatrazio, poruka, storniran, vreme_kreiranja FROM fiskalni_racuni WHERE servis_id = ? `, servisID).Scan( &fr.ID, &pID, &sID, &fr.TipRacuna, &fr.TipTransakcije, &fr.PfrBroj, &fr.PfrVreme, &fr.Brojac, &fr.EkstenzijaBrojaca, &fr.UrlVerifikacija, &fr.QRKod, &fr.PoreskeStavke, &fr.UkupnoZaNaplatu, &fr.UkupanPorez, &fr.SiroviOdgovor, &fr.Potpisao, &fr.Zatrazio, &fr.Poruka, &storniran, &fr.VremeKreiranja, ) if err == sql.ErrNoRows { return nil, nil } if err != nil { return nil, fmt.Errorf("ntech: FiskalRepo.DohvatiPoServisu: %w", err) } if pID.Valid { fr.ProdajaID = pID.Int64 } if sID.Valid { fr.ServisID = sID.Int64 } fr.Storniran = storniran != 0 return fr, nil } // ServisiBezFiskalnog vraća skup ID-eva servisnih naloga koji su u statusu "Preuzeto" // a nemaju fiskalni račun (za oznaku u listi naloga). func (r *FiskalRepo) ServisiBezFiskalnog(ctx context.Context) (map[int64]bool, error) { rows, err := r.db.QueryContext(ctx, ` SELECT sn.id FROM servisni_nalozi sn LEFT JOIN fiskalni_racuni fr ON fr.servis_id = sn.id AND fr.storniran = 0 WHERE sn.status = 'Preuzeto' AND fr.id IS NULL`) if err != nil { return nil, err } defer rows.Close() out := make(map[int64]bool) for rows.Next() { var id int64 if err := rows.Scan(&id); err != nil { return nil, err } out[id] = true } return out, rows.Err() } // ProdajeBezFiskalnog vraća skup ID-eva prodajnih naloga koji nisu stornirani // a nemaju fiskalni račun (za oznaku u listi naloga). func (r *FiskalRepo) ProdajeBezFiskalnog(ctx context.Context) (map[int64]bool, error) { rows, err := r.db.QueryContext(ctx, ` SELECT pn.id FROM prodajni_nalozi pn LEFT JOIN fiskalni_racuni fr ON fr.prodaja_id = pn.id AND fr.storniran = 0 WHERE pn.stornirano = 0 AND fr.id IS NULL`) if err != nil { return nil, err } defer rows.Close() out := make(map[int64]bool) for rows.Next() { var id int64 if err := rows.Scan(&id); err != nil { return nil, err } out[id] = true } return out, rows.Err() } // OznačiKaoStorniran postavlja storniran=1 za fiskalni račun sa datim ID-jem. func (r *FiskalRepo) OznačiKaoStorniran(ctx context.Context, id int64) error { _, err := r.db.ExecContext(ctx, "UPDATE fiskalni_racuni SET storniran = 1 WHERE id = ?", id) if err != nil { return fmt.Errorf("ntech: FiskalRepo.OznačiKaoStorniran: %w", err) } return nil }