[bug] Devolución guarda userId incorrecto, regresa stock al inventario equivocado y no maneja errores #1

Open
opened 2026-10-02 17:35:51 +00:00 by Carlos · 0 comments
Member

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

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 errores 2026-10-02 17:42:53 +00:00
Carlos self-assigned this 2026-10-02 20:07:58 +00:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RynextTechnologies/PDV#1