erp-core/docs/05-user-stories/mgn-007/US-MGN-007-002-003-cancelar-sales-order.md

7.5 KiB

US-MGN-007-002-003: Cancelar Sales Order

RF Asociado: RF-MGN-007-003 Módulo: MGN-007 - Ventas Básico Epic: Órdenes de Venta Prioridad: P1 Story Points: 3 Sprint: Sprint 16 Estado: Ready for Development Fecha: 2025-11-24


User Story

Como vendedor o gerente de ventas, Quiero cancelar una orden de venta, Para anular ventas que no se completarán y liberar recursos reservados.


Descripción Detallada

Cancelar una SO significa:

  • Cambiar estado a cancelled
  • Registrar fecha de cancelación (date_cancelled)
  • Registrar motivo de cancelación (cancellation_reason)
  • Liberar reservas de stock si las hay
  • Cancelar entregas pendientes asociadas
  • Notificar a stakeholders
  • Crear mensaje en chatter con motivo

SO canceladas no pueden reactivarse (son inmutables).


Criterios de Aceptación

Escenario 1: Cancelar SO draft o confirmed (Camino Feliz)

Dado que SO id=1 está en estado draft, Cuando ejecuto action "Cancel" con reason="Cliente canceló pedido", Entonces sistema cambia estado a cancelled, registra date_cancelled y reason.

Escenario 2: Validar que SO no está done

Dado que SO está en estado done (completamente facturada y entregada), Cuando intento cancelar, Entonces sistema retorna error 400 "No se puede cancelar orden completada".

Escenario 3: Validar que SO no tiene entregas validadas

Dado que SO tiene entregas en estado done, Cuando intento cancelar, Entonces sistema retorna error 400 "No se puede cancelar orden con entregas validadas".

Escenario 4: Validar que SO no tiene facturas

Dado que SO tiene facturas emitidas, Cuando intento cancelar, Entonces sistema retorna error 400 "No se puede cancelar orden facturada".

Escenario 5: Cancelar entregas pendientes automáticamente

Dado que SO tiene 2 entregas en estado draft, Cuando cancelo SO, Entonces sistema cancela automáticamente las 2 entregas pendientes.

Escenario 6: Motivo de cancelación obligatorio

Dado que intento cancelar SO sin proporcionar reason, Cuando envío request, Entonces sistema retorna error 400 "El motivo de cancelación es obligatorio".


Reglas de Negocio

  • RN-1: Solo SO en draft o confirmed pueden cancelarse.
  • RN-2: SO con entregas validadas (state=done) no pueden cancelarse.
  • RN-3: SO con facturas no pueden cancelarse (deben hacerse notas de crédito).
  • RN-4: Motivo de cancelación es obligatorio.
  • RN-5: Entregas pendientes (draft) se cancelan automáticamente.
  • RN-6: Reservas de stock se liberan automáticamente.
  • RN-7: SO canceladas no pueden reactivarse.
  • RN-8: Permisos: sales_user puede cancelar sus órdenes draft, sales_manager puede cancelar cualquiera.

Tareas Técnicas

Backend

  • Endpoint: POST /api/v1/sales/orders/:id/cancel
  • Service: SalesOrderService.cancel(id, cancelDto)
  • DTO: CancelSalesOrderDto (reason: string)
  • Validar estado (draft o confirmed)
  • Validar que no tiene entregas validadas
  • Validar que no tiene facturas
  • Validar reason no vacío
  • Actualizar state=cancelled, date_cancelled=now()
  • Cancelar entregas pendientes asociadas
  • Liberar reservas de stock (si aplica)
  • Crear mensaje en chatter con reason
  • Unit tests (>80%)
  • Integration tests
  • Swagger docs

Frontend

  • Botón: "Cancel" en vista de SO
  • Modal: Solicitar motivo de cancelación (textarea)
  • Validación: Motivo mínimo 10 caracteres
  • Mostrar warnings si hay entregas pendientes
  • API client: salesOrderApi.cancel(id, {reason})
  • Notificación toast de éxito
  • Component tests
  • E2E test: "should cancel sales order"

Database

  • Columna: sales.orders.date_cancelled (timestamp)
  • Columna: sales.orders.cancellation_reason (text)
  • Índice: idx_orders_date_cancelled

Mockups / Wireframes

Modal Cancelar SO:

┌─────────────────────────────────────────────┐
│ Cancelar Orden SO/2024/0001                 │
├─────────────────────────────────────────────┤
│ ⚠ Esta acción no puede deshacerse           │
│                                             │
│ Motivo de cancelación: *                    │
│ ┌─────────────────────────────────────────┐ │
│ │ Cliente canceló pedido por...           │ │
│ │                                         │ │
│ └─────────────────────────────────────────┘ │
│                                             │
│ Impactos:                                   │
│ • 2 entregas pendientes serán canceladas    │
│ • Reservas de stock serán liberadas         │
│                                             │
│ [Confirmar Cancelación] [Cerrar]            │
└─────────────────────────────────────────────┘

Casos de Prueba

Funcionales

  1. TC-001: Cancelar SO draft exitosamente
  2. TC-002: Cancelar SO confirmed exitosamente
  3. TC-003: Error al cancelar SO done
  4. TC-004: Error al cancelar SO con entregas validadas
  5. TC-005: Error al cancelar SO con facturas
  6. TC-006: Error si reason está vacío
  7. TC-007: Entregas pendientes se cancelan automáticamente
  8. TC-008: date_cancelled y reason se registran correctamente
  9. TC-009: Mensaje se crea en chatter con reason

No Funcionales

  1. Performance: < 500ms para cancelar orden
  2. Seguridad: JWT + permiso sales_user (own) o sales_manager (all)

Dependencias

  • US bloqueantes:
    • US-MGN-007-002-002 (Confirmar SO)
  • Módulos requeridos: MGN-007 (Entregas)

Notas de Implementación

  • Usar transacción para cancelar SO + entregas pendientes atómicamente
  • Validar permisos: sales_user solo puede cancelar sus propias órdenes draft
  • Considerar agregar campo cancelled_by_user_id para auditoría
  • Frontend: Deshabilitar botón de cancelación si hay entregas validadas

Estimación Detallada

Tarea Horas
Backend 1.5
Frontend 1
Testing 1
Code Review 0.5
TOTAL 4 horas = 3 SP

Definition of Done

  • Código implementado según ET
  • Tests pasando (>80%)
  • Code review aprobado
  • Cancelación funciona correctamente
  • Validaciones aplicadas
  • Entregas pendientes canceladas
  • date_cancelled y reason registrados
  • Mensaje en chatter creado
  • Swagger docs actualizado
  • QA validado
  • PO aprobado

Referencias