feat(pos): add inventory management UI — products, purchases, adjustments, alerts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-31 02:13:03 +00:00
parent 070e2df723
commit ab655e2ff1
2 changed files with 489 additions and 0 deletions

View File

@@ -0,0 +1,191 @@
<!-- /home/Autopartes/pos/templates/inventory.html -->
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inventario - Nexus POS</title>
<link rel="stylesheet" href="/pos/static/css/common.css">
<style>
body { margin: 0; font-family: var(--font-sans, 'Inter', system-ui, sans-serif); background: var(--color-bg, #f5f5f5); }
.header { padding: 12px 20px; background: var(--color-surface, #fff); border-bottom: 1px solid var(--color-border, #e5e5e5); }
.header h1 { margin: 0; font-size: 1.2rem; }
.tabs { display: flex; gap: 0; border-bottom: 2px solid var(--color-border, #e5e5e5); background: var(--color-surface, #fff); padding: 0 20px; overflow-x: auto; }
.tab { padding: 10px 18px; cursor: pointer; font-size: 0.85rem; border-bottom: 2px solid transparent; margin-bottom: -2px; white-space: nowrap; color: #666; }
.tab:hover { color: #333; }
.tab.active { color: var(--color-primary, #2563eb); border-bottom-color: var(--color-primary, #2563eb); font-weight: 600; }
.tab-content { display: none; padding: 20px; }
.tab-content.active { display: block; }
.inv-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.inv-table th { text-align: left; padding: 8px 10px; background: #f9f9f9; border-bottom: 2px solid #e5e5e5; font-weight: 600; font-size: 0.8rem; text-transform: uppercase; color: #666; }
.inv-table td { padding: 8px 10px; border-bottom: 1px solid #eee; }
.inv-table tr:hover { background: #f9fafb; }
.form-group { margin-bottom: 14px; }
.form-group label { display: block; font-size: 0.8rem; font-weight: 600; color: #555; margin-bottom: 4px; }
.form-group input, .form-group select, .form-group textarea { width: 100%; padding: 8px 10px; border: 1px solid var(--color-border, #ddd); border-radius: var(--radius, 6px); font-size: 0.9rem; box-sizing: border-box; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.btn { padding: 8px 18px; border: none; border-radius: var(--radius, 6px); cursor: pointer; font-size: 0.85rem; }
.btn-primary { background: var(--color-primary, #2563eb); color: #fff; }
.btn-secondary { background: #f3f4f6; color: #333; border: 1px solid #ddd; }
.btn-danger { background: #ef4444; color: #fff; }
.alert-card { padding: 12px 16px; border-radius: var(--radius, 6px); margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; }
.alert-critical { background: #fef2f2; border: 1px solid #fecaca; }
.alert-warning { background: #fefce8; border: 1px solid #fef08a; }
.alert-info { background: #eff6ff; border: 1px solid #bfdbfe; }
.count-results { margin-top: 16px; }
.count-row { display: flex; gap: 12px; align-items: center; margin-bottom: 8px; padding: 8px; background: #fff; border: 1px solid #eee; border-radius: 4px; }
.modal-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 100; }
.modal-overlay.show { display: flex; align-items: center; justify-content: center; }
.modal { background: #fff; border-radius: 8px; padding: 24px; width: 600px; max-width: 90vw; max-height: 80vh; overflow-y: auto; }
.search-row { display: flex; gap: 8px; margin-bottom: 16px; }
.search-row input { flex: 1; padding: 8px 12px; border: 1px solid #ddd; border-radius: var(--radius, 6px); }
</style>
</head>
<body>
<div class="header">
<h1>Gestion de Inventario</h1>
</div>
<div class="tabs">
<div class="tab active" data-tab="products">Productos</div>
<div class="tab" data-tab="purchases">Entradas</div>
<div class="tab" data-tab="adjustments">Ajustes</div>
<div class="tab" data-tab="transfers">Transferencias</div>
<div class="tab" data-tab="count">Toma Fisica</div>
<div class="tab" data-tab="alerts">Alertas</div>
</div>
<!-- Products tab -->
<div class="tab-content active" id="tab-products">
<div class="search-row">
<input type="text" id="productSearch" placeholder="Buscar productos...">
<button class="btn btn-primary" onclick="showCreateModal()">+ Nuevo producto</button>
</div>
<div style="overflow-x:auto;">
<table class="inv-table">
<thead>
<tr><th>Cod. Barras</th><th>No. Parte</th><th>Nombre</th><th>Marca</th><th>Stock</th><th>Costo</th><th>P1</th><th>P2</th><th>P3</th><th>Ubicacion</th><th>Acciones</th></tr>
</thead>
<tbody id="productTableBody"></tbody>
</table>
</div>
<div id="productPagination" class="pagination" style="margin-top:12px;"></div>
</div>
<!-- Purchases tab -->
<div class="tab-content" id="tab-purchases">
<h3>Registrar Entrada de Compra</h3>
<div style="max-width:500px;">
<div class="form-group"><label>Producto (ID)</label><input type="number" id="purchaseItemId" placeholder="ID del producto"></div>
<div class="form-row">
<div class="form-group"><label>Cantidad</label><input type="number" id="purchaseQty" min="1" value="1"></div>
<div class="form-group"><label>Costo unitario</label><input type="number" id="purchaseCost" step="0.01" placeholder="0.00"></div>
</div>
<div class="form-group"><label>Factura proveedor</label><input type="text" id="purchaseInvoice" placeholder="FAC-001"></div>
<div class="form-group"><label>Notas</label><textarea id="purchaseNotes" rows="2"></textarea></div>
<button class="btn btn-primary" onclick="recordPurchase()">Registrar compra</button>
<div id="purchaseResult" style="margin-top:12px;"></div>
</div>
</div>
<!-- Adjustments tab -->
<div class="tab-content" id="tab-adjustments">
<h3>Ajuste Manual de Stock</h3>
<div style="max-width:500px;">
<div class="form-group"><label>Producto (ID)</label><input type="number" id="adjustItemId" placeholder="ID del producto"></div>
<div class="form-group"><label>Cantidad (+/-)</label><input type="number" id="adjustQty" placeholder="-2 o +5"></div>
<div class="form-group"><label>Razon (obligatoria)</label><textarea id="adjustReason" rows="2" placeholder="Merma, error de conteo, etc."></textarea></div>
<button class="btn btn-primary" onclick="recordAdjustment()">Registrar ajuste</button>
<div id="adjustResult" style="margin-top:12px;"></div>
</div>
</div>
<!-- Transfers tab -->
<div class="tab-content" id="tab-transfers">
<h3>Transferencia entre Sucursales</h3>
<div style="max-width:500px;">
<div class="form-group"><label>Producto (ID)</label><input type="number" id="transferItemId" placeholder="ID del producto"></div>
<div class="form-row">
<div class="form-group"><label>Sucursal origen (ID)</label><input type="number" id="transferFrom"></div>
<div class="form-group"><label>Sucursal destino (ID)</label><input type="number" id="transferTo"></div>
</div>
<div class="form-group"><label>Cantidad</label><input type="number" id="transferQty" min="1" value="1"></div>
<div class="form-group"><label>Notas</label><textarea id="transferNotes" rows="2"></textarea></div>
<button class="btn btn-primary" onclick="recordTransfer()">Transferir</button>
<div id="transferResult" style="margin-top:12px;"></div>
</div>
</div>
<!-- Physical Count tab -->
<div class="tab-content" id="tab-count">
<h3>Toma Fisica de Inventario</h3>
<p style="font-size:0.85rem;color:#666;">Fase 1: Ingrese los conteos. Se generara un borrador con comparacion esperado vs contado. Fase 2: Apruebe para aplicar ajustes.</p>
<div id="countForm">
<div id="countLines">
<div class="count-row">
<input type="number" placeholder="ID producto" class="count-inv-id" style="width:120px;">
<input type="number" placeholder="Cantidad contada" class="count-qty" style="width:140px;">
<button class="btn btn-secondary" onclick="this.parentElement.remove()">Quitar</button>
</div>
</div>
<button class="btn btn-secondary" onclick="addCountLine()" style="margin:8px 0;">+ Agregar linea</button>
<br>
<button class="btn btn-primary" onclick="startPhysicalCount()">Crear borrador</button>
</div>
<div id="countResults" class="count-results"></div>
</div>
<!-- Alerts tab -->
<div class="tab-content" id="tab-alerts">
<h3>Alertas de Stock</h3>
<button class="btn btn-secondary" onclick="loadAlerts()" style="margin-bottom:12px;">Actualizar</button>
<div id="alertsList"></div>
</div>
<!-- History modal -->
<div class="modal-overlay" id="historyModal">
<div class="modal">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
<h3 style="margin:0;">Historial de movimientos</h3>
<button onclick="closeHistoryModal()" style="background:none;border:none;font-size:1.3rem;cursor:pointer;">&times;</button>
</div>
<div id="historyContent"></div>
</div>
</div>
<!-- Create/Edit modal -->
<div class="modal-overlay" id="createModal">
<div class="modal">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:16px;">
<h3 style="margin:0;" id="createModalTitle">Nuevo Producto</h3>
<button onclick="closeCreateModal()" style="background:none;border:none;font-size:1.3rem;cursor:pointer;">&times;</button>
</div>
<div class="form-row">
<div class="form-group"><label>No. Parte *</label><input type="text" id="newPartNumber"></div>
<div class="form-group"><label>Nombre *</label><input type="text" id="newName"></div>
</div>
<div class="form-row">
<div class="form-group"><label>Marca (fabricante)</label><input type="text" id="newBrand" placeholder="Bosch, NGK..."></div>
<div class="form-group"><label>Codigo de barras</label><input type="text" id="newBarcode" placeholder="Auto-generado si vacio"></div>
</div>
<div class="form-row">
<div class="form-group"><label>Costo</label><input type="number" id="newCost" step="0.01" value="0"></div>
<div class="form-group"><label>Precio 1</label><input type="number" id="newPrice1" step="0.01" value="0"></div>
</div>
<div class="form-row">
<div class="form-group"><label>Precio 2</label><input type="number" id="newPrice2" step="0.01" value="0"></div>
<div class="form-group"><label>Precio 3</label><input type="number" id="newPrice3" step="0.01" value="0"></div>
</div>
<div class="form-row">
<div class="form-group"><label>Stock minimo</label><input type="number" id="newMinStock" value="0"></div>
<div class="form-group"><label>Stock inicial</label><input type="number" id="newInitialStock" value="0"></div>
</div>
<div class="form-group"><label>Ubicacion</label><input type="text" id="newLocation" placeholder="Pasillo A, Estante 3"></div>
<button class="btn btn-primary" onclick="createItem()">Guardar</button>
<div id="createResult" style="margin-top:8px;"></div>
</div>
</div>
<script src="/pos/static/js/inventory.js"></script>
</body>
</html>