# US-MGN-005-003-003: Cancelar Movimiento de Stock **RF Asociado:** [RF-MGN-005-003](../../02-modelado/requerimientos-funcionales/mgn-005/RF-MGN-005-003-movimientos-de-stock.md) **Módulo:** MGN-005 - Inventario Básico **Epic:** Movimientos de Stock **Prioridad:** P0 **Story Points:** 3 **Sprint:** Sprint 10 **Estado:** Ready for Development **Fecha:** 2025-11-24 --- ## User Story **Como** usuario de inventario, **Quiero** cancelar movimientos de stock en estado draft o done, **Para** revertir movimientos erróneos y mantener la integridad del stock. --- ## Descripción Detallada Cancelar un movimiento cambia su estado a cancelled. El comportamiento depende del estado actual: - **draft:** Solo cambia estado a cancelled (no hay reversión de stock) - **done:** Revierte el stock (suma a origen, resta a destino) y cambia estado Los movimientos cancelados no pueden volver a validarse ni editarse. --- ## Criterios de Aceptación ### Escenario 1: Cancelar movimiento draft (Camino Feliz) **Dado que** tengo movimiento en state=draft, **Cuando** cancelo el movimiento, **Entonces** el sistema cambia state=cancelled sin afectar stock. ### Escenario 2: Cancelar movimiento done (revertir stock) **Dado que** tengo movimiento done: product=1, from=Zona A (stock=5), to=Zona B (stock=8), qty=5, **Cuando** cancelo el movimiento, **Entonces** el sistema revierte: Zona A stock=10, Zona B stock=3, state=cancelled. ### Escenario 3: Error al cancelar movimiento ya cancelado **Dado que** movimiento está en state=cancelled, **Cuando** intento cancelar nuevamente, **Entonces** el sistema retorna error 400 "Movimiento ya fue cancelado". ### Escenario 4: Registrar motivo de cancelación **Dado que** cancelo movimiento con reason="Error en cantidad", **Cuando** envío el request, **Entonces** el sistema guarda el motivo y fecha de cancelación. --- ## Reglas de Negocio - **RN-1:** Movimientos draft y done pueden cancelarse. - **RN-2:** Movimientos cancelled no pueden cancelarse nuevamente. - **RN-3:** Cancelar movimiento done revierte stock (operación inversa). - **RN-4:** Cancelar movimiento draft no afecta stock. - **RN-5:** Cancelación registra fecha, usuario y motivo. - **RN-6:** Movimientos cancelados no pueden reactivarse. --- ## Tareas Técnicas ### Backend - [ ] Endpoint: POST /api/v1/inventory/stock-moves/:id/cancel - [ ] Service: StockMoveService.cancel(moveId, reason) - [ ] Service: StockMoveService.reverseQuants(move) - [ ] Validar movimiento no está cancelado - [ ] Si done: revertir quants (suma origen, resta destino) - [ ] Si draft: solo cambiar estado - [ ] Registrar fecha, usuario, motivo - [ ] Transaction para atomicidad - [ ] Unit tests (>80% coverage) - [ ] Integration tests - [ ] Swagger docs ### Frontend - [ ] Componente: CancelStockMoveButton.tsx - [ ] Modal de confirmación con campo motivo - [ ] Mostrar impacto en stock (si done) - [ ] API client: stockMoveApi.cancel(id, reason) - [ ] Component tests - [ ] E2E test: "should cancel stock move" ### Database - [ ] Tabla: inventory.stock_moves (campos: cancelled_date, cancelled_by, cancel_reason) - [ ] Transaction isolation level: READ COMMITTED --- ## Mockups / Wireframes **Modal Cancelar Movimiento:** ``` ┌─────────────────────────────────────────────┐ │ Cancelar Movimiento de Stock │ ├─────────────────────────────────────────────┤ │ Movimiento: #SM-00123 │ │ Estado Actual: Done │ │ │ │ ⚠ Cancelar revertirá los cambios de stock:│ │ Zona A: 5 → 10 (+5) │ │ Zona B: 8 → 3 (-5) │ │ │ │ Motivo de Cancelación: * │ │ [TextArea: Error en cantidad registrada] │ │ │ │ ⚠ Esta acción no puede deshacerse. │ ├─────────────────────────────────────────────┤ │ [Cancelar Movimiento] [Volver] │ └─────────────────────────────────────────────┘ ``` --- ## Casos de Prueba ### Funcionales 1. **TC-001:** Cancelar movimiento draft exitosamente 2. **TC-002:** Cancelar movimiento done y revertir stock 3. **TC-003:** Error por movimiento ya cancelado 4. **TC-004:** Registrar motivo de cancelación 5. **TC-005:** Transaction rollback en caso de error 6. **TC-006:** RLS filtra por empresa ### No Funcionales 1. **Performance:** < 500ms para cancelar movimiento 2. **Seguridad:** JWT + permiso inventory_validate --- ## Dependencias - **US bloqueantes:** US-MGN-005-003-002 (Validar Movimiento Stock) - **Módulos requeridos:** MGN-001, MGN-002 - **Datos maestros:** Ninguno --- ## Notas de Implementación - Usar transaction para garantizar atomicidad - Reversión = operación inversa de validación - Considerar agregar audit trail de cancelaciones - Frontend: Deshabilitar botón cancelar si ya está cancelado --- ## Estimación Detallada | Tarea | Horas | |-------|-------| | Backend | 2 | | Frontend | 1.5 | | Testing | 1 | | Code Review | 0.5 | | **TOTAL** | **5 horas = 3 SP** | --- ## Definition of Done - [ ] Código implementado según ET - [ ] Tests pasando (>80%) - [ ] Code review aprobado - [ ] Documentación actualizada - [ ] Swagger docs completo - [ ] Transaction correctamente implementada - [ ] RLS aplicado - [ ] QA validado - [ ] PO aprobado --- ## Referencias - [RF-MGN-005-003](../../02-modelado/requerimientos-funcionales/mgn-005/RF-MGN-005-003-movimientos-de-stock.md) - [ET Backend](../../02-modelado/especificaciones-tecnicas/backend/mgn-005/ET-BACKEND-MGN-005-003-movimientos-de-stock.md) - [ET Frontend](../../02-modelado/especificaciones-tecnicas/frontend/mgn-005/ET-FRONTEND-MGN-005-003-movimientos-de-stock.md) - [Traceability](../../02-modelado/trazabilidad/TRACEABILITY-MGN-005.yaml) - [Schema](../../02-modelado/database-design/schemas/inventory-schema-ddl.sql)