Pulso·360 v3.7 — Código Creativa
Pulso·360 v3.6
✦ PREMIUM CÓDIGO CREATIVA · v3.6
Panel
Resumen general del negocio
GANANCIAS
Tus ventas superan tus gastos.
₲ 0
Ventas del período
Este mes
₲ 0
Gastos + compras
Gastos + compras
₲ 0
Ganancia neta
0
Productos en stock
Últimas ventas
ProductoMontoEstado
Sin ventas registradas
Últimos gastos
DescripciónCategoríaMonto
Sin gastos registrados
Top productos más vendidos
Registra ventas para ver el ranking
Ventas
Historial completo
₲ 0
Hoy
₲ 0
Esta semana
₲ 0
Este mes
₲ 0
Este año
# Fecha Producto Cant. Precio Unit. Total Método Estado
Sin ventas aún
Compras
Registro de compras a proveedores
₲ 0
Hoy
₲ 0
Esta semana
₲ 0
Este mes
₲ 0
Este año
Fecha Producto Proveedor Cant. Costo Unit. Total Estado
Sin compras registradas
Gastos
Todos los egresos del negocio
₲ 0
Este mes
₲ 0
Fijos este mes
₲ 0
Variables este mes
₲ 0
Este año
Detalle de Gastos
Fecha Descripción Categoría Tipo Monto
Sin gastos registrados
Por Categoría
Inventario
Control de stock en tiempo real
0
Total productos
0
Stock normal
0
Stock bajo
0
Sin stock
Producto Categoría 🔍 Cód. Barra Stock Mín. Unidad P. Costo P. Público P. Mayorista Margen
Sin productos
Punto de Equilibrio
¿Cuánto necesitas vender para no perder dinero?
Parámetros
Ingresa solo números — podés editar cualquier campo
₲
₲
₲
₲
Resultado
Completa los campos para ver el análisis
Visualización del mes actual
Compará lo que necesitás vender para no perder dinero contra lo que vendiste este mes.
Completa los parámetros para ver el gráfico
Reportes
Estado financiero de tu negocio
CALCULANDO...
Registra ventas y gastos para ver el análisis
Estado de Resultados
📈 Total Ventas₲ 0
🛒 Costo de Compras₲ 0
= Ganancia Bruta₲ 0
📋 Gastos Fijos₲ 0
📦 Gastos Variables₲ 0
= GANANCIA NETA₲ 0
Análisis Rápido
Registra movimientos para ver el análisis
Productos más rentables
Sin datos
Plan de Negocio
Envía tus datos financieros reales a Planifica · Código Creativa
Tu situación financiera actual
Ventas este mes
₲ 0
Costos este mes
₲ 0
Ganancia neta
₲ 0
Planifica
por Código Creativa
El archivo exportado incluye tu semáforo financiero, top 5 productos y tendencia de los últimos 3 meses — todo listo para construir proyecciones reales en Planifica.
Semáforo financiero
Top 5 productos
Tendencia 3 meses
Proyecciones base
  Abrir Planifica
El archivo exportado se importa directamente en Planifica · Código Creativa Suite
'; var w=window.open('','_blank'); if(!w){alert('Permite ventanas emergentes para exportar el reporte.');return;} w.document.write(h);w.document.close(); w.focus(); setTimeout(function(){try{w.print();}catch(e){}},800); } /* ── Excel ── */ function exportarExcelHTML(){ _xlsxOk(function(){ var periodo=gv('reportePeriodo')||'mes'; var filterFn=getReporteFilterFn(periodo); var periodoLabel=periodo==='semana'?'Esta semana':periodo==='mes'?'Este mes': periodo==='trimestre'?'Últimos 3 meses':periodo==='semestre'?'Últimos 6 meses': periodo==='anio'?'Este año':'Todo el tiempo'; var negocio=gv('planNombre')||'Mi Negocio'; var hoy=new Date();var sym=moneda.symbol; var vFilt=ventas.filter(function(v){return filterFn(v.fecha);}); var gFilt=gastos.filter(function(g){return filterFn(g.fecha);}); var cFilt=compras.filter(function(c){return filterFn(c.fecha);}); var vTotal=vFilt.reduce(function(a,v){return a+v.total;},0); var cTotal=cFilt.reduce(function(a,c){return a+c.total;},0); var gFijos=gFilt.filter(function(g){return g.tipo==='fijo';}).reduce(function(a,g){return a+g.monto;},0); var gVarTotal=gFilt.filter(function(g){return g.tipo==='variable';}).reduce(function(a,g){return a+g.monto;},0); var gNeta=vTotal-cTotal-gFijos-gVarTotal;var margen=vTotal>0?Math.round(gNeta/vTotal*100):0; var wb=XLSX.utils.book_new(); var ws1Data=[['REPORTE FINANCIERO — '+negocio,'',''],['Período: '+periodoLabel,'',''],['','',''],['ESTADO DE RESULTADOS','MONTO','%'],['Total Ventas',vTotal,1],['Costo Compras',cTotal,vTotal>0?cTotal/vTotal:0],['Ganancia Bruta',vTotal-cTotal,vTotal>0?(vTotal-cTotal)/vTotal:0],['Gastos Fijos',gFijos,vTotal>0?gFijos/vTotal:0],['Gastos Variables',gVarTotal,vTotal>0?gVarTotal/vTotal:0],[gNeta>=0?'GANANCIA NETA':'PÉRDIDA NETA',Math.abs(gNeta),Math.abs(margen)/100]]; XLSX.utils.book_append_sheet(wb,XLSX.utils.aoa_to_sheet(ws1Data),'Resumen'); var ws2Data=[['Fecha','Producto/Servicio','Cantidad','Precio Unit.','Total','Método','Estado']]; vFilt.forEach(function(v){ws2Data.push([v.fecha,v.nombre,v.cantidad,v.precio,v.total,v.metodo,v.estado]);}); XLSX.utils.book_append_sheet(wb,XLSX.utils.aoa_to_sheet(ws2Data),'Ventas'); var ws3Data=[['Fecha','Descripción','Categoría','Tipo','Monto']]; gFilt.forEach(function(g){ws3Data.push([g.fecha,g.descripcion,g.categoria,g.tipo,g.monto]);}); XLSX.utils.book_append_sheet(wb,XLSX.utils.aoa_to_sheet(ws3Data),'Gastos'); if(productos.length){ var ws4Data=[['Producto','Categoría','Cód. Barra','Stock','Stock Mín','Unidad','Precio Costo','Precio Público','Precio Mayorista','Margen']]; productos.forEach(function(p){ws4Data.push([p.nombre,p.categoria,p.codBarra||'',p.stock,p.stockMin,p.unidad,p.costo,p.venta,p.ventaMay||0,p.margen]);}); XLSX.utils.book_append_sheet(wb,XLSX.utils.aoa_to_sheet(ws4Data),'Inventario'); } var filename='Reporte_'+negocio.replace(/\s+/g,'_')+'_'+hoy.getFullYear()+(hoy.getMonth()+1)+hoy.getDate()+'.xlsx'; XLSX.writeFile(wb,filename); toast('Excel exportado ✓','ok'); }); } /* ── Plan de Negocio ── */ function renderPlanSection(){ var sym=moneda.symbol; var vm=totalVentasMes(); var gm=totalGastosMes(); var cm=compras.filter(function(c){return isThisMonth(c.fecha);}).reduce(function(a,c){return a+c.total;},0); var gn=vm-gm-cm; var margen=vm>0?Math.round(gn/vm*100):0; var semaforo=gn>0&&margen>=20?'verde':gn>=0?'amarillo':'rojo'; var semColors={verde:'#3d7010',amarillo:'#92600a',rojo:'#9f1f1f'}; var semBgs={verde:'#edf6e1',amarillo:'#fef3cd',rojo:'#fee2e2'}; var semBorders={verde:'rgba(105,170,45,.3)',amarillo:'rgba(245,158,11,.35)',rojo:'rgba(239,68,68,.3)'}; var semEmojis={verde:'🟢',amarillo:'🟡',rojo:'🔴'}; var semTexts={verde:'GANANCIAS',amarillo:'ZONA AMARILLA',rojo:'PÉRDIDAS'}; var semEl=_ge('planSemResumen'); if(semEl){ semEl.style.cssText='background:'+semBgs[semaforo]+';border:2px solid '+semBorders[semaforo]+';border-radius:12px;padding:14px 18px;display:flex;align-items:center;justify-content:space-between'; semEl.innerHTML='
'+semEmojis[semaforo]+' '+semTexts[semaforo]+'
' +'
Margen: '+margen+'% \xb7 Ventas este mes: '+fmtGs(vm)+'
' +'
'+margen+'%
'; } sv2('planKpiVentas',fmtGs(vm));sv2('planKpiCostos',fmtGs(gm+cm));sv2('planKpiGanancia',fmtGs(gn)); var ganCard=_ge('planKpiGananciaCard');if(ganCard)ganCard.style.background=gn>=0?'var(--green-light)':'var(--red-light)'; var ganEl=_ge('planKpiGanancia');if(ganEl)ganEl.style.color=gn>=0?'var(--green)':'var(--red)'; // Historial dinámico según selector var nMeses=parseInt(gv('planHistPeriodo')||'3',10); var hoy=new Date();var hist=[]; var mNames=['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic']; for(var mi=nMeses;mi>=1;mi--){ var dh=new Date(hoy.getFullYear(),hoy.getMonth()-mi,1); var yh=dh.getFullYear(),mh=dh.getMonth(); var nm=mNames[mh]+' '+yh; var hvv=ventas.filter(function(v){var dt=new Date(v.fecha+'T12:00:00');return dt.getFullYear()===yh&&dt.getMonth()===mh;}).reduce(function(a,v){return a+v.total;},0); var hgc=gastos.filter(function(g){var dt=new Date(g.fecha+'T12:00:00');return dt.getFullYear()===yh&&dt.getMonth()===mh;}).reduce(function(a,g){return a+g.monto;},0); var hcc=compras.filter(function(c){var dt=new Date(c.fecha+'T12:00:00');return dt.getFullYear()===yh&&dt.getMonth()===mh;}).reduce(function(a,c){return a+c.total;},0); hist.push({mes:nm,ventas:hvv,ganancia:hvv-hgc-hcc}); } var nmAct=mNames[hoy.getMonth()]+' '+hoy.getFullYear(); hist.push({mes:nmAct,ventas:vm,ganancia:gn}); var maxH=Math.max.apply(null,hist.map(function(h){return h.ventas||0;}))||1; var tendEl=_ge('planTendencia3m');var tendBars=_ge('planTendenciaBarras'); var histLbl=_ge('planHistLabel');if(histLbl)histLbl.textContent='Historial'; if(tendEl&&tendBars&&hist.some(function(h){return h.ventas>0;})){ var gap=nMeses<=3?12:nMeses<=6?6:3; tendBars.style.gap=gap+'px'; tendBars.innerHTML=hist.map(function(hh){ var pct=Math.max(6,Math.round((hh.ventas/maxH)*100)); var col=hh.ganancia>=0?'var(--green)':'var(--red)'; var isAct=hh.mes===nmAct; return'
' +'
' +(nMeses<=6?'
'+sym+' '+Math.round(hh.ventas||0).toLocaleString('es')+'
':'') +'
'+hh.mes+'
' +'
'; }).join(''); tendEl.style.display='block'; }else if(tendEl){tendEl.style.display='none';} // Top del período seleccionado — usa selector planTopLimit var limPlanTop=parseInt(((_ge('planTopLimit')||{}).value)||5); var dTop=new Date(hoy.getFullYear(),hoy.getMonth()-nMeses,1); var topMap={}; ventas.filter(function(v){return new Date(v.fecha+'T12:00:00')>=dTop;}).forEach(function(v){topMap[v.nombre]=(topMap[v.nombre]||0)+v.total;}); var topArr=Object.keys(topMap).map(function(k){return[k,topMap[k]];}).sort(function(a,b){return b[1]-a[1];}).slice(0,limPlanTop); var topEl=_ge('planTopProds'),topList=_ge('planTopProdsList'); if(topEl&&topList&&topArr.length){ var totalTop=topArr.reduce(function(a,e){return a+e[1];},0)||1; topList.innerHTML=topArr.map(function(e,i){ var pct=Math.round(e[1]/totalTop*100); return'
#'+(i+1)+'' +'
'+e[0]+'
' +'
' +''+sym+' '+Math.round(e[1]).toLocaleString('es')+'
'; }).join(''); topEl.style.display='block'; }else if(topEl){topEl.style.display='none';} } /* ── Enviar a Planifica ── */ function exportarParaPlanifica(){ var negocio=gv('planNombre')||'Mi Negocio'; var hoy=new Date(); var vMes=totalVentasMes(); var gFijosMes=gastos.filter(function(g){return isThisMonth(g.fecha)&&g.tipo==='fijo';}).reduce(function(a,g){return a+g.monto;},0); var gVarMes=gastos.filter(function(g){return isThisMonth(g.fecha)&&g.tipo==='variable';}).reduce(function(a,g){return a+g.monto;},0); var cMes=compras.filter(function(c){return isThisMonth(c.fecha);}).reduce(function(a,c){return a+c.total;},0); var gNetaMes=vMes-gFijosMes-gVarMes-cMes; var margenMes=vMes>0?Math.round(gNetaMes/vMes*100):0; // Top productos — historial completo var topProds={}; ventas.forEach(function(v){topProds[v.nombre]=(topProds[v.nombre]||0)+v.total;}); var topSorted=Object.keys(topProds).map(function(k){return{nombre:k,total:topProds[k]};}).sort(function(a,b){return b.total-a.total;}).slice(0,5); // Historial 6 meses (5 anteriores + mes actual) var mNombres=['Ene','Feb','Mar','Abr','May','Jun','Jul','Ago','Sep','Oct','Nov','Dic']; var historial6=[]; for(var mi=5;mi>=1;mi--){ var dh=new Date(hoy.getFullYear(),hoy.getMonth()-mi,1); var hy=dh.getFullYear(),hm=dh.getMonth(); var hvv=ventas.filter(function(v){var dt=new Date(v.fecha+'T12:00:00');return dt.getFullYear()===hy&&dt.getMonth()===hm;}).reduce(function(a,v){return a+v.total;},0); var hgf=gastos.filter(function(g){var dt=new Date(g.fecha+'T12:00:00');return dt.getFullYear()===hy&&dt.getMonth()===hm&&g.tipo==='fijo';}).reduce(function(a,g){return a+g.monto;},0); var hgv=gastos.filter(function(g){var dt=new Date(g.fecha+'T12:00:00');return dt.getFullYear()===hy&&dt.getMonth()===hm&&g.tipo==='variable';}).reduce(function(a,g){return a+g.monto;},0); var hcc=compras.filter(function(c){var dt=new Date(c.fecha+'T12:00:00');return dt.getFullYear()===hy&&dt.getMonth()===hm;}).reduce(function(a,c){return a+c.total;},0); var hNeta=hvv-hgf-hgv-hcc; historial6.push({mes:mNombres[hm]+' '+hy,ventas:hvv,costos:hgf+hgv+hcc,gastosFijos:hgf,gastosVariables:hgv,compras:hcc,ganancia:hNeta,margenPct:hvv>0?Math.round(hNeta/hvv*100):0}); } var nmAct=mNombres[hoy.getMonth()]+' '+hoy.getFullYear(); historial6.push({mes:nmAct,ventas:vMes,costos:gFijosMes+gVarMes+cMes,gastosFijos:gFijosMes,gastosVariables:gVarMes,compras:cMes,ganancia:gNetaMes,margenPct:margenMes}); // Promedios 3 y 6 meses (solo meses con datos) function promCampo(arr,field){var c=arr.filter(function(h){return h.ventas>0;});return c.length?Math.round(c.reduce(function(a,h){return a+h[field];},0)/c.length):0;} var h3=historial6.slice(2,5); // 3 meses anteriores al actual var h6=historial6.slice(0,5); // 5 meses anteriores al actual var prom3V=promCampo(h3,'ventas');var prom6V=promCampo(h6,'ventas'); var prom3M=promCampo(h3,'margenPct');var prom6M=promCampo(h6,'margenPct'); // Tendencia: segunda mitad vs primera mitad del historial de 6 meses var vHist=h6.map(function(h){return h.ventas;}); var mid=Math.floor(vHist.length/2); var p1=vHist.slice(0,mid).filter(Boolean),p2=vHist.slice(mid).filter(Boolean); var a1=p1.length?p1.reduce(function(a,b){return a+b;},0)/p1.length:0; var a2=p2.length?p2.reduce(function(a,b){return a+b;},0)/p2.length:0; var tendenciaPct=a1>0?Math.round(((a2-a1)/a1)*100):0; // Categorías de gastos var catGastos={}; gastos.forEach(function(g){catGastos[g.categoria]=(catGastos[g.categoria]||0)+g.monto;}); // Semáforo var semaforo=gNetaMes>0&&margenMes>=20?'verde':gNetaMes>=0?'amarillo':'rojo'; var data={ _source:'pulso360',_version:'v3.6', exportedAt:hoy.toISOString(), negocio:{nombre:negocio,moneda:{code:moneda.code,symbol:moneda.symbol,name:moneda.name}}, semaforo:semaforo, financiero:{ mesActual:{ventas:vMes,gastosFijos:gFijosMes,gastosVariables:gVarMes,compras:cMes,gananciaNeta:gNetaMes,margenPct:margenMes}, promedioVentas3Meses:prom3V||Math.round(promedioVentas3Meses()), promedioVentas6Meses:prom6V, margenPromedio3Meses:prom3M, margenPromedio6Meses:prom6M, tendenciaPct:tendenciaPct, historial3Meses:historial6.slice(-4), historial6Meses:historial6, topProductos:topSorted, categoriaGastos:catGastos, puntoEquilibrio:{costosFijos:gv('peCostosFijos'),precioVenta:gv('pePrecioVenta'),costoVariable:gv('peCostoVariable')} } }; var blob=new Blob([JSON.stringify(data,null,2)],{type:'application/json'}); var url=URL.createObjectURL(blob); var a=document.createElement('a'); a.href=url; a.download='Pulso_para_Planifica_'+negocio.replace(/\s+/g,'_')+'_'+hoy.toISOString().slice(0,10)+'.json'; document.body.appendChild(a);a.click(); setTimeout(function(){URL.revokeObjectURL(url);if(a.parentNode)a.parentNode.removeChild(a);},300); toast('Exportado para Planifica \u2713','ok'); } /* ── Stock alert views ── */ function renderStockPills(sinStock,bajoStock){ var sym=moneda.symbol; var ps=_ge('pillsSinStock');var pb=_ge('pillsBajoStock'); if(!ps||!pb)return; ps.innerHTML=sinStock.map(function(p){ return'' +'' +p.nombre +'0/'+p.stockMin+''; }).join('')||'—'; pb.innerHTML=bajoStock.map(function(p){ return'' +'' +p.nombre +''+p.stock+'/'+p.stockMin+''; }).join('')||'—'; } function renderStockCats(sinStock,bajoStock){ var cSin=_ge('catsSinStock');var cBaj=_ge('catsBajoStock'); if(!cSin||!cBaj)return; function buildCatGrid(prods){ var cats={}; prods.forEach(function(p){ var cat=p.categoria||'General'; if(!cats[cat])cats[cat]=[]; cats[cat].push(p); }); if(!Object.keys(cats).length)return'
—
'; return Object.keys(cats).map(function(cat){ return'
' +'
'+cat+'
' +cats[cat].map(function(p){ var isZero=p.stock===0; var chip=''+p.stock+' / '+p.stockMin+''; return'
' +''+p.nombre+'' +chip+'
'; }).join('') +'
'; }).join(''); } cSin.innerHTML=buildCatGrid(sinStock); cBaj.innerHTML=buildCatGrid(bajoStock); } function renderStockTabla(sinStock,bajoStock){ var tbody=_ge('stockTablaBody');if(!tbody)return; var all=sinStock.concat(bajoStock); tbody.innerHTML=all.map(function(p){ var isZero=p.stock===0; var pct=p.stockMin>0?Math.round((p.stock/p.stockMin)*100):0; var pctDisplay=Math.min(pct,100); var chipStyle=isZero?'background:var(--red-light);color:var(--red)':'background:var(--amber-light);color:#92600a'; var chipText=isZero?'Sin stock':'Stock bajo'; var barColor=isZero?'var(--red)':'var(--amber)'; return'' +''+p.nombre+'' +''+chipText+'' +'
' +'
' +''+p.stock+' / '+p.stockMin+' '+p.unidad+'' +'
' +''+pct+'%' +''; }).join(''); } var _stockSortAsc=true; function sortStockTable(col){ var tbody=_ge('stockTablaBody');if(!tbody)return; var rows=Array.from(tbody.querySelectorAll('tr')); rows.sort(function(a,b){ if(col===3){var av=parseInt(a.dataset.pct),bv=parseInt(b.dataset.pct);return _stockSortAsc?av-bv:bv-av;} var av=a.cells[0].textContent.trim().toLowerCase(),bv=b.cells[0].textContent.trim().toLowerCase(); return _stockSortAsc?av.localeCompare(bv,'es'):bv.localeCompare(av,'es'); }); _stockSortAsc=!_stockSortAsc; rows.forEach(function(r){tbody.appendChild(r);}); } var _stockViewNames={pills:'Resumen',cats:'Categoría',tabla:'Tabla'}; function switchStockView(id){ ['pills','cats','tabla'].forEach(function(v){ var el=_ge('stockView'+v.charAt(0).toUpperCase()+v.slice(1)); if(el)el.style.display=v===id?'block':'none'; }); var lbl=_ge('stockViewName');if(lbl)lbl.textContent=_stockViewNames[id]||id; } /* ── Paginación helpers — exported to global scope ── */ function pagVentasPrev(){if(ventasPagina>1){ventasPagina--;renderVentas();}} function pagVentasNext(){var ps=parseInt(((_ge('ventasPageSize')||{value:'50'}).value));var tp=Math.max(1,Math.ceil(ventas.length/ps));if(ventasPagina1){comprasPagina--;renderCompras();}} function pagComprasNext(){var ps=parseInt(((_ge('comprasPageSize')||{value:'50'}).value));var tp=Math.max(1,Math.ceil(compras.length/ps));if(comprasPagina1){gastosPagina--;renderGastos();}} function pagGastosNext(){var ps=parseInt(((_ge('gastosPageSize')||{value:'50'}).value));var tp=Math.max(1,Math.ceil(gastos.length/ps));if(gastosPagina1){invPagina--;renderInventario();}} function pagInvNext(){var ps=parseInt(((_ge('invPageSize')||{value:'50'}).value));var tp=Math.max(1,Math.ceil(productos.length/ps));if(invPagina fecha > texto function _cmpVals(a,b,asc){ var na=parseFloat(String(a).replace(/[^\d,.-]/g,'').replace(/\./g,'').replace(',','.')); var nb=parseFloat(String(b).replace(/[^\d,.-]/g,'').replace(/\./g,'').replace(',','.')); if(!isNaN(na)&&!isNaN(nb)) return asc?na-nb:nb-na; var da=new Date(a),db=new Date(b); if(!isNaN(da)&&!isNaN(db)) return asc?da-db:db-da; return asc?String(a).localeCompare(String(b),'es'):String(b).localeCompare(String(a),'es'); } // Extrae el valor de sort de cada objeto según tabla y columna function _getSortVal(obj,tableId,col){ if(tableId==='tablaVentas'){ return [obj.fecha,obj.fecha,obj.nombre,obj.cantidad,obj.precio,obj.total,obj.metodo,obj.estado][col]||''; } if(tableId==='tablaCompras'){ return [obj.fecha,obj.nombre,obj.proveedor,obj.cantidad,obj.costo,obj.total,obj.estado][col]||''; } if(tableId==='tablaGastos'){ return [obj.fecha,obj.descripcion,obj.categoria,obj.tipo,obj.monto][col]||''; } if(tableId==='tablaInventario'){ return [obj.nombre,obj.categoria,obj.codBarra||'',obj.stock,obj.stockMin,obj.unidad,obj.costo,obj.venta,obj.ventaMay||0,obj.margen][col]||''; } return ''; } function sortTabla(tableId,colIdx){ var key=tableId+'-'+colIdx; var asc=_sortState[key]!==true; _sortState[key]=asc; // Reset all previous sort indicators, set current var table=_ge(tableId); if(table){ table.querySelectorAll('th').forEach(function(th,i){ th.classList.remove('sort-asc','sort-desc'); if(i===colIdx&&th.classList.contains('sortable'))th.classList.add(asc?'sort-asc':'sort-desc'); }); } // Sort the source array and re-render (works with pagination) var arr, renderFn, resetPage; if(tableId==='tablaVentas'){arr=ventas;renderFn=renderVentas;resetPage=function(){ventasPagina=1;};} else if(tableId==='tablaCompras'){arr=compras;renderFn=renderCompras;resetPage=function(){comprasPagina=1;};} else if(tableId==='tablaGastos'){arr=gastos;renderFn=renderGastos;resetPage=function(){gastosPagina=1;};} else if(tableId==='tablaInventario'){arr=productos;renderFn=renderInventario;resetPage=function(){invPagina=1;};} else return; arr.sort(function(a,b){ return _cmpVals(_getSortVal(a,tableId,colIdx),_getSortVal(b,tableId,colIdx),asc); }); resetPage(); renderFn(); // Restore sort indicator after re-render var t=_ge(tableId); if(t){t.querySelectorAll('th').forEach(function(th,i){ if(i===colIdx&&th.classList.contains('sortable'))th.classList.add(asc?'sort-asc':'sort-desc'); });} } function renderAll(){ // Always render dashboard (KPIs always visible) and update storage renderDashboard(); updateStorageIndicator(); // Find active section to prioritize it var activeEl=_d.querySelector('.section.active'); var activeId=activeEl?activeEl.id:''; // Render active section immediately if(activeId==='sec-ventas')renderVentas(); else if(activeId==='sec-compras')renderCompras(); else if(activeId==='sec-gastos')renderGastos(); else if(activeId==='sec-inventario')renderInventario(); else if(activeId==='sec-reporte')renderReporte(); else if(activeId==='sec-plan')renderPlanSection(); // Defer all other sections to next tick — prevents UI freeze with large datasets setTimeout(function(){ if(activeId!=='sec-ventas')renderVentas(); if(activeId!=='sec-compras')renderCompras(); if(activeId!=='sec-gastos')renderGastos(); if(activeId!=='sec-inventario')renderInventario(); if(activeId!=='sec-reporte')renderReporte(); if(activeId!=='sec-plan')renderPlanSection(); },50); } /* ── Storage indicator ── */ function updateStorageIndicator(){ try{ var data=_ls.getItem('cc_pulso360_v3')||''; var usedBytes=new Blob([data]).size; var limitBytes=5*1024*1024; var pct=Math.min(100,Math.round(usedBytes/limitBytes*100)); var usedKB=usedBytes/1024; var usedStr=usedKB<1000?(usedKB.toFixed(1)+' KB'):((usedKB/1024).toFixed(2)+' MB'); var totalRecs=ventas.length+compras.length+gastos.length+productos.length; var colorClass=pct<60?'ok':pct<85?'warn':'danger'; var bar=_ge('storageBarFill');var pctEl=_ge('storagePct');var detailEl=_ge('storageDetail'); if(bar){bar.style.width=Math.max(2,pct)+'%';bar.className='storage-bar-fill '+colorClass;} if(pctEl){pctEl.textContent=(pct<1?'<1':pct)+'%';pctEl.style.color=pct>=85?'#f87171':pct>=60?'#fbbf24':'rgba(255,255,255,.8)';} if(detailEl){detailEl.textContent=usedStr+' de 5 MB · '+totalRecs.toLocaleString('es')+' registros';} }catch(e){} } /* ── Nueva sesión ── */ function nuevaSesion(){ if(!confirm('¿Limpiar todos los datos y empezar una nueva sesión? Esta acción no se puede deshacer.'))return; ventas=[];compras=[];gastos=[];productos=[];nextVid=1;nextCid=1;nextGid=1;nextPid=1; try{_ls.removeItem('cc_pulso360_v3');}catch(e){} renderAll();showSection('dashboard');toast('Sesión limpiada ✓','ok'); } /* ── Guardar / Cargar sesión ── */ function autoSave(){ try{ var peData={costosFijos:gv('peCostosFijos'),precioVenta:gv('pePrecioVenta'),costoVariable:gv('peCostoVariable'),unidad:gv('peUnidad')||'monetario',negocio:gv('planNombre')}; _ls.setItem('cc_pulso360_v3',_jP.stringify({ventas:ventas,compras:compras,gastos:gastos,productos:productos,planData:planData,peData:peData,nextVid:nextVid,nextCid:nextCid,nextGid:nextGid,nextPid:nextPid})); }catch(e){} updateStorageIndicator(); } function autoLoad(){ try{ var m=_ls.getItem('cc_pulso360_moneda'); if(m){moneda=_jP.parse(m);var tm=_ge('topbarMoneda');if(tm)tm.textContent=moneda.symbol+' '+moneda.code;} }catch(e){} var raw=_ls.getItem('cc_pulso360_v3')||_ls.getItem('cc_pulso360_v1'); try{ if(!raw)return; var d=_jP.parse(raw); if(d.ventas)ventas=d.ventas;if(d.compras)compras=d.compras;if(d.gastos)gastos=d.gastos;if(d.productos)productos=d.productos; if(d.planData)planData=d.planData; if(d.nextVid)nextVid=d.nextVid;if(d.nextCid)nextCid=d.nextCid;if(d.nextGid)nextGid=d.nextGid;if(d.nextPid)nextPid=d.nextPid; if(d.peData){ if(_ge('peCostosFijos')&&d.peData.costosFijos)_ge('peCostosFijos').value=d.peData.costosFijos; if(_ge('pePrecioVenta')&&d.peData.precioVenta)_ge('pePrecioVenta').value=d.peData.precioVenta; if(_ge('peCostoVariable')&&d.peData.costoVariable)_ge('peCostoVariable').value=d.peData.costoVariable; if(_ge('peUnidad')&&d.peData.unidad)_ge('peUnidad').value=d.peData.unidad; if(_ge('planNombre')&&d.peData.negocio)_ge('planNombre').value=d.peData.negocio; } }catch(e){} } function guardarSesion(){ var negocio=(gv('planNombre')||'MiNegocio').replace(/[^a-zA-Z0-9áéíóúÁÉÍÓÚñÑ\s]/g,'').trim().replace(/\s+/g,'_'); autoSave(); var data={cc_pulso360_v3:_jP.parse(_ls.getItem('cc_pulso360_v3')||'{}'),cc_pulso360_moneda:moneda,exportedAt:new Date().toISOString(),version:'v3'}; var blob=new Blob([_jP.stringify(data,null,2)],{type:'application/json'}); var url=URL.createObjectURL(blob); var a=document.createElement('a');a.href=url;a.download='Sesion_Pulso_'+negocio+'_'+new Date().toISOString().slice(0,10)+'.json'; document.body.appendChild(a);a.click(); setTimeout(function(){URL.revokeObjectURL(url);if(a.parentNode)a.parentNode.removeChild(a);},300); toast('Sesión exportada ✓','ok'); } function cargarSesion(){ var input=document.createElement('input');input.type='file';input.accept='.json'; input.onchange=function(e){ var file=e.target.files[0];if(!file)return; var reader=new FileReader(); reader.onload=function(ev){ try{ var data=_jP.parse(ev.target.result); // ── Detectar archivo de Mi·Dinero ── if(data._source==='mi_dinero'){ var neg=data.negocio||{}; var movNeg=neg.movimientos||data.movimientos&&data.movimientos.filter(function(m){return m.uso==='negocio';})||[]; var invNeg=neg.inversiones||[]; var sym=data.moneda&&data.moneda.symbol||moneda.symbol; var nGastos=movNeg.filter(function(m){return m.tipo==='gasto';}); var nIngresos=movNeg.filter(function(m){return m.tipo==='ingreso';}); var totalG=nGastos.reduce(function(a,m){return a+m.monto;},0); var totalI=nIngresos.reduce(function(a,m){return a+m.monto;},0); if(!movNeg.length&&!invNeg.length){ toast('No hay movimientos de Negocio en Mi·Dinero','err');return; } var msg='Archivo de Mi\u00b7Dinero detectado.\n\n' +(nGastos.length?'\u2022 '+nGastos.length+' gasto(s) de Negocio: '+sym+' '+Math.round(totalG).toLocaleString('es')+'\n':'') +(nIngresos.length?'\u2022 '+nIngresos.length+' ingreso(s) de Negocio (retiro del due\u00f1o): '+sym+' '+Math.round(totalI).toLocaleString('es')+'\n':'') +(invNeg.length?'\u2022 '+invNeg.length+' inversi\u00f3n(es) del negocio\n':'') +'\n\u00bfImportar estos movimientos a Pulso\u00b7360?'; if(!confirm(msg))return; var usuario=data.usuario||'Due\u00f1o'; // Gastos de negocio → gastos en Pulso nGastos.forEach(function(m){ gastos.push({id:nextGid++,descripcion:m.desc,monto:m.monto,categoria:m.categoria||'Operaciones',tipo:'variable',fecha:m.fecha,nota:'Importado de Mi\u00b7Dinero'}); }); // Ingresos de negocio → gastos tipo "Retiro del dueño" nIngresos.forEach(function(m){ gastos.push({id:nextGid++,descripcion:'Retiro '+usuario+': '+m.desc,monto:m.monto,categoria:'Retiro Due\u00f1o',tipo:'variable',fecha:m.fecha,nota:'Importado de Mi\u00b7Dinero'}); }); autoSave();renderAll(); toast('Mi\u00b7Dinero importado: '+(nGastos.length+nIngresos.length)+' movimientos \u2713','ok'); return; } // ── Sesión normal de Pulso ── var d=data.cc_pulso360_v3||data.cc_pulso360_v1; if(d){ if(d.ventas)ventas=d.ventas;if(d.compras)compras=d.compras;if(d.gastos)gastos=d.gastos;if(d.productos)productos=d.productos; if(d.planData)planData=d.planData; if(d.nextVid)nextVid=d.nextVid;if(d.nextCid)nextCid=d.nextCid;if(d.nextGid)nextGid=d.nextGid;if(d.nextPid)nextPid=d.nextPid; } if(data.cc_pulso360_moneda&&data.cc_pulso360_moneda.code){ moneda=data.cc_pulso360_moneda; var tm=_ge('topbarMoneda');if(tm)tm.textContent=moneda.symbol+' '+moneda.code; _ls.setItem('cc_pulso360_moneda',_jP.stringify(moneda)); } autoSave();renderAll();toast('Sesi\u00f3n cargada \u2713','ok'); }catch(err){toast('Archivo inv\u00e1lido','err');} }; reader.readAsText(file); }; input.click(); } /* ── Mobile ── */ function toggleMobileMore(){ var m=_ge('mobileMore');var o=_ge('mobileMoreOverlay');if(!m||!o)return; var isOpen=m.classList.contains('open'); m.classList.toggle('open',!isOpen);o.style.display=isOpen?'none':'block'; } /* ── Init ── */ window.addEventListener('load',function(){ autoLoad();renderAll(); setInterval(autoSave,60000); // Precargar XLSX en segundo plano para Todo en uno _loadXlsx(function(){_xlsxLoaded=true;}); // Keyboard scroll for tables document.addEventListener('keydown',function(e){ if(e.key!=='ArrowLeft'&&e.key!=='ArrowRight')return; var active=document.activeElement; if(active&&(active.tagName==='INPUT'||active.tagName==='TEXTAREA'||active.tagName==='SELECT'))return; _qs('.table-wrap').forEach(function(w){ var rect=w.getBoundingClientRect(); if(rect.top0&&w.scrollWidth>w.clientWidth){ w.scrollLeft+=e.key==='ArrowRight'?120:-120; } }); }); // Modal ayuda drag (function(){ var overlay=_ge('modalAyuda');if(!overlay)return; var panel=overlay.querySelector('.modal'); var handle=_ge('ayudaDragHandle');if(!handle)return; var mx=0,my=0,dragging=false; handle.addEventListener('mousedown',function(e){if(e.target.closest('button'))return;dragging=true;var r=panel.getBoundingClientRect();if(!panel.style.left){panel.style.left=r.left+'px';panel.style.top=r.top+'px';}mx=e.clientX-parseInt(panel.style.left||r.left);my=e.clientY-parseInt(panel.style.top||r.top);e.preventDefault();}); document.addEventListener('mousemove',function(e){if(!dragging)return;panel.style.left=(e.clientX-mx)+'px';panel.style.top=(e.clientY-my)+'px';}); document.addEventListener('mouseup',function(){dragging=false;}); overlay.addEventListener('click',function(e){if(e.target===overlay)closeModal('modalAyuda');}); })(); // Footer año var fy=_ge('footerYear');if(fy)fy.textContent=new Date().getFullYear(); // Init inline currency csel initMonedaPulso(); }); // Footer móvil WP/Divi hide (function(){ function _hf(){ if(window.innerWidth>768)return; ['.et-l.et-l--footer','.et-l--footer','#colophon','#footer','#main-footer','.site-footer','#site-footer'].forEach(function(s){ _qs(s).forEach(function(el){el.style.cssText='display:none!important;height:0!important;overflow:hidden!important;visibility:hidden!important';}); }); } document.addEventListener('DOMContentLoaded',_hf); window.addEventListener('resize',_hf,{passive:true}); setTimeout(_hf,600);setTimeout(_hf,1500); })(); // Public API ['totalGastosMes','eliminarVenta','renderDashboard','selectMoneda','sv2','isToday','renderInventario', 'editarVenta','editarCompra','editarGasto','editarProducto','eliminarCompra','eliminarGasto','eliminarProducto', 'calcPE','openModal','closeModal','showSection','showImportTab','previewImport','confirmarImport', 'descargarPlantilla','guardarVenta','guardarCompra','guardarGasto','guardarProducto','calcVentaTotal', 'calcCompraTotal','calcMargen','fmtPeInput','peEnter','cambiarMoneda','toggleDS','filterDS','selectMoneda','initMonedaPulso', 'verReportePDF','exportarExcelHTML','renderReporte','nuevaSesion','guardarSesion','cargarSesion', 'exportarParaPlanifica','autocompletarPE','toggleMobileMore','procesarTodo','sortTabla', 'setPeriodo','setPeriodoCustom','buildMesPicker','renderPlanSection','setSemaforoReporte','getReporteFilterFn', 'switchStockView','sortStockTable','renderVentas','renderCompras','renderGastos', 'pagVentasPrev','pagVentasNext','pagComprasPrev','pagComprasNext', 'pagGastosPrev','pagGastosNext','pagInvPrev','pagInvNext', 'pagSizeVentas','pagSizeCompras','pagSizeGastos','pagSizeInv' ].forEach(function(name){ if(typeof eval(name)!=='undefined')_w[name]=eval(name); }); })(window,document,localStorage,JSON,navigator);