window.addEventListener('DOMContentLoaded', () => { const CLP = new Intl.NumberFormat('es-CL', { style: 'currency', currency: 'CLP', maximumFractionDigits: 0 }); // --- Selección de Elementos del DOM --- const invoiceInput = 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 totalSpan = document.getElementById('total'); const errorEl = document.getElementById('error'); const budgetErrorEl = document.getElementById('budget-error'); const processBtn = document.getElementById('process-btn'); const resetBtn = document.getElementById('reset-btn'); const recordsTbody = document.getElementById('records-tbody'); const summaryBudgetEl = document.getElementById('summary-budget'); const summarySpentEl = document.getElementById('summary-spent'); const summaryVariationEl = document.getElementById('summary-variation'); const budgetProgressBar = document.getElementById('budget-progress-bar'); // Este es el elemento que causaba el problema, ahora debería existir en tu HTML const summaryBudgetFilenameEl = document.getElementById('summary-budget-filename'); // --- Almacenamiento de Datos en Memoria --- const docs = []; const records = []; let budgetFile = null; // --- Funciones de Simulación y Ayuda --- const formatDate = isoString => new Date(isoString).toLocaleDateString('es-CL'); const randomAmount = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; const randomFolio = () => 'F' + Math.floor(100000 + Math.random() * 900000); const parseCSVForAmount = 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; }; // --- Funciones para Actualizar la Interfaz (Render) --- const renderDocs = () => { totalSpan.textContent = `${docs.length} en total`; tbody.innerHTML = ''; if (docs.length === 0) { tbody.innerHTML = `Aún no hay documentos.`; } else { docs.forEach((doc, index) => { const tr = document.createElement('tr'); let documentCell = doc.name; if (doc.file && doc.file.type.startsWith('image/')) { const imageURL = URL.createObjectURL(doc.file); documentCell = `${doc.name}`; } tr.innerHTML = ` ${index + 1} ${doc.folio} ${documentCell} ${formatDate(doc.loadedAt)} ${CLP.format(doc.amount)}`; tbody.appendChild(tr); }); } }; const updateSummary = () => { const totalDocsAmount = docs.reduce((sum, doc) => sum + (doc.amount || 0), 0); const budgetAmount = budgetFile ? budgetFile.amount : 0; const variation = budgetAmount - totalDocsAmount; summaryBudgetEl.textContent = CLP.format(budgetAmount); summarySpentEl.textContent = CLP.format(totalDocsAmount); summaryVariationEl.textContent = CLP.format(variation); summaryVariationEl.style.color = variation < 0 ? 'var(--red)' : 'var(--green)'; // Esta sección ahora funcionará porque el elemento existe if (budgetFile && summaryBudgetFilenameEl) { summaryBudgetFilenameEl.textContent = budgetFile.name; } else if (summaryBudgetFilenameEl) { summaryBudgetFilenameEl.textContent = ''; } if (budgetAmount > 0) { const usagePercent = Math.max(0, (totalDocsAmount / budgetAmount) * 100); budgetProgressBar.style.width = `${Math.min(usagePercent, 100)}%`; if (usagePercent > 100) { budgetProgressBar.style.backgroundColor = 'var(--red)'; } else if (usagePercent > 80) { budgetProgressBar.style.backgroundColor = 'var(--yellow)'; } else { budgetProgressBar.style.backgroundColor = 'var(--green)'; } } else { budgetProgressBar.style.width = '0%'; } }; const renderRecords = () => { recordsTbody.innerHTML = ''; if (records.length === 0) { recordsTbody.innerHTML = `No hay registros todavía.`; } else { records.forEach((record, index) => { const tr = document.createElement('tr'); const variationColor = record.diff < 0 ? 'var(--red)' : 'var(--green)'; const sign = record.diff >= 0 ? '+' : ''; tr.innerHTML = ` ${record.docsCount} ${CLP.format(record.docsSum)} ${CLP.format(record.budget)} ${sign}${CLP.format(record.diff)} `; recordsTbody.appendChild(tr); }); } }; const addInvoiceFiles = files => { errorEl.textContent = ''; Array.from(files).forEach(file => { docs.unshift({ name: file.name, loadedAt: new Date().toISOString(), file: file, amount: randomAmount(10000, 250000), folio: randomFolio() }); }); renderDocs(); updateSummary(); }; const setBudgetFile = file => { if (!file || !file.name.toLowerCase().endsWith('.csv')) { budgetErrorEl.textContent = 'Solo se permite un archivo CSV.'; return; } budgetErrorEl.textContent = ''; const reader = new FileReader(); reader.onload = e => { const content = e.target.result; let amount = parseCSVForAmount(content) || randomAmount(200000, 2000000); budgetFile = { name: file.name, loadedAt: new Date().toISOString(), amount: amount, file: file }; updateSummary(); }; reader.readAsText(file); }; // --- Asignación de Eventos --- invoiceInput.addEventListener('change', () => { addInvoiceFiles(invoiceInput.files); invoiceInput.value = ''; }); budgetInput.addEventListener('change', () => { if (budgetInput.files.length > 0) { setBudgetFile(budgetInput.files[0]); budgetInput.value = ''; } }); ['dragenter', 'dragover'].forEach(e => dropzone.addEventListener(e, evt => { evt.preventDefault(); evt.stopPropagation(); dropzone.classList.add('drag'); })); ['dragleave', 'drop'].forEach(e => dropzone.addEventListener(e, evt => { evt.preventDefault(); evt.stopPropagation(); dropzone.classList.remove('drag'); if (e === 'drop') addInvoiceFiles(evt.dataTransfer.files); })); ['dragenter', 'dragover'].forEach(e => budgetDropzone.addEventListener(e, evt => { evt.preventDefault(); evt.stopPropagation(); budgetDropzone.classList.add('drag'); })); ['dragleave', 'drop'].forEach(e => budgetDropzone.addEventListener(e, evt => { evt.preventDefault(); evt.stopPropagation(); budgetDropzone.classList.remove('drag'); if (e === 'drop' && evt.dataTransfer.files.length > 0) setBudgetFile(evt.dataTransfer.files[0]); })); processBtn.addEventListener('click', async () => { if (!budgetFile || docs.length === 0) { alert('Carga un presupuesto y al menos un documento.'); return; } 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(); }); resetBtn.addEventListener('click', () => { if (confirm('¿Estás seguro? Se borrarán todos los datos de la simulación.')) { docs.length = 0; records.length = 0; budgetFile = null; errorEl.textContent = ''; budgetErrorEl.textContent = ''; renderDocs(); updateSummary(); renderRecords(); } }); recordsTbody.addEventListener('click', e => { if (e.target.classList.contains('delete-btn')) { const index = parseInt(e.target.dataset.index, 10); if (!isNaN(index)) { records.splice(index, 1); renderRecords(); } } }); // --- Estado Inicial --- updateSummary(); renderDocs(); renderRecords(); });