Mejoras de sesion: estabilizacion, IA, Nexus Yonke y marketplace B2B
- F0 blindaje: Redis/Celery, pg_hba sin trust LAN, secretos a env 600, backups, webhook auth, jobs muertos - F1 caja: bugs POS, reembolsos al corte, apartados, CFDI, topes de descuento en cotizacion/apartado - F2/F7 IA de catalogo: autocategorizacion, dedup de SKUs con embeddings, OCR de facturas, descripciones, claves SAT, limpieza de fitment - F3 IA en el flujo: vision Yonke, preguntas MeLi, venta cruzada, anomalias, sinonimos, busqueda semantica (rerank/embeddings) - F4/F8 escala y cartera: /metrics+Prometheus+Grafana, pool, score de credito, churn, metricas multi-tenant - Nexus Yonke (M1-M6): modelo de datos 1-de-1, alta por donador, vitrina publica, MeLi usado, consignacion, garantia por dias, reportes, etiquetas - Marketplace B2B entre yonkes (catalogo + pedidos cross-tenant) - Seguridad: auth en /api/admin/* (35 endpoints), alertas por tenant - Migraciones v4.27/v4.28; timers expire-demos y rollup-metrics
This commit is contained in:
@@ -194,11 +194,11 @@
|
||||
<div class="page-header__actions">
|
||||
<button class="btn btn--ghost" id="btnHeaderImport" onclick="document.getElementById('bulkImportModal').classList.add('is-open')">
|
||||
<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="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
|
||||
Importar CSV
|
||||
Importar
|
||||
</button>
|
||||
<button class="btn btn--ghost" onclick="exportVisibleTableCSV('inventario')">
|
||||
<button class="btn btn--ghost" onclick="document.getElementById('exportModal').classList.add('is-open')">
|
||||
<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 CSV
|
||||
Exportar
|
||||
</button>
|
||||
<button class="btn btn--ghost" onclick="loadItems(1,'')">
|
||||
<svg viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-.38-4.93"/></svg>
|
||||
@@ -788,24 +788,41 @@
|
||||
|
||||
<!-- Transfer Modal -->
|
||||
<div class="inv-modal-overlay" id="transferModal">
|
||||
<div class="inv-modal">
|
||||
<div class="inv-modal inv-modal--wide">
|
||||
<div class="inv-modal__header">
|
||||
<h3>Nuevo Traspaso</h3>
|
||||
<h3>Transferencias entre Sucursales</h3>
|
||||
<button class="inv-modal__close" onclick="closeTransferModal()">×</button>
|
||||
</div>
|
||||
<div class="inv-modal__body">
|
||||
<div class="inv-form-grid">
|
||||
<div class="inv-field"><label>ID Producto *</label><input type="number" id="transferItemId" placeholder="ID inventario" /></div>
|
||||
<div class="inv-field"><label>Cantidad *</label><input type="number" id="transferQty" placeholder="Cantidad" /></div>
|
||||
<div class="inv-field"><label>Sucursal Origen *</label><input type="number" id="transferFrom" placeholder="ID sucursal origen" /></div>
|
||||
<div class="inv-field"><label>Sucursal Destino *</label><input type="number" id="transferTo" placeholder="ID sucursal destino" /></div>
|
||||
<div class="inv-field inv-field--full"><label>Notas</label><input type="text" id="transferNotes" placeholder="Notas adicionales" /></div>
|
||||
<div class="cfg-tabs" style="position:static;margin-bottom:12px;">
|
||||
<button type="button" class="cfg-tab-btn active" id="tr-tab-new" onclick="Inventory.switchTransferTab('new')">Nueva Transferencia</button>
|
||||
<button type="button" class="cfg-tab-btn" id="tr-tab-list" onclick="Inventory.switchTransferTab('list')">Enviadas / Por Recibir</button>
|
||||
</div>
|
||||
|
||||
<div id="tr-panel-new">
|
||||
<div class="inv-form-grid">
|
||||
<div class="inv-field"><label>Sucursal Destino *</label><select id="transferTo" class="form-input"><option value="">--</option></select></div>
|
||||
<div class="inv-field inv-field--full"><label>Notas</label><input type="text" id="transferNotes" class="form-input" placeholder="Notas adicionales" /></div>
|
||||
</div>
|
||||
<div style="margin:10px 0;">
|
||||
<input type="text" id="transferScan" class="form-input" placeholder="Escanea código de barras / No. parte y Enter" style="width:100%;" onkeydown="if(event.key==='Enter'){event.preventDefault();Inventory.transferAddByCode(this.value);this.value='';}" />
|
||||
</div>
|
||||
<div id="transferLines" style="max-height:220px;overflow-y:auto;border:1px solid var(--color-border);border-radius:8px;">
|
||||
<p style="padding:10px;color:var(--color-text-muted);font-size:var(--text-caption);">Escanea o busca productos para agregarlos</p>
|
||||
</div>
|
||||
<div id="transferResult" style="margin-top:var(--space-3);min-height:1.5em;"></div>
|
||||
</div>
|
||||
|
||||
<div id="tr-panel-list" style="display:none;">
|
||||
<div id="transferDocsList" style="max-height:380px;overflow-y:auto;">
|
||||
<p style="color:var(--color-text-muted);font-size:var(--text-caption);">Cargando...</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="transferResult" style="margin-top:var(--space-3);min-height:1.5em;"></div>
|
||||
</div>
|
||||
<div class="inv-modal__footer">
|
||||
<button class="btn btn--ghost" onclick="closeTransferModal()">Cancelar</button>
|
||||
<button class="btn btn--primary" onclick="recordTransfer()">Registrar Traspaso</button>
|
||||
<div class="inv-modal__footer" id="transferNewFooter">
|
||||
<button class="btn btn--ghost" onclick="closeTransferModal()">Cerrar</button>
|
||||
<button class="btn btn--primary" onclick="transferDocSave(false)">Guardar Borrador</button>
|
||||
<button class="btn btn--primary" onclick="transferDocSave(true)" style="background:var(--color-success);">Guardar y Enviar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -840,8 +857,13 @@
|
||||
<button class="inv-modal__close" onclick="closeCountModal()">×</button>
|
||||
</div>
|
||||
<div class="inv-modal__body">
|
||||
<div id="countLines"></div>
|
||||
<button class="btn btn--ghost btn--sm" onclick="addCountLine()" style="margin-top:var(--space-2);">+ Agregar Línea</button>
|
||||
<div style="margin-bottom:10px;">
|
||||
<input type="text" id="countScanInput" class="form-input" placeholder="Escanea código de barras / No. parte y Enter" style="width:100%;" onkeydown="if(event.key==='Enter'){event.preventDefault();Inventory.countAddByCode(this.value);this.value='';}" />
|
||||
<p style="margin:6px 0 0;font-size:var(--text-caption);color:var(--color-text-muted);">Cada escaneo suma 1. Edita la cantidad si es necesario. Cantidades vacías = 0 (faltante).</p>
|
||||
</div>
|
||||
<div id="countLines" style="max-height:300px;overflow-y:auto;border:1px solid var(--color-border);border-radius:8px;">
|
||||
<p style="padding:10px;color:var(--color-text-muted);font-size:var(--text-caption);">Sin productos escaneados</p>
|
||||
</div>
|
||||
<div id="countResults" style="margin-top:var(--space-4);"></div>
|
||||
</div>
|
||||
<div class="inv-modal__footer">
|
||||
@@ -913,7 +935,7 @@
|
||||
</div>
|
||||
<div class="inv-field">
|
||||
<label>Tipo de Publicación</label>
|
||||
<select id="meliListingType">
|
||||
<select id="meliListingType" onchange="scheduleMeliFeeEstimates()">
|
||||
<option value="gold_special">Gold Special</option>
|
||||
<option value="gold_pro">Gold Pro</option>
|
||||
<option value="bronze">Bronce (gratis)</option>
|
||||
@@ -1040,6 +1062,12 @@
|
||||
<option value="reject">Rechazar filas sin compatibilidad</option>
|
||||
</select>
|
||||
</div>
|
||||
<div style="margin-bottom:12px;">
|
||||
<label style="display:flex;align-items:center;gap:8px;font-size:var(--text-body-sm);color:var(--color-text);cursor:pointer;">
|
||||
<input type="checkbox" id="bulkImportSkipStock" />
|
||||
No modificar existencias de productos existentes (solo actualizar datos y precios)
|
||||
</label>
|
||||
</div>
|
||||
<div style="font-size:var(--text-caption);color:var(--color-text-muted);background:var(--color-surface);padding:10px;border-radius:6px;">
|
||||
<strong>Columnas esperadas:</strong>
|
||||
<code style="display:block;margin-top:4px;word-break:break-all;">sku, name, brand, price, stock, cost, sku_secondary, description, category, make, model, year, engine, engine_code</code>
|
||||
@@ -1054,6 +1082,42 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ══════════ Export Modal ══════════ -->
|
||||
<div class="inv-modal-overlay" id="exportModal">
|
||||
<div class="inv-modal" style="max-width:480px;">
|
||||
<div class="inv-modal__header">
|
||||
<h3>Exportar Inventario</h3>
|
||||
<button class="inv-modal__close" onclick="document.getElementById('exportModal').classList.remove('is-open')">×</button>
|
||||
</div>
|
||||
<div class="inv-modal__body">
|
||||
<div style="display:flex;flex-direction:column;gap:12px;">
|
||||
<div>
|
||||
<label style="display:block;margin-bottom:4px;font-size:var(--text-caption);color:var(--color-text-muted);">Sucursal</label>
|
||||
<select id="exportBranch" class="select-filter" style="width:100%;">
|
||||
<option value="">Todas las sucursales — existencias totales</option>
|
||||
</select>
|
||||
</div>
|
||||
<button class="btn btn--primary" onclick="exportFullInventory()" type="button" id="btnExportFull">
|
||||
📦 Inventario completo (CSV)
|
||||
</button>
|
||||
<p style="margin:0;font-size:var(--text-caption);color:var(--color-text-muted);">
|
||||
Descarga <strong>todos los productos</strong> con número de parte, SKU alterno, nombre, marca, categoría, precios (mostrador/taller/mayoreo), existencias totales, ubicación y código de barras. Compatible con la plantilla de importación: puedes editarlo y re-importarlo.
|
||||
</p>
|
||||
<hr style="border:none;border-top:1px solid var(--color-border);margin:4px 0;" />
|
||||
<button class="btn btn--ghost" onclick="exportCurrentView()" type="button">
|
||||
📄 Solo la vista actual (lo que se ve en pantalla)
|
||||
</button>
|
||||
<p style="margin:0;font-size:var(--text-caption);color:var(--color-text-muted);">
|
||||
Exporta únicamente las filas y columnas visibles de la tabla actual (según página y filtros aplicados).
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inv-modal__footer">
|
||||
<button class="btn btn--ghost" onclick="document.getElementById('exportModal').classList.remove('is-open')">Cerrar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Offline Banner -->
|
||||
<div id="offlineBanner" class="banner banner--warning" style="display:none;position:fixed;top:0;left:0;right:0;z-index:9999;border-radius:0;animation:none;">
|
||||
<span class="banner__icon"></span>
|
||||
@@ -1062,12 +1126,12 @@
|
||||
</div>
|
||||
|
||||
<script src="/pos/static/js/i18n.js?v=39" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=8" defer></script>
|
||||
<script src="/pos/static/js/app-init.js?v=9" defer></script>
|
||||
<script src="/pos/static/js/splash-loader.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/pos-utils.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/sidebar.js?v=46" defer></script>
|
||||
<script src="/pos/static/js/virtual-scroll.js?v=33" defer></script>
|
||||
<script src="/pos/static/js/inventory.js?v=38" defer></script>
|
||||
<script src="/pos/static/js/inventory.js?v=45" defer></script>
|
||||
<script src="/pos/static/js/offline-banner.js" defer></script>
|
||||
<script src="/pos/static/js/sync-engine.js" defer></script>
|
||||
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
|
||||
|
||||
Reference in New Issue
Block a user