feat(F-186): completed feature

This commit is contained in:
chattie
2026-08-22 22:08:09 +02:00
parent 63a305bdd4
commit a3f6edd325
30 changed files with 3603 additions and 624 deletions

144
docs/pos/POS_CHECKOUT.md Normal file
View File

@@ -0,0 +1,144 @@
# TPV — Checkout, pagos mixtos y tickets
> Implementado en F-186. Complementa `POS_API.md` y 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`, `card` u `other`.
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.
## Flujo de caja
1. Añade productos de catálogo o un **Artículo libre** (nombre y precio positivo).
2. Aplica descuentos por línea si el terminal los permite.
3. Pulsa una forma de pago configurada.
4. 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.
5. En efectivo, indica lo entregado. El TPV muestra el cambio.
6. Revisa las líneas de pago, el total pagado y el importe pendiente. Se puede quitar una asignación antes de confirmar.
7. Cuando el pendiente sea cero, pulsa **Confirmar y cerrar ticket**.
8. 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
```json
{
"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
```json
{
"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
```json
{
"methodCode": "cash",
"amountCents": 1000,
"tenderedCents": 2000
}
```
- La suma de `amountCents` debe coincidir exactamente con el total.
- Solo un método de tipo efectivo acepta `tenderedCents`.
- `tenderedCents` debe 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.
### Email
`POST /pos/sales/:id/receipt/email`
```json
{ "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 `payment` en `reporting_payment_lines` con 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-187: eliminar/desactivar cajeros preservando histórico.
- 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.