Files
Autoparts-DB/pos/templates/pos.html

404 lines
25 KiB
HTML

<!-- /home/Autopartes/pos/templates/pos.html -->
<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Punto de Venta - Nexus POS</title>
<link rel="stylesheet" href="/pos/static/css/common.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: var(--font-body, 'Segoe UI', system-ui, sans-serif); background: var(--color-bg, #f0f2f5); color: var(--color-text, #1a1a2e); height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
/* Top bar */
.pos-topbar { background: var(--color-primary, #1a1a2e); color: #fff; padding: 8px 16px; display: flex; align-items: center; justify-content: space-between; font-size: 14px; }
.pos-topbar .employee-info { display: flex; align-items: center; gap: 12px; }
.pos-topbar .register-info { display: flex; align-items: center; gap: 8px; opacity: 0.8; }
.pos-topbar .register-info.no-register { color: #ff6b6b; opacity: 1; }
.pos-topbar .shortcuts-hint { font-size: 12px; opacity: 0.6; }
/* Main layout */
.pos-main { display: flex; flex: 1; overflow: hidden; }
/* Left panel: search + items */
.pos-left { width: 55%; display: flex; flex-direction: column; border-right: 2px solid var(--color-border, #ddd); background: #fff; }
/* Customer bar */
.customer-bar { padding: 8px 12px; background: var(--color-surface, #f8f9fa); border-bottom: 1px solid var(--color-border, #ddd); display: flex; gap: 8px; align-items: center; }
.customer-bar input { flex: 1; padding: 8px 12px; border: 1px solid var(--color-border, #ddd); border-radius: var(--radius, 6px); font-size: 14px; }
.customer-bar .customer-selected { background: #e8f5e9; padding: 6px 12px; border-radius: var(--radius, 6px); font-size: 13px; display: flex; align-items: center; gap: 8px; }
.customer-bar .customer-selected .tier-badge { font-size: 11px; padding: 2px 6px; border-radius: 3px; background: #1976d2; color: #fff; }
.customer-bar .customer-selected .credit-info { font-size: 11px; color: #666; }
.customer-bar .btn-new-customer { padding: 8px 12px; border: 1px dashed var(--color-border, #ddd); border-radius: var(--radius, 6px); background: none; cursor: pointer; font-size: 13px; white-space: nowrap; }
.customer-autocomplete { position: absolute; top: 100%; left: 0; right: 0; background: #fff; border: 1px solid var(--color-border, #ddd); border-radius: 0 0 var(--radius, 6px) var(--radius, 6px); box-shadow: var(--shadow, 0 4px 12px rgba(0,0,0,0.1)); z-index: 100; max-height: 200px; overflow-y: auto; }
.customer-autocomplete .ac-item { padding: 8px 12px; cursor: pointer; font-size: 13px; border-bottom: 1px solid #f0f0f0; }
.customer-autocomplete .ac-item:hover { background: #f0f2f5; }
.customer-autocomplete .ac-item .ac-meta { font-size: 11px; color: #999; }
/* Vehicle info banner */
.vehicle-banner { display: none; padding: 6px 12px; background: #fff3e0; border-bottom: 1px solid #ffe0b2; font-size: 12px; }
.vehicle-banner.visible { display: flex; align-items: center; gap: 8px; }
/* Search bar */
.search-bar { padding: 8px 12px; border-bottom: 1px solid var(--color-border, #ddd); display: flex; gap: 8px; }
.search-bar input { flex: 1; padding: 10px 14px; border: 2px solid var(--color-primary, #1a1a2e); border-radius: var(--radius, 6px); font-size: 15px; outline: none; }
.search-bar input:focus { border-color: var(--color-accent, #4361ee); box-shadow: 0 0 0 3px rgba(67,97,238,0.15); }
/* Cart items */
.cart-items { flex: 1; overflow-y: auto; padding: 0; }
.cart-items table { width: 100%; border-collapse: collapse; }
.cart-items thead th { position: sticky; top: 0; background: var(--color-surface, #f8f9fa); padding: 8px 10px; text-align: left; font-size: 12px; font-weight: 600; color: #666; border-bottom: 2px solid var(--color-border, #ddd); }
.cart-items tbody tr { border-bottom: 1px solid #f0f0f0; cursor: pointer; }
.cart-items tbody tr:hover { background: #f8f9fa; }
.cart-items tbody tr.selected { background: #e3f2fd; }
.cart-items td { padding: 8px 10px; font-size: 13px; }
.cart-items td.num { text-align: right; font-variant-numeric: tabular-nums; }
.cart-items td .part-name { font-weight: 500; }
.cart-items td .part-number { font-size: 11px; color: #999; }
.cart-items td .margin-info { font-size: 11px; color: #888; }
.cart-items td .margin-warning { color: #e53935; font-weight: 600; }
.cart-items .qty-input { width: 50px; text-align: center; border: 1px solid #ddd; border-radius: 4px; padding: 4px; font-size: 13px; }
.cart-items .discount-input { width: 55px; text-align: center; border: 1px solid #ddd; border-radius: 4px; padding: 4px; font-size: 13px; }
.cart-items .btn-remove { background: none; border: none; color: #e53935; cursor: pointer; font-size: 16px; padding: 2px 6px; }
.cart-empty { display: flex; align-items: center; justify-content: center; flex: 1; color: #999; font-size: 15px; }
/* Right panel: totals + actions */
.pos-right { width: 45%; display: flex; flex-direction: column; background: var(--color-surface, #f8f9fa); }
/* Search results (right side when searching) */
.search-results { flex: 1; overflow-y: auto; padding: 8px; display: none; }
.search-results.active { display: block; }
.search-result-item { padding: 10px 12px; background: #fff; border: 1px solid #eee; border-radius: var(--radius, 6px); margin-bottom: 6px; cursor: pointer; display: flex; justify-content: space-between; align-items: center; }
.search-result-item:hover { border-color: var(--color-primary, #1a1a2e); background: #fafafa; }
.search-result-item .sr-name { font-weight: 500; font-size: 14px; }
.search-result-item .sr-pn { font-size: 12px; color: #666; }
.search-result-item .sr-stock { font-size: 12px; }
.search-result-item .sr-stock.zero { color: #e53935; }
.search-result-item .sr-price { font-weight: 600; font-size: 15px; }
/* Totals panel */
.totals-panel { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; padding: 16px; }
.totals-panel.hidden { display: none; }
.totals-row { display: flex; justify-content: space-between; padding: 6px 0; font-size: 14px; }
.totals-row.discount { color: #e53935; }
.totals-row.total { font-size: 28px; font-weight: 700; padding: 12px 0; border-top: 2px solid var(--color-border, #ddd); margin-top: 8px; }
/* Global discount */
.global-discount { display: flex; align-items: center; gap: 8px; padding: 8px 0; border-top: 1px solid #eee; margin-top: 8px; }
.global-discount label { font-size: 13px; color: #666; }
.global-discount input { width: 60px; text-align: center; border: 1px solid #ddd; border-radius: 4px; padding: 4px; font-size: 13px; }
/* Action buttons */
.action-buttons { padding: 12px 16px; display: grid; grid-template-columns: 1fr 1fr; gap: 8px; border-top: 2px solid var(--color-border, #ddd); }
.action-buttons .btn { padding: 14px; border: none; border-radius: var(--radius, 6px); cursor: pointer; font-size: 14px; font-weight: 600; display: flex; align-items: center; justify-content: center; gap: 6px; transition: opacity 0.15s; }
.action-buttons .btn:hover { opacity: 0.85; }
.action-buttons .btn:active { transform: scale(0.98); }
.btn-cobrar { background: #2e7d32; color: #fff; grid-column: 1 / -1; font-size: 18px; padding: 18px; }
.btn-cotizacion { background: #1565c0; color: #fff; }
.btn-apartado { background: #e65100; color: #fff; }
.btn-credito { background: #6a1b9a; color: #fff; }
.btn-last-sale { background: #455a64; color: #fff; }
.btn-shortcut { font-size: 11px; opacity: 0.7; margin-left: 4px; }
/* Payment modal */
.modal-overlay { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 200; align-items: center; justify-content: center; }
.modal-overlay.active { display: flex; }
.modal { background: #fff; border-radius: 12px; padding: 24px; width: 500px; max-width: 95vw; max-height: 90vh; overflow-y: auto; box-shadow: 0 20px 60px rgba(0,0,0,0.3); }
.modal h2 { margin-bottom: 16px; font-size: 20px; }
.modal .modal-total { font-size: 32px; font-weight: 700; text-align: center; padding: 12px; background: #f5f5f5; border-radius: 8px; margin-bottom: 16px; }
.modal .payment-methods { display: flex; gap: 8px; margin-bottom: 16px; }
.modal .payment-methods .pm-btn { flex: 1; padding: 12px; border: 2px solid #ddd; border-radius: 8px; background: #fff; cursor: pointer; text-align: center; font-size: 13px; font-weight: 500; }
.modal .payment-methods .pm-btn.active { border-color: var(--color-primary, #1a1a2e); background: #f0f4ff; }
.modal .payment-field { margin-bottom: 12px; }
.modal .payment-field label { display: block; font-size: 13px; color: #666; margin-bottom: 4px; }
.modal .payment-field input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 16px; }
.modal .payment-field input.amount-input { font-size: 24px; text-align: right; font-weight: 600; }
.modal .change-display { text-align: center; padding: 12px; font-size: 20px; font-weight: 600; border-radius: 8px; margin: 12px 0; }
.modal .change-display.positive { background: #e8f5e9; color: #2e7d32; }
.modal .change-display.negative { background: #ffebee; color: #c62828; }
/* Mixed payment rows */
.mixed-payments { margin-bottom: 12px; }
.mixed-row { display: flex; gap: 8px; margin-bottom: 8px; align-items: center; }
.mixed-row select { padding: 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 13px; }
.mixed-row input { flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; }
.mixed-row .btn-remove-row { background: none; border: none; color: #e53935; cursor: pointer; font-size: 18px; }
.btn-add-mixed { background: none; border: 1px dashed #999; padding: 6px 12px; border-radius: 6px; cursor: pointer; font-size: 13px; color: #666; }
.modal .modal-actions { display: flex; gap: 8px; margin-top: 16px; }
.modal .modal-actions .btn { flex: 1; padding: 14px; border: none; border-radius: 8px; font-size: 15px; font-weight: 600; cursor: pointer; }
.modal .btn-confirm-payment { background: #2e7d32; color: #fff; }
.modal .btn-cancel-modal { background: #eee; color: #333; }
/* New customer modal */
.modal .form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.modal .form-grid .full-width { grid-column: 1 / -1; }
.modal .form-field { display: flex; flex-direction: column; gap: 4px; }
.modal .form-field label { font-size: 12px; color: #666; font-weight: 500; }
.modal .form-field input, .modal .form-field select { padding: 8px; border: 1px solid #ddd; border-radius: 6px; font-size: 13px; }
/* Ticket preview */
.ticket-preview { font-family: var(--font-mono, 'Courier New', monospace); font-size: 12px; white-space: pre-wrap; background: #fff; padding: 16px; border: 1px dashed #999; max-width: 300px; margin: 0 auto; line-height: 1.4; }
/* Keyboard hint bar */
.keyboard-bar { background: #263238; color: #b0bec5; padding: 6px 16px; display: flex; gap: 16px; font-size: 11px; }
.keyboard-bar .kb-key { background: #37474f; padding: 2px 6px; border-radius: 3px; color: #e0e0e0; font-weight: 600; margin-right: 4px; }
@media print {
body * { visibility: hidden; }
.ticket-preview, .ticket-preview * { visibility: visible; }
.ticket-preview { position: absolute; left: 0; top: 0; }
}
</style>
</head>
<body>
<!-- Top Bar -->
<div class="pos-topbar">
<div class="employee-info">
<span id="employeeName">Cargando...</span>
<span id="branchName" style="opacity: 0.7; font-size: 12px;"></span>
</div>
<div class="register-info" id="registerInfo">
<span>Caja: --</span>
</div>
<div class="shortcuts-hint">F1=Buscar F2=Cliente F3=Cobrar F4=Cotizacion F5=Ult.Venta</div>
</div>
<!-- Main Layout -->
<div class="pos-main">
<!-- Left: Search + Cart -->
<div class="pos-left">
<!-- Customer Bar -->
<div class="customer-bar" style="position: relative;">
<span style="font-size: 13px; color: #666;">Cliente:</span>
<div id="customerSearchWrap" style="flex: 1; position: relative;">
<input type="text" id="customerSearch" placeholder="Buscar cliente por nombre, RFC, telefono... (F2)" autocomplete="off">
<div class="customer-autocomplete" id="customerAutocomplete" style="display:none;"></div>
</div>
<div id="customerSelected" class="customer-selected" style="display:none;">
<span id="customerName"></span>
<span class="tier-badge" id="customerTier"></span>
<span class="credit-info" id="customerCredit"></span>
<button onclick="POS.clearCustomer()" style="background:none;border:none;cursor:pointer;color:#999;font-size:16px;" title="Quitar cliente">&times;</button>
</div>
<button class="btn-new-customer" onclick="POS.showNewCustomerModal()">+ Nuevo</button>
</div>
<!-- Vehicle Banner -->
<div class="vehicle-banner" id="vehicleBanner">
<span style="font-weight: 600;">Vehiculo:</span>
<span id="vehicleInfo"></span>
<span id="lastPurchaseInfo" style="margin-left: auto; font-size: 11px; color: #e65100;"></span>
</div>
<!-- Search Bar -->
<div class="search-bar">
<input type="text" id="itemSearch" placeholder="Buscar por # parte, nombre o codigo de barras... (F1)" autocomplete="off">
</div>
<!-- Cart Table -->
<div class="cart-items" id="cartContainer">
<div class="cart-empty" id="cartEmpty">
<div>Carrito vacio<br><span style="font-size: 13px;">Busca productos o presiona F1</span></div>
</div>
<table id="cartTable" style="display:none;">
<thead>
<tr>
<th style="width: 30px;">#</th>
<th>Producto</th>
<th style="width: 60px;">Cant</th>
<th style="width: 90px;">Precio</th>
<th style="width: 65px;">Desc%</th>
<th style="width: 90px;" class="num">Subtotal</th>
<th id="thCost" style="width: 70px; display:none;" class="num">Costo</th>
<th id="thMargin" style="width: 65px; display:none;" class="num">Margen</th>
<th style="width: 30px;"></th>
</tr>
</thead>
<tbody id="cartBody"></tbody>
</table>
</div>
</div>
<!-- Right: Search Results / Totals + Actions -->
<div class="pos-right">
<!-- Search results (shown when searching) -->
<div class="search-results" id="searchResults"></div>
<!-- Totals panel -->
<div class="totals-panel" id="totalsPanel">
<div>
<div class="totals-row"><span>Subtotal:</span><span id="dispSubtotal">$0.00</span></div>
<div class="totals-row discount" id="discountRow" style="display:none;">
<span>Descuento:</span><span id="dispDiscount">-$0.00</span>
</div>
<div class="totals-row"><span>IVA (16%):</span><span id="dispTax">$0.00</span></div>
<div class="totals-row total"><span>TOTAL:</span><span id="dispTotal">$0.00</span></div>
</div>
<div class="global-discount">
<label>Descuento global:</label>
<input type="number" id="globalDiscount" value="0" min="0" max="100" step="0.5">
<span>%</span>
</div>
</div>
<!-- Action Buttons -->
<div class="action-buttons">
<button class="btn btn-cobrar" onclick="POS.checkout()">
Cobrar<span class="btn-shortcut">F3</span>
</button>
<button class="btn btn-cotizacion" onclick="POS.saveQuotation()">
Cotizacion<span class="btn-shortcut">F4</span>
</button>
<button class="btn btn-apartado" onclick="POS.createLayaway()">
Apartado
</button>
<button class="btn btn-credito" onclick="POS.creditSale()">
Credito
</button>
<button class="btn btn-last-sale" onclick="POS.showLastSale()">
Ult. Venta<span class="btn-shortcut">F5</span>
</button>
<button class="btn" style="background:#78909c;color:#fff;" onclick="POS.openDrawer()">
Cajon<span class="btn-shortcut">F6</span>
</button>
</div>
</div>
</div>
<!-- Keyboard hints -->
<div class="keyboard-bar">
<span><span class="kb-key">F1</span>Buscar</span>
<span><span class="kb-key">F2</span>Cliente</span>
<span><span class="kb-key">F3</span>Cobrar</span>
<span><span class="kb-key">F4</span>Cotizacion</span>
<span><span class="kb-key">F5</span>Ult.Venta</span>
<span><span class="kb-key">F6</span>Cajon</span>
<span><span class="kb-key">+/-</span>Cantidad</span>
<span><span class="kb-key">*</span>Descuento</span>
<span><span class="kb-key">Enter</span>Agregar</span>
<span><span class="kb-key">Del</span>Eliminar</span>
</div>
<!-- Payment Modal -->
<div class="modal-overlay" id="paymentModal">
<div class="modal">
<h2>Cobrar Venta</h2>
<div class="modal-total" id="modalTotal">$0.00</div>
<div class="payment-methods">
<button class="pm-btn active" data-method="efectivo" onclick="POS.selectPaymentMethod('efectivo', this)">Efectivo</button>
<button class="pm-btn" data-method="transferencia" onclick="POS.selectPaymentMethod('transferencia', this)">Transferencia</button>
<button class="pm-btn" data-method="tarjeta" onclick="POS.selectPaymentMethod('tarjeta', this)">Tarjeta</button>
<button class="pm-btn" data-method="mixto" onclick="POS.selectPaymentMethod('mixto', this)">Mixto</button>
</div>
<!-- Cash payment -->
<div id="cashPayment">
<div class="payment-field">
<label>Monto recibido:</label>
<input type="number" id="cashReceived" class="amount-input" step="0.01" min="0" oninput="POS.updateChange()">
</div>
<div class="change-display positive" id="changeDisplay">Cambio: $0.00</div>
</div>
<!-- Transfer/Card payment -->
<div id="refPayment" style="display:none;">
<div class="payment-field">
<label>Referencia:</label>
<input type="text" id="paymentRef" placeholder="Numero de referencia o autorizacion">
</div>
</div>
<!-- Mixed payment -->
<div id="mixedPayment" style="display:none;">
<div class="mixed-payments" id="mixedRows">
<div class="mixed-row">
<select><option value="efectivo">Efectivo</option><option value="transferencia">Transferencia</option><option value="tarjeta">Tarjeta</option></select>
<input type="number" step="0.01" min="0" placeholder="Monto" class="mixed-amount" oninput="POS.updateMixedTotal()">
<input type="text" placeholder="Ref." style="width: 100px;">
</div>
<div class="mixed-row">
<select><option value="transferencia">Transferencia</option><option value="efectivo">Efectivo</option><option value="tarjeta">Tarjeta</option></select>
<input type="number" step="0.01" min="0" placeholder="Monto" class="mixed-amount" oninput="POS.updateMixedTotal()">
<input type="text" placeholder="Ref." style="width: 100px;">
</div>
</div>
<div id="mixedRemaining" style="text-align:center; font-size: 14px; color: #666; padding: 8px 0;">Faltante: $0.00</div>
</div>
<div class="modal-actions">
<button class="btn btn-cancel-modal" onclick="POS.closePaymentModal()">Cancelar (Esc)</button>
<button class="btn btn-confirm-payment" id="btnConfirmPayment" onclick="POS.confirmPayment()">Confirmar Pago</button>
</div>
</div>
</div>
<!-- New Customer Modal -->
<div class="modal-overlay" id="newCustomerModal">
<div class="modal">
<h2>Nuevo Cliente</h2>
<div class="form-grid">
<div class="form-field full-width"><label>Nombre *</label><input type="text" id="ncName"></div>
<div class="form-field"><label>RFC</label><input type="text" id="ncRfc" maxlength="13" placeholder="XAXX010101000"></div>
<div class="form-field"><label>Razon Social</label><input type="text" id="ncRazonSocial"></div>
<div class="form-field"><label>Regimen Fiscal</label>
<select id="ncRegimenFiscal">
<option value="">Seleccionar...</option>
<option value="601">601 - General de Ley PM</option>
<option value="603">603 - Personas Morales Fines No Lucrativos</option>
<option value="605">605 - Sueldos y Salarios</option>
<option value="606">606 - Arrendamiento</option>
<option value="612">612 - Personas Fisicas Actividad Empresarial</option>
<option value="616">616 - Sin Obligaciones Fiscales</option>
<option value="621">621 - Incorporacion Fiscal</option>
<option value="625">625 - RESICO</option>
</select>
</div>
<div class="form-field"><label>Uso CFDI</label>
<select id="ncUsoCfdi">
<option value="G03">G03 - Gastos en general</option>
<option value="G01">G01 - Adquisicion de mercancias</option>
<option value="P01">P01 - Por definir</option>
</select>
</div>
<div class="form-field"><label>Telefono</label><input type="tel" id="ncPhone"></div>
<div class="form-field"><label>Email</label><input type="email" id="ncEmail"></div>
<div class="form-field"><label>Lista de precio</label>
<select id="ncPriceTier">
<option value="1">Mostrador (Precio 1)</option>
<option value="2">Taller (Precio 2)</option>
<option value="3">Mayoreo (Precio 3)</option>
</select>
</div>
<div class="form-field"><label>Limite de credito</label><input type="number" id="ncCreditLimit" value="0" min="0" step="100"></div>
<div class="form-field full-width"><label>Vehiculo (opcional)</label></div>
<div class="form-field"><label>Marca</label><input type="text" id="ncVehMake" placeholder="Nissan, Toyota..."></div>
<div class="form-field"><label>Modelo</label><input type="text" id="ncVehModel" placeholder="Tsuru, Corolla..."></div>
<div class="form-field"><label>Ano</label><input type="number" id="ncVehYear" placeholder="2020"></div>
<div class="form-field"><label>Placas</label><input type="text" id="ncVehPlates"></div>
</div>
<div class="modal-actions" style="margin-top: 16px;">
<button class="btn btn-cancel-modal" onclick="POS.closeNewCustomerModal()">Cancelar</button>
<button class="btn btn-confirm-payment" onclick="POS.saveNewCustomer()">Guardar Cliente</button>
</div>
</div>
</div>
<!-- Ticket Modal -->
<div class="modal-overlay" id="ticketModal">
<div class="modal" style="width: 380px;">
<h2>Venta Completada</h2>
<div class="ticket-preview" id="ticketPreview"></div>
<div class="modal-actions" style="margin-top: 16px;">
<button class="btn btn-cancel-modal" onclick="POS.closeTicketModal()">Cerrar</button>
<button class="btn btn-confirm-payment" onclick="POS.printTicket()">Imprimir</button>
</div>
</div>
</div>
<script src="/pos/static/js/pos.js"></script>
</body>
</html>