feat(kalkulacija): Faza C — marža po kategoriji/artiklu + zavisni troškovi (backend)
Celina 1 (kompletna) — marža po kategoriji/artiklu: - migracija 046: nullable marza na artikli i kategorije - model Marza *float64 (Artikal, Kategorija) + KategorijaMarza u ArtikalSaKategorijom - repo: čitanje/pisanje marže; nove DohvatiID/Izmeni za kategoriju - dozvola kategorija.izmeni; handler IzmeniKategoriju + ruta - UI: polje marže u formi artikla i kategorije; modal izmene kategorije - nabavka: fallback predlog marže artikal → kategorija → globalna (izaberiArtikal) Celina 2 (backend) — zavisni troškovi nabavke: - migracija 047: tabela nabavka_troskovi + kolona metod_raspodele na nabavke - model NabavkaTrosak, MetodRaspodele; čista funkcija RasporediTroskove + test - repo: Kreiraj upisuje troškove i metod; DohvatiTroskove - handler: parsiranje troškova/metoda; kalkulativna nabavna cena na serveru UI forme troškova i prikaz u detaljima nabavke slede.
This commit is contained in:
@@ -228,6 +228,16 @@ document.addEventListener('alpine:init', () => {
|
||||
const a = this.artikliOpcije.find(x => String(x.id) === String(artikalId))
|
||||
return a ? (parseFloat(a.pdv_stopa) || 0) : 0
|
||||
},
|
||||
// pri izboru artikla predloži maržu: artikal → kategorija → globalna, pa izračunaj prodajnu
|
||||
izaberiArtikal(s) {
|
||||
const a = this.artikliOpcije.find(x => String(x.id) === String(s.artikal_id))
|
||||
if (a) {
|
||||
if (a.marza != null) s.marza = a.marza
|
||||
else if (a.kategorija_marza != null) s.marza = a.kategorija_marza
|
||||
else s.marza = this.marzaDefault
|
||||
}
|
||||
this.izracunajProdajnu(s)
|
||||
},
|
||||
// prodajna (sa PDV) = nabavna × (1 + marža/100) × (1 + pdvStopa/100), zaokruženo na 2 decimale
|
||||
izracunajProdajnu(s) {
|
||||
const cena = parseFloat(s.cena) || 0
|
||||
|
||||
@@ -52,6 +52,11 @@
|
||||
<input type="text" name="opis" placeholder="Kratak opis kategorije..."
|
||||
style="width:100%;">
|
||||
</div>
|
||||
<div>
|
||||
<label class="polje-labela">Marža (%)</label>
|
||||
<input type="number" name="marza" min="0" step="0.01"
|
||||
placeholder="prazno = globalna marža" style="width:100%;">
|
||||
</div>
|
||||
<div style="display:flex;justify-content:flex-end;">
|
||||
<button type="submit"
|
||||
style="padding:8px 20px;background:var(--sb-akcent);color:#fff;border:none;border-radius:8px;font-size:14px;font-weight:500;cursor:pointer;">
|
||||
@@ -76,6 +81,38 @@
|
||||
<div style="font-size:12px;color:var(--tekst-sporedni);margin-top:2px;">{{.Opis}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if .Marza}}
|
||||
<span style="font-size:12px;color:var(--tekst-sporedni);white-space:nowrap;">marža {{.Marza}}%</span>
|
||||
{{end}}
|
||||
{{if index $.Dozvole "kategorija.izmeni"}}
|
||||
<button type="button" class="btn-primarno-malo" onclick="this.nextElementSibling.showModal()">Izmeni</button>
|
||||
{{/* nativni modal — isti obrazac kao u magacinu (top layer, centriran) */}}
|
||||
<dialog id="kat-{{.ID}}" class="premesti-modal" onclick="if(event.target===this)this.close()">
|
||||
<form method="dialog" class="premesti-zaglavlje">
|
||||
<h3>Izmeni kategoriju</h3>
|
||||
<button type="submit" class="premesti-zatvori" aria-label="Zatvori">×</button>
|
||||
</form>
|
||||
<form method="POST" action="/magacin/kategorije/izmeni/{{.ID}}" style="display:flex;flex-direction:column;gap:12px;padding:16px;">
|
||||
<div>
|
||||
<label class="polje-labela">Naziv <span style="color:#dc2626;">*</span></label>
|
||||
<input type="text" name="naziv" value="{{.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;">
|
||||
</div>
|
||||
<div>
|
||||
<label class="polje-labela">Opis</label>
|
||||
<input type="text" name="opis" value="{{.Opis}}"
|
||||
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;">
|
||||
</div>
|
||||
<div>
|
||||
<label class="polje-labela">Marža (%)</label>
|
||||
<input type="number" name="marza" min="0" step="0.01" value="{{if .Marza}}{{.Marza}}{{end}}"
|
||||
placeholder="prazno = globalna marža"
|
||||
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;">
|
||||
</div>
|
||||
<button type="submit" class="btn-primarno" style="align-self:flex-end;">Sačuvaj</button>
|
||||
</form>
|
||||
</dialog>
|
||||
{{end}}
|
||||
{{if index $.Dozvole "kategorija.obrisi"}}
|
||||
<a href="/magacin/kategorije/obrisi/{{.ID}}" class="btn-obrisi-malo"
|
||||
data-potvrda="Da li ste sigurni da želite da obrišete ovu kategoriju?">
|
||||
|
||||
@@ -71,6 +71,13 @@
|
||||
<input type="number" name="prodajna_cena" value="{{.Artikal.ProdajnaCena}}" min="0" step="0.01" style="width:100%;">
|
||||
</div>
|
||||
|
||||
<!-- marža za kalkulaciju; prazno = nasleđuje maržu kategorije ili globalnu -->
|
||||
<div>
|
||||
<label class="polje-labela">Marža (%)</label>
|
||||
<input type="number" name="marza" value="{{if .Artikal.Marza}}{{.Artikal.Marza}}{{end}}" min="0" step="0.01" style="width:100%;"
|
||||
placeholder="prazno = po kategoriji / globalna">
|
||||
</div>
|
||||
|
||||
<!-- lokacija -->
|
||||
<div>
|
||||
<label class="polje-labela">Lokacija u magacinu</label>
|
||||
|
||||
@@ -98,7 +98,7 @@
|
||||
<tr style="border-bottom:0.5px solid var(--ivica);">
|
||||
<td style="padding:8px 10px;">
|
||||
<select :name="'artikal_id[]'" x-model="stavka.artikal_id"
|
||||
@change="izracunajProdajnu(stavka)" :disabled="isMobile" style="width:100%;">
|
||||
@change="izaberiArtikal(stavka)" :disabled="isMobile" style="width:100%;">
|
||||
<option value="">— odaberi artikal —</option>
|
||||
<template x-for="a in artikliOpcije" :key="a.id">
|
||||
<option :value="a.id" x-text="a.naziv"></option>
|
||||
@@ -166,7 +166,7 @@
|
||||
<div>
|
||||
<label style="font-size:12px;color:var(--tekst-sporedni);display:block;margin-bottom:4px;">Artikal</label>
|
||||
<select :name="'artikal_id[]'" x-model="stavka.artikal_id"
|
||||
@change="izracunajProdajnu(stavka)" :disabled="!isMobile" style="width:100%;">
|
||||
@change="izaberiArtikal(stavka)" :disabled="!isMobile" style="width:100%;">
|
||||
<option value="">— odaberi artikal —</option>
|
||||
<template x-for="a in artikliOpcije" :key="a.id">
|
||||
<option :value="a.id" x-text="a.naziv"></option>
|
||||
|
||||
Reference in New Issue
Block a user