570 lines
20 KiB
Plaintext
570 lines
20 KiB
Plaintext
<!-- services/manso/src/views/comandas.ejs -->
|
|
<div class="d-flex align-items-center justify-content-between mb-3">
|
|
<h1 class="h4 m-0">📋 Nueva Comanda</h1>
|
|
<span class="badge rounded-pill text-bg-light">/api/*</span>
|
|
</div>
|
|
|
|
<div class="row g-3">
|
|
<!-- Columna izquierda: Productos -->
|
|
<div class="col-12 col-lg-7">
|
|
<div class="card shadow-sm">
|
|
<div class="card-header d-flex align-items-center">
|
|
<strong>Productos</strong>
|
|
<div class="ms-auto small text-muted" id="prodCount">0 ítems</div>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-2 align-items-center mb-2">
|
|
<div class="col-12 col-sm">
|
|
<input id="busqueda" type="search" class="form-control" placeholder="Buscar por nombre o categoría…">
|
|
</div>
|
|
<div class="col-auto">
|
|
<button class="btn btn-outline-secondary" id="limpiarBusqueda">Limpiar</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="listadoProductos" class="border rounded" style="max-height:58vh; overflow:auto;">
|
|
<!-- tabla de productos renderizada por JS -->
|
|
<div class="p-3 text-muted">Cargando…</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Columna derecha: Detalles + Carrito -->
|
|
<div class="col-12 col-lg-5">
|
|
<div class="card shadow-sm mb-3">
|
|
<div class="card-header"><strong>Detalles</strong></div>
|
|
<div class="card-body">
|
|
<div class="row g-2">
|
|
<div class="col-12 col-sm-6">
|
|
<label for="selMesa" class="form-label text-muted small mb-1">Mesa</label>
|
|
<select id="selMesa" class="form-select"></select>
|
|
</div>
|
|
<div class="col-12 col-sm-6">
|
|
<label for="selUsuario" class="form-label text-muted small mb-1">Usuario</label>
|
|
<select id="selUsuario" class="form-select"></select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-2">
|
|
<label for="obs" class="form-label text-muted small mb-1">Observaciones</label>
|
|
<textarea id="obs" class="form-control" rows="3"></textarea>
|
|
</div>
|
|
|
|
<div class="alert alert-secondary mt-3 mb-0 small">
|
|
La fecha se completa automáticamente y los estados/activos usan sus valores por defecto.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card shadow-sm">
|
|
<div class="card-header"><strong>Carrito</strong></div>
|
|
<div class="card-body p-0" id="carritoWrap">
|
|
<div class="p-3 text-muted">Aún no agregaste productos.</div>
|
|
</div>
|
|
<div class="d-flex align-items-center gap-2 p-3 border-top" style="position:sticky; bottom:0; background:#fff;">
|
|
<div class="small"><span class="text-muted">Ítems:</span> <strong id="kpiItems">0</strong></div>
|
|
<div class="small ms-2"><span class="text-muted">Total:</span> <strong id="kpiTotal">$ 0.00</strong></div>
|
|
<div class="ms-auto"></div>
|
|
<button class="btn btn-outline-secondary" id="vaciar">Vaciar</button>
|
|
<button class="btn btn-primary" id="crear">Crear Comanda</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="msg" class="mt-2 small text-muted"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ====== LÓGICA ====== -->
|
|
<script>
|
|
// Helpers DOM
|
|
const $ = (s, r=document) => r.querySelector(s);
|
|
const $$ = (s, r=document) => Array.from(r.querySelectorAll(s));
|
|
|
|
// Estado
|
|
const state = {
|
|
productos: [],
|
|
mesas: [],
|
|
usuarios: [],
|
|
categorias: [], // <--- NUEVO
|
|
carrito: [],
|
|
filtro: ''
|
|
};
|
|
|
|
function norm(s='') {
|
|
return s.toString().toLowerCase()
|
|
.normalize('NFD').replace(/\p{Diacritic}/gu,''); // "café" -> "cafe"
|
|
}
|
|
|
|
function isTakeaway(apodo) {
|
|
return /^takeaway$/i.test(String(apodo || '').trim());
|
|
}
|
|
|
|
function groupOrderByCatName(catName='') {
|
|
const n = norm(catName);
|
|
if (n.includes('bar')) return 1;
|
|
if (n.includes('cafe')) return 2;
|
|
if (n.includes('cafeter')) return 3;
|
|
if (n.includes('trago') || n.includes('refresc')) return 4;
|
|
return 99; // otros
|
|
}
|
|
|
|
// Genera el HTML del ticket de cocina (80mm aprox)
|
|
function buildKitchenTicketHTML(data) {
|
|
const apodo = String(data.mesa_apodo ?? '').trim();
|
|
const numero = data.mesa_numero ?? '';
|
|
const take = isTakeaway(apodo);
|
|
|
|
const mesaTxt = take ? apodo.toUpperCase() : `Mesa #${numero}${apodo ? ' · ' + apodo : ''}`;
|
|
// const isTakeaway = /Takeaway/i.test(String(data.mesa_apodo ?? '')) || /Takeaway/i.test(String(data.mesa_numero ?? ''));
|
|
const mesaClass = take ? 'bigline' : 'mesa-medium';
|
|
const obs = (data.observaciones && data.observaciones.trim()) ? data.observaciones.trim() : '';
|
|
|
|
|
|
|
|
// Productos ya vienen con su "g" (grupo numérico 1..4/99) y cantidad formateada
|
|
const items = data.productos.slice().sort((a,b)=> (a.g||99) - (b.g||99));
|
|
|
|
let productosHtml = '';
|
|
let prevG = null;
|
|
for (const p of items) {
|
|
if (prevG !== null && p.g !== prevG) {
|
|
productosHtml += `<div class="hr dotted"></div>`; // separación punteada entre grupos
|
|
}
|
|
productosHtml += `
|
|
<div class="row">
|
|
<div class="qty">x${p.cantidad}</div>
|
|
<div class="name">${p.nombre}</div>
|
|
</div>`;
|
|
prevG = p.g;
|
|
}
|
|
|
|
return `<!doctype html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Ticket Cocina</title>
|
|
<style>
|
|
:root {
|
|
--w: 80mm;
|
|
--fz-base: 16px;
|
|
--fz-md: 16px; /* observaciones */
|
|
--fz-item: 18px; /* filas */
|
|
--fz-xl: 26px; /* <--- NUEVO: tamaño “grande” (mesa) */
|
|
--fz-xxl: 34px; /* título (#comanda) */
|
|
--fz-sm: 12px;
|
|
}
|
|
html, body { margin:0; padding:0; }
|
|
body {
|
|
width: var(--w);
|
|
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
font-size: var(--fz-base);
|
|
line-height: 1.35;
|
|
color:#000;
|
|
font-weight: 700;
|
|
}
|
|
#ticket { padding: 10px 8px; }
|
|
.center { text-align:center; }
|
|
.row { display:flex; gap:8px; margin: 4px 0; }
|
|
.row .qty { min-width: 22mm; font-size: var(--fz-item); letter-spacing:.2px; }
|
|
.row .name { flex:1; font-size: var(--fz-item); text-transform: uppercase; word-break: break-word; }
|
|
.hr { border-top: 2px dashed #000; margin: 8px 0; }
|
|
.hr.dotted { border-top: 2px dotted #000; }
|
|
.small { font-size: var(--fz-sm); }
|
|
.bigline { font-size: var(--fz-xxl); text-transform: uppercase; }
|
|
.mesa-medium { font-size: var(--fz-xl); text-transform: uppercase; }
|
|
.obs { font-size: var(--fz-md); }
|
|
.mt4{margin-top:4px}.mt8{margin-top:8px}.mb4{margin-bottom:4px}.mb8{margin-bottom:8px}
|
|
@page { size: var(--w) auto; margin: 0; }
|
|
@media print { body { width: var(--w); } }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="ticket">
|
|
<!-- SIN TÍTULO -->
|
|
<div class="center bigline">#${data.id_comanda}</div>
|
|
<div class="center ${mesaClass}">${mesaTxt}</div>
|
|
|
|
<div class="small mt4">Fecha: ${data.fecha} ${data.hora}</div>
|
|
<div class="small mt4">Mozo: ${data.usuario || '—'}</div>
|
|
${obs ? `<div class="obs mt8">Obs: ${obs}</div>` : ''}
|
|
|
|
<div class="hr"></div>
|
|
${productosHtml}
|
|
|
|
<div class="hr"></div>
|
|
<div class="small">Ítems: ${data.items} · Unidades: ${data.units}</div>
|
|
<div class="center mt8 small">— fin —</div>
|
|
</div>
|
|
<script>window.onload = () => { window.focus(); window.print(); }<\/script>
|
|
</body>
|
|
</html>`;
|
|
}
|
|
|
|
// Imprime HTML usando un iframe oculto (menos bloqueos de pop-up)
|
|
function printHtmlViaIframe(html) {
|
|
return new Promise((resolve) => {
|
|
let iframe = document.getElementById('printFrame');
|
|
if (!iframe) {
|
|
iframe = document.createElement('iframe');
|
|
iframe.id = 'printFrame';
|
|
iframe.style.position = 'fixed';
|
|
iframe.style.right = '-9999px';
|
|
iframe.style.bottom = '0';
|
|
iframe.style.width = '0';
|
|
iframe.style.height = '0';
|
|
iframe.style.border = '0';
|
|
document.body.appendChild(iframe);
|
|
}
|
|
const doc = iframe.contentWindow.document;
|
|
doc.open();
|
|
doc.write(html);
|
|
doc.close();
|
|
|
|
// Salida: remover iframe después de un rato para no acumular
|
|
setTimeout(() => {
|
|
resolve();
|
|
// (si prefieres mantenerlo para reimpresiones, no lo quites)
|
|
// document.body.removeChild(iframe);
|
|
}, 1500);
|
|
});
|
|
}
|
|
|
|
|
|
|
|
// Utils
|
|
const money = (n) => (isNaN(n) ? '—' : new Intl.NumberFormat('es-UY', { style:'currency', currency:'UYU' }).format(Number(n)));
|
|
const toast = (msg, ok=false) => {
|
|
const el = $('#msg');
|
|
el.className = ok ? 'mt-2 small ok text-success' : 'mt-2 small err text-danger';
|
|
el.textContent = msg;
|
|
setTimeout(()=>{ el.textContent=''; el.className='mt-2 small text-muted'; }, 3500);
|
|
};
|
|
|
|
async function jget(url) {
|
|
const res = await fetch(url);
|
|
let data; try { data = await res.json(); } catch { data = null; }
|
|
if (!res.ok) throw new Error(data?.error || `${res.status} ${res.statusText}`);
|
|
return data;
|
|
}
|
|
async function jpost(url, body) {
|
|
const res = await fetch(url, { method:'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify(body) });
|
|
const data = await res.json().catch(()=>null);
|
|
if (!res.ok) throw new Error(data?.error || `${res.status} ${res.statusText}`);
|
|
return data;
|
|
}
|
|
|
|
// Carga inicial
|
|
async function init() {
|
|
const [prods, mesas, usuarios, categorias] = await Promise.all([
|
|
jget('/api/table/productos?limit=1000'),
|
|
jget('/api/table/mesas?limit=1000'),
|
|
jget('/api/table/usuarios?limit=1000'),
|
|
jget('/api/table/categorias?limit=1000') // <--- NUEVO
|
|
]);
|
|
|
|
state.productos = prods.filter(p => p.activo !== false);
|
|
state.mesas = mesas;
|
|
state.usuarios = usuarios.filter(u => u.activo !== false);
|
|
state.categorias = Array.isArray(categorias) ? categorias : [];
|
|
|
|
// Mapas para buscar categoría por id de producto
|
|
state.catById = new Map(state.categorias.map(c => [c.id_categoria, (c.nombre||'').toString()]));
|
|
state.prodCatNameById = new Map(state.productos.map(p => [p.id_producto, state.catById.get(p.id_categoria)||'']));
|
|
|
|
hydrateMesas();
|
|
hydrateUsuarios();
|
|
renderProductos();
|
|
renderCarrito();
|
|
|
|
$('#busqueda').addEventListener('input', () => {
|
|
state.filtro = $('#busqueda').value.trim().toLowerCase();
|
|
renderProductos();
|
|
});
|
|
$('#limpiarBusqueda').addEventListener('click', () => {
|
|
$('#busqueda').value = '';
|
|
state.filtro = '';
|
|
renderProductos();
|
|
});
|
|
$('#vaciar').addEventListener('click', () => { state.carrito = []; renderCarrito(); });
|
|
$('#crear').addEventListener('click', crearComanda);
|
|
}
|
|
|
|
function hydrateMesas() {
|
|
const sel = $('#selMesa');
|
|
sel.innerHTML = '';
|
|
// Ordena por número de mesa (o por id si no hay número)
|
|
const rows = state.mesas
|
|
.slice()
|
|
.sort((a, b) => Number(a?.numero ?? a?.id_mesa ?? 0) - Number(b?.numero ?? b?.id_mesa ?? 0));
|
|
|
|
for (const m of rows) {
|
|
const o = document.createElement('option');
|
|
o.value = m.id_mesa;
|
|
o.textContent = `#${m.numero} · ${m.apodo} (${m.estado})`;
|
|
sel.appendChild(o);
|
|
}
|
|
}
|
|
function hydrateUsuarios() {
|
|
const sel = $('#selUsuario');
|
|
sel.innerHTML = '';
|
|
// 🔽 Orden ascendente por id_usuario
|
|
const rows = state.usuarios
|
|
.slice()
|
|
.sort((a, b) => Number(a?.id_usuario ?? 0) - Number(b?.id_usuario ?? 0));
|
|
|
|
for (const u of rows) {
|
|
const o = document.createElement('option');
|
|
o.value = u.id_usuario;
|
|
o.textContent = `${u.nombre} ${u.apellido}`.trim();
|
|
sel.appendChild(o);
|
|
}
|
|
}
|
|
// Render productos
|
|
function renderProductos() {
|
|
let rows = state.productos.slice();
|
|
if (state.filtro) {
|
|
rows = rows.filter(p =>
|
|
(p.nombre || '').toLowerCase().includes(state.filtro) ||
|
|
String(p.id_categoria ?? '').includes(state.filtro)
|
|
);
|
|
}
|
|
$('#prodCount').textContent = `${rows.length} ítems`;
|
|
|
|
if (!rows.length) {
|
|
$('#listadoProductos').innerHTML = '<div class="p-3 text-muted">Sin resultados.</div>';
|
|
return;
|
|
}
|
|
|
|
const tbl = document.createElement('table');
|
|
tbl.className = 'table table-sm align-middle mb-0';
|
|
tbl.innerHTML = `
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Producto</th>
|
|
<th class="text-end">Precio</th>
|
|
<th style="width:210px;">Cantidad</th>
|
|
<th style="width:100px;"></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
`;
|
|
const tb = tbl.querySelector('tbody');
|
|
|
|
for (const p of rows) {
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td>${p.nombre}</td>
|
|
<td class="text-end">${money(p.precio)}</td>
|
|
<td>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<input type="number" min="0.001" step="0.001" value="1.000" data-qty class="form-control form-control-sm" style="max-width:120px;">
|
|
<button class="btn btn-sm btn-outline-secondary" data-dec>-</button>
|
|
<button class="btn btn-sm btn-outline-secondary" data-inc>+</button>
|
|
</div>
|
|
</td>
|
|
<td><button class="btn btn-sm btn-primary" data-add>Agregar</button></td>
|
|
`;
|
|
const qty = tr.querySelector('[data-qty]');
|
|
tr.querySelector('[data-dec]').addEventListener('click', () => { qty.value = Math.max(0.001, (parseFloat(qty.value||'0') - 1)).toFixed(3); });
|
|
tr.querySelector('[data-inc]').addEventListener('click', () => { qty.value = (parseFloat(qty.value||'0') + 1).toFixed(3); });
|
|
tr.querySelector('[data-add]').addEventListener('click', () => addToCart(p, parseFloat(qty.value||'1')) );
|
|
tb.appendChild(tr);
|
|
}
|
|
|
|
$('#listadoProductos').innerHTML = '';
|
|
$('#listadoProductos').appendChild(tbl);
|
|
}
|
|
|
|
function addToCart(prod, cantidad) {
|
|
if (!(cantidad > 0)) { toast('Cantidad inválida'); return; }
|
|
const precio = parseFloat(prod.precio);
|
|
const it = state.carrito.find(i => i.id_producto === prod.id_producto && i.pre_unitario === precio);
|
|
if (it) it.cantidad = Number((it.cantidad + cantidad).toFixed(3));
|
|
else state.carrito.push({ id_producto: prod.id_producto, nombre: prod.nombre, pre_unitario: precio, cantidad: Number(cantidad.toFixed(3)) });
|
|
renderCarrito();
|
|
}
|
|
|
|
// Render carrito
|
|
function renderCarrito() {
|
|
const wrap = $('#carritoWrap');
|
|
if (!state.carrito.length) {
|
|
wrap.innerHTML = '<div class="p-3 text-muted">Aún no agregaste productos.</div>';
|
|
$('#kpiItems').textContent='0'; $('#kpiTotal').textContent=money(0);
|
|
return;
|
|
}
|
|
|
|
const tbl = document.createElement('table');
|
|
tbl.className = 'table table-sm align-middle mb-0';
|
|
tbl.innerHTML = `
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Producto</th>
|
|
<th class="text-end">Unitario</th>
|
|
<th class="text-end">Cantidad</th>
|
|
<th class="text-end">Subtotal</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
`;
|
|
const tb = tbl.querySelector('tbody');
|
|
|
|
let items = 0, total = 0;
|
|
state.carrito.forEach((it, idx) => {
|
|
items += 1;
|
|
const sub = Number(it.pre_unitario) * Number(it.cantidad);
|
|
total += sub;
|
|
|
|
const tr = document.createElement('tr');
|
|
tr.innerHTML = `
|
|
<td>${it.nombre}</td>
|
|
<td class="text-end">${money(it.pre_unitario)}</td>
|
|
<td class="text-end">
|
|
<input type="number" min="0.001" step="0.001" value="${it.cantidad.toFixed(3)}" class="form-control form-control-sm text-end" style="max-width:120px;">
|
|
</td>
|
|
<td class="text-end">${money(sub)}</td>
|
|
<td class="text-end">
|
|
<button class="btn btn-sm btn-outline-secondary" data-del>Quitar</button>
|
|
</td>
|
|
`;
|
|
const qty = tr.querySelector('input[type="number"]');
|
|
qty.addEventListener('change', () => {
|
|
const v = parseFloat(qty.value||'0');
|
|
if (!(v>0)) { toast('Cantidad inválida'); qty.value = it.cantidad.toFixed(3); return; }
|
|
it.cantidad = Number(v.toFixed(3));
|
|
renderCarrito();
|
|
});
|
|
tr.querySelector('[data-del]').addEventListener('click', () => {
|
|
state.carrito.splice(idx,1);
|
|
renderCarrito();
|
|
});
|
|
|
|
tb.appendChild(tr);
|
|
});
|
|
|
|
wrap.innerHTML = '';
|
|
wrap.appendChild(tbl);
|
|
$('#kpiItems').textContent = String(items);
|
|
$('#kpiTotal').textContent = money(total);
|
|
}
|
|
|
|
const fmtQty = (n) => Number(n).toFixed(3).replace(/\.?0+$/,'');
|
|
|
|
async function crearComanda() {
|
|
if (!state.carrito.length) { toast('Agrega al menos un producto'); return; }
|
|
const id_mesa = parseInt($('#selMesa').value, 10);
|
|
const id_usuario = parseInt($('#selUsuario').value, 10);
|
|
if (!id_mesa || !id_usuario) { toast('Selecciona mesa y usuario'); return; }
|
|
|
|
// Snapshot del carrito ANTES de limpiar (para imprimir)
|
|
const cartSnapshot = state.carrito.map(it => ({ ...it }));
|
|
|
|
const observaciones = $('#obs').value.trim() || null;
|
|
|
|
try {
|
|
// 1) encabezado comanda
|
|
const { inserted: com } = await jpost('/api/table/comandas', {
|
|
id_usuario,
|
|
id_mesa,
|
|
estado: 'abierta',
|
|
observaciones
|
|
});
|
|
|
|
// 2) detalle
|
|
const id_comanda = com.id_comanda;
|
|
const payloads = cartSnapshot.map(it => ({
|
|
id_comanda,
|
|
id_producto: it.id_producto,
|
|
cantidad: it.cantidad,
|
|
pre_unitario: it.pre_unitario
|
|
}));
|
|
await Promise.all(payloads.map(p => jpost('/api/table/deta_comandas', p)));
|
|
|
|
// 3) Datos auxiliares para ticket
|
|
const mesa = state.mesas.find(m => m.id_mesa === id_mesa) || {};
|
|
const usuario = state.usuarios.find(u => u.id_usuario === id_usuario) || {};
|
|
const now = new Date();
|
|
const fecha = now.toLocaleDateString();
|
|
const hora = now.toLocaleTimeString();
|
|
|
|
// 4) Construir e imprimir Ticket de Cocina (sin precios)
|
|
const units = cartSnapshot.reduce((acc, it) => acc + Number(it.cantidad || 0), 0);
|
|
const items = cartSnapshot.length;
|
|
|
|
// map producto -> nombre de categoría
|
|
const prodCat = state.prodCatNameById || new Map();
|
|
|
|
const productosParaTicket = cartSnapshot.map(it => ({
|
|
nombre: it.nombre,
|
|
cantidad: fmtQty(it.cantidad),
|
|
g: groupOrderByCatName(prodCat.get(it.id_producto) || '') // 1..4/99
|
|
}));
|
|
|
|
const ticketHtml = buildKitchenTicketHTML({
|
|
id_comanda,
|
|
fecha, hora,
|
|
mesa_numero: mesa?.numero,
|
|
mesa_apodo: mesa?.apodo,
|
|
usuario: `${usuario?.nombre || ''} ${usuario?.apellido || ''}`.trim(),
|
|
observaciones,
|
|
items,
|
|
units,
|
|
productos: productosParaTicket // <--- con grupos
|
|
});
|
|
|
|
await printHtmlViaIframe(ticketHtml);
|
|
|
|
// 5) Reset UI
|
|
state.carrito = [];
|
|
renderCarrito();
|
|
$('#obs').value = '';
|
|
toast(`Comanda #${id_comanda} creada e impresa`, true);
|
|
|
|
} catch (e) {
|
|
toast(e.message || 'No se pudo crear la comanda');
|
|
}
|
|
}
|
|
|
|
// // Crear comanda
|
|
// async function crearComanda() {
|
|
// if (!state.carrito.length) { toast('Agrega al menos un producto'); return; }
|
|
// const id_mesa = parseInt($('#selMesa').value, 10);
|
|
// const id_usuario = parseInt($('#selUsuario').value, 10);
|
|
// if (!id_mesa || !id_usuario) { toast('Selecciona mesa y usuario'); return; }
|
|
|
|
// const observaciones = $('#obs').value.trim() || null;
|
|
|
|
// try {
|
|
// // 1) encabezado comanda
|
|
// const { inserted: com } = await jpost('/api/table/comandas', {
|
|
// id_usuario,
|
|
// id_mesa,
|
|
// estado: 'abierta',
|
|
// observaciones
|
|
// });
|
|
|
|
// // 2) detalle
|
|
// const id_comanda = com.id_comanda;
|
|
// const payloads = state.carrito.map(it => ({
|
|
// id_comanda,
|
|
// id_producto: it.id_producto,
|
|
// cantidad: it.cantidad,
|
|
// pre_unitario: it.pre_unitario
|
|
// }));
|
|
|
|
// await Promise.all(payloads.map(p => jpost('/api/table/deta_comandas', p)));
|
|
|
|
// state.carrito = [];
|
|
// renderCarrito();
|
|
// $('#obs').value = '';
|
|
// toast(`Comanda #${id_comanda} creada`, true);
|
|
// } catch (e) {
|
|
// toast(e.message || 'No se pudo crear la comanda');
|
|
// }
|
|
// }
|
|
|
|
// GO
|
|
init().catch(err => toast(err.message || 'Error cargando datos'));
|
|
</script>
|