window.addEventListener('DOMContentLoaded', () => { const CLP = new Intl.NumberFormat('es-CL', { style: 'currency', currency: 'CLP', maximumFractionDigits: 0 }); // Inputs y elementos const input = document.getElementById('invoice-input'); const budgetInput = document.getElementById('budget-input'); const dropzone = document.getElementById('dropzone'); const budgetDropzone = document.getElementById('budget-dropzone'); const tbody = document.getElementById('tbody'); const budgetTbody = document.getElementById('budget-tbody-right'); const total = document.getElementById('total'); const errorEl = document.getElementById('error'); const budgetError = document.getElementById('budget-error'); const processBtn = document.getElementById('process-btn'); const resetBtn = document.getElementById('reset-btn'); const summary = document.getElementById('summary'); const recordsTbody = document.getElementById('records-tbody'); // Datos const docs = []; const records = []; let budgetFile = null; // Funciones auxiliares const fmtDate = iso => new Date(iso).toLocaleString(); const randomAmount = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; const randomFolio = () => 'F' + Math.floor(100000 + Math.random() * 900000); const parseCSV = content => { const lines = content.split('\n'); for (const line of lines) { const matches = line.match(/(\d{1,3}(?:\.\d{3})*|\d+)/g); if (matches && matches.length > 0) { const amount = parseInt(matches[0].replace(/\./g, '')); if (!isNaN(amount) && amount > 1000) return amount; } } return null; }; // Renderizar tablas const renderDocs = () => { total.textContent = `${docs.length} en total`; tbody.innerHTML = ''; if (!docs.length) { tbody.innerHTML = `Aún no hay documentos. Sube un PDF o imagen para comenzar.`; } else { docs.forEach((d, i) => { const monto = CLP.format(d.amount); const tr = document.createElement('tr'); tr.innerHTML = `${i+1}${d.folio}${d.name}${fmtDate(d.loadedAt)}${monto}`; tbody.appendChild(tr); }); } }; const renderBudget = () => { budgetTbody.innerHTML = ''; if (!budgetFile) { budgetTbody.innerHTML = `Aún no se ha cargado el presupuesto.`; } else { const tr = document.createElement('tr'); tr.innerHTML = `${budgetFile.name}${fmtDate(budgetFile.loadedAt)}${CLP.format(budgetFile.amount)}`; budgetTbody.appendChild(tr); } }; const updateSummary = () => { if (!budgetFile || !docs.length) { summary.innerHTML = ''; return; } const totalDocs = docs.reduce((sum, doc) => sum + (doc.amount || 0), 0); const variation = budgetFile.amount - totalDocs; const percentVariation = budgetFile.amount > 0 ? (variation / budgetFile.amount * 100) : 0; summary.innerHTML = `Total documentos: ${CLP.format(totalDocs)}
Presupuesto: ${CLP.format(budgetFile.amount)}
Variación: ${CLP.format(variation)} (${percentVariation.toFixed(1)}%)`; }; const renderRecords = () => { recordsTbody.innerHTML = ''; if (!records.length) { recordsTbody.innerHTML = `No hay registros todavía. Guarda un proceso para verlo aquí.`; } else { records.forEach((record, i) => { const tr = document.createElement('tr'); const variationText = record.diff >= 0 ? `+${CLP.format(record.diff)}` : CLP.format(record.diff); tr.innerHTML = `${record.docsCount}${CLP.format(record.docsSum)}${CLP.format(record.budget)}${variationText} `; recordsTbody.appendChild(tr); }); } }; window.deleteRecord = idx => { if (!isNaN(idx) && idx >= 0 && idx < records.length) { records.splice(idx, 1); renderRecords(); } }; // Subir archivos const addFiles = files => { const nowIso = new Date().toISOString(); errorEl.textContent = ''; Array.from(files).forEach(f => { if (f) { docs.unshift({ name: f.name, loadedAt: nowIso, file: f, amount: randomAmount(10000, 250000), folio: randomFolio() }); } else { errorEl.textContent = 'Solo se permiten archivos PDF o imágenes.'; } }); renderDocs(); updateSummary(); }; const setBudgetFromFile = f => { if (!f) return; if (!f.name.toLowerCase().endsWith('.csv')) { budgetError.textContent = 'Solo se permite un archivo CSV.'; return; } budgetError.textContent = ''; const reader = new FileReader(); reader.onload = e => { const content = e.target.result; let amount = parseCSV(content); if (amount === null) amount = randomAmount(200000, 2000000); budgetFile = { name: f.name, loadedAt: new Date().toISOString(), amount }; renderBudget(); updateSummary(); }; reader.readAsText(f); }; // Eventos input.addEventListener('change', () => { addFiles(input.files); input.value = ''; }); budgetInput.addEventListener('change', () => { if (budgetInput.files.length > 0) { setBudgetFromFile(budgetInput.files[0]); budgetInput.value = ''; } }); // Drag & drop facturas ['dragenter', 'dragover'].forEach(evt => dropzone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); dropzone.classList.add('drag'); })); ['dragleave', 'drop'].forEach(evt => dropzone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); dropzone.classList.remove('drag'); if (e.type === 'drop') addFiles(e.dataTransfer.files); })); // Drag & drop presupuesto ['dragenter', 'dragover'].forEach(evt => budgetDropzone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); budgetDropzone.classList.add('drag'); })); ['dragleave', 'drop'].forEach(evt => budgetDropzone.addEventListener(evt, e => { e.preventDefault(); e.stopPropagation(); budgetDropzone.classList.remove('drag'); if (e.type === 'drop' && e.dataTransfer.files.length > 0) setBudgetFromFile(e.dataTransfer.files[0]); })); processBtn.addEventListener('click', async () => { if (!budgetFile || !docs.length) { alert('Carga al menos un PDF/imagen y un CSV de presupuesto.'); return; } processBtn.textContent = 'Procesando...'; processBtn.disabled = true; await new Promise(r => setTimeout(r, 800)); const totalDocs = docs.reduce((sum, d) => sum + d.amount, 0); records.push({ docsCount: docs.length, docsSum: totalDocs, budget: budgetFile.amount, diff: budgetFile.amount - totalDocs }); renderRecords(); processBtn.textContent = 'Procesar'; processBtn.disabled = false; }); resetBtn.addEventListener('click', () => { if (confirm('¿Estás seguro de que quieres reiniciar todo?')) { docs.length = 0; records.length = 0; budgetFile = null; errorEl.textContent = ''; budgetError.textContent = ''; input.value = ''; budgetInput.value = ''; renderDocs(); renderBudget(); renderRecords(); updateSummary(); summary.innerHTML = ''; } }); // Inicial renderDocs(); renderBudget(); renderRecords(); });