# US-MGN-006-002-003: Cancelar Orden de Compra

**RF Asociado:** [RF-MGN-006-002](../../02-modelado/requerimientos-funcionales/mgn-006/RF-MGN-006-002-gestión-de-órdenes-de-compra.md)
**Módulo:** MGN-006 - Compras Básico
**Epic:** Órdenes de Compra
**Prioridad:** P0
**Story Points:** 3
**Sprint:** Sprint 13
**Estado:** Ready for Development
**Fecha:** 2025-11-24

---

## User Story
**Como** usuario de compras,
**Quiero** cancelar órdenes de compra,
**Para** anular compras no procedentes y liberar compromisos.

---

## Descripción Detallada
Cancelar una PO:
1. Cambia estado → cancelled
2. Cancela pickings pendientes asociados
3. Libera reservas presupuestarias
4. Registra motivo de cancelación
5. No permite reactivación

---

## Criterios de Aceptación

### Escenario 1: Cancelar PO en draft (Camino Feliz)
**Dado que** PO está en draft,
**Cuando** cancelo con reason="Proveedor no disponible",
**Entonces** sistema cambia state=cancelled, guarda motivo.

### Escenario 2: Cancelar PO confirmada
**Dado que** PO está en purchase con picking pendiente,
**Cuando** cancelo,
**Entonces** sistema cancela PO y picking asociado.

### Escenario 3: Error al cancelar PO con recepciones validadas
**Dado que** PO tiene picking validado (done),
**Cuando** intento cancelar,
**Entonces** sistema retorna error 400 "No se puede cancelar PO con recepciones validadas".

---

## Reglas de Negocio
- **RN-1:** PO draft y purchase pueden cancelarse.
- **RN-2:** PO con recepciones validadas no pueden cancelarse.
- **RN-3:** Cancelar PO cancela pickings pendientes.
- **RN-4:** Requiere motivo de cancelación.

---

## Tareas Técnicas

### Backend
- [ ] Endpoint: POST /api/v1/purchase/orders/:id/cancel
- [ ] Service: PurchaseOrderService.cancel(id, reason)
- [ ] Service: PickingService.cancelFromPO(poId)
- [ ] Validar sin recepciones validadas
- [ ] Transaction atomicidad
- [ ] Unit tests
- [ ] Integration tests
- [ ] Swagger docs

### Frontend
- [ ] Botón Cancelar con confirmación
- [ ] Modal con campo motivo requerido
- [ ] API client: purchaseOrderApi.cancel(id, reason)
- [ ] Component tests
- [ ] E2E test

### Database
- [ ] Campo: purchase.purchase_orders.cancelled_date
- [ ] Campo: purchase.purchase_orders.cancel_reason

---

## 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
- [ ] Transaction implementada
- [ ] QA validado
- [ ] PO aprobado

---

## Referencias
- [RF-MGN-006-002](../../02-modelado/requerimientos-funcionales/mgn-006/RF-MGN-006-002-gestión-de-órdenes-de-compra.md)
- [Traceability](../../02-modelado/trazabilidad/TRACEABILITY-MGN-006.yaml)
