fix(audit): corrige errores criticos y mayores, mejora UX/accesibilidad y optimiza rendimiento
- Arregla @require_auth, permisos, race conditions, locks de caja/stock - Elimina N+1 en layaway, flotilla, dashboard y global_invoice - Asegura folios atomicos para CFDI, ordenes de servicio y polizas - Protege client_secret de MercadoLibre en backend - Conecta botones/filtros de config, customers, accounting e invoicing - Mejora accesibilidad (labels/aria-label) y estados de carga/vacio - Limpia accounting.js obsoleto y consolida accounting.v9.js - Actualiza cache busting a v32 y Service Worker a v32 - Documenta todo en docs/AUDIT_Y_MEJORAS_2026-06-15.md Tests: 35 passed
This commit is contained in:
@@ -43,6 +43,9 @@ const Customers = (() => {
|
||||
const tierClass = { 1: 'mostrador', 2: 'taller', 3: 'mayoreo' };
|
||||
|
||||
function statusBadge(c) {
|
||||
if (c.is_active === false) {
|
||||
return '<span class="badge badge--inactive"><span class="badge-dot"></span>Inactivo</span>';
|
||||
}
|
||||
// Derive status: if credit_balance > credit_limit => Mora, else Activo
|
||||
if (c.credit_balance > 0 && c.credit_limit > 0 && c.credit_balance > c.credit_limit) {
|
||||
return '<span class="badge badge--warning"><span class="badge-dot"></span>Mora</span>';
|
||||
@@ -82,14 +85,27 @@ const Customers = (() => {
|
||||
const searchEl = document.getElementById('searchInput');
|
||||
q = q !== undefined ? q : (searchEl ? searchEl.value || '' : '');
|
||||
|
||||
const tipoEl = document.getElementById('tipoFilter');
|
||||
const estadoEl = document.getElementById('estadoFilter');
|
||||
const tipo = tipoEl ? tipoEl.value : '';
|
||||
// Map UI status labels to backend values
|
||||
const estadoMap = { 'Activo': 'active', 'Inactivo': 'inactive', 'Mora': 'overdue' };
|
||||
const estado = estadoEl ? (estadoMap[estadoEl.value] || 'all') : 'active';
|
||||
|
||||
const tbody = document.getElementById('customersBody');
|
||||
if (tbody) tbody.innerHTML = '<tr><td colspan="11">' + renderLoadingState({ message: 'Cargando clientes...' }) + '</td></tr>';
|
||||
|
||||
try {
|
||||
const params = new URLSearchParams({ page, per_page: 50 });
|
||||
if (q) params.append('q', q);
|
||||
if (tipo) params.append('price_tier', tipo);
|
||||
if (estado) params.append('status', estado);
|
||||
|
||||
const data = await api(`/pos/api/customers?${params}`);
|
||||
renderTable(data.data || []);
|
||||
renderPagination(data.pagination || {});
|
||||
} catch (e) {
|
||||
if (tbody) tbody.innerHTML = '<tr><td colspan="11">' + renderEmptyState({ title: 'Error', subtitle: 'No se pudieron cargar los clientes.' }) + '</td></tr>';
|
||||
console.error('Load customers failed:', e);
|
||||
}
|
||||
}
|
||||
@@ -136,7 +152,7 @@ const Customers = (() => {
|
||||
icon: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/></svg>',
|
||||
title: 'Sin clientes',
|
||||
subtitle: 'No se encontraron clientes registrados.',
|
||||
action: '<button class="btn btn--primary btn--sm" onclick="Customers.openCreateModal()">Nuevo cliente</button>'
|
||||
action: '<button class="btn btn--primary btn--sm" onclick="Customers.showCreateModal()">Nuevo cliente</button>'
|
||||
}) + '</td></tr>';
|
||||
return;
|
||||
}
|
||||
@@ -650,7 +666,7 @@ const Customers = (() => {
|
||||
try {
|
||||
await api(`/pos/api/customers/${currentCustomer.id}/payment`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ amount, method, reference }),
|
||||
body: JSON.stringify({ amount, payment_method: method, reference }),
|
||||
});
|
||||
closePayment();
|
||||
selectCustomer(currentCustomer.id);
|
||||
@@ -892,6 +908,14 @@ const Customers = (() => {
|
||||
showCustomerHistory, closeCustomerHistoryModal,
|
||||
};
|
||||
|
||||
// Register Cmd+K items
|
||||
if (typeof registerCmdKItem === "function") {
|
||||
registerCmdKItem({ group: "Principal", label: "POS Ventas", href: "/pos/sale", icon: "🛒" });
|
||||
registerCmdKItem({ group: "Principal", label: "Catálogo", href: "/pos/catalog", icon: "📁" });
|
||||
registerCmdKItem({ group: "Principal", label: "Clientes", href: "/pos/customers", icon: "👤" });
|
||||
registerCmdKItem({ group: "Principal", label: "Dashboard", href: "/pos/dashboard", icon: "📊" });
|
||||
}
|
||||
|
||||
// Bulk selection
|
||||
publicApi.toggleCustomerSelection = function(id) {
|
||||
if (selectedCustomers.has(id)) selectedCustomers.delete(id);
|
||||
@@ -927,15 +951,15 @@ const Customers = (() => {
|
||||
updateBulkToolbar();
|
||||
};
|
||||
|
||||
publicApi.featureProximamente = function(name) {
|
||||
if (typeof window.featureProximamente === 'function') {
|
||||
window.featureProximamente(name);
|
||||
} else {
|
||||
alert(name + ' — próximamente');
|
||||
}
|
||||
};
|
||||
|
||||
// Expose globally for inline HTML onclick handlers
|
||||
window.Customers = publicApi;
|
||||
return publicApi;
|
||||
// Register Cmd+K items
|
||||
if (typeof registerCmdKItem === "function") {
|
||||
registerCmdKItem({ group: "Principal", label: "POS Ventas", href: "/pos/sale", icon: "🛒" });
|
||||
registerCmdKItem({ group: "Principal", label: "Catálogo", href: "/pos/catalog", icon: "📁" });
|
||||
registerCmdKItem({ group: "Principal", label: "Clientes", href: "/pos/customers", icon: "👤" });
|
||||
registerCmdKItem({ group: "Principal", label: "Dashboard", href: "/pos/dashboard", icon: "📊" });
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user