feat(pos): add Cut Z (close register) UI flow
- Add 'Corte Z' button in secondary actions panel - Add modal showing register summary before closing: - opening amount, total sales, cash sales, change given - cash movements in/out, cancellations, expected cash - payment method breakdown and movement detail list - loadCutX() fetches current register summary (read-only) - confirmCutZ() calls POST /pos/api/register/cut-z with counted amount - Auto-fills closing amount with expected cash - Shows toast with difference after closing - Resets register state to 'Sin caja abierta' after close - Bump pos.css and pos.js cache-bust to v=3
This commit is contained in:
@@ -162,6 +162,78 @@ const POS = (() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Cut X / Z (Close Register) ──────
|
||||||
|
function showCutZModal() {
|
||||||
|
document.getElementById('cutZModal').classList.add('open');
|
||||||
|
document.getElementById('cutZResult').innerHTML = '';
|
||||||
|
loadCutX();
|
||||||
|
}
|
||||||
|
function closeCutZModal() {
|
||||||
|
document.getElementById('cutZModal').classList.remove('open');
|
||||||
|
document.getElementById('cutZResult').innerHTML = '';
|
||||||
|
}
|
||||||
|
async function loadCutX() {
|
||||||
|
const el = document.getElementById('cutZSummary');
|
||||||
|
try {
|
||||||
|
const data = await api('/pos/api/register/cut-x');
|
||||||
|
if (data.error) {
|
||||||
|
el.innerHTML = '<p style="color:var(--color-error);">' + data.error + '</p>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let html = '<div style="display:grid;grid-template-columns:1fr 1fr;gap:var(--space-3);font-size:var(--text-body-sm);">';
|
||||||
|
html += '<div><span style="color:var(--color-text-muted);">Efectivo inicial</span><br/><strong>$' + fmt(data.opening_amount) + '</strong></div>';
|
||||||
|
html += '<div><span style="color:var(--color-text-muted);">Ventas totales</span><br/><strong>$' + fmt(data.total_sales) + '</strong> (' + data.total_sales_count + ')</div>';
|
||||||
|
html += '<div><span style="color:var(--color-text-muted);">Efectivo en ventas</span><br/><strong>$' + fmt(data.cash_from_sales) + '</strong></div>';
|
||||||
|
html += '<div><span style="color:var(--color-text-muted);">Cambio entregado</span><br/><strong>-$' + fmt(data.change_given) + '</strong></div>';
|
||||||
|
html += '<div><span style="color:var(--color-text-muted);">Entradas de efectivo</span><br/><strong>+$' + fmt(data.cash_movements_in) + '</strong></div>';
|
||||||
|
html += '<div><span style="color:var(--color-text-muted);">Salidas de efectivo</span><br/><strong>-$' + fmt(data.cash_movements_out) + '</strong></div>';
|
||||||
|
html += '<div><span style="color:var(--color-text-muted);">Cancelaciones</span><br/><strong>' + data.cancelled_count + ' ($' + fmt(data.cancelled_amount) + ')</strong></div>';
|
||||||
|
html += '<div><span style="color:var(--color-text-muted);">Efectivo esperado</span><br/><strong style="color:var(--color-accent);font-size:1.1em;">$' + fmt(data.expected_cash) + '</strong></div>';
|
||||||
|
html += '</div>';
|
||||||
|
if (data.sales_by_method && Object.keys(data.sales_by_method).length) {
|
||||||
|
html += '<div style="margin-top:var(--space-3);padding-top:var(--space-3);border-top:1px solid var(--color-border);">';
|
||||||
|
html += '<span style="color:var(--color-text-muted);font-size:var(--text-caption);">Por metodo de pago:</span><br/>';
|
||||||
|
for (const [method, info] of Object.entries(data.sales_by_method)) {
|
||||||
|
html += '<span style="font-size:var(--text-body-sm);margin-right:var(--space-3);">' + method + ': $' + fmt(info.amount) + ' (' + info.count + ')</span>';
|
||||||
|
}
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
if (data.movement_detail && data.movement_detail.length) {
|
||||||
|
html += '<div style="margin-top:var(--space-3);padding-top:var(--space-3);border-top:1px solid var(--color-border);">';
|
||||||
|
html += '<span style="color:var(--color-text-muted);font-size:var(--text-caption);">Movimientos de caja:</span><br/>';
|
||||||
|
data.movement_detail.forEach(function(m) {
|
||||||
|
html += '<div style="font-size:var(--text-body-sm);">' + m.type + ' $' + fmt(m.amount) + ' — ' + (m.reason || '') + '</div>';
|
||||||
|
});
|
||||||
|
html += '</div>';
|
||||||
|
}
|
||||||
|
el.innerHTML = html;
|
||||||
|
document.getElementById('cutZClosingAmount').value = data.expected_cash.toFixed(2);
|
||||||
|
} catch (e) {
|
||||||
|
el.innerHTML = '<p style="color:var(--color-error);">Error cargando resumen</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function confirmCutZ() {
|
||||||
|
const amount = parseFloat(document.getElementById('cutZClosingAmount').value) || 0;
|
||||||
|
try {
|
||||||
|
const data = await api('/pos/api/register/cut-z', {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({ closing_amount: amount })
|
||||||
|
});
|
||||||
|
if (data.error) {
|
||||||
|
document.getElementById('cutZResult').innerHTML = '<span style="color:var(--color-error);">' + data.error + '</span>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const diffColor = data.difference > 0 ? 'var(--color-success)' : (data.difference < 0 ? 'var(--color-error)' : 'var(--color-text-muted)');
|
||||||
|
document.getElementById('cutZResult').innerHTML = '<span style="color:var(--color-success);">Caja cerrada correctamente</span>';
|
||||||
|
document.getElementById('registerInfo').innerHTML = '<span style="color:var(--color-text-muted);" onclick="POS.showOpenRegisterModal()">Sin caja abierta</span>';
|
||||||
|
currentRegister = null;
|
||||||
|
closeCutZModal();
|
||||||
|
showToast('Corte Z completado. Diferencia: $' + data.difference.toFixed(2));
|
||||||
|
} catch (e) {
|
||||||
|
document.getElementById('cutZResult').innerHTML = '<span style="color:var(--color-error);">Error de red</span>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Cart ────────────────────────────
|
// ─── Cart ────────────────────────────
|
||||||
function addToCart(item) {
|
function addToCart(item) {
|
||||||
const existing = cart.find(c => c.inventory_id === item.inventory_id);
|
const existing = cart.find(c => c.inventory_id === item.inventory_id);
|
||||||
@@ -1195,5 +1267,6 @@ const POS = (() => {
|
|||||||
showTicket, closeTicketModal, printTicket,
|
showTicket, closeTicketModal, printTicket,
|
||||||
connectThermal, thermalPrint,
|
connectThermal, thermalPrint,
|
||||||
showOpenRegisterModal, closeOpenRegisterModal, openRegister,
|
showOpenRegisterModal, closeOpenRegisterModal, openRegister,
|
||||||
|
showCutZModal, closeCutZModal, loadCutX, confirmCutZ,
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<meta name="theme-color" content="#F5A623" />
|
<meta name="theme-color" content="#F5A623" />
|
||||||
<script src="/pos/static/js/native-bridge.js" defer></script>
|
<script src="/pos/static/js/native-bridge.js" defer></script>
|
||||||
|
|
||||||
<link rel="stylesheet" href="/pos/static/css/pos.css?v=2">
|
<link rel="stylesheet" href="/pos/static/css/pos.css?v=3">
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body class="pos-shell" id="appBody">
|
<body class="pos-shell" id="appBody">
|
||||||
@@ -202,6 +202,7 @@
|
|||||||
<div class="secondary-actions" role="toolbar" aria-label="Acciones secundarias">
|
<div class="secondary-actions" role="toolbar" aria-label="Acciones secundarias">
|
||||||
<button class="btn-secondary-action" onclick="POS.saveQuotation()" title="Cotizacion (F4)">Cotizar</button>
|
<button class="btn-secondary-action" onclick="POS.saveQuotation()" title="Cotizacion (F4)">Cotizar</button>
|
||||||
<button class="btn-secondary-action" onclick="POS.showLastSale()" title="Ultima venta (F5)">Ult.Venta</button>
|
<button class="btn-secondary-action" onclick="POS.showLastSale()" title="Ultima venta (F5)">Ult.Venta</button>
|
||||||
|
<button class="btn-secondary-action" onclick="POS.showCutZModal()" title="Corte Z - Cerrar caja">Corte Z</button>
|
||||||
<button class="btn-secondary-action danger" id="btnCancelSale" title="Cancelar (Esc)">Cancelar</button>
|
<button class="btn-secondary-action danger" id="btnCancelSale" title="Cancelar (Esc)">Cancelar</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -490,6 +491,35 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ================================================================
|
||||||
|
CUT Z MODAL (Cerrar Caja)
|
||||||
|
================================================================ -->
|
||||||
|
<div class="modal-overlay" id="cutZModal">
|
||||||
|
<div class="modal-pago" style="width:520px;max-height:90vh;overflow-y:auto;">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3>Corte Z — Cerrar Caja</h3>
|
||||||
|
<button class="modal-close" onclick="POS.closeCutZModal()">✕</button>
|
||||||
|
</div>
|
||||||
|
<div style="padding:var(--space-6);">
|
||||||
|
<div id="cutZSummary" style="margin-bottom:var(--space-4);">
|
||||||
|
<p style="color:var(--color-text-muted);font-size:var(--text-body-sm);">Cargando resumen...</p>
|
||||||
|
</div>
|
||||||
|
<div style="border-top:1px solid var(--color-border);padding-top:var(--space-4);">
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="form-label">Efectivo contado en caja *</label>
|
||||||
|
<input type="number" class="form-input" id="cutZClosingAmount" value="0" min="0" step="0.01" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="cutZResult" style="min-height:1.5em;font-size:var(--text-body-sm);margin-top:var(--space-3);"></div>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class="btn btn-ghost" onclick="POS.closeCutZModal()">Cancelar</button>
|
||||||
|
<button class="btn btn-secondary" onclick="POS.loadCutX()">Ver Resumen (Corte X)</button>
|
||||||
|
<button class="btn btn-primary" onclick="POS.confirmCutZ()">Cerrar Caja</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- ================================================================
|
<!-- ================================================================
|
||||||
OPEN REGISTER MODAL
|
OPEN REGISTER MODAL
|
||||||
================================================================ -->
|
================================================================ -->
|
||||||
@@ -533,7 +563,7 @@
|
|||||||
<script src="/pos/static/js/app-init.js" defer></script>
|
<script src="/pos/static/js/app-init.js" defer></script>
|
||||||
<script src="/pos/static/js/push.js" defer></script>
|
<script src="/pos/static/js/push.js" defer></script>
|
||||||
<script src="/pos/static/js/printer.js" defer></script>
|
<script src="/pos/static/js/printer.js" defer></script>
|
||||||
<script src="/pos/static/js/pos.js?v=2" defer></script>
|
<script src="/pos/static/js/pos.js?v=3" defer></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Cancel sale button wiring
|
// Cancel sale button wiring
|
||||||
|
|||||||
Reference in New Issue
Block a user