@@ -381,7 +434,7 @@ async function loadKlubovi() {
.filter(k => k.naziv)
.sort((a,b) => a.naziv.localeCompare(b.naziv,'hr'))
.map(k => `
`).join('');
- ['oc_klub','pn_klub'].forEach(id => { const e=$('#'+id); if (e) e.innerHTML=opts; });
+ ['oc_klub','pn_klub','st_klub'].forEach(id => { const e=$('#'+id); if (e) e.innerHTML=opts; });
}
let ocrUploadId = null, ocrParsed = null;
@@ -523,15 +576,106 @@ function pnInit() {
});
}
+const _bulkSel = new Set();
+function bulkUpdateUI() {
+ const n = _bulkSel.size;
+ const c = document.getElementById('bulk_count'); if (c) c.textContent = n;
+ ['bulk_pay_btn','bulk_cancel_btn'].forEach(id => { const b=document.getElementById(id); if (b) b.disabled = n===0; });
+ document.querySelectorAll('.inv_chk').forEach(cb => cb.checked = _bulkSel.has(parseInt(cb.dataset.id)));
+ const all = document.getElementById('bulk_all'); if (all) all.checked = n>0 && document.querySelectorAll('.inv_chk').length === n;
+}
+function bulkToggle(id, on) { if (on) _bulkSel.add(id); else _bulkSel.delete(id); bulkUpdateUI(); }
+function bulkSelectAll(on) {
+ _bulkSel.clear();
+ if (on) document.querySelectorAll('.inv_chk').forEach(cb => _bulkSel.add(parseInt(cb.dataset.id)));
+ bulkUpdateUI();
+}
+function bulkClear() { _bulkSel.clear(); bulkUpdateUI(); }
+
async function loadInvoices() {
- const r = await fetch(`${ERP_API}/invoices?limit=50`, {headers: AUTH_HDR()}).then(r=>r.json()).catch(()=>null);
+ const r = await fetch(`${ERP_API}/invoices?limit=200`, {headers: AUTH_HDR()}).then(r=>r.json()).catch(()=>null);
if (!r || !r.rows) return;
+ _bulkSel.clear();
$('#invTable tbody').innerHTML = r.rows.length ? r.rows.map(i=>`
-
| ${i.id} | ${i.invoice_kind||'—'} | ${i.invoice_no||'—'} |
- ${i.vendor_name||'—'} | ${i.vendor_oib||'—'} |
- ${i.klub_naziv||'—'} | ${fmtEur(i.amount_gross)} |
- ${sBadge(i.payment_status)} | ${fmtDate(i.invoice_date)} |
`).join('')
- : '
| Nema podataka |
';
+
+ |
+ ${i.id} |
+ ${i.invoice_kind||'—'} |
+ ${i.invoice_no||'—'} |
+ ${i.vendor_name||'—'} |
+ ${i.vendor_oib||'—'} |
+ ${i.klub_naziv||'—'} |
+ ${fmtEur(i.amount_gross)} |
+ ${sBadge(i.payment_status)} |
+ ${fmtDate(i.invoice_date)} |
+
`).join('')
+ : '
| Nema podataka |
';
+ bulkUpdateUI();
+ const exp = document.getElementById('inv_export_btn');
+ if (exp) exp.href = `${ERP_API}/export/invoices.xlsx`;
+}
+
+function openBulkPay() {
+ if (!_bulkSel.size) return;
+ $('#bulkPayList').textContent = `Računi: #${[..._bulkSel].sort((a,b)=>a-b).join(', #')}`;
+ $('#bp_date').value = new Date().toISOString().substring(0,10);
+ $('#bp_method').value = 'transfer';
+ $('#bulkPayStatus').textContent = '';
+ openModal('bulkPayModal');
+ $('#bulkPayConfirm').onclick = async () => {
+ const body = {
+ ids: [..._bulkSel],
+ paid_date: $('#bp_date').value,
+ payment_method: $('#bp_method').value,
+ iban_from: $('#bp_iban_from').value.trim(),
+ reference: $('#bp_ref').value.trim(),
+ };
+ $('#bulkPayStatus').textContent = '⏳';
+ const r = await fetch(`${ERP_API}/invoices/bulk-pay`, {method:'POST', headers: AUTH_HDR_JSON(), body: JSON.stringify(body)}).then(r=>r.json()).catch(()=>null);
+ if (r && r.ok) {
+ $('#bulkPayStatus').innerHTML = `✓ paid:${r.summary.paid} skipped:${r.summary.skipped} forbidden:${r.summary.forbidden}`;
+ $('#bulkPayStatus').style.color = 'var(--green)';
+ setTimeout(() => { closeModal('bulkPayModal'); loadInvoices(); }, 1200);
+ } else $('#bulkPayStatus').textContent = '❌ Greška';
+ };
+}
+
+async function bulkCancel() {
+ if (!_bulkSel.size) return;
+ const reason = prompt('Razlog otkazivanja za ' + _bulkSel.size + ' računa:', 'duplikat / pogrešan upis');
+ if (reason === null) return;
+ const r = await fetch(`${ERP_API}/invoices/bulk-cancel`, {method:'POST', headers: AUTH_HDR_JSON(), body: JSON.stringify({ids:[..._bulkSel], razlog:reason})}).then(r=>r.json()).catch(()=>null);
+ if (r && r.ok) { alert(`Otkazano: ${r.summary.cancelled}, preskočeno: ${r.summary.skipped}, zabrana: ${r.summary.forbidden}`); loadInvoices(); }
+ else alert('Greška pri otkazivanju.');
+}
+
+// === STATS (R5.6) ===
+async function loadStats() {
+ const klub = $('#st_klub')?.value || '';
+ const url = `${ERP_API}/stats${klub ? '?klub_id='+klub : ''}`;
+ const r = await fetch(url, {headers: AUTH_HDR()}).then(r=>r.json()).catch(()=>null);
+ if (!r || !r.ok) return;
+ const inv = r.invoices;
+ const card = (label, period, accent) => `
+
+
${label}
+
€${period.total.toLocaleString('hr-HR')}
+
${period.n} računa · plaćeno €${period.paid.toLocaleString('hr-HR')} · neplaćeno €${period.unpaid.toLocaleString('hr-HR')}
+
od ${period.since}
+ ${period.by_kind && period.by_kind.length ? '
' + period.by_kind.slice(0,4).map(k => `${k.invoice_kind||'?'}: €${Math.round(k.total)}`).join(' · ') + '
' : ''}
+
`;
+ $('#stats_grid').innerHTML = card('Mjesec', inv.month, 'var(--accent)') + card('Kvartal', inv.quarter, 'var(--yellow)') + card('Godina', inv.year, 'var(--green)');
+ $('#st_top_table tbody').innerHTML = (r.top_klubovi_godina||[]).map(t => `
+
| ${escHtml(t.klub_naziv||'—')} | ${t.n} | ${fmtEur(t.total)} |
`).join('') || '
| Nema podataka |
';
+ const pn = r.putni_nalozi;
+ const pnCard = (label, period, accent) => `
+
+
${label}
+
€${period.total.toLocaleString('hr-HR')}
+
${period.n} naloga · dnevnice €${Math.round(period.dnevnice||0)} · transport €${Math.round(period.transport||0)}
+
`;
+ $('#st_pn').innerHTML = pnCard('Mjesec', pn.month, 'var(--accent)') + pnCard('Kvartal', pn.quarter, 'var(--yellow)') + pnCard('Godina', pn.year, 'var(--green)');
+ const exp = $('#st_export'); if (exp) exp.href = `${ERP_API}/export/invoices.xlsx${klub?'?klub_id='+klub:''}`;
}
async function loadPutni() {
@@ -780,6 +924,7 @@ async function openPutni(id) {
if (a.approve) acts.push(`
`);
if (a.reject) acts.push(`
`);
if (a.pay) acts.push(`
`);
+ acts.push(`
📄 HUB-3 uplatnica (PDF)`);
if (a.edit) acts.push(`
`);
if (!acts.length) acts.push('
Bez dostupnih akcija (samo pregled).');
$('#pn_actions').innerHTML = acts.join('');
@@ -839,19 +984,22 @@ function openPayPnModal(id) {
}
function activate(name) {
- $$('.nav-item').forEach(n => n.classList.toggle('active', n.dataset.tab === name));
+ if (!name) return;
+ $$('.nav-item').forEach(n => n.classList.toggle('active', n.dataset && n.dataset.tab === name));
$$('.tab').forEach(t => t.classList.toggle('active', t.id === 'tab-' + name));
- const titles = {ocr:'Skeniraj račun (OCR)',invoices:'Računi',putni:'Novi putni nalog','putni-list':'Lista putnih naloga'};
+ const titles = {stats:'Statistika',ocr:'Skeniraj račun (OCR)',invoices:'Računi',putni:'Novi putni nalog','putni-list':'Lista putnih naloga'};
$('#pageTitle').textContent = titles[name] || name;
+ if (name === 'stats') loadStats();
if (name === 'invoices') loadInvoices();
if (name === 'putni-list') loadPutni();
}
-$$('.nav-item').forEach(n => n.addEventListener('click', () => activate(n.dataset.tab)));
+$$('.nav-item').forEach(n => { if (n.dataset && n.dataset.tab) n.addEventListener('click', () => activate(n.dataset.tab)); });
(async () => {
await loadKlubovi();
ocrInit();
pnInit();
+ loadStats();
})();