5.8 KiB
TPV — Checkout, pagos mixtos y tickets
Implementado en F-186 y ampliado en F-187. Complementa
POS_API.mdy sustituye el flujo inmediato de pago único descrito en documentos de discovery antiguos.
Configuración en administración
En Administración → TPV, selecciona una tienda para configurar:
Terminal
- Navegación táctil por categorías.
- Hasta ocho productos rápidos por terminal.
- Permitir descuentos por línea. Desactívalo en terminales de autopago. La restricción se aplica también en backend, no solo ocultando el botón.
Formas de pago
Cada tienda puede activar o desactivar métodos con:
- código estable (
cash,card,bizum,stripe,apple_pay, etc.); - etiqueta visible;
- tipo
cash,carduother.
Una etiqueta no integra automáticamente una pasarela. Stripe, Apple Pay, Bizum u otros métodos se registran manualmente hasta que exista un adaptador de proveedor.
Empresa y ticket
Configura:
- nombre o razón social;
- NIF/CIF;
- dirección, teléfono y email;
- cabecera y pie;
- prefijo, próximo número y dígitos de relleno;
- política de devolución.
La asignación del siguiente número se bloquea dentro de la transacción de venta, evitando números duplicados entre terminales concurrentes.
Cajeros
En Administración → TPV → Cajeros puedes crear cuentas con rol cajero y consultar su estado:
- Activo: puede iniciar sesión y abrir caja.
- Inactivo: no puede autenticarse; se puede reactivar.
- Eliminado: no puede autenticarse ni reactivarse.
Desactivar revoca inmediatamente todas las sesiones del cajero. Al reactivarlo tendrá que iniciar una sesión nueva.
Eliminar requiere confirmación y realiza una baja lógica irreversible. La fila y el UUID se conservan para que tickets, ventas, sesiones de caja, reporting y auditoría sigan indicando quién realizó la operación.
No se puede desactivar ni eliminar un cajero con una sesión de caja abierta. Primero debe cerrarse y cuadrarse esa sesión.
API administrativa:
GET /pos/users: lista personal POS y estado de lifecycle.POST /pos/users: crea cajero/manager; contraseña mínima de ocho caracteres.PATCH /pos/users/:id/statuscon{ "active": false }o{ "active": true }.DELETE /pos/users/:id: baja lógica irreversible del cajero.
Las operaciones de estado y eliminación solo admiten objetivos con rol pos_cashier, requieren administrador y generan eventos de auditoría.
Flujo de caja
- Añade productos de catálogo o un Artículo libre (nombre y precio positivo).
- Aplica descuentos por línea si el terminal los permite.
- Pulsa una forma de pago configurada.
- En el modal elige:
- Paga el total: asigna todo el importe pendiente.
- Paga una parte: introduce un importe menor y añade después otro método.
- En efectivo, indica lo entregado. El TPV muestra el cambio.
- Revisa las líneas de pago, el total pagado y el importe pendiente. Se puede quitar una asignación antes de confirmar.
- Cuando el pendiente sea cero, pulsa Confirmar y cerrar ticket.
- Imprime o envía el ticket por email. La caja se limpia después de completar una de estas acciones.
Una venta totalmente pagada queda en estado COMPLETED. Los pagos pendientes se implementan aparte en F-188.
Contrato de venta
POST /pos/sales
Línea de stock
{
"kind": "stock",
"variantId": "uuid",
"quantity": 2,
"discountCents": 100
}
El backend ignora snapshots antiguos enviados por el cliente y vuelve a cargar nombre, SKU, EAN y precio. También bloquea y actualiza el stock de la tienda.
Artículo libre
{
"kind": "free",
"name": "Servicio de asesoría",
"unitPriceCents": 2500,
"quantity": 1
}
No reserva ni descuenta inventario. La base de datos exige que product_id y variant_id sean nulos únicamente en estas líneas.
Pago
{
"methodCode": "cash",
"amountCents": 1000,
"tenderedCents": 2000
}
- La suma de
amountCentsdebe coincidir exactamente con el total. - Solo un método de tipo efectivo acepta
tenderedCents. tenderedCentsdebe ser mayor o igual que el importe aplicado.- El cambio es
tenderedCents - amountCents. - Un código inactivo o de otra tienda se rechaza.
La venta, stock, pagos, líneas de reporting, saldo esperado de caja, número y ticket se confirman en una única transacción idempotente.
Ticket
La respuesta de venta y GET /pos/sales/:id/receipt contienen:
- datos de empresa;
- fecha y hora;
- número de ticket;
- terminal, sesión y cajero;
- artículos, cantidad, precio, subtotal, descuento, IVA y total de línea;
- subtotal, descuentos, IVA y total de venta;
- métodos e importes pagados;
- efectivo entregado y cambio;
- política de devolución.
Impresión
GET /pos/sales/:id/print devuelve el mismo payload estructurado. El cliente usa impresión estándar del navegador, sin controlador específico de hardware.
POST /pos/sales/:id/receipt/email
{ "email": "cliente@example.es" }
Usa la configuración SMTP de Ajustes → SMTP / Email. El asunto y contenido se generan exclusivamente desde el ticket; el cliente no puede suministrar HTML arbitrario.
Reporting y caja
Por cada asignación se crea:
- una transacción en
payments_transactions; - una línea
paymentenreporting_payment_linescon tienda, terminal, sesión y método.
El efectivo esperado aumenta por el importe aplicado, no por el efectivo entregado; el cambio no cuenta como ingreso ni efectivo retenido.
Próximas ampliaciones
- F-188: ventas con saldo pendiente.
- F-189: cantidades negativas, devoluciones parciales/totales y ticket de devolución.
- F-190: auditoría completa de actualización/refresco de reporting.
- F-191: cierre de terminal y cierre diario conciliando efectivo, tarjetas, devoluciones y pendientes.