POS — Nueva Venta
¿Qué es?
La pantalla de Nueva Venta es donde el cajero o mesero construye una orden seleccionando artículos del menú y luego la cobra. Se accede desde el ítem "Nueva venta" en el sidebar (ícono de carrito), URL: .../home-products.
Requiere el permiso orders:create. Solo está disponible si el empleado activo tiene permiso para esa sucursal.
---
Layout general
La pantalla está dividida en dos áreas principales:
En pantallas grandes (lg+, ≥1024px):
- Área izquierda (flexible): catálogo de artículos
- Área derecha (272px fijo): carrito / orden actual
En móvil y tablet:
- El catálogo ocupa toda la pantalla
- El carrito aparece como un botón flotante en la parte inferior central con el total y cantidad de artículos
- Al pulsar el botón, el carrito se abre como un drawer desde la parte inferior (hasta 85% de la altura de pantalla)
---
Encabezado
En la parte superior de la pantalla:
- Título "Nueva venta" a la izquierda
- Campo de búsqueda en el centro: busca artículos por nombre en tiempo real
- Botón de imágenes (ícono ojo/ojo tachado): activa/desactiva la visualización de imágenes en las tarjetas de artículos. La preferencia se guarda en
localStorage - Botón de vista (ícono cuadrícula/lista): alterna entre vista de cuadrícula y vista de lista. La preferencia se guarda en
localStorage
---
Advertencias de configuración
Justo debajo del encabezado pueden aparecer banners de advertencia (fondo amarillo/rojo) en estas situaciones:
- Sin turno de caja abierto: Si
requireOpenShiftToSell = truey no hay un turno abierto en la sucursal. El cajero no puede completar ventas hasta abrir un turno en/cash-register. - Sin sucursal configurada: Si no hay sucursal activa seleccionada. Las órdenes no se pueden enviar.
- Empleado no asignado a esta sucursal: Si el empleado activo tiene asignaciones de sucursal y la sucursal seleccionada no está entre ellas. Incluye botón "Recargar" por si las asignaciones cambiaron recientemente.
---
Barra de categorías
Debajo del encabezado, una fila horizontal scrolleable con las categorías disponibles:
- "Todas" (seleccionado por defecto)
- Una pastilla por cada categoría creada en
/categories - Al seleccionar una categoría, el catálogo se filtra a solo esa categoría
- La barra es scrolleable horizontalmente en móvil sin mostrar scrollbar
---
Catálogo de artículos
Vista cuadrícula
Artículos en grid responsivo (2 columnas en móvil, 3 en tablet, 4 en desktop, 5 en pantallas XL). Cada tarjeta muestra:
- Imagen del artículo (si está disponible y si la vista de imágenes está activada)
- Nombre del artículo
- Precio
- Badge con cantidad en carrito (si ya fue agregado)
Vista lista
Artículos en filas horizontales con nombre, precio y controles de cantidad.
Comportamiento al tocar un artículo
Depende del tipo de artículo:
| Tipo de artículo | Acción al pulsar |
|---|---|
| Artículo simple (sin variantes, sin modificadores, sin composición) | Se agrega directamente al carrito |
| Artículo con variantes | Abre el Diálogo de selección de variante |
| Artículo con grupos de modificadores | Abre el Diálogo de modificadores |
Artículo con composición (has_composition = true) | Abre el Personalizador de composición (pizza) |
---
Diálogo de variantes
Se abre cuando el artículo tiene variantes (ej. tamaños: Chica, Mediana, Grande). Muestra:
- Nombre del artículo en el título
- Lista de variantes con nombre y precio de cada una
- Al seleccionar una variante, se agrega al carrito y el diálogo se cierra
---
Diálogo de modificadores
Se abre para artículos con grupos de modificadores (ej. "Extras": Queso extra, Chile de agua...). Muestra:
- Grupos de modificadores con su nombre y regla (selección única, múltiple, requerido)
- Cada modificador con nombre y precio extra (si tiene)
- Validación: si un grupo es requerido y
minSelections > 0, el botón "Agregar" está deshabilitado hasta que se seleccione la cantidad mínima - Botón "Agregar al carrito" al pie
---
Personalizador de composición (Pizza customizer)
Para artículos con has_composition = true. Pantalla más compleja que permite:
- Seleccionar ingredientes base, con la posibilidad de marcarlos como mitad-izquierda o mitad-derecha
- Opción de mezclar dos artículos de composición (ej. mitad pepperoni, mitad hawaiana)
- El precio puede variar según los ingredientes seleccionados
---
El carrito (Orden actual)
Encabezado del carrito (desktop)
Visible en la columna derecha en pantallas ≥1024px:
- Título "Orden actual" con badge de cantidad de artículos
- Botón "Vaciar" (ícono basura) para limpiar el carrito completamente
Items del carrito
Cada artículo agregado muestra:
- Nombre (con variante y modificadores seleccionados)
- Controles de cantidad (+/-)
- Campo de notas por artículo
- Precio total del ítem
- Botón para eliminar
Promociones en el carrito
Si el módulo de promociones está habilitado:
- Se puede seleccionar manualmente una promoción de la lista
- Si
promotionAutoApply = true, el sistema aplica automáticamente la promoción con mayor descuento al cambiar el carrito - Si el usuario elimina manualmente una promoción, no se vuelve a auto-aplicar hasta que vacíe el carrito
Totales
Al pie del carrito:
- Subtotal
- Descuento aplicado (si hay promoción)
- Impuesto (si
taxIncluded = false, se muestra desglosado) - Total a pagar
Botón de cobrar
El botón "Cobrar" / "Proceder al pago" está al pie del carrito. Si el empleado no está autorizado en la sucursal, muestra un toast de error en lugar de abrir el checkout.
---
Checkout (pago)
Al pulsar "Cobrar" se abre el CheckoutSheet — un panel lateral o bottom drawer que permite:
Tipo de orden
El cajero selecciona el canal de venta:
- En mesa (dine_in): pide número de mesa
- Para llevar (takeout): sin datos adicionales
- A domicilio (delivery): muestra campos de dirección del cliente (nombre, teléfono, dirección, ciudad, estado, código postal) con autocompletado de Google Maps
Método de pago
Según la configuración de orderSettings, los métodos disponibles pueden incluir:
- Efectivo
- Tarjeta de débito
- Tarjeta de crédito
- Transferencia
- Contra entrega (pay_on_delivery, solo para domicilio)
- Cortesía (requiere gerente si
requireManagerForDiscount = true)
Pagos divididos: se puede ingresar un monto por cada método, y el sistema valida que la suma iguale el total.
Cupones
Si enableCoupons = true, aparece un campo para ingresar un código de cupón. Al aplicarlo:
- El sistema valida el código contra la API
- Si es válido, aplica el descuento y muestra el nuevo total
- Si el cupón ya fue usado, expiró o no aplica, muestra un error
Notas de la orden
Campo de texto libre para agregar instrucciones generales (ej. "sin cebolla en todo").
Cambio
Si el método de pago incluye efectivo, aparece un campo "Efectivo recibido" y el sistema calcula automáticamente el cambio a dar.
Confirmar orden
Al pulsar "Confirmar orden":
- Se crea la orden en la base de datos vía
POST /api/orders - Si
enableOrderApproval = truey la orden cumple las reglas de aprobación, se crea con statusawaiting_confirmation; de lo contrario, conpending - Si el inventario está activo (
enableInventory = trueyinventoryDeductionTrigger = "on_order"), se deduce el stock automáticamente - Se publica el evento en Ably para que la Kitchen app lo reciba en tiempo real
- Si
lockAfterSale = true, la terminal se bloquea y pide el PIN del siguiente empleado - El carrito se vacía automáticamente
---
Validaciones importantes
- Si
requireOpenShiftToSell = truey no hay turno abierto, el botón de cobrar muestra un error - Si la sucursal tiene tipo de órdenes deshabilitado (ej. sin delivery), ese tipo no aparece en el checkout
- El campo de mesa solo aparece para órdenes dine_in
- La dirección de domicilio es obligatoria para órdenes delivery