diff --git a/internal/db/sqlite/klijent.go b/internal/db/sqlite/klijent.go index 19cf72f..76bb318 100644 --- a/internal/db/sqlite/klijent.go +++ b/internal/db/sqlite/klijent.go @@ -23,7 +23,7 @@ func NoviKlijentRepo(db *sql.DB) *KlijentRepo { // Lista vraća listu klijenata sa opcionom pretragom po imenu, prezimenu ili nazivu firme func (r *KlijentRepo) Lista(ctx context.Context, pretraga string) ([]model.Klijent, error) { upit := ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE 1=1` @@ -46,9 +46,9 @@ func (r *KlijentRepo) Lista(ctx context.Context, pretraga string) ([]model.Klije var rezultat []model.Klijent for redovi.Next() { var k model.Klijent - var ime, prezime, jmbg, tipIdent, nazivFirme, pib, telefon, email, mesto, napomena sql.NullString + var ime, prezime, jmbg, tipIdent, nazivFirme, pib, telefon, email, mesto, adresa, napomena sql.NullString err := redovi.Scan( - &k.ID, &k.Tip, &ime, &prezime, &jmbg, &tipIdent, &nazivFirme, &pib, &telefon, &email, &mesto, &napomena, &k.DatumUnosa, + &k.ID, &k.Tip, &ime, &prezime, &jmbg, &tipIdent, &nazivFirme, &pib, &telefon, &email, &mesto, &adresa, &napomena, &k.DatumUnosa, ) if err != nil { return nil, fmt.Errorf("ntech: KlijentRepo.Lista: scan: %w", err) @@ -62,6 +62,7 @@ func (r *KlijentRepo) Lista(ctx context.Context, pretraga string) ([]model.Klije k.Telefon = telefon.String k.Email = email.String k.Mesto = mesto.String + k.Adresa = adresa.String k.Napomena = napomena.String rezultat = append(rezultat, k) } @@ -72,12 +73,12 @@ func (r *KlijentRepo) Lista(ctx context.Context, pretraga string) ([]model.Klije // DohvatiID vraća jednog klijenta po ID-u func (r *KlijentRepo) DohvatiID(ctx context.Context, id int64) (*model.Klijent, error) { var k model.Klijent - var ime, prezime, jmbg, tipIdent, nazivFirme, pib, telefon, email, mesto, napomena sql.NullString + var ime, prezime, jmbg, tipIdent, nazivFirme, pib, telefon, email, mesto, adresa, napomena sql.NullString err := r.db.QueryRowContext(ctx, ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE id = ?`, id).Scan( - &k.ID, &k.Tip, &ime, &prezime, &jmbg, &tipIdent, &nazivFirme, &pib, &telefon, &email, &mesto, &napomena, &k.DatumUnosa, + &k.ID, &k.Tip, &ime, &prezime, &jmbg, &tipIdent, &nazivFirme, &pib, &telefon, &email, &mesto, &adresa, &napomena, &k.DatumUnosa, ) if err != nil { return nil, fmt.Errorf("ntech: KlijentRepo.DohvatiID: %w", err) @@ -92,6 +93,7 @@ func (r *KlijentRepo) DohvatiID(ctx context.Context, id int64) (*model.Klijent, k.Telefon = telefon.String k.Email = email.String k.Mesto = mesto.String + k.Adresa = adresa.String k.Napomena = napomena.String return &k, nil @@ -102,11 +104,11 @@ func (r *KlijentRepo) DohvatiID(ctx context.Context, id int64) (*model.Klijent, // Pretraga ide od najočiglednijeg ka opštijem: JMBG/PIB → ime+prezime+mesto → telefon → email → samo ime+prezime. func (r *KlijentRepo) Pronadji(ctx context.Context, tip, ime, prezime, nazivFirme, jmbg, telefon, email, mesto string) (*model.Klijent, error) { var k model.Klijent - var imeN, prezimeN, jmbgN, tipIdent, nazivFirmeN, pibN, telefonN, emailN, mestoN, napomena sql.NullString + var imeN, prezimeN, jmbgN, tipIdent, nazivFirmeN, pibN, telefonN, emailN, mestoN, adresaN, napomena sql.NullString skeniraj := func(row *sql.Row) error { return row.Scan( - &k.ID, &k.Tip, &imeN, &prezimeN, &jmbgN, &tipIdent, &nazivFirmeN, &pibN, &telefonN, &emailN, &mestoN, &napomena, &k.DatumUnosa, + &k.ID, &k.Tip, &imeN, &prezimeN, &jmbgN, &tipIdent, &nazivFirmeN, &pibN, &telefonN, &emailN, &mestoN, &adresaN, &napomena, &k.DatumUnosa, ) } @@ -116,7 +118,7 @@ func (r *KlijentRepo) Pronadji(ctx context.Context, tip, ime, prezime, nazivFirm // 1. PIB (najpreciznije za firmu) if jmbg != "" { row = r.db.QueryRowContext(ctx, ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE tip = 'pravno' AND pib = ? LIMIT 1`, jmbg) if err := skeniraj(row); err == nil { goto popuni @@ -125,7 +127,7 @@ func (r *KlijentRepo) Pronadji(ctx context.Context, tip, ime, prezime, nazivFirm // 2. Naziv firme + mesto if nazivFirme != "" && mesto != "" { row = r.db.QueryRowContext(ctx, ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE tip = 'pravno' AND naziv_firme = ? AND mesto = ? LIMIT 1`, nazivFirme, mesto) if err := skeniraj(row); err == nil { goto popuni @@ -133,7 +135,7 @@ func (r *KlijentRepo) Pronadji(ctx context.Context, tip, ime, prezime, nazivFirm } // 3. Samo naziv firme row = r.db.QueryRowContext(ctx, ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE tip = 'pravno' AND naziv_firme = ? LIMIT 1`, nazivFirme) if err := skeniraj(row); err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -146,7 +148,7 @@ func (r *KlijentRepo) Pronadji(ctx context.Context, tip, ime, prezime, nazivFirm // 1. JMBG ili broj lične karte if jmbg != "" { row = r.db.QueryRowContext(ctx, ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE tip != 'pravno' AND jmbg = ? LIMIT 1`, jmbg) if err := skeniraj(row); err == nil { goto popuni @@ -155,7 +157,7 @@ func (r *KlijentRepo) Pronadji(ctx context.Context, tip, ime, prezime, nazivFirm // 2. Ime + prezime + mesto if ime != "" && prezime != "" && mesto != "" { row = r.db.QueryRowContext(ctx, ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE tip != 'pravno' AND ime = ? AND prezime = ? AND mesto = ? LIMIT 1`, ime, prezime, mesto) if err := skeniraj(row); err == nil { goto popuni @@ -164,7 +166,7 @@ func (r *KlijentRepo) Pronadji(ctx context.Context, tip, ime, prezime, nazivFirm // 3. Ime + prezime + telefon if ime != "" && prezime != "" && telefon != "" { row = r.db.QueryRowContext(ctx, ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE tip != 'pravno' AND ime = ? AND prezime = ? AND telefon = ? LIMIT 1`, ime, prezime, telefon) if err := skeniraj(row); err == nil { goto popuni @@ -173,7 +175,7 @@ func (r *KlijentRepo) Pronadji(ctx context.Context, tip, ime, prezime, nazivFirm // 4. Ime + prezime + email if ime != "" && prezime != "" && email != "" { row = r.db.QueryRowContext(ctx, ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE tip != 'pravno' AND ime = ? AND prezime = ? AND email = ? LIMIT 1`, ime, prezime, email) if err := skeniraj(row); err == nil { goto popuni @@ -181,7 +183,7 @@ func (r *KlijentRepo) Pronadji(ctx context.Context, tip, ime, prezime, nazivFirm } // 5. Samo ime + prezime (poslednji fallback) row = r.db.QueryRowContext(ctx, ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE tip != 'pravno' AND ime = ? AND prezime = ? LIMIT 1`, ime, prezime) if err := skeniraj(row); err != nil { if errors.Is(err, sql.ErrNoRows) { @@ -201,6 +203,7 @@ popuni: k.Telefon = telefonN.String k.Email = emailN.String k.Mesto = mestoN.String + k.Adresa = adresaN.String k.Napomena = napomena.String return &k, nil @@ -215,11 +218,11 @@ func (r *KlijentRepo) Kreiraj(ctx context.Context, k *model.Klijent) (int64, err k.TipIdentifikacije = "jmbg" } rezultat, err := r.db.ExecContext(ctx, ` - INSERT INTO klijenti (tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + INSERT INTO klijenti (tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, k.Tip, nullString(k.Ime), nullString(k.Prezime), nullString(k.JMBG), k.TipIdentifikacije, nullString(k.NazivFirme), nullString(k.PIB), nullString(k.Telefon), - nullString(k.Email), nullString(k.Mesto), nullString(k.Napomena), + nullString(k.Email), nullString(k.Mesto), nullString(k.Adresa), nullString(k.Napomena), ) if err != nil { return 0, fmt.Errorf("ntech: KlijentRepo.Kreiraj: %w", err) @@ -244,11 +247,11 @@ func (r *KlijentRepo) Izmeni(ctx context.Context, k *model.Klijent) error { _, err := r.db.ExecContext(ctx, ` UPDATE klijenti SET tip = ?, ime = ?, prezime = ?, jmbg = ?, tip_identifikacije = ?, naziv_firme = ?, - pib = ?, telefon = ?, email = ?, mesto = ?, napomena = ? + pib = ?, telefon = ?, email = ?, mesto = ?, adresa = ?, napomena = ? WHERE id = ?`, k.Tip, nullString(k.Ime), nullString(k.Prezime), nullString(k.JMBG), k.TipIdentifikacije, nullString(k.NazivFirme), nullString(k.PIB), nullString(k.Telefon), - nullString(k.Email), nullString(k.Mesto), nullString(k.Napomena), k.ID, + nullString(k.Email), nullString(k.Mesto), nullString(k.Adresa), nullString(k.Napomena), k.ID, ) if err != nil { return fmt.Errorf("ntech: KlijentRepo.Izmeni: %w", err) @@ -260,7 +263,7 @@ func (r *KlijentRepo) Izmeni(ctx context.Context, k *model.Klijent) error { // ListaFilter vraća listu klijenata sa limitom i offsetom (paginacija) func (r *KlijentRepo) ListaFilter(ctx context.Context, filter db.KlijentFilter) ([]model.Klijent, error) { upit := ` - SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa + SELECT id, tip, ime, prezime, jmbg, tip_identifikacije, naziv_firme, pib, telefon, email, mesto, adresa, napomena, datum_unosa FROM klijenti WHERE 1=1` @@ -296,9 +299,9 @@ func (r *KlijentRepo) ListaFilter(ctx context.Context, filter db.KlijentFilter) var rezultat []model.Klijent for redovi.Next() { var k model.Klijent - var ime, prezime, jmbg, tipIdent, nazivFirme, pib, telefon, email, mesto, napomena sql.NullString + var ime, prezime, jmbg, tipIdent, nazivFirme, pib, telefon, email, mesto, adresa, napomena sql.NullString err := redovi.Scan( - &k.ID, &k.Tip, &ime, &prezime, &jmbg, &tipIdent, &nazivFirme, &pib, &telefon, &email, &mesto, &napomena, &k.DatumUnosa, + &k.ID, &k.Tip, &ime, &prezime, &jmbg, &tipIdent, &nazivFirme, &pib, &telefon, &email, &mesto, &adresa, &napomena, &k.DatumUnosa, ) if err != nil { return nil, fmt.Errorf("ntech: KlijentRepo.ListaFilter: scan: %w", err) @@ -312,6 +315,7 @@ func (r *KlijentRepo) ListaFilter(ctx context.Context, filter db.KlijentFilter) k.Telefon = telefon.String k.Email = email.String k.Mesto = mesto.String + k.Adresa = adresa.String k.Napomena = napomena.String rezultat = append(rezultat, k) } diff --git a/internal/handler/klijent.go b/internal/handler/klijent.go index 208843b..3f258af 100644 --- a/internal/handler/klijent.go +++ b/internal/handler/klijent.go @@ -336,6 +336,11 @@ func parseFormuKlijenta(r *http.Request) (model.Klijent, string) { return model.Klijent{Tip: tip}, "Za pravno lice obavezan je naziv firme." } + adresa := strings.TrimSpace(r.FormValue("adresa")) + if tip == "pravno" && adresa == "" { + return model.Klijent{Tip: tip}, "Za pravno lice obavezna je adresa (zakon o PDV zahteva adresu kupca na fakturi)." + } + email := strings.TrimSpace(r.FormValue("email")) if email != "" && !strings.Contains(email, "@") { return model.Klijent{Tip: tip}, "Adresa e-pošte nije ispravna." @@ -365,6 +370,7 @@ func parseFormuKlijenta(r *http.Request) (model.Klijent, string) { Telefon: strings.TrimSpace(r.FormValue("telefon")), Email: email, Mesto: strings.TrimSpace(r.FormValue("mesto")), + Adresa: adresa, Napomena: strings.TrimSpace(r.FormValue("napomena")), }, "" } diff --git a/internal/handler/prodaja.go b/internal/handler/prodaja.go index dbec250..329692c 100644 --- a/internal/handler/prodaja.go +++ b/internal/handler/prodaja.go @@ -104,23 +104,35 @@ func artikalUJSONSaCenom(artikli []model.ArtikalSaKategorijom) template.JS { return template.JS(b) } -// klijentiUJSON pretvara listu klijenata u template.JS vrednost za pretragu na strani klijenta (naziv, tip) +// klijentiUJSON pretvara listu klijenata u template.JS vrednost za pretragu na strani klijenta +// (naziv, tip za filter/prikaz u listi) i pun prikaz podataka kad je klijent izabran +// (PIB/JMBG, mesto, telefon, email — u zavisnosti od pravno/fizičko lice). func klijentiUJSON(klijenti []model.Klijent) template.JS { type stavka struct { - ID int64 `json:"id"` - Naziv string `json:"naziv"` - Tip string `json:"tip"` - Mesto string `json:"mesto"` - Telefon string `json:"telefon"` + ID int64 `json:"id"` + Naziv string `json:"naziv"` + Tip string `json:"tip"` + Mesto string `json:"mesto"` + Adresa string `json:"adresa"` + Telefon string `json:"telefon"` + PIB string `json:"pib"` + JMBG string `json:"jmbg"` + TipIdentifikacije string `json:"tipIdentifikacije"` + Email string `json:"email"` } lista := make([]stavka, 0, len(klijenti)) for _, k := range klijenti { lista = append(lista, stavka{ - ID: k.ID, - Naziv: k.PunoIme(), - Tip: k.Tip, - Mesto: k.Mesto, - Telefon: k.Telefon, + ID: k.ID, + Naziv: k.PunoIme(), + Tip: k.Tip, + Mesto: k.Mesto, + Adresa: k.Adresa, + Telefon: k.Telefon, + PIB: k.PIB, + JMBG: k.JMBG, + TipIdentifikacije: k.TipIdentifikacije, + Email: k.Email, }) } b, _ := json.Marshal(lista) diff --git a/internal/model/klijent.go b/internal/model/klijent.go index 8ba824a..5e06dd1 100644 --- a/internal/model/klijent.go +++ b/internal/model/klijent.go @@ -20,6 +20,7 @@ type Klijent struct { Telefon string Email string Mesto string + Adresa string // ulica i broj — zakon o PDV (čl. 42) zahteva adresu kupca na fakturi za pravna lica Napomena string DatumUnosa time.Time } diff --git a/migrations/097_klijent_adresa.sql b/migrations/097_klijent_adresa.sql new file mode 100644 index 0000000..43f2fce --- /dev/null +++ b/migrations/097_klijent_adresa.sql @@ -0,0 +1,3 @@ +-- Dodaje adresu (ulica i broj) u klijente. Zakon o PDV (čl. 42, sadržaj računa) +-- zahteva adresu kupca na fakturi za pravna lica — mesto (grad) samo nije dovoljno. +ALTER TABLE klijenti ADD COLUMN adresa TEXT; diff --git a/web/static/js/ntech.js b/web/static/js/ntech.js index 1602667..54c319f 100644 --- a/web/static/js/ntech.js +++ b/web/static/js/ntech.js @@ -201,6 +201,21 @@ document.addEventListener('alpine:init', () => { if (k.telefon) d.push(k.telefon) return d.join(' · ') }, + // puna lista podataka izabranog klijenta (labela + vrednost), za prikaz kad je klijent već izabran + poljaIzabranogKlijenta(k) { + if (!k) return [] + const p = [] + if (k.tip === 'pravno') { + if (k.pib) p.push({ labela: 'PIB', vrednost: k.pib }) + } else { + if (k.jmbg) p.push({ labela: k.tipIdentifikacije === 'licna_karta' ? 'Br. lične karte' : 'JMBG', vrednost: k.jmbg }) + } + if (k.adresa) p.push({ labela: 'Adresa', vrednost: k.adresa }) + if (k.mesto) p.push({ labela: 'Mesto', vrednost: k.mesto }) + if (k.telefon) p.push({ labela: 'Telefon', vrednost: k.telefon }) + if (k.email) p.push({ labela: 'Email', vrednost: k.email }) + return p + }, init() { this.artikliOpcije = window._ntechArtikli || [] this.klijenti = window._ntechKlijenti || [] diff --git a/web/templates/stranice/klijent_forma.html b/web/templates/stranice/klijent_forma.html index d3666f7..5ff6c13 100644 --- a/web/templates/stranice/klijent_forma.html +++ b/web/templates/stranice/klijent_forma.html @@ -145,6 +145,11 @@ +