Izveštaji: prava zarada (cena - nabavna cena) za prodaju i servis
Nabavna cena se zamrzava na stavci prodaje i ugrađenom servisnom delu u trenutku unosa (migracija 101), umesto naknadnog join-a na trenutnu artikli.nabavna_cena. Tabela mesečnog prihoda sad prikazuje zaradu uz prodaju/servis/ukupno, a grafikon dobija preklopljene tamne barove (zarada) preko svetlih (ukupan prihod), odvojene za prodaju i servis.
This commit is contained in:
@@ -383,6 +383,8 @@ type IzvestajRepository interface {
|
|||||||
// izveštaji
|
// izveštaji
|
||||||
MesecniPrihodProdaja(ctx context.Context) ([]model.MesecniIznos, error)
|
MesecniPrihodProdaja(ctx context.Context) ([]model.MesecniIznos, error)
|
||||||
MesecniPrihodServis(ctx context.Context) ([]model.MesecniIznos, error)
|
MesecniPrihodServis(ctx context.Context) ([]model.MesecniIznos, error)
|
||||||
|
MesecniZaradaProdaja(ctx context.Context) ([]model.MesecniIznos, error)
|
||||||
|
MesecniZaradaServis(ctx context.Context) ([]model.MesecniIznos, error)
|
||||||
StariOtvoreniNalozi(ctx context.Context) ([]model.StariNalogRed, error)
|
StariOtvoreniNalozi(ctx context.Context) ([]model.StariNalogRed, error)
|
||||||
TopArtikli(ctx context.Context, limit int) ([]model.TopArtikalRed, error)
|
TopArtikli(ctx context.Context, limit int) ([]model.TopArtikalRed, error)
|
||||||
TopKlijenti(ctx context.Context, limit int) ([]model.TopKlijentRed, error)
|
TopKlijenti(ctx context.Context, limit int) ([]model.TopKlijentRed, error)
|
||||||
|
|||||||
@@ -165,6 +165,42 @@ func (r *sqliteIzvestajRepo) MesecniPrihodServis(ctx context.Context) ([]model.M
|
|||||||
GROUP BY substr(datum_zavrsetka, 1, 7)`, "MesecniPrihodServis")
|
GROUP BY substr(datum_zavrsetka, 1, 7)`, "MesecniPrihodServis")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MesecniZaradaProdaja je zarada prodaje (ukupno − nabavna cena × količina),
|
||||||
|
// nabavna_cena je zamrznuta na stavci u trenutku prodaje (vidi migraciju 101).
|
||||||
|
func (r *sqliteIzvestajRepo) MesecniZaradaProdaja(ctx context.Context) ([]model.MesecniIznos, error) {
|
||||||
|
return r.mesecniPrihod(ctx, `
|
||||||
|
SELECT substr(pn.datum, 1, 7), SUM(sp.ukupno - sp.nabavna_cena * sp.kolicina)
|
||||||
|
FROM prodajni_nalozi pn
|
||||||
|
JOIN stavke_prodaje sp ON sp.nalog_id = pn.id
|
||||||
|
WHERE pn.stornirano = 0
|
||||||
|
AND substr(pn.datum, 1, 10) >= date('now', '-11 months', 'start of month')
|
||||||
|
GROUP BY substr(pn.datum, 1, 7)`, "MesecniZaradaProdaja")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MesecniZaradaServis je zarada servisa: ugrađeni delovi (cena − nabavna cena) +
|
||||||
|
// rad u punom iznosu (servis_radovi nema polje za trošak). Isti filter statusa/
|
||||||
|
// naplate kao MesecniPrihodServis.
|
||||||
|
func (r *sqliteIzvestajRepo) MesecniZaradaServis(ctx context.Context) ([]model.MesecniIznos, error) {
|
||||||
|
return r.mesecniPrihod(ctx, `
|
||||||
|
SELECT mesec, SUM(zarada) FROM (
|
||||||
|
SELECT substr(sn.datum_zavrsetka, 1, 7) AS mesec,
|
||||||
|
(sd.cena_komada - sd.nabavna_cena) * sd.kolicina AS zarada
|
||||||
|
FROM servisni_delovi sd
|
||||||
|
JOIN servisni_nalozi sn ON sn.id = sd.nalog_id
|
||||||
|
WHERE sn.datum_zavrsetka IS NOT NULL AND sn.status = 'Preuzeto'
|
||||||
|
AND (sn.naplaceno > 0 OR sn.avans > 0) AND sd.predlozeno = 0
|
||||||
|
UNION ALL
|
||||||
|
SELECT substr(sn.datum_zavrsetka, 1, 7) AS mesec,
|
||||||
|
sr.cena_komada * sr.kolicina AS zarada
|
||||||
|
FROM servis_radovi sr
|
||||||
|
JOIN servisni_nalozi sn ON sn.id = sr.nalog_id
|
||||||
|
WHERE sn.datum_zavrsetka IS NOT NULL AND sn.status = 'Preuzeto'
|
||||||
|
AND (sn.naplaceno > 0 OR sn.avans > 0)
|
||||||
|
)
|
||||||
|
WHERE mesec >= strftime('%Y-%m', date('now', '-11 months', 'start of month'))
|
||||||
|
GROUP BY mesec`, "MesecniZaradaServis")
|
||||||
|
}
|
||||||
|
|
||||||
func (r *sqliteIzvestajRepo) StariOtvoreniNalozi(ctx context.Context) ([]model.StariNalogRed, error) {
|
func (r *sqliteIzvestajRepo) StariOtvoreniNalozi(ctx context.Context) ([]model.StariNalogRed, error) {
|
||||||
rows, err := r.db.QueryContext(ctx, `
|
rows, err := r.db.QueryContext(ctx, `
|
||||||
SELECT sn.id, sn.broj_naloga, sn.uredjaj, sn.status, sn.datum_prijema,
|
SELECT sn.id, sn.broj_naloga, sn.uredjaj, sn.status, sn.datum_prijema,
|
||||||
|
|||||||
@@ -204,9 +204,10 @@ func (r *ProdajaRepo) Kreiraj(ctx context.Context, n *model.ProdajniNalog, stavk
|
|||||||
s := stavke[i]
|
s := stavke[i]
|
||||||
var naziv, tip string
|
var naziv, tip string
|
||||||
var stanjePre int
|
var stanjePre int
|
||||||
|
var nabavnaCena float64
|
||||||
err := tx.QueryRowContext(ctx,
|
err := tx.QueryRowContext(ctx,
|
||||||
"SELECT naziv, kolicina, tip FROM artikli WHERE id = ?", s.ArtikalID,
|
"SELECT naziv, kolicina, tip, nabavna_cena FROM artikli WHERE id = ?", s.ArtikalID,
|
||||||
).Scan(&naziv, &stanjePre, &tip)
|
).Scan(&naziv, &stanjePre, &tip, &nabavnaCena)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("ntech: ProdajaRepo.Kreiraj: dohvati artikal: %w", err)
|
return 0, fmt.Errorf("ntech: ProdajaRepo.Kreiraj: dohvati artikal: %w", err)
|
||||||
}
|
}
|
||||||
@@ -248,10 +249,10 @@ func (r *ProdajaRepo) Kreiraj(ctx context.Context, n *model.ProdajniNalog, stavk
|
|||||||
stavke[i].Ukupno = ukupnoStavke
|
stavke[i].Ukupno = ukupnoStavke
|
||||||
_, err = tx.ExecContext(ctx, `
|
_, err = tx.ExecContext(ctx, `
|
||||||
INSERT INTO stavke_prodaje
|
INSERT INTO stavke_prodaje
|
||||||
(nalog_id, artikal_id, kolicina, cena_po_komadu, popust_procenat, ukupno, pdv_stopa, pdv_iznos, cena_bez_pdv)
|
(nalog_id, artikal_id, kolicina, cena_po_komadu, popust_procenat, ukupno, pdv_stopa, pdv_iznos, cena_bez_pdv, nabavna_cena)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
nalogID, s.ArtikalID, s.Kolicina, s.CenaPoKomadu, s.PopustProcenat, ukupnoStavke,
|
nalogID, s.ArtikalID, s.Kolicina, s.CenaPoKomadu, s.PopustProcenat, ukupnoStavke,
|
||||||
s.PdvStopa, pdvIznos, cenaBezPdv,
|
s.PdvStopa, pdvIznos, cenaBezPdv, nabavnaCena,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, fmt.Errorf("ntech: ProdajaRepo.Kreiraj: insert stavka: %w", err)
|
return 0, fmt.Errorf("ntech: ProdajaRepo.Kreiraj: insert stavka: %w", err)
|
||||||
|
|||||||
@@ -95,9 +95,10 @@ func ugradiIliPotrazujTx(ctx context.Context, tx *sql.Tx, nalogID, artikalID int
|
|||||||
|
|
||||||
// Ugrađeni delovi: standardna logika — skidamo sa lagera
|
// Ugrađeni delovi: standardna logika — skidamo sa lagera
|
||||||
var stanjePre int
|
var stanjePre int
|
||||||
|
var nabavnaCena float64
|
||||||
if err = tx.QueryRowContext(ctx,
|
if err = tx.QueryRowContext(ctx,
|
||||||
"SELECT kolicina FROM artikli WHERE id = ?", artikalID,
|
"SELECT kolicina, nabavna_cena FROM artikli WHERE id = ?", artikalID,
|
||||||
).Scan(&stanjePre); err != nil {
|
).Scan(&stanjePre, &nabavnaCena); err != nil {
|
||||||
return 0, 0, fmt.Errorf("ntech: ugradiIliPotrazujTx: dohvati stanje: %w", err)
|
return 0, 0, fmt.Errorf("ntech: ugradiIliPotrazujTx: dohvati stanje: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,15 +120,15 @@ func ugradiIliPotrazujTx(ctx context.Context, tx *sql.Tx, nalogID, artikalID int
|
|||||||
).Scan(&postojeciID, &postojeciKol)
|
).Scan(&postojeciID, &postojeciKol)
|
||||||
if errRed == nil {
|
if errRed == nil {
|
||||||
if _, err = tx.ExecContext(ctx,
|
if _, err = tx.ExecContext(ctx,
|
||||||
"UPDATE servisni_delovi SET kolicina = ?, cena_komada = ? WHERE id = ?",
|
"UPDATE servisni_delovi SET kolicina = ?, cena_komada = ?, nabavna_cena = ? WHERE id = ?",
|
||||||
postojeciKol+ugradjeno, cenaKomada, postojeciID,
|
postojeciKol+ugradjeno, cenaKomada, nabavnaCena, postojeciID,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return 0, 0, fmt.Errorf("ntech: ugradiIliPotrazujTx: merge: %w", err)
|
return 0, 0, fmt.Errorf("ntech: ugradiIliPotrazujTx: merge: %w", err)
|
||||||
}
|
}
|
||||||
} else if errors.Is(errRed, sql.ErrNoRows) {
|
} else if errors.Is(errRed, sql.ErrNoRows) {
|
||||||
if _, err = tx.ExecContext(ctx,
|
if _, err = tx.ExecContext(ctx,
|
||||||
"INSERT INTO servisni_delovi (nalog_id, artikal_id, kolicina, cena_komada, predlozeno) VALUES (?, ?, ?, ?, 0)",
|
"INSERT INTO servisni_delovi (nalog_id, artikal_id, kolicina, cena_komada, predlozeno, nabavna_cena) VALUES (?, ?, ?, ?, 0, ?)",
|
||||||
nalogID, artikalID, ugradjeno, cenaKomada,
|
nalogID, artikalID, ugradjeno, cenaKomada, nabavnaCena,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return 0, 0, fmt.Errorf("ntech: ugradiIliPotrazujTx: insert: %w", err)
|
return 0, 0, fmt.Errorf("ntech: ugradiIliPotrazujTx: insert: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,6 +196,13 @@ func (r *ServisniPotrazivaniDeloviRepo) ProveriIPocistiZaArtikal(ctx context.Con
|
|||||||
// uvećava postojeći red za isti artikal na nalogu ili kreira novi. Radi unutar
|
// uvećava postojeći red za isti artikal na nalogu ili kreira novi. Radi unutar
|
||||||
// prosleđene transakcije. Ne dira magacin — to radi skiniSaMagacina zasebno.
|
// prosleđene transakcije. Ne dira magacin — to radi skiniSaMagacina zasebno.
|
||||||
func (r *ServisniPotrazivaniDeloviRepo) ugradiUNalog(ctx context.Context, tx *sql.Tx, nalogID, artikalID int64, kolicina int, cenaKomada float64) error {
|
func (r *ServisniPotrazivaniDeloviRepo) ugradiUNalog(ctx context.Context, tx *sql.Tx, nalogID, artikalID int64, kolicina int, cenaKomada float64) error {
|
||||||
|
var nabavnaCena float64
|
||||||
|
if err := tx.QueryRowContext(ctx,
|
||||||
|
"SELECT nabavna_cena FROM artikli WHERE id = ?", artikalID,
|
||||||
|
).Scan(&nabavnaCena); err != nil {
|
||||||
|
return fmt.Errorf("ntech: ServisniPotrazivaniDeloviRepo.ugradiUNalog: nabavna cena: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
var postojeciID int64
|
var postojeciID int64
|
||||||
var postojeciKol int
|
var postojeciKol int
|
||||||
err := tx.QueryRowContext(ctx,
|
err := tx.QueryRowContext(ctx,
|
||||||
@@ -207,8 +214,8 @@ func (r *ServisniPotrazivaniDeloviRepo) ugradiUNalog(ctx context.Context, tx *sq
|
|||||||
// ne pregazi postojeću cenu nulom (npr. legacy potraživani red bez cene)
|
// ne pregazi postojeću cenu nulom (npr. legacy potraživani red bez cene)
|
||||||
if cenaKomada > 0 {
|
if cenaKomada > 0 {
|
||||||
_, err = tx.ExecContext(ctx,
|
_, err = tx.ExecContext(ctx,
|
||||||
"UPDATE servisni_delovi SET kolicina = ?, cena_komada = ? WHERE id = ?",
|
"UPDATE servisni_delovi SET kolicina = ?, cena_komada = ?, nabavna_cena = ? WHERE id = ?",
|
||||||
postojeciKol+kolicina, cenaKomada, postojeciID,
|
postojeciKol+kolicina, cenaKomada, nabavnaCena, postojeciID,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
_, err = tx.ExecContext(ctx,
|
_, err = tx.ExecContext(ctx,
|
||||||
@@ -223,9 +230,9 @@ func (r *ServisniPotrazivaniDeloviRepo) ugradiUNalog(ctx context.Context, tx *sq
|
|||||||
}
|
}
|
||||||
if errors.Is(err, sql.ErrNoRows) {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
_, err = tx.ExecContext(ctx, `
|
_, err = tx.ExecContext(ctx, `
|
||||||
INSERT INTO servisni_delovi (nalog_id, artikal_id, kolicina, cena_komada)
|
INSERT INTO servisni_delovi (nalog_id, artikal_id, kolicina, cena_komada, nabavna_cena)
|
||||||
VALUES (?, ?, ?, ?)`,
|
VALUES (?, ?, ?, ?, ?)`,
|
||||||
nalogID, artikalID, kolicina, cenaKomada,
|
nalogID, artikalID, kolicina, cenaKomada, nabavnaCena,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("ntech: ServisniPotrazivaniDeloviRepo.ugradiUNalog: insert: %w", err)
|
return fmt.Errorf("ntech: ServisniPotrazivaniDeloviRepo.ugradiUNalog: insert: %w", err)
|
||||||
|
|||||||
@@ -53,10 +53,13 @@ type PodaciIzvestaja struct {
|
|||||||
|
|
||||||
// MesecniPrihod drži prihod od prodaje i servisa za jedan mesec
|
// MesecniPrihod drži prihod od prodaje i servisa za jedan mesec
|
||||||
type MesecniPrihod struct {
|
type MesecniPrihod struct {
|
||||||
MesecPrikaz string
|
MesecPrikaz string
|
||||||
Prodaja float64
|
Prodaja float64
|
||||||
Servis float64
|
Servis float64
|
||||||
Ukupno float64
|
Ukupno float64
|
||||||
|
Zarada float64
|
||||||
|
ZaradaProdaja float64
|
||||||
|
ZaradaServis float64
|
||||||
}
|
}
|
||||||
|
|
||||||
// StariNalog je servisni nalog bez datuma završetka stariji od 14 dana
|
// StariNalog je servisni nalog bez datuma završetka stariji od 14 dana
|
||||||
@@ -120,35 +123,68 @@ func (h *Handler) Izvestaji(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- mesečna zarada: prodaja ---
|
||||||
|
zaradaProdajaPoMesecu := map[string]float64{}
|
||||||
|
if redovi, err := h.IzvestajRepo.MesecniZaradaProdaja(ctx); err != nil {
|
||||||
|
slog.Error("izvestaji: zarada prodaja", "error", err)
|
||||||
|
} else {
|
||||||
|
for _, m := range redovi {
|
||||||
|
zaradaProdajaPoMesecu[m.Mesec] = m.Iznos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- mesečna zarada: servis ---
|
||||||
|
zaradaServisPoMesecu := map[string]float64{}
|
||||||
|
if redovi, err := h.IzvestajRepo.MesecniZaradaServis(ctx); err != nil {
|
||||||
|
slog.Error("izvestaji: zarada servis", "error", err)
|
||||||
|
} else {
|
||||||
|
for _, m := range redovi {
|
||||||
|
zaradaServisPoMesecu[m.Mesec] = m.Iznos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// gradimo niz za poslednjih 12 meseci (hronološki)
|
// gradimo niz za poslednjih 12 meseci (hronološki)
|
||||||
var mesecniPrihodi []MesecniPrihod
|
var mesecniPrihodi []MesecniPrihod
|
||||||
var grafikonLabele []string
|
var grafikonLabele []string
|
||||||
var grafikonProdaja []float64
|
var grafikonProdaja []float64
|
||||||
var grafikonServis []float64
|
var grafikonServis []float64
|
||||||
|
var grafikonZaradaProdaja []float64
|
||||||
|
var grafikonZaradaServis []float64
|
||||||
|
|
||||||
for _, kljuc := range kljuceviMeseci(time.Now(), 12) {
|
for _, kljuc := range kljuceviMeseci(time.Now(), 12) {
|
||||||
prod := prodajaPoMesecu[kljuc]
|
prod := prodajaPoMesecu[kljuc]
|
||||||
serv := servisPoMesecu[kljuc]
|
serv := servisPoMesecu[kljuc]
|
||||||
|
zaradaProd := zaradaProdajaPoMesecu[kljuc]
|
||||||
|
zaradaServ := zaradaServisPoMesecu[kljuc]
|
||||||
mesecniPrihodi = append(mesecniPrihodi, MesecniPrihod{
|
mesecniPrihodi = append(mesecniPrihodi, MesecniPrihod{
|
||||||
MesecPrikaz: formatujMesec(kljuc),
|
MesecPrikaz: formatujMesec(kljuc),
|
||||||
Prodaja: prod,
|
Prodaja: prod,
|
||||||
Servis: serv,
|
Servis: serv,
|
||||||
Ukupno: prod + serv,
|
Ukupno: prod + serv,
|
||||||
|
Zarada: zaradaProd + zaradaServ,
|
||||||
|
ZaradaProdaja: zaradaProd,
|
||||||
|
ZaradaServis: zaradaServ,
|
||||||
})
|
})
|
||||||
grafikonLabele = append(grafikonLabele, formatujMesec(kljuc))
|
grafikonLabele = append(grafikonLabele, formatujMesec(kljuc))
|
||||||
grafikonProdaja = append(grafikonProdaja, prod)
|
grafikonProdaja = append(grafikonProdaja, prod)
|
||||||
grafikonServis = append(grafikonServis, serv)
|
grafikonServis = append(grafikonServis, serv)
|
||||||
|
grafikonZaradaProdaja = append(grafikonZaradaProdaja, zaradaProd)
|
||||||
|
grafikonZaradaServis = append(grafikonZaradaServis, zaradaServ)
|
||||||
}
|
}
|
||||||
|
|
||||||
type grafikonPodaci struct {
|
type grafikonPodaci struct {
|
||||||
Labele []string `json:"labele"`
|
Labele []string `json:"labele"`
|
||||||
Prodaja []float64 `json:"prodaja"`
|
Prodaja []float64 `json:"prodaja"`
|
||||||
Servis []float64 `json:"servis"`
|
Servis []float64 `json:"servis"`
|
||||||
|
ZaradaProdaja []float64 `json:"zaradaProdaja"`
|
||||||
|
ZaradaServis []float64 `json:"zaradaServis"`
|
||||||
}
|
}
|
||||||
jsonBytes, _ := json.Marshal(grafikonPodaci{
|
jsonBytes, _ := json.Marshal(grafikonPodaci{
|
||||||
Labele: grafikonLabele,
|
Labele: grafikonLabele,
|
||||||
Prodaja: grafikonProdaja,
|
Prodaja: grafikonProdaja,
|
||||||
Servis: grafikonServis,
|
Servis: grafikonServis,
|
||||||
|
ZaradaProdaja: grafikonZaradaProdaja,
|
||||||
|
ZaradaServis: grafikonZaradaServis,
|
||||||
})
|
})
|
||||||
|
|
||||||
// --- stari otvoreni nalozi (>14 dana bez završetka) ---
|
// --- stari otvoreni nalozi (>14 dana bez završetka) ---
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
-- Zamrzava nabavnu cenu na stavci prodaje i ugrađenom servisnom delu u trenutku
|
||||||
|
-- unosa — potrebno za obračun prave zarade (cena − nabavna cena) u Izveštajima
|
||||||
|
-- bez retroaktivnog preračunavanja ako se artikli.nabavna_cena kasnije promeni.
|
||||||
|
ALTER TABLE stavke_prodaje ADD COLUMN nabavna_cena REAL NOT NULL DEFAULT 0;
|
||||||
|
ALTER TABLE servisni_delovi ADD COLUMN nabavna_cena REAL NOT NULL DEFAULT 0;
|
||||||
|
|
||||||
|
UPDATE stavke_prodaje
|
||||||
|
SET nabavna_cena = COALESCE((SELECT a.nabavna_cena FROM artikli a WHERE a.id = stavke_prodaje.artikal_id), 0);
|
||||||
|
|
||||||
|
UPDATE servisni_delovi
|
||||||
|
SET nabavna_cena = COALESCE((SELECT a.nabavna_cena FROM artikli a WHERE a.id = servisni_delovi.artikal_id), 0);
|
||||||
@@ -51,23 +51,41 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr style="border-bottom:0.5px solid var(--ivica);">
|
<tr style="border-bottom:0.5px solid var(--ivica);">
|
||||||
<th style="padding:8px 12px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Mesec</th>
|
<th style="padding:8px 12px;text-align:left;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Mesec</th>
|
||||||
<th style="padding:8px 12px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Prodaja</th>
|
<th style="padding:8px 12px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Prodaja</th>
|
||||||
<th style="padding:8px 12px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Servis</th>
|
<th style="padding:8px 12px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Servis</th>
|
||||||
<th style="padding:8px 12px;text-align:right;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Ukupno</th>
|
<th style="padding:8px 12px;text-align:center;font-size:12px;font-weight:500;color:var(--tekst-sporedni);">Ukupno</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{range .MesecniPrihodi}}
|
{{range .MesecniPrihodi}}
|
||||||
<tr style="border-bottom:0.5px solid var(--ivica);">
|
<tr style="border-bottom:0.5px solid var(--ivica);">
|
||||||
<td style="padding:8px 12px;font-size:13px;color:var(--tekst-glavni);">{{.MesecPrikaz}}</td>
|
<td style="padding:8px 12px;font-size:13px;color:var(--tekst-glavni);">{{.MesecPrikaz}}</td>
|
||||||
<td style="padding:8px 12px;text-align:right;font-size:13px;color:var(--tekst-sporedni);">
|
<td style="padding:8px 12px;font-size:13px;color:var(--tekst-sporedni);">
|
||||||
{{if gt .Prodaja 0.0}}{{dinariCeli .Prodaja}} din{{else}}—{{end}}
|
{{if gt .Prodaja 0.0}}
|
||||||
|
<div style="display:grid;grid-template-columns:1fr auto 1fr;gap:6px;">
|
||||||
|
<span style="text-align:right;">{{dinariCeli .Prodaja}} din</span>
|
||||||
|
<span>-</span>
|
||||||
|
<span style="text-align:left;">{{dinariCeli .ZaradaProdaja}} din</span>
|
||||||
|
</div>
|
||||||
|
{{else}}<div style="text-align:center;">—</div>{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:8px 12px;text-align:right;font-size:13px;color:var(--tekst-sporedni);">
|
<td style="padding:8px 12px;font-size:13px;color:var(--tekst-sporedni);">
|
||||||
{{if gt .Servis 0.0}}{{dinariCeli .Servis}} din{{else}}—{{end}}
|
{{if gt .Servis 0.0}}
|
||||||
|
<div style="display:grid;grid-template-columns:1fr auto 1fr;gap:6px;">
|
||||||
|
<span style="text-align:right;">{{dinariCeli .Servis}} din</span>
|
||||||
|
<span>-</span>
|
||||||
|
<span style="text-align:left;">{{dinariCeli .ZaradaServis}} din</span>
|
||||||
|
</div>
|
||||||
|
{{else}}<div style="text-align:center;">—</div>{{end}}
|
||||||
</td>
|
</td>
|
||||||
<td style="padding:8px 12px;text-align:right;font-size:13px;font-weight:500;color:var(--tekst-glavni);">
|
<td style="padding:8px 12px;font-size:13px;font-weight:500;color:var(--tekst-glavni);">
|
||||||
{{if gt .Ukupno 0.0}}{{dinariCeli .Ukupno}} din{{else}}—{{end}}
|
{{if gt .Ukupno 0.0}}
|
||||||
|
<div style="display:grid;grid-template-columns:1fr auto 1fr;gap:6px;">
|
||||||
|
<span style="text-align:right;">{{dinariCeli .Ukupno}} din</span>
|
||||||
|
<span>-</span>
|
||||||
|
<span style="text-align:left;">{{dinariCeli .Zarada}} din</span>
|
||||||
|
</div>
|
||||||
|
{{else}}<div style="text-align:center;">—</div>{{end}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{{end}}
|
{{end}}
|
||||||
@@ -207,18 +225,40 @@
|
|||||||
labels: podaci.labele,
|
labels: podaci.labele,
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: 'Prodaja',
|
label: 'Prodaja — ukupno',
|
||||||
data: podaci.prodaja,
|
data: podaci.prodaja,
|
||||||
backgroundColor: 'rgba(79, 126, 248, 0.75)',
|
backgroundColor: 'rgba(79, 126, 248, 0.35)',
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
borderSkipped: false,
|
borderSkipped: false,
|
||||||
|
stack: 'prodaja',
|
||||||
|
order: 2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Servis',
|
label: 'Servis — ukupno',
|
||||||
data: podaci.servis,
|
data: podaci.servis,
|
||||||
backgroundColor: 'rgba(22, 163, 74, 0.75)',
|
backgroundColor: 'rgba(22, 163, 74, 0.35)',
|
||||||
borderRadius: 4,
|
borderRadius: 4,
|
||||||
borderSkipped: false,
|
borderSkipped: false,
|
||||||
|
stack: 'servis',
|
||||||
|
order: 2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Zarada — prodaja',
|
||||||
|
data: podaci.zaradaProdaja,
|
||||||
|
backgroundColor: 'rgba(29, 66, 168, 0.9)',
|
||||||
|
borderRadius: 4,
|
||||||
|
borderSkipped: false,
|
||||||
|
stack: 'prodaja',
|
||||||
|
order: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Zarada — servis',
|
||||||
|
data: podaci.zaradaServis,
|
||||||
|
backgroundColor: 'rgba(21, 94, 51, 0.9)',
|
||||||
|
borderRadius: 4,
|
||||||
|
borderSkipped: false,
|
||||||
|
stack: 'servis',
|
||||||
|
order: 1,
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -239,11 +279,12 @@
|
|||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
x: {
|
x: {
|
||||||
stacked: false,
|
stacked: true,
|
||||||
ticks: { color: tekstBoja, font: { size: 11, family: 'DejaVu Sans, Ubuntu, system-ui, sans-serif' } },
|
ticks: { color: tekstBoja, font: { size: 11, family: 'DejaVu Sans, Ubuntu, system-ui, sans-serif' } },
|
||||||
grid: { color: ivicaBoja }
|
grid: { color: ivicaBoja }
|
||||||
},
|
},
|
||||||
y: {
|
y: {
|
||||||
|
stacked: false,
|
||||||
ticks: {
|
ticks: {
|
||||||
color: tekstBoja,
|
color: tekstBoja,
|
||||||
font: { size: 11, family: 'DejaVu Sans, Ubuntu, system-ui, sans-serif' },
|
font: { size: 11, family: 'DejaVu Sans, Ubuntu, system-ui, sans-serif' },
|
||||||
|
|||||||
Reference in New Issue
Block a user