/* ============================================================ HISTORY AND MOVEMENTS TRACKING ============================================================ */ let historyData = { dateFrom: null, dateTo: null, typeFilter: null, movements: [] }; function getHistoryDateRange() { const fromInput = document.getElementById('history-date-from'); const toInput = document.getElementById('history-date-to'); const typeFilter = document.getElementById('history-type-filter'); const today = new Date(); const thirtyDaysAgo = new Date(today.getTime() - (30 * 24 * 60 * 60 * 1000)); historyData.dateFrom = fromInput?.value ? new Date(fromInput.value) : thirtyDaysAgo; historyData.dateTo = toInput?.value ? new Date(toInput.value + 'T23:59:59') : today; historyData.typeFilter = typeFilter?.value || null; } const _historyTypeConfig = { factura: { icon: 'fa-receipt', color: 'blue', label: 'FACTURA' }, stock: { icon: 'fa-boxes-stacked', color: 'green', label: 'STOCK' }, precio: { icon: 'fa-tag', color: 'amber', label: 'PRECIO' }, producto: { icon: 'fa-edit', color: 'purple', label: 'PRODUCTO' }, bulk: { icon: 'fa-table', color: 'indigo', label: 'MASIVO' }, unknown: { icon: 'fa-circle', color: 'gray', label: 'OTRO' }, }; function getAllMovements() { const movements = []; // Facturas if (!historyData.typeFilter || historyData.typeFilter === 'factura') { STATE.adminInvoices.forEach(inv => { const invDate = new Date(inv.date); if (invDate >= historyData.dateFrom && invDate <= historyData.dateTo) { movements.push({ date: inv.date, type: 'factura', title: `Factura #${inv.invoiceNumber || inv.id}`, description: `Cliente: ${inv.client}`, amount: fmt(inv.total), details: `${(inv.items || []).length} producto(s)`, oldValue: null, newValue: null, note: null }); } }); } // changeLog entries const logEntries = STATE.changeLog || []; logEntries.forEach(entry => { const entryDate = new Date(entry.date); if (entryDate < historyData.dateFrom || entryDate > historyData.dateTo) return; if (historyData.typeFilter && entry.type !== historyData.typeFilter) return; const cfg = _historyTypeConfig[entry.type] || _historyTypeConfig.unknown; movements.push({ date: entry.date, type: entry.type, title: entry.productName ? `${cfg.label}: ${entry.productName}` : `${cfg.label}`, description: entry.note || (entry.field ? `Campo: ${entry.field}` : ''), amount: entry.newValue ? `→ ${entry.newValue}` : '', details: entry.oldValue ? `Anterior: ${entry.oldValue}` : '', oldValue: entry.oldValue, newValue: entry.newValue, note: entry.note }); }); historyData.movements = movements.sort((a, b) => new Date(b.date) - new Date(a.date)); return historyData.movements; } function generateHistoryReport() { getHistoryDateRange(); const movements = getAllMovements(); const invoiceCount = movements.filter(m => m.type === 'factura').length; const priceChanges = movements.filter(m => m.type === 'precio').length; const productChanges = movements.filter(m => m.type === 'producto' || m.type === 'bulk').length; const stockChanges = movements.filter(m => m.type === 'stock').length; const inv = document.getElementById('history-total-invoices'); const pc = document.getElementById('history-price-changes'); const pd = document.getElementById('history-product-changes'); const sc = document.getElementById('history-stock-changes'); if (inv) inv.textContent = invoiceCount; if (pc) pc.textContent = priceChanges; if (pd) pd.textContent = productChanges; if (sc) sc.textContent = stockChanges; renderHistoryTimeline(movements); renderHistoryTable(movements); } const _colorBg = { blue: '#DBEAFE', green: '#DCFCE7', amber: '#FEF3C7', purple: '#F3E8FF', indigo: '#E0E7FF', gray: '#F3F4F6' }; const _colorText = { blue: '#1E40AF', green: '#166534', amber: '#92400E', purple: '#6B21A8', indigo: '#3730A3', gray: '#374151' }; const _colorDot = { blue: '#3B82F6', green: '#10B981', amber: '#F59E0B', purple: '#8B5CF6', indigo: '#6366F1', gray: '#9CA3AF' }; function renderHistoryTimeline(movements) { const timeline = document.getElementById('history-timeline'); if (!timeline) return; if (movements.length === 0) { timeline.innerHTML = '
No hay movimientos en este período
'; return; } const grouped = {}; movements.forEach(m => { const date = formatDate(new Date(m.date)); if (!grouped[date]) grouped[date] = []; grouped[date].push(m); }); const cfg = (type) => _historyTypeConfig[type] || _historyTypeConfig.unknown; timeline.innerHTML = Object.entries(grouped).map(([date, items]) => `
${date}
${items.map(m => { const c = cfg(m.type); return `

${escapeHtml(m.title)}

${escapeHtml(m.description)}

${m.oldValue || m.newValue ? `

${m.oldValue ? `◀ ${escapeHtml(m.oldValue)}` : ''} ${m.oldValue && m.newValue ? ' → ' : ''} ${m.newValue ? `${escapeHtml(m.newValue)} ▶` : ''}

` : ''}

${escapeHtml(m.details)} ${m.amount ? '• ' + escapeHtml(m.amount) : ''}

`; }).join('')}
`).join(''); } function renderHistoryTable(movements) { const tbody = document.getElementById('history-table-tbody'); if (!tbody) return; if (movements.length === 0) { tbody.innerHTML = 'No hay movimientos'; return; } const cfg = (type) => _historyTypeConfig[type] || _historyTypeConfig.unknown; tbody.innerHTML = movements.map(m => { const c = cfg(m.type); return ` ${new Date(m.date).toLocaleString('es-AR')} ${c.label} ${escapeHtml(m.title)} ${escapeHtml(m.description)} ${m.oldValue ? escapeHtml(m.oldValue) : '—'} ${m.newValue ? escapeHtml(m.newValue) : (m.amount || '—')} `; }).join(''); }