File size: 7,670 Bytes
db51998
 
5ecd624
 
db51998
 
 
 
 
 
 
 
 
 
 
 
 
 
5ecd624
db51998
 
 
 
5ecd624
 
 
 
db51998
5ecd624
db51998
5ecd624
db51998
5ecd624
 
 
db51998
 
 
5ecd624
db51998
5ecd624
 
db51998
 
5ecd624
db51998
 
5ecd624
db51998
 
 
 
 
 
5ecd624
7ed0821
5ecd624
 
 
db51998
 
 
 
 
 
5ecd624
db51998
5ecd624
 
 
 
 
 
db51998
5ecd624
db51998
 
 
5ecd624
db51998
5ecd624
 
 
db51998
 
5ecd624
db51998
5ecd624
 
db51998
 
 
 
5ecd624
db51998
5ecd624
 
 
 
 
7ed0821
e1c6223
5ecd624
 
db51998
5ecd624
 
 
 
db51998
5ecd624
db51998
 
 
 
5ecd624
fd992de
5ecd624
 
 
 
 
 
 
db51998
5ecd624
db51998
 
5ecd624
 
db51998
 
 
 
5ecd624
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
db51998
5ecd624
db51998
 
5ecd624
 
 
 
 
 
 
 
 
 
 
 
 
 
db51998
 
 
5ecd624
 
 
 
7ed0821
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
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 = `<tr><td colspan="5" style="text-align:center;color:var(--muted);padding:24px;">Aún no hay documentos. Sube un PDF o imagen para comenzar.</td></tr>`;
    } else {
      docs.forEach((d, i) => {
        const monto = CLP.format(d.amount);
        const tr = document.createElement('tr');
        tr.innerHTML = `<td>${i+1}</td><td>${d.folio}</td><td>${d.name}</td><td>${fmtDate(d.loadedAt)}</td><td>${monto}</td>`;
        tbody.appendChild(tr);
      });
    }
  };

  const renderBudget = () => {
    budgetTbody.innerHTML = '';
    if (!budgetFile) {
      budgetTbody.innerHTML = `<tr><td colspan="3" style="text-align:center;color:var(--muted);padding:16px;">Aún no se ha cargado el presupuesto.</td></tr>`;
    } else {
      const tr = document.createElement('tr');
      tr.innerHTML = `<td>${budgetFile.name}</td><td>${fmtDate(budgetFile.loadedAt)}</td><td>${CLP.format(budgetFile.amount)}</td>`;
      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 = `<strong>Total documentos:</strong> ${CLP.format(totalDocs)}<br>
                         <strong>Presupuesto:</strong> ${CLP.format(budgetFile.amount)}<br>
                         <strong>Variación:</strong> ${CLP.format(variation)} (${percentVariation.toFixed(1)}%)`;
  };

  const renderRecords = () => {
    recordsTbody.innerHTML = '';
    if (!records.length) {
      recordsTbody.innerHTML = `<tr><td class="empty" colspan="5">No hay registros todavía. Guarda un proceso para verlo aquí.</td></tr>`;
    } 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 = `<td>${record.docsCount}</td><td>${CLP.format(record.docsSum)}</td><td>${CLP.format(record.budget)}</td><td>${variationText}</td>
                        <td><button class="delete-btn" onclick="deleteRecord(${i})">Eliminar</button></td>`;
        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();
});