feat: complete session — catalog, marketplace, WhatsApp, peer-to-peer, install scripts

Major features:
- Pixel-Perfect glassmorphism design (landing + POS + public catalog)
- OEM/Local catalog toggle with Nexpart taxonomy (14 groups, 108 subgroups, 558 part types)
- Marketplace B2B Phase 1 (bodegas, POs, status machine, WA+email notifications)
- Peer-to-peer inventory (multi-instance, LAN discovery)
- WhatsApp: photo→Vision AI, voice→Whisper, conversational quotations
- Smart unified search (VIN/plate/part_number/keyword auto-detect)
- Shop Supplies tab (vehicle-independent parts)
- Chatbot AI fallback chain (5 models) + response cache
- CSV inventory import tool + setup_instance.sh installer
- Tablet-responsive CSS + sidebar toggle
- Filters, export CSV, employee edit, business data save
- Quotation system (WA→POS) with auto-print on confirmation
- Live stats on landing page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-18 05:35:53 +00:00
parent 6b097614a0
commit e95f7cf684
54 changed files with 11226 additions and 1422 deletions

View File

@@ -6,6 +6,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Facturación CFDI — Nexus Autoparts POS</title>
<link rel="stylesheet" href="/pos/static/css/tokens.css" />
<link rel="stylesheet" href="/pos/static/css/pos-glass.css" />
<link rel="manifest" href="/pos/static/pwa/manifest.json" />
<meta name="theme-color" content="#F5A623" />
@@ -1516,15 +1517,43 @@
<h1 class="page-header__title">Facturación CFDI</h1>
</div>
<div class="page-header__actions">
<button class="btn btn--ghost">
<button class="btn btn--ghost" onclick="openInvoiceFilters(this)">
<svg viewBox="0 0 24 24"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>
Filtros
</button>
<button class="btn btn--ghost" onclick="exportVisibleTableCSV('facturacion')">
<svg viewBox="0 0 24 24">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
<polyline points="7 10 12 15 17 10"/>
<line x1="12" y1="15" x2="12" y2="3"/>
</svg>
Exportar
Exportar CSV
</button>
<button class="btn btn--secondary" onclick="window.notaCreditoPlaceholder()">
<script>
function openInvoiceFilters(btn) {
var table = document.querySelector('table');
if (!table) { showToast('Carga las facturas primero', 'warn'); return; }
var ths = table.querySelectorAll('thead th');
var colMap = {};
ths.forEach(function(th, i) {
var t = th.textContent.trim().toLowerCase();
if (t.indexOf('status') !== -1 || t.indexOf('estado') !== -1) colMap.status = i;
if (t.indexOf('cliente') !== -1 || t.indexOf('receptor') !== -1) colMap.cliente = i;
if (t.indexOf('tipo') !== -1) colMap.tipo = i;
});
var filters = [];
if (colMap.status !== undefined) filters.push({label:'Estado', column: colMap.status, values: getUniqueColumnValues(table, colMap.status)});
if (colMap.tipo !== undefined) filters.push({label:'Tipo', column: colMap.tipo, values: getUniqueColumnValues(table, colMap.tipo)});
if (colMap.cliente !== undefined) filters.push({label:'Cliente', column: colMap.cliente, values: getUniqueColumnValues(table, colMap.cliente, 15)});
if (filters.length === 0) {
for (var i = 1; i < Math.min(3, ths.length); i++) {
filters.push({label: ths[i].textContent.trim(), column: i, values: getUniqueColumnValues(table, i)});
}
}
toggleFilterPanel(btn, filters);
}
</script>
<button class="btn btn--secondary" onclick="showToast('Nota de Crédito requiere integración SAT — disponible en siguiente actualización', 'info')">
<svg viewBox="0 0 24 24">
<path d="M9 14l-4-4 4-4"/>
<path d="M5 10h11a4 4 0 0 1 0 8h-1"/>
@@ -2359,6 +2388,7 @@
<script src="/pos/static/js/i18n.js"></script>
<script src="/pos/static/js/app-init.js"></script>
<script src="/pos/static/js/pos-utils.js"></script>
<script src="/pos/static/js/sidebar.js"></script>
<script src="/pos/static/js/invoicing.js"></script>
<script src="/pos/static/js/sync-engine.js"></script>