Al registrar una devolución desde el historial de ventas (apps/web/src/app/pos/historial/page.tsx, handleReturn), el pago de reembolso se guarda con un userId incorrecto, el stock puede regresar a un inventario distinto al de la venta, y si algún paso falla el cajero no ve ningún error.
Entorno
local
Módulos
Web / frontend, Punto de venta (POS), Inventario / almacenes, Caja / sesiones de caja
Severidad
Bloquea un flujo
Pasos para reproducir
Iniciar sesión como cajero con una caja abierta y registrar una venta.
Ir a Historial de ventas, seleccionar la venta y hacer la devolución.
Revisar el pago con status: "refunded" y el movimiento de inventario creados.
Repetir con un producto que tenga stock en dos inventarios distintos.
Repetir con el API caído o devolviendo error a mitad del flujo.
Esperado
El pago de reembolso tiene como userId al usuario que hace la devolución.
El stock regresa al inventario donde se hizo la venta original.
Si algo falla, el cajero ve un mensaje de error y no quedan datos a medias.
Actual
Se envía userId: original?.id ?? 0 (línea ~106). original.id es el id del pago, no del usuario. Si no hay pago original se envía 0, que viola la FK payment.userId → user.id.
Para cada línea se hace GET /warehouses completo dentro del loop (líneas ~91-93) y se toma el primer warehouse que tenga el producto, sin importar el inventario de la venta.
handleReturn no tiene try/catch: si falla a mitad, el error no se muestra y pueden quedar stock devuelto, pago creado o carrito sin marcar refunded por separado.
Evidencia
Encontrado en auditoría de código (revisión de apps/web/src/app/pos/historial/page.tsx); no reproducido en ejecución.
const original= paymentFor(returning.id);
await apiClient.post("/payments", {
cartId: returning.id,
userId: original?.id ?? 0, // id del pago, no del usuario
...
});
const warehouses= await apiClient.get(... "/warehouses");
const inventoryId= warehouses.find((w)=> w.productId === line.productId)?.inventoryId; // primer inventario con el producto
Arreglo sugerido: usar user.id de useSessionUser(), usar el inventoryId de la sesión de caja de la venta (o guardarlo en ShopCart) y envolver el flujo en try/catch. A mediano plazo, mover el flujo al servidor (#3).
¿Está también en producción?
Sí — este issue no basta; hay que abrir un Incidente / hotfix
No — solo local / staging
No sé
Antes de abrir
Busqué un issue duplicado
### Qué pasa
Al registrar una devolución desde el historial de ventas (`apps/web/src/app/pos/historial/page.tsx`, `handleReturn`), el pago de reembolso se guarda con un `userId` incorrecto, el stock puede regresar a un inventario distinto al de la venta, y si algún paso falla el cajero no ve ningún error.
### Entorno
local
### Módulos
Web / frontend, Punto de venta (POS), Inventario / almacenes, Caja / sesiones de caja
### Severidad
Bloquea un flujo
### Pasos para reproducir
1. Iniciar sesión como cajero con una caja abierta y registrar una venta.
2. Ir a **Historial de ventas**, seleccionar la venta y hacer la devolución.
3. Revisar el pago con `status: "refunded"` y el movimiento de inventario creados.
4. Repetir con un producto que tenga stock en dos inventarios distintos.
5. Repetir con el API caído o devolviendo error a mitad del flujo.
### Esperado
- El pago de reembolso tiene como `userId` al usuario que hace la devolución.
- El stock regresa al inventario donde se hizo la venta original.
- Si algo falla, el cajero ve un mensaje de error y no quedan datos a medias.
### Actual
- Se envía `userId: original?.id ?? 0` (línea ~106). `original.id` es el **id del pago**, no del usuario. Si no hay pago original se envía `0`, que viola la FK `payment.userId → user.id`.
- Para cada línea se hace `GET /warehouses` completo dentro del loop (líneas ~91-93) y se toma el **primer** warehouse que tenga el producto, sin importar el inventario de la venta.
- `handleReturn` no tiene `try/catch`: si falla a mitad, el error no se muestra y pueden quedar stock devuelto, pago creado o carrito sin marcar `refunded` por separado.
### Evidencia
```shell
Encontrado en auditoría de código (revisión de apps/web/src/app/pos/historial/page.tsx); no reproducido en ejecución.
const original = paymentFor(returning.id);
await apiClient.post("/payments", {
cartId: returning.id,
userId: original?.id ?? 0, // id del pago, no del usuario
...
});
const warehouses = await apiClient.get(... "/warehouses");
const inventoryId = warehouses.find((w) => w.productId === line.productId)?.inventoryId; // primer inventario con el producto
```
Arreglo sugerido: usar `user.id` de `useSessionUser()`, usar el `inventoryId` de la sesión de caja de la venta (o guardarlo en `ShopCart`) y envolver el flujo en `try/catch`. A mediano plazo, mover el flujo al servidor (#3).
### ¿Está también en producción?
- [ ] Sí — este issue no basta; hay que abrir un Incidente / hotfix
- [ ] No — solo local / staging
- [x] No sé
### Antes de abrir
- [x] Busqué un issue duplicado
Carlos
changed title from Bugs en el flujo de devolución (userId incorrecto, inventario equivocado, sin manejo de errores) to [bug] Devolución guarda userId incorrecto, regresa stock al inventario equivocado y no maneja errores2026-10-02 17:42:53 +00:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Qué pasa
Al registrar una devolución desde el historial de ventas (
apps/web/src/app/pos/historial/page.tsx,handleReturn), el pago de reembolso se guarda con unuserIdincorrecto, el stock puede regresar a un inventario distinto al de la venta, y si algún paso falla el cajero no ve ningún error.Entorno
local
Módulos
Web / frontend, Punto de venta (POS), Inventario / almacenes, Caja / sesiones de caja
Severidad
Bloquea un flujo
Pasos para reproducir
status: "refunded"y el movimiento de inventario creados.Esperado
userIdal usuario que hace la devolución.Actual
userId: original?.id ?? 0(línea ~106).original.ides el id del pago, no del usuario. Si no hay pago original se envía0, que viola la FKpayment.userId → user.id.GET /warehousescompleto dentro del loop (líneas ~91-93) y se toma el primer warehouse que tenga el producto, sin importar el inventario de la venta.handleReturnno tienetry/catch: si falla a mitad, el error no se muestra y pueden quedar stock devuelto, pago creado o carrito sin marcarrefundedpor separado.Evidencia
Arreglo sugerido: usar
user.iddeuseSessionUser(), usar elinventoryIdde la sesión de caja de la venta (o guardarlo enShopCart) y envolver el flujo entry/catch. A mediano plazo, mover el flujo al servidor (#3).¿Está también en producción?
Antes de abrir
Bugs en el flujo de devolución (userId incorrecto, inventario equivocado, sin manejo de errores)to [bug] Devolución guarda userId incorrecto, regresa stock al inventario equivocado y no maneja errores