feat(reports/dashboard): integrate historical sales viewer
- 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
This commit is contained in:
@@ -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 = '<div class="table-card__header"><span class="table-card__title">Ventas Históricas Importadas</span>' +
|
||||
'<span class="pill pill--muted">' + allRows.length + ' registros</span></div>';
|
||||
html += '<div class="table-wrap"><table class="data-table"><thead><tr>' +
|
||||
'<th>Fecha</th><th>Documento</th><th>Cliente</th><th>Pago</th>' +
|
||||
'<th class="align-right">Subtotal</th><th class="align-right">Total</th>' +
|
||||
'<th class="align-right">Pagado</th><th class="align-right">Saldo</th>' +
|
||||
'</tr></thead><tbody>';
|
||||
allRows.slice(0, 200).forEach(function(r) {
|
||||
html += '<tr>' +
|
||||
'<td>' + fmtDate(r.sale_date) + '</td>' +
|
||||
'<td class="td-mono">' + esc(r.document_no || r.external_document_id || '--') + '</td>' +
|
||||
'<td>' + esc(r.customer_name || '--') + '</td>' +
|
||||
'<td><span class="pill pill--muted">' + esc(r.payment_method || '--') + '</span></td>' +
|
||||
'<td class="align-right td-mono">$' + fmt(r.subtotal) + '</td>' +
|
||||
'<td class="align-right td-mono-accent">$' + fmt(r.total) + '</td>' +
|
||||
'<td class="align-right td-mono">$' + fmt(r.amount_paid) + '</td>' +
|
||||
'<td class="align-right td-mono">$' + fmt(r.balance) + '</td>' +
|
||||
'</tr>';
|
||||
});
|
||||
html += '</tbody></table></div>';
|
||||
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") {
|
||||
|
||||
Reference in New Issue
Block a user