feat(pdv): izbor klijenta u KIR formi + mesto/grad u klijentima
KIR forma nudi padajuću listu postojećih klijenata koja popunjava naziv, PIB/JMBG i mesto kupca (uz ručni unos za kupce van baze). KIR i dalje čuva kupca kao tekst, ne kao vezu. Klijenti dobili polje mesto (migracija 042), provučeno kroz model, repo, formu i handler.
This commit is contained in:
@@ -21,7 +21,7 @@ func NoviKlijentRepo(db *sql.DB) *KlijentRepo {
|
|||||||
// Lista vraća listu klijenata sa opcionom pretragom po imenu, prezimenu ili nazivu firme
|
// 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) {
|
func (r *KlijentRepo) Lista(ctx context.Context, pretraga string) ([]model.Klijent, error) {
|
||||||
upit := `
|
upit := `
|
||||||
SELECT id, tip, ime, prezime, jmbg, naziv_firme, pib, telefon, email, napomena, datum_unosa
|
SELECT id, tip, ime, prezime, jmbg, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa
|
||||||
FROM klijenti
|
FROM klijenti
|
||||||
WHERE 1=1`
|
WHERE 1=1`
|
||||||
|
|
||||||
@@ -44,9 +44,9 @@ func (r *KlijentRepo) Lista(ctx context.Context, pretraga string) ([]model.Klije
|
|||||||
var rezultat []model.Klijent
|
var rezultat []model.Klijent
|
||||||
for redovi.Next() {
|
for redovi.Next() {
|
||||||
var k model.Klijent
|
var k model.Klijent
|
||||||
var ime, prezime, jmbg, nazivFirme, pib, telefon, email, napomena sql.NullString
|
var ime, prezime, jmbg, nazivFirme, pib, telefon, email, mesto, napomena sql.NullString
|
||||||
err := redovi.Scan(
|
err := redovi.Scan(
|
||||||
&k.ID, &k.Tip, &ime, &prezime, &jmbg, &nazivFirme, &pib, &telefon, &email, &napomena, &k.DatumUnosa,
|
&k.ID, &k.Tip, &ime, &prezime, &jmbg, &nazivFirme, &pib, &telefon, &email, &mesto, &napomena, &k.DatumUnosa,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ntech: KlijentRepo.Lista: scan: %w", err)
|
return nil, fmt.Errorf("ntech: KlijentRepo.Lista: scan: %w", err)
|
||||||
@@ -58,6 +58,7 @@ func (r *KlijentRepo) Lista(ctx context.Context, pretraga string) ([]model.Klije
|
|||||||
k.PIB = pib.String
|
k.PIB = pib.String
|
||||||
k.Telefon = telefon.String
|
k.Telefon = telefon.String
|
||||||
k.Email = email.String
|
k.Email = email.String
|
||||||
|
k.Mesto = mesto.String
|
||||||
k.Napomena = napomena.String
|
k.Napomena = napomena.String
|
||||||
rezultat = append(rezultat, k)
|
rezultat = append(rezultat, k)
|
||||||
}
|
}
|
||||||
@@ -68,12 +69,12 @@ func (r *KlijentRepo) Lista(ctx context.Context, pretraga string) ([]model.Klije
|
|||||||
// DohvatiID vraća jednog klijenta po ID-u
|
// DohvatiID vraća jednog klijenta po ID-u
|
||||||
func (r *KlijentRepo) DohvatiID(ctx context.Context, id int64) (*model.Klijent, error) {
|
func (r *KlijentRepo) DohvatiID(ctx context.Context, id int64) (*model.Klijent, error) {
|
||||||
var k model.Klijent
|
var k model.Klijent
|
||||||
var ime, prezime, jmbg, nazivFirme, pib, telefon, email, napomena sql.NullString
|
var ime, prezime, jmbg, nazivFirme, pib, telefon, email, mesto, napomena sql.NullString
|
||||||
|
|
||||||
err := r.db.QueryRowContext(ctx, `
|
err := r.db.QueryRowContext(ctx, `
|
||||||
SELECT id, tip, ime, prezime, jmbg, naziv_firme, pib, telefon, email, napomena, datum_unosa
|
SELECT id, tip, ime, prezime, jmbg, naziv_firme, pib, telefon, email, mesto, napomena, datum_unosa
|
||||||
FROM klijenti WHERE id = ?`, id).Scan(
|
FROM klijenti WHERE id = ?`, id).Scan(
|
||||||
&k.ID, &k.Tip, &ime, &prezime, &jmbg, &nazivFirme, &pib, &telefon, &email, &napomena, &k.DatumUnosa,
|
&k.ID, &k.Tip, &ime, &prezime, &jmbg, &nazivFirme, &pib, &telefon, &email, &mesto, &napomena, &k.DatumUnosa,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("ntech: KlijentRepo.DohvatiID: %w", err)
|
return nil, fmt.Errorf("ntech: KlijentRepo.DohvatiID: %w", err)
|
||||||
@@ -86,6 +87,7 @@ func (r *KlijentRepo) DohvatiID(ctx context.Context, id int64) (*model.Klijent,
|
|||||||
k.PIB = pib.String
|
k.PIB = pib.String
|
||||||
k.Telefon = telefon.String
|
k.Telefon = telefon.String
|
||||||
k.Email = email.String
|
k.Email = email.String
|
||||||
|
k.Mesto = mesto.String
|
||||||
k.Napomena = napomena.String
|
k.Napomena = napomena.String
|
||||||
|
|
||||||
return &k, nil
|
return &k, nil
|
||||||
@@ -97,11 +99,11 @@ func (r *KlijentRepo) Kreiraj(ctx context.Context, k *model.Klijent) (int64, err
|
|||||||
k.Tip = "fizicko"
|
k.Tip = "fizicko"
|
||||||
}
|
}
|
||||||
rezultat, err := r.db.ExecContext(ctx, `
|
rezultat, err := r.db.ExecContext(ctx, `
|
||||||
INSERT INTO klijenti (tip, ime, prezime, jmbg, naziv_firme, pib, telefon, email, napomena)
|
INSERT INTO klijenti (tip, ime, prezime, jmbg, naziv_firme, pib, telefon, email, mesto, napomena)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
k.Tip, nullString(k.Ime), nullString(k.Prezime), nullString(k.JMBG),
|
k.Tip, nullString(k.Ime), nullString(k.Prezime), nullString(k.JMBG),
|
||||||
nullString(k.NazivFirme), nullString(k.PIB), nullString(k.Telefon),
|
nullString(k.NazivFirme), nullString(k.PIB), nullString(k.Telefon),
|
||||||
nullString(k.Email), nullString(k.Napomena),
|
nullString(k.Email), nullString(k.Mesto), nullString(k.Napomena),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("ntech: KlijentRepo.Kreiraj: %w", err)
|
return 0, fmt.Errorf("ntech: KlijentRepo.Kreiraj: %w", err)
|
||||||
@@ -123,11 +125,11 @@ func (r *KlijentRepo) Izmeni(ctx context.Context, k *model.Klijent) error {
|
|||||||
_, err := r.db.ExecContext(ctx, `
|
_, err := r.db.ExecContext(ctx, `
|
||||||
UPDATE klijenti SET
|
UPDATE klijenti SET
|
||||||
tip = ?, ime = ?, prezime = ?, jmbg = ?, naziv_firme = ?,
|
tip = ?, ime = ?, prezime = ?, jmbg = ?, naziv_firme = ?,
|
||||||
pib = ?, telefon = ?, email = ?, napomena = ?
|
pib = ?, telefon = ?, email = ?, mesto = ?, napomena = ?
|
||||||
WHERE id = ?`,
|
WHERE id = ?`,
|
||||||
k.Tip, nullString(k.Ime), nullString(k.Prezime), nullString(k.JMBG),
|
k.Tip, nullString(k.Ime), nullString(k.Prezime), nullString(k.JMBG),
|
||||||
nullString(k.NazivFirme), nullString(k.PIB), nullString(k.Telefon),
|
nullString(k.NazivFirme), nullString(k.PIB), nullString(k.Telefon),
|
||||||
nullString(k.Email), nullString(k.Napomena), k.ID,
|
nullString(k.Email), nullString(k.Mesto), nullString(k.Napomena), k.ID,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("ntech: KlijentRepo.Izmeni: %w", err)
|
return fmt.Errorf("ntech: KlijentRepo.Izmeni: %w", err)
|
||||||
|
|||||||
@@ -249,6 +249,7 @@ func parseFormuKlijenta(r *http.Request) (model.Klijent, string) {
|
|||||||
PIB: strings.TrimSpace(r.FormValue("pib")),
|
PIB: strings.TrimSpace(r.FormValue("pib")),
|
||||||
Telefon: strings.TrimSpace(r.FormValue("telefon")),
|
Telefon: strings.TrimSpace(r.FormValue("telefon")),
|
||||||
Email: email,
|
Email: email,
|
||||||
|
Mesto: strings.TrimSpace(r.FormValue("mesto")),
|
||||||
Napomena: strings.TrimSpace(r.FormValue("napomena")),
|
Napomena: strings.TrimSpace(r.FormValue("napomena")),
|
||||||
}, ""
|
}, ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,8 +25,9 @@ type PodaciPdvKir struct {
|
|||||||
// PodaciPdvKirForma su podaci za formu unosa zapisa KIR
|
// PodaciPdvKirForma su podaci za formu unosa zapisa KIR
|
||||||
type PodaciPdvKirForma struct {
|
type PodaciPdvKirForma struct {
|
||||||
model.PodaciStranice
|
model.PodaciStranice
|
||||||
Greska string
|
Greska string
|
||||||
Danas string // podrazumevani datum u formi
|
Danas string // podrazumevani datum u formi
|
||||||
|
Klijenti []model.Klijent // za izbor kupca iz postojećih klijenata
|
||||||
}
|
}
|
||||||
|
|
||||||
// parsiraDatumOpcionalno vraća datum iz YYYY-MM-DD; prazan string daje nulti datum (bez filtera)
|
// parsiraDatumOpcionalno vraća datum iz YYYY-MM-DD; prazan string daje nulti datum (bez filtera)
|
||||||
@@ -88,12 +89,16 @@ func (h *Handler) NoviPdvKir(w http.ResponseWriter, r *http.Request) {
|
|||||||
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
|
http.Error(w, "Greška pri učitavanju podešavanja", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// klijenti za izbor kupca; greška se ne prekida (forma radi i sa ručnim unosom)
|
||||||
|
klijenti, _ := h.KlijentiRepo.Lista(r.Context(), "")
|
||||||
|
|
||||||
ps := h.popuniPodaciStranice(r, podesavanja)
|
ps := h.popuniPodaciStranice(r, podesavanja)
|
||||||
ps.Stranica = "pdv-kir"
|
ps.Stranica = "pdv-kir"
|
||||||
ps.NaslovStranice = "Novi izlazni račun (KIR)"
|
ps.NaslovStranice = "Novi izlazni račun (KIR)"
|
||||||
h.renderujTemplate(w, "pdv_kir_forma", PodaciPdvKirForma{
|
h.renderujTemplate(w, "pdv_kir_forma", PodaciPdvKirForma{
|
||||||
PodaciStranice: ps,
|
PodaciStranice: ps,
|
||||||
Danas: time.Now().Format("2006-01-02"),
|
Danas: time.Now().Format("2006-01-02"),
|
||||||
|
Klijenti: klijenti,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type Klijent struct {
|
|||||||
PIB string
|
PIB string
|
||||||
Telefon string
|
Telefon string
|
||||||
Email string
|
Email string
|
||||||
|
Mesto string
|
||||||
Napomena string
|
Napomena string
|
||||||
DatumUnosa time.Time
|
DatumUnosa time.Time
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
-- Dodaje mesto/grad u klijente. Koristi se pri izboru klijenta u KIR formi
|
||||||
|
-- (popunjava mesto kupca), a korisno je i samo po sebi za adresu klijenta.
|
||||||
|
ALTER TABLE klijenti ADD COLUMN mesto TEXT;
|
||||||
@@ -117,6 +117,11 @@
|
|||||||
<input type="text" name="email" value="{{.Klijent.Email}}"
|
<input type="text" name="email" value="{{.Klijent.Email}}"
|
||||||
placeholder="npr. marko@example.com">
|
placeholder="npr. marko@example.com">
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="polje-labela">Mesto / grad</label>
|
||||||
|
<input type="text" name="mesto" value="{{.Klijent.Mesto}}"
|
||||||
|
placeholder="npr. Niš">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,18 @@
|
|||||||
<div class="kartica animiraj" style="margin-bottom:16px;">
|
<div class="kartica animiraj" style="margin-bottom:16px;">
|
||||||
<div style="font-size:15px;font-weight:500;color:var(--tekst-glavni);margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">Kupac</div>
|
<div style="font-size:15px;font-weight:500;color:var(--tekst-glavni);margin-bottom:16px;padding-bottom:12px;border-bottom:0.5px solid var(--ivica);">Kupac</div>
|
||||||
<div class="kolona" style="gap:14px;">
|
<div class="kolona" style="gap:14px;">
|
||||||
|
{{if .Klijenti}}
|
||||||
|
<div>
|
||||||
|
<label class="polje-labela">Izaberi iz klijenata (opciono)</label>
|
||||||
|
<select onchange="popuniKupca(this)" style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;">
|
||||||
|
<option value="">— ručni unos —</option>
|
||||||
|
{{range .Klijenti}}
|
||||||
|
<option data-naziv="{{.PunoIme}}" data-pib="{{if eq .Tip "pravno"}}{{.PIB}}{{else}}{{.JMBG}}{{end}}" data-mesto="{{.Mesto}}">{{.PunoIme}}{{if .Mesto}} — {{.Mesto}}{{end}}</option>
|
||||||
|
{{end}}
|
||||||
|
</select>
|
||||||
|
<div class="pomocni-tekst" style="font-size:12px;margin-top:4px;">Izbor popunjava naziv i PIB/JMBG; možeš ih i ručno izmeniti.</div>
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
<div>
|
<div>
|
||||||
<label class="polje-labela">Naziv kupca</label>
|
<label class="polje-labela">Naziv kupca</label>
|
||||||
<input type="text" name="kupac_naziv" required style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;">
|
<input type="text" name="kupac_naziv" required style="width:100%;padding:8px 12px;border:0.5px solid var(--ivica);border-radius:8px;font-size:14px;background:var(--pozadina);color:var(--tekst-glavni);outline:none;">
|
||||||
@@ -94,4 +106,18 @@
|
|||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Izbor klijenta iz padajuće liste popunjava polja kupca (naziv, PIB/JMBG, mesto).
|
||||||
|
// KIR i dalje čuva kupca kao tekst — klijent samo popuni polja, koja se mogu i ručno menjati.
|
||||||
|
function popuniKupca(sel) {
|
||||||
|
var o = sel.options[sel.selectedIndex];
|
||||||
|
var naziv = o.getAttribute('data-naziv');
|
||||||
|
if (!naziv) return; // „— ručni unos —"
|
||||||
|
var f = sel.closest('form');
|
||||||
|
f.querySelector('input[name="kupac_naziv"]').value = naziv;
|
||||||
|
f.querySelector('input[name="kupac_pib"]').value = o.getAttribute('data-pib') || '';
|
||||||
|
f.querySelector('input[name="kupac_mesto"]').value = o.getAttribute('data-mesto') || '';
|
||||||
|
}
|
||||||
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|||||||
Reference in New Issue
Block a user