From 1967ad10731ad1bab960cf2d5fba19edc6b52ad8 Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Fri, 12 Jun 2026 07:33:37 +0000 Subject: [PATCH] feat(reports/dashboard): integrate historical sales viewer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add 'Histórico' tab inside Reports page with date/customer filters - Show historical sales KPIs and detail table in reports - Add historical sales summary cards to Dashboard - Load current month totals and total imported records --- pos/static/js/dashboard.js | 45 +++++++++++++++++++ pos/static/js/reports.js | 84 +++++++++++++++++++++++++++++++++++- pos/templates/dashboard.html | 62 ++++++++++++++++++++++++++ pos/templates/reports.html | 32 ++++++++++++++ 4 files changed, 221 insertions(+), 2 deletions(-) diff --git a/pos/static/js/dashboard.js b/pos/static/js/dashboard.js index fd42637..c57dcbc 100644 --- a/pos/static/js/dashboard.js +++ b/pos/static/js/dashboard.js @@ -205,6 +205,50 @@ const Dashboard = (() => { return data; } + // ------------------------------------------------------------------------- + // 1b. Historical sales KPIs (imported data) + // ------------------------------------------------------------------------- + async function loadHistoricalSummary() { + try { + const now = new Date(); + const firstDay = new Date(now.getFullYear(), now.getMonth(), 1).toISOString().slice(0, 10); + const lastDay = new Date(now.getFullYear(), now.getMonth() + 1, 0).toISOString().slice(0, 10); + + // All historical sales + const all = await apiFetch('/pos/api/historical-sales?per_page=1'); + const totalRecords = all.pagination ? all.pagination.total : 0; + + // Current month historical sales + const month = await apiFetch(`/pos/api/historical-sales?date_from=${firstDay}&date_to=${lastDay}&per_page=200`); + const monthRows = month.data || []; + const monthTotal = monthRows.reduce((a, r) => a + (r.total || 0), 0); + + const totalEl = document.getElementById('kpi-historico-total-value'); + const totalMetaEl = document.getElementById('kpi-historico-total-meta'); + if (totalEl) totalEl.textContent = fmt(monthTotal); + if (totalMetaEl) totalMetaEl.innerHTML = `${fmtInt(totalRecords)} tickets importados`; + + const mesEl = document.getElementById('kpi-historico-mes-value'); + const mesMetaEl = document.getElementById('kpi-historico-mes-meta'); + if (mesEl) mesEl.textContent = fmt(monthTotal); + if (mesMetaEl) mesMetaEl.innerHTML = `${monthRows.length} tickets este mes`; + + const countEl = document.getElementById('kpi-historico-count-value'); + const countMetaEl = document.getElementById('kpi-historico-count-meta'); + if (countEl) countEl.textContent = fmtInt(totalRecords); + if (countMetaEl) countMetaEl.innerHTML = `Registros históricos`; + + } catch (err) { + console.error('Error loading historical summary:', err); + const ids = [ + ['kpi-historico-total-value', 'kpi-historico-total-meta'], + ['kpi-historico-mes-value', 'kpi-historico-mes-meta'], + ['kpi-historico-count-value', 'kpi-historico-count-meta'], + ]; + ids.forEach(([v, m]) => setKpiError(v, m)); + } + } + function setKpiError(valueId, metaId) { const v = document.getElementById(valueId); const m = document.getElementById(metaId); @@ -533,6 +577,7 @@ const Dashboard = (() => { // Load all data in parallel loadDailySummary(); + loadHistoricalSummary(); loadAlerts(); loadTopProducts(); loadWeeklyChart(); diff --git a/pos/static/js/reports.js b/pos/static/js/reports.js index 7539c90..7e78c2d 100644 --- a/pos/static/js/reports.js +++ b/pos/static/js/reports.js @@ -54,7 +54,7 @@ const Reports = (() => { } // Track which tabs have been loaded - var loaded = { ventas: false, inventario: false, clientes: false, financieros: false }; + var loaded = { ventas: false, inventario: false, clientes: false, financieros: false, historico: false }; // ------------------------------------------------------------------------- // Theme switcher @@ -85,6 +85,7 @@ const Reports = (() => { else if (id === 'inventario') loadInventario(); else if (id === 'clientes') loadClientes(); else if (id === 'financieros') loadFinancieros(); + else if (id === 'historico') loadHistorico(); } } window.switchTab = switchTab; @@ -289,6 +290,85 @@ const Reports = (() => { } } + // ========================================================================= + // TAB 5: HISTÓRICO + // ========================================================================= + async function loadHistorico() { + loaded.historico = true; + var dateFrom = document.getElementById('historico-date-from').value; + var dateTo = document.getElementById('historico-date-to').value; + var customer = document.getElementById('historico-customer').value.trim(); + + var params = new URLSearchParams(); + if (dateFrom) params.set('date_from', dateFrom); + if (dateTo) params.set('date_to', dateTo); + if (customer) params.set('customer', customer); + params.set('per_page', '200'); + + var kpiEl = document.getElementById('historico-kpis'); + var detalleEl = document.getElementById('historico-detalle'); + kpiEl.innerHTML = spinner(); + detalleEl.innerHTML = spinner(); + + try { + var allRows = []; + var page = 1; + var totalPages = 1; + + while (page <= totalPages) { + params.set('page', page); + var json = await apiFetch('/pos/api/historical-sales?' + params.toString()); + allRows = allRows.concat(json.data || []); + totalPages = json.pagination ? json.pagination.total_pages : 1; + page++; + if (page > 50) break; + } + + var total = allRows.reduce(function(a, r) { return a + r.total; }, 0); + var subtotal = allRows.reduce(function(a, r) { return a + r.subtotal; }, 0); + var balance = allRows.reduce(function(a, r) { return a + r.balance; }, 0); + + kpiEl.innerHTML = + kpiCard('Total Histórico', '$' + fmt(total), allRows.length + ' registros') + + kpiCard('Subtotal', '$' + fmt(subtotal), '') + + kpiCard('Saldo Pendiente', '$' + fmt(balance), '') + + kpiCard('Tickets', fmtInt(allRows.length), ''); + + var html = '
Ventas Históricas Importadas' + + '' + allRows.length + ' registros
'; + html += '
' + + '' + + '' + + '' + + ''; + allRows.slice(0, 200).forEach(function(r) { + html += '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + }); + html += '
FechaDocumentoClientePagoSubtotalTotalPagadoSaldo
' + fmtDate(r.sale_date) + '' + esc(r.document_no || r.external_document_id || '--') + '' + esc(r.customer_name || '--') + '' + esc(r.payment_method || '--') + '$' + fmt(r.subtotal) + '$' + fmt(r.total) + '$' + fmt(r.amount_paid) + '$' + fmt(r.balance) + '
'; + detalleEl.innerHTML = html; + + } catch (err) { + kpiEl.innerHTML = errorMsg('Error cargando histórico: ' + err.message); + detalleEl.innerHTML = ''; + } + } + + function esc(s) { + if (s == null) return ''; + return String(s).replace(/[&<>"']/g, function(c) { + return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; + }); + } + // ========================================================================= // TAB 2: INVENTARIO // ========================================================================= @@ -712,7 +792,7 @@ const Reports = (() => { return { init, setTheme, switchTab, - loadVentas, loadInventario, loadClientes, loadFinancieros, fmt + loadVentas, loadInventario, loadClientes, loadFinancieros, loadHistorico, fmt }; // Register Cmd+K items if (typeof registerCmdKItem === "function") { diff --git a/pos/templates/dashboard.html b/pos/templates/dashboard.html index 436dc31..3984d9b 100644 --- a/pos/templates/dashboard.html +++ b/pos/templates/dashboard.html @@ -329,6 +329,68 @@ + +
+
+ Histórico importado + Ver en reportes → +
+
+
+
+
+ Total Histórico + + + + + + +
+
+
+
+
+
+ +
+
+
+ Este Mes (Histórico) + + + + + + +
+
+
+
+
+
+ +
+
+
+ Tickets Históricos + + + + + + +
+
+
+
+
+
+
+
+ diff --git a/pos/templates/reports.html b/pos/templates/reports.html index 3fab3de..df4b243 100644 --- a/pos/templates/reports.html +++ b/pos/templates/reports.html @@ -216,6 +216,14 @@ Financieros +
+ + +
+ + +
+ Desde + + Hasta + + Cliente + +
+ +
+ + +
+ + +
+