# 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)
