Taller Estética Automotriz / Gestión
🚗 Trabajos del mes
0
Sin datos
💵 Facturado del mes
$0
0 entregados
🛠️ En taller
0
activos
📈 Ganancia del mes
$0
Facturación − Costos

Trabajos

FechaSucursalClienteVehículoTrabajos TotalSaldo EstadoAcciones

Empleados y sueldos

NombreRolTeléfono Valor horaHoras del mes A pagarAcciones
💸 Gastos del mes
$0
0 registros
📅 Gastos del año
$0
0 registros
🔩 Repuestos mes
$0
Categoría principal
📊 Promedio diario
$0
Mes actual

Gastos

FechaDescripciónCategoría MontoAcciones

Ventas del período

FechaN° OrdenClienteVehículo TotalGanancia

Estado de Resultados

📈 Ingresos (órdenes entregadas) $0
📋 Costo de repuestos/materiales ($0)
Ganancia bruta $0
💸 Gastos operativos ($0)
👷 Sueldos ($0)
💰 RESULTADO NETO $0
Órdenes entregadas
0
Ticket promedio
$0
Margen neto
Saldo por cobrar
$0

⚙️ Configuración

🏢 Sucursales
Configurá las sucursales del taller. Se usan al cargar los trabajos y para filtrar por ubicación.
💼 Datos del taller
'; var blob = new Blob(['\ufeff', html], { type: 'application/msword' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'Presupuesto_'+o.num+'_'+o.cliente.replace(/[^a-z0-9]/gi,'_')+'.doc'; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } // ════════════════════════════════════════ // CATÁLOGO // ════════════════════════════════════════ function setFiltroCat(c){ filtroCat = c; document.querySelectorAll('#filtros-catalogo .filtro').forEach(b => b.classList.toggle('on', b.dataset.cat === c)); renderCatalogo(); } function renderCatalogo(){ var lista = catalogo.slice(); if(filtroCat !== 'todos') lista = lista.filter(p => p.categoria === filtroCat); var tb = document.getElementById('tbody-catalogo'); if(!lista.length){ tb.innerHTML = 'Sin ítems en el catálogo.'; return; } tb.innerHTML = lista.map(p => { var idx = catalogo.indexOf(p); var gan = p.precio - p.costo; var marg = p.precio > 0 ? Math.round(gan/p.precio*100) : 0; var catCls = ({'Chapa y pintura':'cat-cha', 'Mecánica':'cat-rep', 'Pulido':'cat-cha', 'Cerámico':'cat-ele', 'Sacabollos':'cat-rep', 'Retoques':'cat-cha', 'Polarizado':'cat-otros', 'Ploteo':'cat-ele', 'Limpieza':'cat-rep', 'Repuesto':'cat-otros', 'Otros':'cat-otros'})[p.categoria] || 'cat-otros'; var margCls = marg >= 30 ? 'est-entreg' : marg >= 15 ? 'est-term' : 'est-pre'; return '' +''+p.nombre+''+(p.desc?'
'+p.desc+'':'')+'' +''+p.categoria+'' +''+(p.categoria === 'Repuesto' || p.categoria === 'Otros' ? 'Producto' : 'Servicio')+'' +''+fmt(p.costo)+'' +''+fmt(p.precio)+'' +''+fmt(gan)+'' +''+marg+'%' +'' +' ' +'' +''; }).join(''); } function abrirModalProd(idx){ editIdxProd = idx != null ? idx : null; var p = editIdxProd != null ? catalogo[editIdxProd] : {}; document.getElementById('mc-tit').textContent = editIdxProd != null ? 'Editar ítem' : 'Nuevo ítem'; document.getElementById('mc-nombre').value = p.nombre || ''; document.getElementById('mc-cat').value = p.categoria || 'Reparación'; document.getElementById('mc-costo').value = p.costo || ''; document.getElementById('mc-precio').value = p.precio || ''; document.getElementById('mc-desc').value = p.desc || ''; document.getElementById('ov-prod').classList.add('on'); } function cerrarModalProd(){ document.getElementById('ov-prod').classList.remove('on'); editIdxProd = null; } function guardarProd(){ var n = document.getElementById('mc-nombre').value.trim(); if(!n){ alert('Ingresá el nombre.'); return; } var p = { id: editIdxProd != null ? catalogo[editIdxProd].id : Date.now(), nombre: n, categoria: document.getElementById('mc-cat').value, costo: parseFloat(document.getElementById('mc-costo').value) || 0, precio: parseFloat(document.getElementById('mc-precio').value) || 0, desc: document.getElementById('mc-desc').value.trim() }; if(editIdxProd != null) catalogo[editIdxProd] = p; else catalogo.push(p); save(); cerrarModalProd(); renderCatalogo(); } function elimProd(i){ if(!confirm('¿Eliminar '+catalogo[i].nombre+'?')) return; catalogo.splice(i,1); save(); renderCatalogo(); } // ════════════════════════════════════════ // SUELDOS (simple) // ════════════════════════════════════════ function renderSueldos(){ var tb = document.getElementById('tbody-sueldos'); if(!empleados.length){ tb.innerHTML = 'Sin empleados. Agregá el primero con "+ Nuevo empleado".'; return; } var hoy = new Date(); var mesPref = hoy.getFullYear()+'-'+String(hoy.getMonth()+1).padStart(2,'0'); tb.innerHTML = empleados.map((e,i) => { var hsMes = 0; Object.keys(horas).forEach(f => { if(f.startsWith(mesPref) && horas[f] && horas[f][e.id]) hsMes += horas[f][e.id]||0; }); var pagar = hsMes * (e.vhora||0); return '' +''+e.nombre+'' +''+(e.rol||'—')+'' +''+(e.tel||'—')+'' +''+fmt(e.vhora)+'/hr' +''+hsMes.toFixed(1)+'h' +''+fmt(pagar)+'' +' ' +''; }).join(''); } function abrirModalEmp(idx){ editIdxEmp = idx != null ? idx : null; var e = editIdxEmp != null ? empleados[editIdxEmp] : {}; document.getElementById('me-tit').textContent = editIdxEmp != null ? 'Editar empleado' : 'Nuevo empleado'; document.getElementById('me-nombre').value = e.nombre || ''; document.getElementById('me-rol').value = e.rol || ''; document.getElementById('me-tel').value = e.tel || ''; document.getElementById('me-vhora').value = e.vhora || ''; document.getElementById('ov-emp').classList.add('on'); } function cerrarModalEmp(){ document.getElementById('ov-emp').classList.remove('on'); editIdxEmp = null; } function guardarEmp(){ var n = document.getElementById('me-nombre').value.trim(); if(!n){ alert('Ingresá el nombre.'); return; } var e = { id: editIdxEmp != null ? empleados[editIdxEmp].id : Date.now(), nombre: n, rol: document.getElementById('me-rol').value.trim(), tel: document.getElementById('me-tel').value.trim(), vhora: parseFloat(document.getElementById('me-vhora').value) || 0 }; if(editIdxEmp != null) empleados[editIdxEmp] = e; else empleados.push(e); save(); cerrarModalEmp(); renderSueldos(); } function elimEmp(i){ if(!confirm('¿Eliminar a '+empleados[i].nombre+'?')) return; empleados.splice(i,1); save(); renderSueldos(); } // ════════════════════════════════════════ // GASTOS // ════════════════════════════════════════ function renderGastos(){ var hoy = new Date(); var mesPref = hoy.getFullYear()+'-'+String(hoy.getMonth()+1).padStart(2,'0'); var anioPref = String(hoy.getFullYear()); var gMes = gastos.filter(g => g.fecha && g.fecha.startsWith(mesPref)); var gAnio = gastos.filter(g => g.fecha && g.fecha.startsWith(anioPref)); var gRep = gMes.filter(g => g.cat === 'Repuestos'); var totalMes = gMes.reduce((s,g)=>s+g.monto,0); var diaActual = hoy.getDate(); document.getElementById('k-g-mes').textContent = fmt(totalMes); document.getElementById('k-g-mes-sub').textContent = gMes.length+' registros'; document.getElementById('k-g-anio').textContent = fmt(gAnio.reduce((s,g)=>s+g.monto,0)); document.getElementById('k-g-anio-sub').textContent = gAnio.length+' registros'; document.getElementById('k-g-rep').textContent = fmt(gRep.reduce((s,g)=>s+g.monto,0)); document.getElementById('k-g-prom').textContent = fmt(diaActual > 0 ? totalMes / diaActual : 0); var tb = document.getElementById('tbody-gastos'); var lista = gastos.slice().sort((a,b) => b.fecha.localeCompare(a.fecha) || b.id - a.id); if(!lista.length){ tb.innerHTML = 'Sin gastos.'; return; } tb.innerHTML = lista.map(g => { var idx = gastos.indexOf(g); return '' +''+fmtF(g.fecha)+'' +''+g.desc+'' +''+g.cat+'' +''+fmt(g.monto)+'' +' ' +''; }).join(''); } function abrirModalGasto(idx){ editIdxGasto = idx != null ? idx : null; var g = editIdxGasto != null ? gastos[editIdxGasto] : {}; document.getElementById('mg-tit').textContent = editIdxGasto != null ? 'Editar gasto' : 'Nuevo gasto'; document.getElementById('mg-fecha').value = g.fecha || today(); document.getElementById('mg-monto').value = g.monto || ''; document.getElementById('mg-desc').value = g.desc || ''; document.getElementById('mg-cat').value = g.cat || 'Repuestos'; document.getElementById('ov-gasto').classList.add('on'); } function cerrarModalGasto(){ document.getElementById('ov-gasto').classList.remove('on'); editIdxGasto = null; } function guardarGasto(){ var d = document.getElementById('mg-desc').value.trim(); var m = parseFloat(document.getElementById('mg-monto').value) || 0; if(!d){ alert('Ingresá descripción.'); return; } if(!m){ alert('Ingresá monto.'); return; } var g = { id: editIdxGasto != null ? gastos[editIdxGasto].id : Date.now(), fecha: document.getElementById('mg-fecha').value || today(), desc: d, monto: m, cat: document.getElementById('mg-cat').value }; if(editIdxGasto != null) gastos[editIdxGasto] = g; else gastos.push(g); save(); cerrarModalGasto(); renderGastos(); } function elimGasto(i){ if(!confirm('¿Eliminar gasto?')) return; gastos.splice(i,1); save(); renderGastos(); } // ════════════════════════════════════════ // VENTAS // ════════════════════════════════════════ function renderVentas(){ var periodo = document.getElementById('ventas-periodo').value; var hoy = new Date(); var filtro = () => true; if(periodo==='mes'){ var p = hoy.getFullYear()+'-'+String(hoy.getMonth()+1).padStart(2,'0'); filtro = f => f && f.startsWith(p); } else if(periodo==='anio'){ var p = String(hoy.getFullYear()); filtro = f => f && f.startsWith(p); } var entregadas = ordenes.filter(o => o.estado === 'Entregado' && filtro(o.fecha)); var total = entregadas.reduce((s,o)=>s+(o.total||0),0); var ganancia = entregadas.reduce((s,o)=>s+(o.ganancia||0),0); var costo = entregadas.reduce((s,o)=>s+(o.costo||0),0); var ticket = entregadas.length>0 ? total/entregadas.length : 0; document.getElementById('ventas-kpis').innerHTML = '
💵 Total facturado
'+fmt(total)+'
'+entregadas.length+' órdenes
' +'
📋 Costo
'+fmt(costo)+'
Repuestos/materiales
' +'
📈 Ganancia
'+fmt(ganancia)+'
'+(total>0?Math.round(ganancia/total*100)+'% margen':'—')+'
' +'
🎯 Ticket promedio
'+fmt(ticket)+'
por orden
'; var tb = document.getElementById('tbody-ventas'); if(!entregadas.length){ tb.innerHTML = 'Sin ventas en este período.'; return; } tb.innerHTML = entregadas.sort((a,b) => b.fecha.localeCompare(a.fecha)).map(o => { return '' +''+fmtF(o.fecha)+'' +''+o.num+'' +''+o.cliente+'' +''+(o.marca||'')+' '+(o.modelo||'')+' '+(o.patente||'')+'' +''+fmt(o.total)+'' +''+fmt(o.ganancia||0)+'' +''; }).join(''); } // ════════════════════════════════════════ // ER // ════════════════════════════════════════ function calcularER(){ var periodo = document.getElementById('er-periodo').value; var hoy = new Date(); var filtro = () => true; if(periodo==='mes'){ var p = hoy.getFullYear()+'-'+String(hoy.getMonth()+1).padStart(2,'0'); filtro = f => f && f.startsWith(p); } else if(periodo==='anio'){ var p = String(hoy.getFullYear()); filtro = f => f && f.startsWith(p); } var entregadas = ordenes.filter(o => o.estado === 'Entregado' && filtro(o.fecha)); var pendientes = ordenes.filter(o => o.estado !== 'Entregado' && filtro(o.fecha)); var ing = entregadas.reduce((s,o)=>s+(o.total||0),0); var cpv = entregadas.reduce((s,o)=>s+(o.costo||0),0); var bruto = ing - cpv; var gast = gastos.filter(g => filtro(g.fecha)).reduce((s,g)=>s+g.monto,0); // Sueldos del período var sueldos = 0; Object.keys(horas).forEach(f => { if(!filtro(f)) return; Object.keys(horas[f]||{}).forEach(empId => { var e = empleados.find(x => x.id == empId); if(e) sueldos += (horas[f][empId]||0) * (e.vhora||0); }); }); var neto = bruto - gast - sueldos; document.getElementById('er-ing').textContent = fmt(ing); document.getElementById('er-cpv').textContent = '('+fmt(cpv)+')'; document.getElementById('er-bruto').textContent = fmt(bruto); document.getElementById('er-bruto').style.color = bruto>=0 ? 'var(--verde)' : 'var(--rojo)'; document.getElementById('er-gast').textContent = '('+fmt(gast)+')'; document.getElementById('er-sueldos').textContent = '('+fmt(sueldos)+')'; document.getElementById('er-neto').textContent = fmt(neto); document.getElementById('er-ord').textContent = entregadas.length; document.getElementById('er-ticket').textContent = entregadas.length>0 ? fmt(ing/entregadas.length) : '$0'; document.getElementById('er-margen').textContent = ing>0 ? Math.round(neto/ing*100)+'%' : '—'; document.getElementById('er-pend').textContent = fmt(pendientes.reduce((s,o)=>s+Math.max(0,(o.total||0)-(o.sena||0)),0)); } // ════════════════════════════════════════ // INIT // ════════════════════════════════════════ (function(){ var prev = sessionStorage.getItem('taller_session'); if(prev && USUARIOS[prev]){ currentUser = Object.assign({ usuario: prev }, USUARIOS[prev]); iniciarApp(); } else { setTimeout(() => { var u = document.getElementById('l-user'); if(u) u.focus(); }, 100); } })();