FEB 2026
Ingresos totales
15.100 €
Costes totales
-14.008 €
Resultado neto
1092 €
Margen
7.2%
Por departamento
Reparto
-1856 €
Ingresos2978 €
Personal-4316 €
Costes directos0 €
Admin (parte)-519 €
Facturado vs cobrado
Facturado (CSV)2978 €
Cobrado (banco)2991 €
100% cobrado
Ingresos por cliente 2978 €
ClienteImporte
EatyJet - Some Like It Hot 156 entregas 1875,55 €
Dottor Pizza 36 entregas 400,71 €
Dolomiti 14 entregas 158,86 €
Burger House 13 entregas 158,43 €
El 7 17 entregas 150,91 €
Pollo Bueno 5 entregas 52,79 €
El Txoko 4 entregas 45,33 €
Gasteiz Courier 31 entregas 29,62 €
EatyJet 3 entregas 29,04 €
Paneko Vitoria 3 entregas 26,88 €
Güre Poke 2 entregas 20,93 €
Carniceria Apellaniz Harategiak 2 entregas 18,76 €
Hong Kong 1 entregas 10,25 €
Total2978,06 €
Coste de personal (nóminas) -4315,66 €
Trabajador/aHoras / NetCoste real
NAVARRO FIGUEROA
Adonay Navarro
90.3h (85%) | net: 1947,00 € -2477,30 €
BOSQUET ETIENNE DIDIER JULES
Etienne Bosquet
2.4h (2%) | net: 1098,00 € -38,33 €
DE CASTRO ALMEIDA
Gonzalo gonzalo
13.6h (13%) | net: 687,36 € -128,59 €
LUIZ MENUZZI
Benjamín Luiz
54.7h (100%) | net: 530,74 € -777,38 €
HAYBOR WALI
Walid Haybor
47.3h (100%) | net: 356,56 € -522,27 €
PÉREZ RODRÍGUEZ
José Pérez
59.8h (100%) | net: 153,76 € -223,09 €
DJBLI ISSAM
Issam Djebli
42.9h (100%) | net: 102,50 € -148,70 €
Total real-4315,66 €
Admin (parte asignada) -168,63 €
33% del pool total de -160,00 € = -168,63 €
Ver desglose completo en la sección inferior ↓
Comercio Local
1151 €
Ingresos5224 €
Personal-3555 €
Costes directos0 €
Admin (parte)-519 €
Facturado vs cobrado
Facturado (CSV)2428 €
Cobrado (banco)2796 €
100% cobrado
Ingresos por cliente 2428 €
ClienteImporte
Gestoria ABOACER 11 entregas 616,81 €
Olaritxugestoria 21 entregas 499,72 €
Irtenbide 33 entregas 309,36 €
La Huerta de Jose Mari 30 entregas 273,14 €
Auzoko Gestion de Comunidades 10 entregas 244,94 €
Gestoria Inmoley 16 entregas 172,23 €
SEUR 6 entregas 116,40 €
Gestoria Finetxalde 4 entregas 86,40 €
Kerkus Florista 4 entregas 40,50 €
IKA 1 entregas 21,78 €
Elvira 2 entregas 20,25 €
Kirolkak 2 entregas 14,40 €
Quirón 1 entregas 11,98 €
Total2427,91 €
Coste de personal (nóminas) -3113,03 €
Trabajador/aHoras / NetCoste real
NAVARRO FIGUEROA
Adonay Navarro
16.0h (15%) | net: 1947,00 € -438,80 €
BOSQUET ETIENNE DIDIER JULES
Etienne Bosquet
98.0h (98%) | net: 1098,00 € -1552,27 €
PLA TORRES
Peter Pla Pla
15.3h (16%) | net: 1087,45 € -253,33 €
DE CASTRO ALMEIDA
Gonzalo gonzalo
91.7h (87%) | net: 687,36 € -868,63 €
Total real-3113,03 €
Admin (parte asignada) -168,63 €
33% del pool total de -160,00 € = -168,63 €
Ver desglose completo en la sección inferior ↓
Bizieskola
1797 €
Ingresos6898 €
Personal-4566 €
Costes directos0 €
Admin (parte)-534 €
Ingresos por cliente
ClienteImporte
Sin ingresos registrados
Coste de personal (nóminas) -4374,21 €
Trabajador/aHoras / NetCoste real
PLA TORRES
Peter Pla Pla
80.0h (84%) | net: 1087,45 € -1321,99 €
EGAÑA DE CELIS
Maider Egaña
80.5h (100%) | net: 1046,59 € -1532,98 €
PORTILLA TXURRUKA
Unai Unai
78.8h (100%) | net: 1047,16 € -1519,24 €
Total real-4374,21 €
Admin (parte asignada) -173,74 €
34% del pool total de -160,00 € = -173,74 €
Ver desglose completo en la sección inferior ↓
Proyectos
0 €
Ingresos0 €
Personal0 €
Costes directos0 €
Admin (parte)0 €
Ingresos por cliente
ClienteImporte
Sin ingresos registrados
Coste de personal (nóminas)
Trabajador/aHoras / NetCoste real
Sin nóminas este mes
Admin (parte asignada)
Sin costes admin asignados
Costes fijos compartidos
Pool admin — personal + costes fijos (distribuido entre departamentos) -511,00 €
Concepto / Trabajador/aDetalleImporte
Auri Auri 26.0h × 13,50 €/h -351,00 €
AGUAS MUNICIPALES admin -84,37 €
RECIBO IZARKOM KOOP. ELK admin -56,25 €
AGUAS MUNICIPALES admin -19,38 €
Total pool admin-511,00 €
${rows}
#Cliente BaseTotal+IVA EstadoEmisión
`; } app.innerHTML = summaryBar + `
Por departamento
${deptCards}
Detalle de facturas
` + tables; } catch(e) { app.innerHTML = `
Error: ${e.message}
`; } } function toggleFactura(id) { const row = document.getElementById('fdetail-'+id); if (row) row.style.display = row.style.display === 'none' ? 'table-row' : 'none'; } async function markPaid(id, e) { e.stopPropagation(); await fetch('/api/facturas/'+id, { method: 'PATCH', headers: {'Content-Type':'application/json','Authorization':'Bearer '+tok}, body: JSON.stringify({status:'paid', payment_date: new Date().toISOString().split('T')[0]}) }); renderFacturas(); } async function markUnpaid(id, e) { e.stopPropagation(); await fetch('/api/facturas/'+id, { method: 'PATCH', headers: {'Content-Type':'application/json','Authorization':'Bearer '+tok}, body: JSON.stringify({status:'unpaid'}) }); renderFacturas(); } document.getElementById('btn-facturas')?.addEventListener('click', () => { view = 'facturas'; document.getElementById('btn-monthly').classList.remove('active'); document.getElementById('btn-hours').classList.remove('active'); document.getElementById('btn-costs').classList.remove('active'); document.getElementById('btn-facturas').classList.add('active'); renderFacturas(); }); // Allocation Modal const ALLOC_DEPTS = [ {k:'food_delivery', l:'Reparto', c:'food'}, {k:'local_commerce', l:'Comercio Local', c:'local'}, {k:'teaching', l:'Bizieskola', c:'teach'}, {k:'projects', l:'Proyectos', c:'proj'}, ]; function openAllocModal(factura) { document.getElementById('alloc-modal')?.remove(); const defaultMonth = factura.month || months[idx]; const yr = parseInt(defaultMonth.split('-')[0]); const years = [yr-1, yr, yr+1]; const MONTHS_LABELS = ['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic']; const selectedMonths = new Set([defaultMonth]); const deptSplit = {}; if (factura.dept && factura.dept !== 'unknown') { ALLOC_DEPTS.forEach(d => deptSplit[d.k] = d.k === factura.dept ? 100 : 0); } else { ALLOC_DEPTS.forEach(d => deptSplit[d.k] = 25); } window._allocState = {deptSplit, selectedMonths, factura, years, MONTHS_LABELS}; const sliders = ALLOC_DEPTS.map(d => `
${d.l} ${Math.round(deptSplit[d.k])}%
`).join(''); const monthGrid = years.map(y => `
${y}
${MONTHS_LABELS.map((ml, i) => { const mkey = y+'-'+String(i+1).padStart(2,'0'); const sel = mkey === defaultMonth; return ``; }).join('')}
`).join(''); const modal = document.createElement('div'); modal.id = 'alloc-modal'; modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,.7);z-index:9999;display:flex;align-items:center;justify-content:center'; modal.innerHTML = `
Asignar coste / ingreso
#${factura.inv_num} · ${(factura.client||'sin cliente').substring(0,35)} · ${fmtD(factura.base)}
Por departamento
${sliders}
Total: 100%
Meses
${monthGrid}
`; document.body.appendChild(modal); } function updateSlider(deptKey, val) { const state = window._allocState; if (!state) return; const oldVal = state.deptSplit[deptKey]; const newVal = parseInt(val); const diff = newVal - oldVal; state.deptSplit[deptKey] = newVal; const others = ALLOC_DEPTS.filter(d => d.k !== deptKey); const otherTotal = others.reduce((s,d) => s + state.deptSplit[d.k], 0); if (otherTotal > 0) others.forEach(d => { state.deptSplit[d.k] = Math.max(0, state.deptSplit[d.k] - (state.deptSplit[d.k] / otherTotal) * diff); }); const total = ALLOC_DEPTS.reduce((s,d) => s + state.deptSplit[d.k], 0); if (total > 0) ALLOC_DEPTS.forEach(d => { state.deptSplit[d.k] = state.deptSplit[d.k] / total * 100; }); ALLOC_DEPTS.forEach(d => { const el = document.getElementById('pct-'+d.k); const sl = document.getElementById('slider-'+d.k); if (el) el.textContent = Math.round(state.deptSplit[d.k]) + '%'; if (sl && d.k !== deptKey) sl.value = Math.round(state.deptSplit[d.k]); }); const t = document.getElementById('total-pct'); const g = Math.round(ALLOC_DEPTS.reduce((s,d) => s + state.deptSplit[d.k], 0)); if (t) { t.textContent = g + '%'; t.style.color = g === 100 ? 'var(--green)' : 'var(--red)'; } } function toggleMonth(mkey) { const state = window._allocState; if (!state) return; if (state.selectedMonths.has(mkey)) { if (state.selectedMonths.size > 1) state.selectedMonths.delete(mkey); } else { state.selectedMonths.add(mkey); } state.years.forEach(y => { for (let mo = 1; mo <= 12; mo++) { const k = y+'-'+String(mo).padStart(2,'0'); const btn = document.getElementById('mb-'+k); if (!btn) continue; const sel = state.selectedMonths.has(k); btn.style.background = sel ? 'var(--amber)' : 'var(--bg3)'; btn.style.color = sel ? '#000' : 'var(--muted)'; btn.style.borderColor = sel ? 'var(--amber)' : 'var(--border)'; } }); } async function saveAllocation(facturaId) { const state = window._allocState; if (!state) return; const allocations = []; for (const month of state.selectedMonths) { for (const d of ALLOC_DEPTS) { if (state.deptSplit[d.k] > 0.5) { allocations.push({month, dept: d.k, pct: Math.round(state.deptSplit[d.k]*10)/10, amount: Math.round(state.factura.base * state.deptSplit[d.k] / 100 * 100)/100, type:'cost'}); } } } const r = await fetch('/api/factura-allocations', {method:'POST', headers:{'Content-Type':'application/json','Authorization':'Bearer '+tok}, body: JSON.stringify({factura_id:facturaId, allocations})}); const res = await r.json(); if (res.ok) { document.getElementById('alloc-modal').remove(); renderFacturas(); } }