Files
pgz-sport/static/erp_full.html
T
Damir Radulić 38383d07c5 Task 4: Universal Export ▾ — CSV/XLSX/PDF dropdown across all screens
- routers/export_router.py: /api/v2/export?format=...&endpoint=...&filters=...
- static/js/export_dropdown.js: shared attachExportDropdown helper
- sport2/app/crm_v2/erp_full: Export ▾ button wired to representative tables
- pgz_sport_api.py: mount export_router with try/except

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 18:33:36 +02:00

1280 lines
68 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<!--
erp_full.html — FULL ERP (SAP-Lite) za PGŽ Sport
Author: Damir Radulić <dradulic@outlook.com> / damir@rinet.one
Version: 1.0.0
Date: 2026-05-05
Description: Sub-tabs Dnevnik | Glavna knjiga | Partneri | Računi | PDV | Plaće | Proračun | Izvještaji
-->
<html lang="hr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>PGŽ SPORT — ERP (SAP-Lite)</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;600&display=swap" rel="stylesheet">
<style>
:root{
--pgz-blue:#003087; --pgz-blue2:#004CC4; --pgz-gold:#F4C430;
--bg0:#08090e; --bg1:#0d1021; --bg2:#111628; --bg3:#161d35; --bg4:#1c2542;
--rim:#1e2a50; --rim2:#283560;
--t0:#fff; --t1:#e2e6f0; --t2:#8a95b4; --t4:#4e5a7a;
--green:#00e88f; --red:#ff2d55; --amber:#f59e0b; --cyan:#00c8e8;
--font:'Inter',sans-serif; --mono:'JetBrains Mono',monospace;
}
*{box-sizing:border-box;margin:0;padding:0}
html,body{height:100%}
body{font-family:var(--font);background:var(--bg0);color:var(--t1);font-size:13px;overflow-x:hidden}
button,input,select,textarea{font-family:inherit;font-size:inherit;outline:none}
::-webkit-scrollbar{width:8px;height:8px}
::-webkit-scrollbar-track{background:var(--bg1)}
::-webkit-scrollbar-thumb{background:var(--rim2);border-radius:4px}
.main{padding:0 0 0 0;flex:1;min-width:0}
.tb{background:var(--bg1);border-bottom:1px solid var(--rim);padding:12px 22px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:5}
.tb-t{font-size:15px;font-weight:700;color:var(--t0)}
.tb-s{font-size:11px;color:var(--t2)}
.content{padding:18px 22px}
.tabs{display:flex;gap:4px;border-bottom:1px solid var(--rim);margin-bottom:14px;flex-wrap:wrap}
.tab{padding:9px 16px;cursor:pointer;color:var(--t2);font-weight:600;font-size:12px;border-bottom:2px solid transparent;transition:all .15s;background:none;border-left:0;border-right:0;border-top:0}
.tab:hover{color:var(--t1)}
.tab.active{color:var(--pgz-gold);border-bottom-color:var(--pgz-gold)}
.panel{display:none}
.panel.active{display:block}
.card{background:var(--bg2);border:1px solid var(--rim);border-radius:8px;padding:14px;margin-bottom:14px}
.card-h{display:flex;align-items:center;justify-content:space-between;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--rim)}
.card-t{font-weight:700;color:var(--t0);font-size:13px}
.toolbar{display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-wrap:wrap}
.toolbar input,.toolbar select{background:var(--bg2);border:1px solid var(--rim);border-radius:5px;padding:7px 10px;color:var(--t1);font-size:12px}
.toolbar input:focus,.toolbar select:focus{border-color:var(--pgz-blue2)}
.toolbar label{font-size:11px;color:var(--t2);display:flex;align-items:center;gap:6px}
.btn{background:var(--pgz-blue2);border:0;color:#fff;padding:7px 14px;border-radius:5px;cursor:pointer;font-size:12px;font-weight:600;transition:background .15s}
.btn:hover{background:var(--pgz-blue)}
.btn.gold{background:var(--pgz-gold);color:var(--bg0)}
.btn.gold:hover{background:#e0b220}
.btn.green{background:var(--green);color:var(--bg0)}
.btn.red{background:var(--red);color:#fff}
.btn.sec{background:var(--bg3);color:var(--t1);border:1px solid var(--rim)}
table{width:100%;border-collapse:collapse;font-size:12px}
table th{background:var(--bg3);color:var(--t2);text-transform:uppercase;font-size:10px;letter-spacing:.5px;padding:8px 10px;text-align:left;border-bottom:1px solid var(--rim);font-weight:700}
table td{padding:7px 10px;border-bottom:1px solid var(--rim);color:var(--t1)}
table tbody tr{cursor:pointer;transition:background .15s}
table tbody tr:hover{background:var(--bg3)}
.num{font-family:var(--mono);text-align:right}
.tbl-wrap{overflow-x:auto;max-height:600px;overflow-y:auto;border:1px solid var(--rim);border-radius:6px}
.modal-bg{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.7);z-index:100;align-items:flex-start;justify-content:center;padding-top:40px}
.modal-bg.show{display:flex}
.modal{background:var(--bg1);border:1px solid var(--rim);border-radius:8px;padding:18px;width:min(720px,94vw);max-height:90vh;overflow-y:auto}
.modal h3{font-size:14px;font-weight:700;color:var(--pgz-gold);margin-bottom:14px;padding-bottom:8px;border-bottom:1px solid var(--rim)}
.form-row{display:grid;grid-template-columns:140px 1fr;gap:8px;margin-bottom:8px;align-items:center}
.form-row label{font-size:11px;color:var(--t2)}
.form-row input,.form-row select,.form-row textarea{width:100%;background:var(--bg2);border:1px solid var(--rim);border-radius:4px;padding:6px 9px;color:var(--t1);font-size:12px}
.form-actions{display:flex;gap:8px;justify-content:flex-end;margin-top:14px;padding-top:10px;border-top:1px solid var(--rim)}
.kpi-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px;margin-bottom:14px}
.kpi{background:linear-gradient(135deg,var(--bg2) 0%,var(--bg1) 100%);border:1px solid var(--rim);border-radius:8px;padding:12px 14px;position:relative}
.kpi::before{content:"";position:absolute;top:0;left:0;width:3px;height:100%;background:var(--pgz-gold)}
.kpi.g::before{background:var(--green)}
.kpi.r::before{background:var(--red)}
.kpi-l{font-size:10.5px;color:var(--t2);text-transform:uppercase;letter-spacing:1px;font-weight:600}
.kpi-v{font-size:22px;font-weight:800;color:var(--t0);margin-top:4px;font-family:var(--mono)}
.badge{display:inline-block;padding:2px 7px;border-radius:3px;font-size:10px;font-weight:600;text-transform:uppercase}
.badge.nacrt{background:var(--bg4);color:var(--t1)}
.badge.knjizen{background:var(--green);color:var(--bg0)}
.badge.placen{background:var(--pgz-gold);color:var(--bg0)}
.badge.otkazan{background:var(--red);color:#fff}
.dnev-line-row{display:grid;grid-template-columns:140px 1fr 100px 100px 1fr 30px;gap:6px;margin-bottom:6px;align-items:center}
.dnev-line-row input,.dnev-line-row select{background:var(--bg2);border:1px solid var(--rim);border-radius:4px;padding:5px 8px;color:var(--t1);font-size:12px;width:100%}
.dnev-balans{padding:8px;background:var(--bg3);border-radius:4px;margin-top:8px;font-family:var(--mono);font-size:11px}
.dnev-balans.ok{color:var(--green)}
.dnev-balans.bad{color:var(--red)}
</style>
<link rel="stylesheet" href="/static/shared/sidebar.css">
<script src="/static/shared/sidebar.js" defer data-active="erp_full"></script>
</head>
<body>
<main class="main">
<div class="tb">
<div>
<div class="tb-t">ERP — SAP-Lite</div>
<div class="tb-s">Dvostavno knjigovodstvo · HR-RRIF kontni plan · FINA e-Račun</div>
</div>
<div class="tb-s"><span style="color:var(--green)"></span> /api/v2/erp/*</div>
</div>
<div class="content">
<div class="tabs">
<button class="tab active" data-panel="dnevnik">📓 Dnevnik</button>
<button class="tab" data-panel="glavna">📊 Glavna knjiga</button>
<button class="tab" data-panel="partneri">🤝 Partneri</button>
<button class="tab" data-panel="racuni">🧾 Računi</button>
<button class="tab" data-panel="uploads">📎 Uploads (OCR)</button>
<button class="tab" data-panel="putni">✈ Putni nalozi</button>
<button class="tab" data-panel="payments">💰 Plaćanja</button>
<button class="tab" data-panel="pdv">% PDV</button>
<button class="tab" data-panel="place">💼 Plaće</button>
<button class="tab" data-panel="proracun">€ Proračun</button>
<button class="tab" data-panel="izvjestaji">📈 Izvještaji</button>
<button class="tab" data-panel="kontni">📚 Kontni plan</button>
</div>
<!-- ============ DNEVNIK ============ -->
<section class="panel active" id="panel-dnevnik">
<div class="card">
<div class="card-h">
<div class="card-t">Dnevnik knjiženja</div>
<button class="btn gold" onclick="openDnevnikModal()">+ Novi zapis</button>
</div>
<div class="toolbar">
<label>Godina <input type="number" id="dnev-godina" value="2026" style="width:90px"></label>
<label>Tip <select id="dnev-tip"><option value="">— svi —</option><option value="rucno">Ručno</option><option value="racun_u">Račun ulazni</option><option value="racun_i">Račun izlazni</option><option value="placa">Plaća</option><option value="storno">Storno</option></select></label>
<button class="btn" onclick="loadDnevnik()">Osvježi</button>
</div>
<div class="tbl-wrap">
<table id="dnev-tbl"><thead><tr><th>#</th><th>Datum</th><th>Opis</th><th>Tip</th><th class="num">Stavki</th><th class="num">Ukupno</th><th>Akcije</th></tr></thead><tbody></tbody></table>
</div>
</div>
</section>
<!-- ============ GLAVNA KNJIGA ============ -->
<section class="panel" id="panel-glavna">
<div class="card">
<div class="card-h">
<div class="card-t">Glavna knjiga (saldo po kontu)</div>
<button class="btn sec" onclick="exportXlsx('glavna-knjiga', 2026)">⬇ XLSX</button>
</div>
<div class="toolbar">
<label>Klasa <select id="gk-klasa"><option value="">— sve —</option><option>0</option><option>1</option><option>2</option><option>3</option><option>4</option><option>7</option><option>9</option></select></label>
<button class="btn" onclick="loadGlavnaKnjiga()">Osvježi</button>
</div>
<div class="tbl-wrap">
<table id="gk-tbl"><thead><tr><th>Šifra</th><th>Naziv</th><th>Klasa</th><th>Vrsta</th><th class="num">Duguje</th><th class="num">Potražuje</th><th class="num">Saldo</th><th class="num">Stavki</th></tr></thead><tbody></tbody></table>
</div>
</div>
</section>
<!-- ============ PARTNERI ============ -->
<section class="panel" id="panel-partneri">
<div class="card">
<div class="card-h">
<div class="card-t">Partneri (kupci/dobavljači)</div>
<button class="btn gold" onclick="openPartnerModal()">+ Novi partner</button>
</div>
<div class="toolbar">
<input id="part-q" placeholder="Pretraži (naziv/OIB)…">
<label>Vrsta <select id="part-vrsta"><option value="">— sve —</option><option value="kupac">Kupac</option><option value="dobavljac">Dobavljač</option><option value="oba">Oba</option></select></label>
<button class="btn" onclick="loadPartneri()">Osvježi</button>
</div>
<div class="tbl-wrap">
<table id="part-tbl"><thead><tr><th>#</th><th>OIB</th><th>Naziv</th><th>Vrsta</th><th>Grad</th><th>IBAN</th><th class="num">Saldo</th><th>Akcije</th></tr></thead><tbody></tbody></table>
</div>
</div>
</section>
<!-- ============ RAČUNI ============ -->
<section class="panel" id="panel-racuni">
<div class="card">
<div class="card-h">
<div class="card-t">Računi</div>
<div style="display:flex;gap:6px">
<button class="btn gold" onclick="openRacunModal('ulazni')">+ Ulazni</button>
<button class="btn green" onclick="openRacunModal('izlazni')">+ Izlazni</button>
<label class="btn sec" style="cursor:pointer;display:inline-flex;align-items:center;gap:6px">
📥 Import e-Račun XML
<input type="file" id="eracun-file" accept=".xml,application/xml" style="display:none" onchange="importERacun()">
</label>
</div>
</div>
<div class="toolbar">
<label>Tip <select id="rac-tip"><option value="ulazni">Ulazni</option><option value="izlazni">Izlazni</option></select></label>
<label>Status <select id="rac-status"><option value="">— svi —</option><option value="nacrt">Nacrt</option><option value="knjizen">Knjižen</option><option value="placen">Plaćen</option><option value="otkazan">Otkazan</option></select></label>
<label>Godina <input type="number" id="rac-godina" value="2026" style="width:90px"></label>
<button class="btn" onclick="loadRacuni()">Osvježi</button>
<button id="rac-export-btn" class="export-btn" type="button">Export ▾</button>
</div>
<div class="tbl-wrap">
<table id="rac-tbl"><thead><tr><th>#</th><th>Broj</th><th>Datum</th><th>Partner</th><th>OIB</th><th class="num">Neto</th><th class="num">PDV</th><th class="num">Brutto</th><th>Status</th><th>Akcije</th></tr></thead><tbody><tr><td colspan="10" style="color:var(--t2);text-align:center;padding:18px">Klikni "Osvježi" za učitavanje…</td></tr></tbody></table>
</div>
<div id="rac-detail" style="display:none;margin-top:14px;border-top:1px solid var(--bd);padding-top:12px">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
<h4 id="rac-detail-title" style="font-size:12px;color:var(--t2);margin:0">Stavke</h4>
<button class="btn sec" onclick="document.getElementById('rac-detail').style.display='none'">× Zatvori</button>
</div>
<div class="tbl-wrap">
<table id="rac-stavke-tbl"><thead><tr><th>#</th><th>Naziv</th><th class="num">Količina</th><th>JM</th><th class="num">Cijena</th><th class="num">Popust %</th><th class="num">PDV %</th><th class="num">Neto</th><th class="num">Brutto</th></tr></thead><tbody></tbody></table>
</div>
<h4 id="rac-uploads-title" style="font-size:12px;color:var(--t2);margin:12px 0 6px;display:none">Privitci (uploads)</h4>
<div id="rac-uploads-wrap" style="display:none" class="tbl-wrap">
<table id="rac-uploads-tbl"><thead><tr><th>#</th><th>Datoteka</th><th class="num">Veličina</th><th>Mime</th><th>OCR</th><th>Datum</th></tr></thead><tbody></tbody></table>
</div>
</div>
</div>
</section>
<!-- ============ INVOICE UPLOADS (OCR) ============ -->
<section class="panel" id="panel-uploads">
<div class="card">
<div class="card-h">
<div class="card-t">Računi (OCR) — Invoice Uploads / AI extraction</div>
<div style="display:flex;gap:6px">
<button class="btn primary" onclick="document.getElementById('up-file').click()">📎 Upload novi račun</button>
<input type="file" id="up-file" accept="application/pdf,image/*" style="display:none" onchange="uploadInvoiceFile(this.files[0])">
</div>
</div>
<div id="up-drop" style="border:2px dashed var(--rim2);border-radius:8px;padding:18px;text-align:center;color:var(--t2);margin-bottom:10px;background:var(--bg3)">
Dovuci PDF / JPG / PNG ovdje (max 25 MB) ili koristi gumb gore.
<div id="up-progress" style="margin-top:6px;font-size:11px;color:var(--t1)"></div>
</div>
<div class="toolbar">
<input id="up-q" placeholder="Datoteka / vendor / broj…">
<label>Status <select id="up-status"><option value="">— svi —</option><option value="pending">pending</option><option value="ocr_done">ocr_done</option><option value="approved">approved</option><option value="rejected">rejected</option></select></label>
<button class="btn" onclick="loadUploads()">Osvježi</button>
</div>
<div class="tbl-wrap">
<table id="up-tbl"><thead><tr><th>#</th><th>Datoteka</th><th class="num">Veličina</th><th>Vendor</th><th>OIB</th><th>Br. računa</th><th>Datum</th><th class="num">Brutto</th><th>OCR status</th><th class="num">Conf</th><th>Račun</th><th>Akcije</th></tr></thead><tbody><tr><td colspan="12" style="color:var(--t2);text-align:center;padding:18px">Klikni "Osvježi"…</td></tr></tbody></table>
</div>
</div>
</section>
<!-- ============ PUTNI NALOZI / EXPENSE REPORTS ============ -->
<section class="panel" id="panel-putni">
<div class="card">
<div class="card-h"><div class="card-t">Putni nalozi i ostali troškovi (expense_reports)</div></div>
<div class="toolbar">
<label>Tip <select id="pn-type"><option value="">— svi —</option><option value="putni_nalog">Putni nalog</option><option value="expense">Trošak</option></select></label>
<label>Status <select id="pn-status"><option value="">— svi —</option><option value="draft">draft</option><option value="podnesen">podnesen</option><option value="odobren">odobren</option><option value="isplacen">isplacen</option><option value="rejected">rejected</option></select></label>
<label>Godina <input type="number" id="pn-godina" placeholder="2026" style="width:90px"></label>
<button class="btn" onclick="loadExpenseReports()">Osvježi</button>
<button id="pn-export-btn" class="export-btn" type="button">Export ▾</button>
</div>
<div class="tbl-wrap">
<table id="pn-tbl"><thead><tr><th>#</th><th>Tip</th><th>Klub</th><th>Odredište</th><th>Svrha</th><th>Od</th><th>Do</th><th class="num">Km</th><th class="num">Trošak</th><th class="num">Dnevnice</th><th>Status</th></tr></thead><tbody><tr><td colspan="11" style="color:var(--t2);text-align:center;padding:18px">Klikni "Osvježi"…</td></tr></tbody></table>
</div>
<div id="pn-detail" style="display:none;margin-top:14px;border-top:1px solid var(--bd);padding-top:12px">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:8px">
<h4 id="pn-detail-title" style="font-size:12px;color:var(--t2);margin:0">Vezani računi</h4>
<button class="btn sec" onclick="document.getElementById('pn-detail').style.display='none'">× Zatvori</button>
</div>
<div class="tbl-wrap">
<table id="pn-rac-tbl"><thead><tr><th>#</th><th>Broj računa</th><th>Vendor</th><th class="num">Brutto</th><th>Valuta</th><th>Kategorija</th><th>Datum</th></tr></thead><tbody></tbody></table>
</div>
</div>
</div>
</section>
<!-- ============ PAYMENTS ============ -->
<section class="panel" id="panel-payments">
<div class="card">
<div class="card-h"><div class="card-t">Plaćanja / Bank Reconciliation (payments)</div></div>
<div class="toolbar">
<label>Status <select id="py-status"><option value="">— svi —</option><option value="unmatched">unmatched</option><option value="matched">matched</option><option value="manual">manual</option></select></label>
<label>Način <select id="py-method"><option value="">— svi —</option><option value="transfer">transfer</option><option value="cash">cash</option><option value="card">card</option></select></label>
<label>Godina <input type="number" id="py-godina" placeholder="2026" style="width:90px"></label>
<button class="btn" onclick="loadPayments()">Osvježi</button>
<button id="py-export-btn" class="export-btn" type="button">Export ▾</button>
</div>
<div class="tbl-wrap">
<table id="py-tbl"><thead><tr><th>#</th><th>Datum</th><th>Klub</th><th class="num">Iznos</th><th>Valuta</th><th>Način</th><th>IBAN OD</th><th>IBAN ZA</th><th>Referenca</th><th>Račun</th><th>Putni nalog</th><th>Match</th></tr></thead><tbody><tr><td colspan="12" style="color:var(--t2);text-align:center;padding:18px">Klikni "Osvježi"…</td></tr></tbody></table>
</div>
</div>
</section>
<!-- ============ PDV ============ -->
<section class="panel" id="panel-pdv">
<div class="card">
<div class="card-h">
<div class="card-t">PDV — Knjige + Obrazac</div>
<div style="display:flex;gap:6px">
<button class="btn sec" onclick="exportXlsx('pdv-u', document.getElementById('pdv-godina').value, document.getElementById('pdv-mjesec').value)">⬇ U XLSX</button>
<button class="btn sec" onclick="exportXlsx('pdv-i', document.getElementById('pdv-godina').value, document.getElementById('pdv-mjesec').value)">⬇ I XLSX</button>
</div>
</div>
<div class="toolbar">
<label>Godina <input type="number" id="pdv-godina" value="2026" style="width:90px"></label>
<label>Mjesec <input type="number" id="pdv-mjesec" min="1" max="12" placeholder="1-12" style="width:80px"></label>
<button class="btn" onclick="loadPdv()">Osvježi</button>
</div>
<div id="pdv-summary" class="kpi-grid"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px">
<div>
<h4 style="font-size:12px;color:var(--t2);margin-bottom:8px">KNJIGA U-RA (ulazni)</h4>
<div class="tbl-wrap"><table id="pdv-u-tbl"><thead><tr><th>Broj</th><th>Datum</th><th>Partner</th><th>OIB</th><th class="num">Neto</th><th class="num">PDV</th></tr></thead><tbody></tbody></table></div>
</div>
<div>
<h4 style="font-size:12px;color:var(--t2);margin-bottom:8px">KNJIGA I-RA (izlazni)</h4>
<div class="tbl-wrap"><table id="pdv-i-tbl"><thead><tr><th>Broj</th><th>Datum</th><th>Partner</th><th>OIB</th><th class="num">Neto</th><th class="num">PDV</th></tr></thead><tbody></tbody></table></div>
</div>
</div>
</div>
</section>
<!-- ============ PLAĆE ============ -->
<section class="panel" id="panel-place">
<div class="card">
<div class="card-h">
<div class="card-t">Zaposlenici i obračun plaća</div>
<div style="display:flex;gap:6px">
<button class="btn gold" onclick="openZapModal()">+ Zaposlenik</button>
<button class="btn green" onclick="openPlacaModal()">€ Obračun plaće</button>
</div>
</div>
<h4 style="font-size:12px;color:var(--t2);margin:8px 0">Zaposlenici</h4>
<div class="tbl-wrap" style="margin-bottom:14px">
<table id="zap-tbl"><thead><tr><th>#</th><th>OIB</th><th>Ime</th><th>Prezime</th><th>Klub</th><th>Mjesto</th><th class="num">Bruto</th><th>Aktivan</th></tr></thead><tbody></tbody></table>
</div>
<h4 style="font-size:12px;color:var(--t2);margin:8px 0">Obračuni plaća</h4>
<div class="toolbar">
<label>Godina <input type="number" id="pl-godina" value="2026" style="width:90px"></label>
<label>Mjesec <input type="number" id="pl-mjesec" placeholder="1-12" style="width:80px"></label>
<button class="btn" onclick="loadPlace()">Osvježi</button>
</div>
<div class="tbl-wrap">
<table id="pl-tbl"><thead><tr><th>#</th><th>Zaposlenik</th><th>God/Mj</th><th class="num">Bruto</th><th class="num">Doprinosi iz</th><th class="num">Dohodnina</th><th class="num">Neto</th><th class="num">Doprinosi na</th><th class="num">Trošak</th></tr></thead><tbody></tbody></table>
</div>
</div>
</section>
<!-- ============ PRORAČUN ============ -->
<section class="panel" id="panel-proracun">
<div class="card">
<div class="card-h"><div class="card-t">Proračun PGŽ Sport (po godinama)</div></div>
<div class="tbl-wrap">
<table id="pr-tbl"><thead><tr><th>Godina</th><th class="num">Proračun PGŽ</th><th class="num">Rebalans 1</th><th class="num">Rebalans 2</th><th class="num">Ukupno PGŽ</th><th class="num">Ministarstvo</th><th class="num">Ukupno</th><th>Napomena</th></tr></thead><tbody></tbody></table>
</div>
</div>
</section>
<!-- ============ IZVJEŠTAJI ============ -->
<section class="panel" id="panel-izvjestaji">
<div class="card">
<div class="card-h">
<div class="card-t">Izvještaji (Bilanca · PnL · Cashflow)</div>
</div>
<div class="toolbar">
<label>Tip <select id="iz-tip"><option value="bilanca">Bilanca</option><option value="pnl">PnL (Račun dobiti/gubitka)</option><option value="cashflow">Cashflow</option></select></label>
<label>Godina <input type="number" id="iz-godina" value="2026" style="width:90px"></label>
<button class="btn" onclick="loadIzvjestaj()">Generiraj</button>
<button class="btn sec" onclick="exportXlsx(document.getElementById('iz-tip').value, document.getElementById('iz-godina').value)">⬇ XLSX</button>
<button class="btn sec" onclick="exportPdf(document.getElementById('iz-tip').value, document.getElementById('iz-godina').value)">⬇ PDF</button>
</div>
<div id="iz-out"></div>
</div>
</section>
<!-- ============ KONTNI PLAN ============ -->
<section class="panel" id="panel-kontni">
<div class="card">
<div class="card-h">
<div class="card-t">Kontni plan (HR-RRIF)</div>
<button class="btn gold" onclick="openKontoModal()">+ Novi konto</button>
</div>
<div class="toolbar">
<input id="kp-q" placeholder="Šifra ili naziv…">
<label>Klasa <select id="kp-klasa"><option value="">— sve —</option><option>0</option><option>1</option><option>2</option><option>3</option><option>4</option><option>7</option><option>9</option></select></label>
<label>Vrsta <select id="kp-vrsta"><option value="">— sve —</option><option value="aktiva">Aktiva</option><option value="pasiva">Pasiva</option><option value="prihod">Prihod</option><option value="rashod">Rashod</option><option value="kapital">Kapital</option></select></label>
<button class="btn" onclick="loadKontniPlan()">Osvježi</button>
</div>
<div class="tbl-wrap">
<table id="kp-tbl"><thead><tr><th>Šifra</th><th>Naziv</th><th>Klasa</th><th>Vrsta</th><th>Aktivan</th><th>Akcije</th></tr></thead><tbody></tbody></table>
</div>
</div>
</section>
</div>
</main>
<!-- ===== MODALS ===== -->
<div class="modal-bg" id="m-konto" onclick="if(event.target===this)closeModal('m-konto')">
<div class="modal">
<h3>Novi / izmjena konta</h3>
<div class="form-row"><label>Šifra</label><input id="k-sifra"></div>
<div class="form-row"><label>Naziv</label><input id="k-naziv"></div>
<div class="form-row"><label>Klasa</label><input type="number" id="k-klasa" min="0" max="9"></div>
<div class="form-row"><label>Vrsta</label><select id="k-vrsta"><option value="aktiva">Aktiva</option><option value="pasiva">Pasiva</option><option value="prihod">Prihod</option><option value="rashod">Rashod</option><option value="kapital">Kapital</option><option value="izvanbilanca">Izvanbilanca</option></select></div>
<div class="form-actions">
<button class="btn sec" onclick="closeModal('m-konto')">Odustani</button>
<button class="btn gold" onclick="saveKonto()">Spremi</button>
</div>
</div>
</div>
<div class="modal-bg" id="m-partner" onclick="if(event.target===this)closeModal('m-partner')">
<div class="modal">
<h3>Novi / izmjena partnera</h3>
<div class="form-row"><label>Naziv</label><input id="p-naziv"></div>
<div class="form-row"><label>OIB</label><input id="p-oib" maxlength="11"></div>
<div class="form-row"><label>Vrsta</label><select id="p-vrsta"><option value="oba">Oba</option><option value="kupac">Kupac</option><option value="dobavljac">Dobavljač</option></select></div>
<div class="form-row"><label>IBAN</label><input id="p-iban"></div>
<div class="form-row"><label>Adresa</label><input id="p-adresa"></div>
<div class="form-row"><label>Grad</label><input id="p-grad"></div>
<div class="form-row"><label>Email</label><input id="p-email"></div>
<div class="form-row"><label>Telefon</label><input id="p-telefon"></div>
<div class="form-actions">
<button class="btn sec" onclick="closeModal('m-partner')">Odustani</button>
<button class="btn gold" onclick="savePartner()">Spremi</button>
</div>
</div>
</div>
<div class="modal-bg" id="m-dnev" onclick="if(event.target===this)closeModal('m-dnev')">
<div class="modal" style="width:min(960px,96vw)">
<h3>Novi zapis u dnevniku</h3>
<div class="form-row"><label>Datum</label><input type="date" id="d-datum"></div>
<div class="form-row"><label>Opis</label><input id="d-opis"></div>
<div class="form-row"><label>Tip dokumenta</label><select id="d-tip"><option value="rucno">Ručno</option><option value="racun_u">Račun ulazni</option><option value="racun_i">Račun izlazni</option><option value="placa">Plaća</option></select></div>
<h4 style="font-size:11px;color:var(--t2);margin:14px 0 6px">STAVKE (D=duguje, P=potražuje, samo jedno > 0)</h4>
<div id="d-lines"></div>
<button class="btn sec" onclick="addDnevLine()" style="margin-top:8px">+ Dodaj stavku</button>
<div id="d-balans" class="dnev-balans">Duguje: 0,00 · Potražuje: 0,00</div>
<div class="form-actions">
<button class="btn sec" onclick="closeModal('m-dnev')">Odustani</button>
<button class="btn gold" onclick="saveDnev()">Spremi zapis</button>
</div>
</div>
</div>
<div class="modal-bg" id="m-rac" onclick="if(event.target===this)closeModal('m-rac')">
<div class="modal" style="width:min(900px,96vw)">
<h3 id="m-rac-title">Novi račun</h3>
<div class="form-row"><label>Broj</label><input id="r-broj"></div>
<div class="form-row"><label>Partner</label><select id="r-partner"></select></div>
<div class="form-row"><label>Datum izdavanja</label><input type="date" id="r-datum"></div>
<div class="form-row"><label>Dospijeće</label><input type="date" id="r-dospjece"></div>
<div class="form-row"><label>Status</label><select id="r-status"><option value="nacrt">Nacrt</option><option value="knjizen">Knjižen (auto-knjiženje)</option></select></div>
<h4 style="font-size:11px;color:var(--t2);margin:14px 0 6px">STAVKE</h4>
<div id="r-lines"></div>
<button class="btn sec" onclick="addRacLine()" style="margin-top:8px">+ Dodaj stavku</button>
<div id="r-summary" class="dnev-balans">Neto: 0,00 · PDV: 0,00 · Brutto: 0,00</div>
<div class="form-actions">
<button class="btn sec" onclick="closeModal('m-rac')">Odustani</button>
<button class="btn gold" onclick="saveRac()">Spremi račun</button>
</div>
</div>
</div>
<div class="modal-bg" id="m-zap" onclick="if(event.target===this)closeModal('m-zap')">
<div class="modal">
<h3>Novi zaposlenik</h3>
<div class="form-row"><label>Ime</label><input id="z-ime"></div>
<div class="form-row"><label>Prezime</label><input id="z-prezime"></div>
<div class="form-row"><label>OIB</label><input id="z-oib" maxlength="11"></div>
<div class="form-row"><label>Klub ID</label><input type="number" id="z-klub"></div>
<div class="form-row"><label>Radno mjesto</label><input id="z-mjesto"></div>
<div class="form-row"><label>Plata bruto</label><input type="number" step="0.01" id="z-bruto"></div>
<div class="form-row"><label>IBAN</label><input id="z-iban"></div>
<div class="form-actions">
<button class="btn sec" onclick="closeModal('m-zap')">Odustani</button>
<button class="btn gold" onclick="saveZap()">Spremi</button>
</div>
</div>
</div>
<div class="modal-bg" id="m-pl" onclick="if(event.target===this)closeModal('m-pl')">
<div class="modal">
<h3>Obračun plaće (HR 2026)</h3>
<div class="form-row"><label>Zaposlenik</label><select id="pl-zap"></select></div>
<div class="form-row"><label>Godina</label><input type="number" id="pl-god" value="2026"></div>
<div class="form-row"><label>Mjesec</label><input type="number" id="pl-mj" min="1" max="12"></div>
<div class="form-row"><label>Bruto (€)</label><input type="number" step="0.01" id="pl-bruto" placeholder="prazno = iz zaposlenika"></div>
<div class="form-row"><label>Osobni odbitak</label><input type="number" step="0.01" id="pl-odb" value="600"></div>
<div class="form-row"><label>Prirez %</label><input type="number" step="0.1" id="pl-prirez" value="0"></div>
<div class="form-row"><label>Datum isplate</label><input type="date" id="pl-isplata"></div>
<div class="form-row"><label>Knjiži u dnevnik</label><input type="checkbox" id="pl-knjizi"></div>
<div class="form-actions">
<button class="btn sec" onclick="closeModal('m-pl')">Odustani</button>
<button class="btn gold" onclick="savePlaca()">Obračunaj</button>
</div>
</div>
</div>
<script>
const API = '/api/v2/erp';
const AUTH = () => ({ 'Authorization': 'Bearer ' + (localStorage.getItem('jwt') || localStorage.getItem('access_token') || 'admin-pgz-2026') });
const fmt = n => (Number(n||0)).toLocaleString('hr-HR',{minimumFractionDigits:2,maximumFractionDigits:2});
async function api(path, opts={}) {
const r = await fetch(API + path, { headers: { 'Content-Type':'application/json', ...AUTH() }, ...opts });
if (!r.ok) {
let detail = await r.text();
throw new Error(`${r.status}: ${detail}`);
}
return r.json();
}
// Tab switching
document.querySelectorAll('.tab').forEach(t => t.addEventListener('click', () => {
document.querySelectorAll('.tab').forEach(x=>x.classList.remove('active'));
document.querySelectorAll('.panel').forEach(x=>x.classList.remove('active'));
t.classList.add('active');
const panel = t.dataset.panel;
document.getElementById('panel-' + panel).classList.add('active');
loaders[panel] && loaders[panel]();
}));
function closeModal(id){ document.getElementById(id).classList.remove('show'); }
function openModal(id){ document.getElementById(id).classList.add('show'); }
// ===== KONTNI PLAN =====
async function loadKontniPlan(){
const q = document.getElementById('kp-q').value;
const klasa = document.getElementById('kp-klasa').value;
const vrsta = document.getElementById('kp-vrsta').value;
const params = new URLSearchParams();
if (q) params.set('q', q);
if (klasa) params.set('klasa', klasa);
if (vrsta) params.set('vrsta', vrsta);
const d = await api('/kontni-plan?' + params.toString());
const tbody = document.querySelector('#kp-tbl tbody');
tbody.innerHTML = d.rows.map(r=>`<tr>
<td><b>${r.sifra}</b></td><td>${r.naziv}</td><td>${r.klasa}</td><td>${r.vrsta}</td>
<td>${r.aktivan?'✓':'✗'}</td>
<td><button class="btn sec" onclick="event.stopPropagation();editKonto(${r.id})">✎</button></td>
</tr>`).join('');
}
function openKontoModal(){
['k-sifra','k-naziv','k-klasa'].forEach(i=>document.getElementById(i).value='');
document.getElementById('m-konto').dataset.id='';
openModal('m-konto');
}
async function editKonto(id){
const d = await api('/kontni-plan?'); // we don't have a single GET, so fetch & filter
const r = d.rows.find(x=>x.id===id);
if(!r) return;
document.getElementById('k-sifra').value=r.sifra;
document.getElementById('k-naziv').value=r.naziv;
document.getElementById('k-klasa').value=r.klasa;
document.getElementById('k-vrsta').value=r.vrsta;
document.getElementById('m-konto').dataset.id=id;
openModal('m-konto');
}
async function saveKonto(){
const body = {
sifra: document.getElementById('k-sifra').value.trim(),
naziv: document.getElementById('k-naziv').value.trim(),
klasa: parseInt(document.getElementById('k-klasa').value),
vrsta: document.getElementById('k-vrsta').value,
aktivan: true
};
const id = document.getElementById('m-konto').dataset.id;
try {
if(id) await api('/kontni-plan/'+id, { method:'PUT', body: JSON.stringify(body) });
else await api('/kontni-plan', { method:'POST', body: JSON.stringify(body) });
closeModal('m-konto'); loadKontniPlan();
} catch(e){ alert(e.message); }
}
// ===== PARTNERI =====
async function loadPartneri(){
const q = document.getElementById('part-q').value;
const v = document.getElementById('part-vrsta').value;
const p = new URLSearchParams();
if(q)p.set('q',q); if(v)p.set('vrsta',v);
const d = await api('/partneri?'+p.toString());
const tbody = document.querySelector('#part-tbl tbody');
tbody.innerHTML = d.rows.map(r=>`<tr onclick="loadPartnerSaldo(${r.id})">
<td>${r.id}</td><td>${r.oib||'—'}</td><td><b>${r.naziv}</b></td>
<td>${r.vrsta}</td><td>${r.grad||''}</td><td>${r.iban||''}</td>
<td class="num" id="ps-${r.id}">…</td>
<td><button class="btn sec" onclick="event.stopPropagation();editPartner(${r.id})">✎</button></td>
</tr>`).join('');
d.rows.forEach(r => {
api('/partneri/'+r.id+'/saldo').then(x => {
const el = document.getElementById('ps-'+r.id);
if(el) el.textContent = fmt(x.info?.saldo || 0);
}).catch(()=>{});
});
}
function openPartnerModal(){
['p-naziv','p-oib','p-iban','p-adresa','p-grad','p-email','p-telefon'].forEach(i=>document.getElementById(i).value='');
document.getElementById('m-partner').dataset.id='';
openModal('m-partner');
}
async function editPartner(id){
const d = await api('/partneri?');
const r = d.rows.find(x=>x.id===id);
if(!r) return;
document.getElementById('p-naziv').value=r.naziv||'';
document.getElementById('p-oib').value=r.oib||'';
document.getElementById('p-vrsta').value=r.vrsta;
document.getElementById('p-iban').value=r.iban||'';
document.getElementById('p-adresa').value=r.adresa||'';
document.getElementById('p-grad').value=r.grad||'';
document.getElementById('p-email').value=r.email||'';
document.getElementById('p-telefon').value=r.telefon||'';
document.getElementById('m-partner').dataset.id=id;
openModal('m-partner');
}
async function savePartner(){
const body = {
naziv: document.getElementById('p-naziv').value.trim(),
oib: document.getElementById('p-oib').value.trim() || null,
vrsta: document.getElementById('p-vrsta').value,
iban: document.getElementById('p-iban').value.trim() || null,
adresa: document.getElementById('p-adresa').value || null,
grad: document.getElementById('p-grad').value || null,
email: document.getElementById('p-email').value || null,
telefon: document.getElementById('p-telefon').value || null,
};
const id = document.getElementById('m-partner').dataset.id;
try {
if(id) await api('/partneri/'+id, { method:'PUT', body: JSON.stringify(body) });
else await api('/partneri', { method:'POST', body: JSON.stringify(body) });
closeModal('m-partner'); loadPartneri();
} catch(e){ alert(e.message); }
}
async function loadPartnerSaldo(id){
const d = await api('/partneri/'+id+'/saldo');
alert(`Partner saldo: ${fmt(d.info?.saldo||0)} EUR\nDuguje: ${fmt(d.info?.uk_duguje||0)}\nPotražuje: ${fmt(d.info?.uk_potrazuje||0)}\nBroj stavki: ${d.stavke.length}`);
}
// ===== DNEVNIK =====
async function loadDnevnik(){
const g = document.getElementById('dnev-godina').value;
const t = document.getElementById('dnev-tip').value;
const p = new URLSearchParams();
if(g) p.set('godina', g);
if(t) p.set('dokument_tip', t);
const d = await api('/dnevnik?'+p.toString());
const tbody = document.querySelector('#dnev-tbl tbody');
tbody.innerHTML = d.rows.map(r=>`<tr onclick="dnevDetail(${r.id})">
<td>${r.redni_broj||r.id}</td><td>${r.datum}</td><td>${r.opis||''}</td>
<td>${r.dokument_tip||''}</td><td class="num">${r.broj_stavki}</td>
<td class="num">${fmt(r.uk_duguje)}</td>
<td><button class="btn sec" onclick="event.stopPropagation();dnevStorno(${r.id})">↺ Storno</button></td>
</tr>`).join('');
}
async function dnevDetail(id){
const d = await api('/dnevnik/'+id);
let html = `<b>${d.head.opis||''}</b> · ${d.head.datum} · #${d.head.id}\n\nSTAVKE:\n`;
d.stavke.forEach(s => html += `${s.konto_sifra} ${s.konto_naziv}: D=${fmt(s.duguje)} P=${fmt(s.potrazuje)}\n`);
alert(html);
}
async function dnevStorno(id){
if(!confirm('Sigurno storno za zapis #'+id+'?')) return;
try {
await api('/dnevnik/'+id+'/storno', { method:'POST' });
loadDnevnik();
} catch(e){ alert(e.message); }
}
let kontoCache = [];
async function loadKontoCache(){
if(kontoCache.length) return kontoCache;
const d = await api('/kontni-plan?aktivan=true');
kontoCache = d.rows;
return kontoCache;
}
async function openDnevnikModal(){
await loadKontoCache();
document.getElementById('d-datum').value = new Date().toISOString().slice(0,10);
document.getElementById('d-opis').value = '';
document.getElementById('d-tip').value = 'rucno';
document.getElementById('d-lines').innerHTML = '';
addDnevLine(); addDnevLine();
updateDnevBalans();
openModal('m-dnev');
}
function addDnevLine(){
const opts = kontoCache.map(k=>`<option value="${k.id}">${k.sifra}${k.naziv}</option>`).join('');
const div = document.createElement('div');
div.className = 'dnev-line-row';
div.innerHTML = `
<select class="d-konto">${opts}</select>
<input class="d-opis" placeholder="opis stavke">
<input type="number" step="0.01" class="d-duguje" placeholder="Duguje" oninput="updateDnevBalans()">
<input type="number" step="0.01" class="d-potrazuje" placeholder="Potražuje" oninput="updateDnevBalans()">
<input type="number" class="d-partner" placeholder="Partner ID (opc)">
<button class="btn red" onclick="this.parentElement.remove();updateDnevBalans()">×</button>`;
document.getElementById('d-lines').appendChild(div);
}
function updateDnevBalans(){
const rows = document.querySelectorAll('#d-lines .dnev-line-row');
let d=0,p=0;
rows.forEach(r => {
d += parseFloat(r.querySelector('.d-duguje').value)||0;
p += parseFloat(r.querySelector('.d-potrazuje').value)||0;
});
const el = document.getElementById('d-balans');
el.textContent = `Duguje: ${fmt(d)} · Potražuje: ${fmt(p)} · Razlika: ${fmt(d-p)}`;
el.className = 'dnev-balans ' + (d===p && d>0 ? 'ok' : 'bad');
}
async function saveDnev(){
const stavke = Array.from(document.querySelectorAll('#d-lines .dnev-line-row')).map(r => ({
konto_id: parseInt(r.querySelector('.d-konto').value),
opis: r.querySelector('.d-opis').value,
duguje: parseFloat(r.querySelector('.d-duguje').value)||0,
potrazuje: parseFloat(r.querySelector('.d-potrazuje').value)||0,
partner_id: parseInt(r.querySelector('.d-partner').value)||null
}));
const body = {
datum: document.getElementById('d-datum').value,
opis: document.getElementById('d-opis').value,
dokument_tip: document.getElementById('d-tip').value,
stavke
};
try {
await api('/dnevnik', { method:'POST', body: JSON.stringify(body) });
closeModal('m-dnev'); loadDnevnik();
} catch(e){ alert(e.message); }
}
// ===== GLAVNA KNJIGA =====
async function loadGlavnaKnjiga(){
const k = document.getElementById('gk-klasa').value;
const p = new URLSearchParams();
if(k) p.set('klasa', k);
const d = await api('/glavna-knjiga?'+p.toString());
const tbody = document.querySelector('#gk-tbl tbody');
tbody.innerHTML = d.rows.map(r=>`<tr>
<td><b>${r.sifra}</b></td><td>${r.naziv}</td><td>${r.klasa}</td><td>${r.vrsta}</td>
<td class="num">${fmt(r.sum_duguje)}</td>
<td class="num">${fmt(r.sum_potrazuje)}</td>
<td class="num"><b>${fmt(r.saldo)}</b></td>
<td class="num">${r.broj_stavki}</td>
</tr>`).join('');
}
// ===== RAČUNI =====
let partnerCache = [];
async function loadPartnerCache(){
if(partnerCache.length) return partnerCache;
const d = await api('/partneri?');
partnerCache = d.rows;
return partnerCache;
}
async function loadRacuni(){
const tip = document.getElementById('rac-tip').value;
const status = document.getElementById('rac-status').value;
const godina = document.getElementById('rac-godina').value;
const p = new URLSearchParams();
if(status) p.set('status', status);
if(godina) p.set('godina', godina);
const d = await api('/racuni/'+tip+'?'+p.toString());
const tbody = document.querySelector('#rac-tbl tbody');
tbody.innerHTML = d.rows.map(r=>`<tr onclick="racDetail('${tip}',${r.id})">
<td>${r.id}</td><td>${r.broj||''}</td><td>${r.datum_izdavanja}</td>
<td>${r.partner_naziv||''}</td><td>${r.partner_oib||''}</td>
<td class="num">${fmt(r.iznos_neto)}</td>
<td class="num">${fmt(r.iznos_pdv)}</td>
<td class="num"><b>${fmt(r.iznos_brutto)}</b></td>
<td><span class="badge ${r.status}">${r.status}</span></td>
<td>${r.status==='nacrt'?`<button class="btn green" onclick="event.stopPropagation();knjizi('${tip}',${r.id})">Knjiži</button>`:''}</td>
</tr>`).join('');
}
async function knjizi(tip, id){
if(!confirm('Knjižiti račun #'+id+' u dnevnik?')) return;
try{ await api('/racuni/'+tip+'/'+id+'/knjizi', {method:'POST'}); loadRacuni(); }
catch(e){ alert(e.message); }
}
async function racDetail(tip, id){
try {
const d = await api('/racuni/'+tip+'/'+id);
document.getElementById('rac-detail').style.display = 'block';
document.getElementById('rac-detail-title').textContent =
`Stavke računa ${tip} · #${id} · ${d.head.broj||'(bez broja)'} · ${d.head.partner_naziv||''} · Neto ${fmt(d.head.iznos_neto)} · Brutto ${fmt(d.head.iznos_brutto)}`;
const sb = document.querySelector('#rac-stavke-tbl tbody');
sb.innerHTML = (d.stavke||[]).length
? d.stavke.map((s,ix)=>`<tr><td>${ix+1}</td><td>${s.naziv||''}</td><td class="num">${fmt(s.kolicina)}</td><td>${s.jed_mjera||''}</td><td class="num">${fmt(s.cijena_jed)}</td><td class="num">${fmt(s.popust_pct)}</td><td class="num">${fmt(s.pdv_pct)}</td><td class="num">${fmt(s.iznos_neto)}</td><td class="num"><b>${fmt(s.iznos_brutto)}</b></td></tr>`).join('')
: `<tr><td colspan="9" style="color:var(--t2);text-align:center;padding:14px">Nema stavki.</td></tr>`;
// For ulazni: show linked invoice_uploads (file_name)
const upT = document.getElementById('rac-uploads-title');
const upW = document.getElementById('rac-uploads-wrap');
if (tip === 'ulazni') {
upT.style.display='block'; upW.style.display='block';
try {
const u = await api('/racuni/ulazni/'+id+'/uploads');
const ub = document.querySelector('#rac-uploads-tbl tbody');
ub.innerHTML = (u.rows||[]).length
? u.rows.map(r=>`<tr><td>${r.id}</td><td><a href="/uploads/${r.file_path||''}" target="_blank">${r.file_name||''}</a></td><td class="num">${fmt((r.file_size||0)/1024)} KB</td><td>${r.mime||''}</td><td>${r.ocr_status||''}</td><td>${(r.uploaded_at||'').slice(0,10)}</td></tr>`).join('')
: `<tr><td colspan="6" style="color:var(--t2);text-align:center;padding:10px">Nema privitaka.</td></tr>`;
} catch(e){
document.querySelector('#rac-uploads-tbl tbody').innerHTML = `<tr><td colspan="6" style="color:var(--red)">Greška: ${e.message}</td></tr>`;
}
} else {
upT.style.display='none'; upW.style.display='none';
}
} catch(e) { alert('Greška: '+e.message); }
}
async function openRacunModal(tip){
await loadPartnerCache();
document.getElementById('m-rac').dataset.tip = tip;
document.getElementById('m-rac-title').textContent = 'Novi ' + tip + ' račun';
document.getElementById('r-broj').value='';
document.getElementById('r-datum').value = new Date().toISOString().slice(0,10);
document.getElementById('r-dospjece').value = '';
document.getElementById('r-status').value = 'nacrt';
document.getElementById('r-partner').innerHTML = partnerCache.map(p=>`<option value="${p.id}">${p.naziv}${p.oib?' · '+p.oib:''}</option>`).join('');
document.getElementById('r-lines').innerHTML='';
addRacLine();
updateRacSummary();
openModal('m-rac');
}
function addRacLine(){
const div = document.createElement('div');
div.className = 'dnev-line-row';
div.style.gridTemplateColumns = '1fr 60px 80px 80px 60px 30px';
div.innerHTML = `
<input class="r-naziv" placeholder="Naziv stavke">
<input type="number" step="0.01" class="r-kol" value="1" oninput="updateRacSummary()">
<input type="number" step="0.01" class="r-cij" placeholder="Cijena" oninput="updateRacSummary()">
<input type="number" step="0.01" class="r-pop" value="0" placeholder="Popust %" oninput="updateRacSummary()">
<input type="number" step="0.01" class="r-pdv" value="25" placeholder="PDV %" oninput="updateRacSummary()">
<button class="btn red" onclick="this.parentElement.remove();updateRacSummary()">×</button>`;
document.getElementById('r-lines').appendChild(div);
}
function updateRacSummary(){
const rows = document.querySelectorAll('#r-lines .dnev-line-row');
let neto=0, pdv=0;
rows.forEach(r => {
const k = parseFloat(r.querySelector('.r-kol').value)||0;
const c = parseFloat(r.querySelector('.r-cij').value)||0;
const pop = parseFloat(r.querySelector('.r-pop').value)||0;
const ppdv = parseFloat(r.querySelector('.r-pdv').value)||0;
const n = k*c*(1-pop/100);
neto += n;
pdv += n*ppdv/100;
});
document.getElementById('r-summary').textContent = `Neto: ${fmt(neto)} · PDV: ${fmt(pdv)} · Brutto: ${fmt(neto+pdv)}`;
}
async function saveRac(){
const tip = document.getElementById('m-rac').dataset.tip;
const stavke = Array.from(document.querySelectorAll('#r-lines .dnev-line-row')).map(r => ({
naziv: r.querySelector('.r-naziv').value,
kolicina: parseFloat(r.querySelector('.r-kol').value)||1,
cijena_jed: parseFloat(r.querySelector('.r-cij').value)||0,
popust_pct: parseFloat(r.querySelector('.r-pop').value)||0,
pdv_pct: parseFloat(r.querySelector('.r-pdv').value)||25,
}));
const body = {
broj: document.getElementById('r-broj').value || null,
partner_id: parseInt(document.getElementById('r-partner').value),
datum_izdavanja: document.getElementById('r-datum').value,
datum_dospjeca: document.getElementById('r-dospjece').value || null,
status: document.getElementById('r-status').value,
stavke
};
try {
await api('/racuni/'+tip, { method:'POST', body: JSON.stringify(body) });
closeModal('m-rac');
loadRacuni();
} catch(e){ alert(e.message); }
}
async function importERacun(){
const f = document.getElementById('eracun-file').files[0];
if(!f) return;
const fd = new FormData();
fd.append('file', f);
const r = await fetch(API+'/racuni/eracun-import', { method:'POST', headers: AUTH(), body: fd });
const d = await r.json();
alert(r.ok ? `e-Račun importiran:\nBroj: ${d.broj}\nNeto: ${fmt(d.neto)} PDV: ${fmt(d.pdv)} Brutto: ${fmt(d.brutto)}` : 'Greška: '+JSON.stringify(d));
document.getElementById('eracun-file').value='';
loadRacuni();
}
// ===== PDV =====
async function loadPdv(){
const g = document.getElementById('pdv-godina').value;
const m = document.getElementById('pdv-mjesec').value;
const params = new URLSearchParams({ godina:g });
if(m) params.set('mjesec', m);
const [u,i,o] = await Promise.all([
api('/pdv/knjiga-u?'+params.toString()),
api('/pdv/knjiga-i?'+params.toString()),
api('/pdv/obrazac?'+params.toString())
]);
document.querySelector('#pdv-u-tbl tbody').innerHTML = u.rows.map(r=>`<tr><td>${r.broj||''}</td><td>${r.datum_izdavanja}</td><td>${r.partner_naziv||''}</td><td>${r.partner_oib||''}</td><td class="num">${fmt(r.iznos_neto)}</td><td class="num">${fmt(r.iznos_pdv)}</td></tr>`).join('');
document.querySelector('#pdv-i-tbl tbody').innerHTML = i.rows.map(r=>`<tr><td>${r.broj||''}</td><td>${r.datum_izdavanja}</td><td>${r.partner_naziv||''}</td><td>${r.partner_oib||''}</td><td class="num">${fmt(r.iznos_neto)}</td><td class="num">${fmt(r.iznos_pdv)}</td></tr>`).join('');
document.getElementById('pdv-summary').innerHTML = `
<div class="kpi"><div class="kpi-l">Pretporez (U)</div><div class="kpi-v">${fmt(o.ulazni.pdv)} €</div></div>
<div class="kpi"><div class="kpi-l">PDV obveza (I)</div><div class="kpi-v">${fmt(o.izlazni.pdv)} €</div></div>
<div class="kpi ${o.obveza_za_uplatu>0?'r':'g'}"><div class="kpi-l">Obveza za uplatu</div><div class="kpi-v">${fmt(o.obveza_za_uplatu)} €</div></div>
<div class="kpi g"><div class="kpi-l">Pretporez za povrat</div><div class="kpi-v">${fmt(o.pretporez_za_povrat)} €</div></div>`;
}
// ===== ZAPOSLENICI + PLAĆE =====
async function loadZap(){
const d = await api('/zaposlenici');
document.querySelector('#zap-tbl tbody').innerHTML = d.rows.map(r=>`<tr>
<td>${r.id}</td><td>${r.oib||'—'}</td><td>${r.ime}</td><td>${r.prezime}</td>
<td>${r.klub_naziv||'—'}</td><td>${r.radno_mjesto||''}</td>
<td class="num">${fmt(r.plata_bruto)}</td><td>${r.aktivan?'✓':'✗'}</td>
</tr>`).join('');
}
async function loadPlace(){
const g = document.getElementById('pl-godina').value;
const m = document.getElementById('pl-mjesec').value;
const p = new URLSearchParams();
if(g)p.set('godina',g); if(m)p.set('mjesec',m);
const d = await api('/place/obracun?'+p.toString());
document.querySelector('#pl-tbl tbody').innerHTML = d.rows.map(r=>`<tr>
<td>${r.id}</td><td>${r.ime} ${r.prezime}</td><td>${r.godina}/${String(r.mjesec).padStart(2,'0')}</td>
<td class="num">${fmt(r.bruto)}</td>
<td class="num">${fmt(r.doprinosi_iz_plate)}</td>
<td class="num">${fmt(r.dohodnina)}</td>
<td class="num"><b>${fmt(r.neto)}</b></td>
<td class="num">${fmt(r.doprinosi_na_plate)}</td>
<td class="num">${fmt(r.ukupni_trosak)}</td>
</tr>`).join('');
}
function openZapModal(){
['z-ime','z-prezime','z-oib','z-klub','z-mjesto','z-bruto','z-iban'].forEach(i=>document.getElementById(i).value='');
openModal('m-zap');
}
async function saveZap(){
const body = {
ime: document.getElementById('z-ime').value,
prezime: document.getElementById('z-prezime').value,
oib: document.getElementById('z-oib').value || null,
klub_id: parseInt(document.getElementById('z-klub').value)||null,
radno_mjesto: document.getElementById('z-mjesto').value || null,
plata_bruto: parseFloat(document.getElementById('z-bruto').value)||0,
iban: document.getElementById('z-iban').value || null,
aktivan: true
};
try { await api('/zaposlenici', { method:'POST', body: JSON.stringify(body) }); closeModal('m-zap'); loadZap(); }
catch(e){ alert(e.message); }
}
async function openPlacaModal(){
const d = await api('/zaposlenici');
document.getElementById('pl-zap').innerHTML = d.rows.map(r=>`<option value="${r.id}">${r.ime} ${r.prezime}</option>`).join('');
openModal('m-pl');
}
async function savePlaca(){
const body = {
zaposlenik_id: parseInt(document.getElementById('pl-zap').value),
godina: parseInt(document.getElementById('pl-god').value),
mjesec: parseInt(document.getElementById('pl-mj').value),
bruto: parseFloat(document.getElementById('pl-bruto').value) || null,
osobni_odbitak: parseFloat(document.getElementById('pl-odb').value)||600,
prirez_pct: parseFloat(document.getElementById('pl-prirez').value)||0,
datum_isplate: document.getElementById('pl-isplata').value || null,
knjizi: document.getElementById('pl-knjizi').checked
};
try {
const r = await api('/place/obracun', { method:'POST', body: JSON.stringify(body) });
alert(`Bruto: ${fmt(r.calc.bruto)}\nDoprinosi iz: ${fmt(r.calc.doprinosi_iz_plate)}\nDohodnina: ${fmt(r.calc.dohodnina)}\nNeto: ${fmt(r.calc.neto)}\nUkupni trošak: ${fmt(r.calc.ukupni_trosak)}`);
closeModal('m-pl'); loadPlace();
} catch(e){ alert(e.message); }
}
// ===== PRORAČUN =====
async function loadProracun(){
const tbody = document.querySelector('#pr-tbl tbody');
tbody.innerHTML = `<tr><td colspan="8" style="color:var(--t2);text-align:center;padding:14px">Učitavam…</td></tr>`;
try {
const d = await api('/proracun');
tbody.innerHTML = (d.rows||[]).length
? d.rows.map(r=>`<tr>
<td><b>${r.godina}</b></td><td class="num">${fmt(r.proracun_pgz)}</td>
<td class="num">${fmt(r.rebalans1)}</td><td class="num">${fmt(r.rebalans2)}</td>
<td class="num"><b>${fmt(r.ukupno_pgz)}</b></td><td class="num">${fmt(r.ministarstvo)}</td>
<td class="num"><b>${fmt(r.ukupno)}</b></td><td>${r.napomena||''}</td>
</tr>`).join('')
: `<tr><td colspan="8" style="color:var(--t2);text-align:center;padding:14px">Nema podataka.</td></tr>`;
} catch(e) {
tbody.innerHTML = `<tr><td colspan="8" style="color:var(--red)">Greška: ${e.message}</td></tr>`;
}
}
// ===== INVOICE UPLOADS =====
async function loadUploads(){
const tbody = document.querySelector('#up-tbl tbody');
tbody.innerHTML = `<tr><td colspan="12" style="color:var(--t2);text-align:center;padding:14px">Učitavam…</td></tr>`;
try {
const q = (document.getElementById('up-q').value||'').trim();
const st = document.getElementById('up-status').value;
const p = new URLSearchParams();
if(q) p.set('q', q);
if(st) p.set('ocr_status', st);
const d = await api('/invoice-uploads?'+p.toString());
tbody.innerHTML = (d.rows||[]).length
? d.rows.map(r=>`<tr>
<td>${r.id}</td>
<td><a href="/uploads/${r.file_path||''}" target="_blank">${r.file_name||''}</a></td>
<td class="num">${fmt((r.file_size||0)/1024)} KB</td>
<td>${r.ai_vendor_name||''}</td>
<td>${r.ai_vendor_oib||''}</td>
<td>${r.ai_invoice_no||''}</td>
<td>${r.ai_invoice_date||''}</td>
<td class="num">${fmt(r.ai_amount_gross)}</td>
<td><span class="badge ${r.ocr_status||''}">${r.ocr_status||'—'}</span></td>
<td class="num">${r.ocr_confidence!=null?fmt(r.ocr_confidence)+' %':''}</td>
<td>${r.invoice_id?('#'+r.invoice_id):'—'}</td>
<td><a class="btn sec" href="/uploads/${r.file_path||''}" target="_blank">Otvori</a></td>
</tr>`).join('')
: `<tr><td colspan="12" style="color:var(--t2);text-align:center;padding:14px">Nema uploadova.</td></tr>`;
} catch(e) {
tbody.innerHTML = `<tr><td colspan="12" style="color:var(--red)">Greška: ${e.message}</td></tr>`;
}
}
// Upload new invoice file via multipart
async function uploadInvoiceFile(file){
if(!file) return;
const prog = document.getElementById('up-progress');
prog.textContent = 'Šaljem ' + file.name + ' (' + Math.round(file.size/1024) + ' KB)…';
try {
const fd = new FormData();
fd.append('file', file);
// Note: no Content-Type header — browser sets multipart boundary
const r = await fetch(API + '/invoice-uploads', {
method:'POST', body: fd, headers: AUTH()
});
if(!r.ok) throw new Error('HTTP ' + r.status + ' ' + (await r.text()));
const j = await r.json();
prog.textContent = '✓ Uploaded #' + j.id + ' (' + Math.round((j.file_size||0)/1024) + ' KB) — OCR pending.';
document.getElementById('up-file').value = '';
loadUploads();
} catch(e) {
prog.textContent = '✗ Greška: ' + e.message;
}
}
// Drag & drop on uploads card
document.addEventListener('DOMContentLoaded', () => {
const drop = document.getElementById('up-drop');
if(!drop) return;
['dragenter','dragover'].forEach(ev => drop.addEventListener(ev, e => {
e.preventDefault(); e.stopPropagation();
drop.style.background='var(--bg2)';
}));
['dragleave','drop'].forEach(ev => drop.addEventListener(ev, e => {
e.preventDefault(); e.stopPropagation();
drop.style.background='var(--bg3)';
}));
drop.addEventListener('drop', e => {
if(e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0]){
uploadInvoiceFile(e.dataTransfer.files[0]);
}
});
});
// ===== PUTNI NALOZI / EXPENSE REPORTS =====
async function loadExpenseReports(){
const tbody = document.querySelector('#pn-tbl tbody');
tbody.innerHTML = `<tr><td colspan="11" style="color:var(--t2);text-align:center;padding:14px">Učitavam…</td></tr>`;
try {
const t = document.getElementById('pn-type').value;
const s = document.getElementById('pn-status').value;
const g = document.getElementById('pn-godina').value;
const p = new URLSearchParams();
if(t) p.set('report_type', t);
if(s) p.set('status', s);
if(g) p.set('godina', g);
const d = await api('/expense-reports?'+p.toString());
tbody.innerHTML = (d.rows||[]).length
? d.rows.map(r=>`<tr onclick="expenseDetail(${r.id})" style="cursor:pointer">
<td>${r.id}</td>
<td>${r.report_type||''}</td>
<td>${r.klub_naziv||r.klub_id||''}</td>
<td>${r.destination||''}</td>
<td>${r.purpose||''}</td>
<td>${r.date_from||''}</td>
<td>${r.date_to||''}</td>
<td class="num">${fmt(r.km_driven)}</td>
<td class="num">${fmt(r.cost_total)}</td>
<td class="num">${fmt(r.dnevnice_amount)}</td>
<td><span class="badge ${r.status||''}">${r.status||''}</span></td>
</tr>`).join('')
: `<tr><td colspan="11" style="color:var(--t2);text-align:center;padding:14px">Nema putnih naloga.</td></tr>`;
} catch(e) {
tbody.innerHTML = `<tr><td colspan="11" style="color:var(--red)">Greška: ${e.message}</td></tr>`;
}
}
async function expenseDetail(id){
try {
document.getElementById('pn-detail').style.display='block';
document.getElementById('pn-detail-title').textContent = `Vezani računi za putni nalog #${id}`;
const d = await api('/putni-nalog-racuni?putni_nalog_id='+id);
const tb = document.querySelector('#pn-rac-tbl tbody');
tb.innerHTML = (d.rows||[]).length
? d.rows.map(r=>`<tr><td>${r.id}</td><td>${r.invoice_no||('#'+r.invoice_id)}</td><td>${r.vendor_name||''}</td><td class="num">${fmt(r.amount_gross)}</td><td>${r.currency||''}</td><td>${r.kategorija||''}</td><td>${(r.attached_at||'').slice(0,10)}</td></tr>`).join('')
: `<tr><td colspan="7" style="color:var(--t2);text-align:center;padding:10px">Nema vezanih računa.</td></tr>`;
} catch(e) { alert('Greška: '+e.message); }
}
// ===== PAYMENTS =====
async function loadPayments(){
const tbody = document.querySelector('#py-tbl tbody');
tbody.innerHTML = `<tr><td colspan="12" style="color:var(--t2);text-align:center;padding:14px">Učitavam…</td></tr>`;
try {
const s = document.getElementById('py-status').value;
const m = document.getElementById('py-method').value;
const g = document.getElementById('py-godina').value;
const p = new URLSearchParams();
if(s) p.set('matched_status', s);
if(m) p.set('payment_method', m);
if(g) p.set('godina', g);
const d = await api('/payments?'+p.toString());
tbody.innerHTML = (d.rows||[]).length
? d.rows.map(r=>`<tr>
<td>${r.id}</td>
<td>${r.payment_date||''}</td>
<td>${r.klub_naziv||r.klub_id||''}</td>
<td class="num"><b>${fmt(r.amount)}</b></td>
<td>${r.currency||''}</td>
<td>${r.payment_method||''}</td>
<td>${r.iban_from||''}</td>
<td>${r.iban_to||''}</td>
<td>${r.reference||''}</td>
<td>${r.invoice_id?('#'+r.invoice_id):'—'}</td>
<td>${r.expense_report_id?('#'+r.expense_report_id):'—'}</td>
<td><span class="badge ${r.matched_status||''}">${r.matched_status||''}</span></td>
</tr>`).join('')
: `<tr><td colspan="12" style="color:var(--t2);text-align:center;padding:14px">Nema plaćanja.</td></tr>`;
} catch(e) {
tbody.innerHTML = `<tr><td colspan="12" style="color:var(--red)">Greška: ${e.message}</td></tr>`;
}
}
// ===== IZVJEŠTAJI =====
async function loadIzvjestaj(){
const tip = document.getElementById('iz-tip').value;
const g = document.getElementById('iz-godina').value;
const d = await api('/izvjestaji/'+tip+'?godina='+g);
const out = document.getElementById('iz-out');
if(tip==='bilanca'){
out.innerHTML = `
<div class="kpi-grid">
<div class="kpi g"><div class="kpi-l">Ukupno aktiva</div><div class="kpi-v">${fmt(d.ukupno_aktiva)} €</div></div>
<div class="kpi"><div class="kpi-l">Ukupno pasiva</div><div class="kpi-v">${fmt(d.ukupno_pasiva)} €</div></div>
<div class="kpi ${d.balans_ok?'g':'r'}"><div class="kpi-l">Balans</div><div class="kpi-v">${d.balans_ok?'✓ OK':'✗ neusklađen'}</div></div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px">
<div><h4 style="font-size:12px;color:var(--t2);margin-bottom:6px">AKTIVA</h4>
<div class="tbl-wrap"><table><thead><tr><th>Šifra</th><th>Naziv</th><th class="num">Saldo</th></tr></thead>
<tbody>${d.aktiva.map(r=>`<tr><td>${r.sifra}</td><td>${r.naziv}</td><td class="num">${fmt(r.saldo)}</td></tr>`).join('')}</tbody></table></div></div>
<div><h4 style="font-size:12px;color:var(--t2);margin-bottom:6px">PASIVA</h4>
<div class="tbl-wrap"><table><thead><tr><th>Šifra</th><th>Naziv</th><th class="num">Saldo</th></tr></thead>
<tbody>${d.pasiva.map(r=>`<tr><td>${r.sifra}</td><td>${r.naziv}</td><td class="num">${fmt(r.saldo)}</td></tr>`).join('')}</tbody></table></div></div>
</div>`;
} else if(tip==='pnl'){
out.innerHTML = `
<div class="kpi-grid">
<div class="kpi g"><div class="kpi-l">Prihodi</div><div class="kpi-v">${fmt(d.ukupno_prihodi)} €</div></div>
<div class="kpi r"><div class="kpi-l">Rashodi</div><div class="kpi-v">${fmt(d.ukupno_rashodi)} €</div></div>
<div class="kpi ${d.rezultat>=0?'g':'r'}"><div class="kpi-l">Rezultat (${d.tip_rezultata})</div><div class="kpi-v">${fmt(d.rezultat)} €</div></div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:14px">
<div><h4 style="font-size:12px;color:var(--t2);margin-bottom:6px">PRIHODI</h4>
<div class="tbl-wrap"><table><thead><tr><th>Šifra</th><th>Naziv</th><th class="num">Iznos</th></tr></thead>
<tbody>${d.prihodi.map(r=>`<tr><td>${r.sifra}</td><td>${r.naziv}</td><td class="num">${fmt(r.iznos)}</td></tr>`).join('')}</tbody></table></div></div>
<div><h4 style="font-size:12px;color:var(--t2);margin-bottom:6px">RASHODI</h4>
<div class="tbl-wrap"><table><thead><tr><th>Šifra</th><th>Naziv</th><th class="num">Iznos</th></tr></thead>
<tbody>${d.rashodi.map(r=>`<tr><td>${r.sifra}</td><td>${r.naziv}</td><td class="num">${fmt(r.iznos)}</td></tr>`).join('')}</tbody></table></div></div>
</div>`;
} else {
out.innerHTML = `
<div class="tbl-wrap"><table><thead><tr><th>Mjesec</th><th class="num">Uplate</th><th class="num">Isplate</th><th class="num">Net</th></tr></thead>
<tbody>${d.po_mjesecu.map(r=>`<tr><td>${r.mjesec}</td><td class="num">${fmt(r.uplate)}</td><td class="num">${fmt(r.isplate)}</td><td class="num"><b>${fmt(Number(r.uplate)-Number(r.isplate))}</b></td></tr>`).join('')}</tbody></table></div>`;
}
}
function exportXlsx(report, godina, mjesec){
let url = API+'/export/xlsx/'+report+'?godina='+godina;
if(mjesec) url += '&mjesec='+mjesec;
window.open(url, '_blank');
}
function exportPdf(report, godina){
window.open(API+'/export/pdf/'+report+'?godina='+godina, '_blank');
}
// Lazy loaders per panel
const loaders = {
dnevnik: loadDnevnik,
glavna: loadGlavnaKnjiga,
partneri: loadPartneri,
racuni: loadRacuni,
uploads: loadUploads,
putni: loadExpenseReports,
payments: loadPayments,
pdv: loadPdv,
place: () => { loadZap(); loadPlace(); },
proracun: loadProracun,
izvjestaji: loadIzvjestaj,
kontni: loadKontniPlan
};
// Switch programmatically (used by deep links: ?tab=uploads / #tab=putni)
function activateTab(panelId){
const t = document.querySelector('.tab[data-panel="' + panelId + '"]');
if(!t) return false;
t.click();
return true;
}
// Initial
document.addEventListener('DOMContentLoaded', () => {
loadKontoCache();
loadPartnerCache();
// Deep-link support: ?tab=<panel> or #tab=<panel>
let target = null;
try {
const u = new URL(window.location.href);
target = u.searchParams.get('tab');
if(!target && u.hash){
const m = u.hash.match(/tab=([a-z]+)/i);
if(m) target = m[1];
}
} catch(e) {}
if(target && activateTab(target)){
// tab.click() already triggers loader
return;
}
loadDnevnik();
});
// ── Universal Export ▾ — wired to representative ERP tabs (racuni,
// putni nalozi, payments). Uses live filter values so the exported
// rows match what's on screen.
document.addEventListener('DOMContentLoaded', function(){
if (!window.attachExportDropdown) return;
const racBtn = document.getElementById('rac-export-btn');
if (racBtn) window.attachExportDropdown(racBtn, function(){
const tip = (document.getElementById('rac-tip')||{}).value || 'ulazni';
const status = (document.getElementById('rac-status')||{}).value || '';
const god = (document.getElementById('rac-godina')||{}).value || '';
const qp = new URLSearchParams(); qp.set('limit','2000');
if (status) qp.set('status', status);
if (god) qp.set('godina', god);
return '/api/v2/erp/racuni/'+tip+'?'+qp.toString();
}, 'racuni');
const pnBtn = document.getElementById('pn-export-btn');
if (pnBtn) window.attachExportDropdown(pnBtn, function(){
const t = (document.getElementById('pn-type')||{}).value || '';
const s = (document.getElementById('pn-status')||{}).value || '';
const g = (document.getElementById('pn-godina')||{}).value || '';
const qp = new URLSearchParams(); qp.set('limit','2000');
if (t) qp.set('tip', t);
if (s) qp.set('status', s);
if (g) qp.set('godina', g);
return '/api/v2/erp/expense-reports?'+qp.toString();
}, 'expense_reports');
const pyBtn = document.getElementById('py-export-btn');
if (pyBtn) window.attachExportDropdown(pyBtn, function(){
const s = (document.getElementById('py-status')||{}).value || '';
const m = (document.getElementById('py-method')||{}).value || '';
const g = (document.getElementById('py-godina')||{}).value || '';
const qp = new URLSearchParams(); qp.set('limit','2000');
if (s) qp.set('status', s);
if (m) qp.set('metoda', m);
if (g) qp.set('godina', g);
return '/api/v2/erp/payments?'+qp.toString();
}, 'payments');
});
</script>
<script src="/static/js/export_dropdown.js"></script>
</body>
</html>