8.6 KiB
US-MGN-007-002-001: Crear Sales Order desde Cotización
RF Asociado: RF-MGN-007-002 Módulo: MGN-007 - Ventas Básico Epic: Órdenes de Venta Prioridad: P0 Story Points: 5 Sprint: Sprint 16 Estado: Ready for Development Fecha: 2025-11-24
User Story
Como vendedor, Quiero convertir una cotización confirmada en una orden de venta (Sales Order), Para formalizar la venta y poder continuar con entregas y facturación.
Descripción Detallada
Una Sales Order (SO) es el documento formal que confirma la venta. Se crea desde una cotización aceptada copiando:
- Datos del cliente
- Líneas de productos con cantidades y precios
- Descuentos e impuestos
- Términos de pago
- Fecha de entrega esperada
Estados de SO: draft, confirmed, done (entregado y facturado), cancelled.
La conversión de cotización → SO:
- Valida que cotización esté en estado sent o draft
- Crea nueva SO copiando todos los datos
- Genera número secuencial SO/YYYY/NNNN
- Cambia estado de cotización a sale (convertida)
- Vincula quotation_id en sales.orders
Criterios de Aceptación
Escenario 1: Convertir cotización a SO (Camino Feliz)
Dado que cotización id=1 está en estado sent con total=1000, Cuando ejecuto action "Convert to Sales Order", Entonces sistema crea SO con número SO/2024/0001, copia todas las líneas, marca cotización como sale, vincula quotation_id=1.
Escenario 2: Validar que cotización no está convertida
Dado que cotización ya está en estado sale (convertida), Cuando intento convertir nuevamente, Entonces sistema retorna error 400 "Cotización ya fue convertida a orden de venta".
Escenario 3: Validar que cotización no está cancelada
Dado que cotización está en estado cancelled, Cuando intento convertir, Entonces sistema retorna error 400 "No se puede convertir cotización cancelada".
Escenario 4: Copiar líneas correctamente
Dado que cotización tiene 3 líneas con productos, cantidades, precios, Cuando se convierte a SO, Entonces sistema copia las 3 líneas manteniendo todos los valores exactos.
Escenario 5: Generar número secuencial de SO
Dado que última SO fue SO/2024/0005, Cuando creo nueva SO desde cotización, Entonces sistema genera número SO/2024/0006.
Escenario 6: Vincular cotización y SO
Dado que cotización id=1 se convierte a SO id=10, Cuando se completa conversión, Entonces SO.quotation_id=1 y Quotation.state=sale.
Reglas de Negocio
- RN-1: Solo cotizaciones en draft o sent pueden convertirse.
- RN-2: Una cotización solo puede convertirse una vez.
- RN-3: SO se crea en estado draft inicialmente.
- RN-4: Número secuencial: SO/{year}/{seq:04d}.
- RN-5: Todos los datos (líneas, impuestos, descuentos, totales) se copian exactamente.
- RN-6: Estado de cotización cambia a sale (no editable después).
- RN-7: Se crea mensaje en chatter: "Cotización convertida a SO/2024/NNNN".
Tareas Técnicas
Backend
- Endpoint:
POST /api/v1/sales/quotations/:id/convert-to-order - Service:
QuotationService.convertToSalesOrder(id) - Service:
SalesOrderService.createFromQuotation(quotation) - Validar estado de cotización (draft o sent)
- Validar que no esté ya convertida
- Copiar header de cotización
- Copiar líneas con todos los campos
- Generar número secuencial SO
- Actualizar quotation.state = sale
- Crear mensaje en chatter
- Unit tests (>80%)
- Integration tests
- Swagger docs
Frontend
- Botón: "Convert to Sales Order" en vista de cotización
- Confirmación modal antes de convertir
- Navegación automática a SO creada después de conversión
- API client:
quotationApi.convertToOrder(id) - Notificación toast de éxito
- Deshabilitar botón si ya está convertida
- Component tests
- E2E test: "should convert quotation to sales order"
Database
- Tabla:
sales.orders(id, company_id, quotation_id, number, customer_id, state, total) - Tabla:
sales.order_lines(id, order_id, product_id, quantity, price_unit, subtotal) - FK:
sales.orders.quotation_id → sales.quotations.id - Índices: idx_orders_quotation_id, idx_orders_customer_id, idx_orders_state
- RLS policy: company_isolation_sales_orders
Mockups / Wireframes
Vista Cotización con botón Convertir:
┌─────────────────────────────────────────────┐
│ Cotización QT/2024/0001 [sent] │
├─────────────────────────────────────────────┤
│ Cliente: ABC Corp │
│ Total: $1,000.00 │
│ │
│ [Líneas de productos...] │
├─────────────────────────────────────────────┤
│ [Edit] [Send Email] [Convert to SO] [Cancel]│
└─────────────────────────────────────────────┘
Modal de Confirmación:
┌─────────────────────────────────────────────┐
│ Confirmar Conversión │
├─────────────────────────────────────────────┤
│ ¿Desea convertir la cotización QT/2024/0001│
│ en una orden de venta? │
│ │
│ Esta acción: │
│ • Creará una nueva Sales Order │
│ • Marcará la cotización como convertida │
│ • No es reversible │
│ │
│ [Confirmar] [Cancelar] │
└─────────────────────────────────────────────┘
Casos de Prueba
Funcionales
- TC-001: Convertir cotización sent exitosamente
- TC-002: Convertir cotización draft exitosamente
- TC-003: Error al convertir cotización ya convertida
- TC-004: Error al convertir cotización cancelada
- TC-005: Todas las líneas se copian correctamente
- TC-006: Totales coinciden entre cotización y SO
- TC-007: Número secuencial se genera correctamente
- TC-008: quotation_id se vincula correctamente
- TC-009: Estado de cotización cambia a sale
- TC-010: Mensaje se crea en chatter
No Funcionales
- Performance: < 500ms para convertir cotización con 20 líneas
- Seguridad: JWT + permiso sales_user
Dependencias
- US bloqueantes:
- US-MGN-007-001-001 (Crear Cotización)
- Módulos requeridos: MGN-003 (Partners), MGN-005 (Productos)
Notas de Implementación
- Usar transacción para garantizar atomicidad (crear SO + actualizar quotation)
- Copiar campos: customer_id, payment_term_id, delivery_date, notes, etc.
- Validar que productos de las líneas sigan existiendo y activos
- Considerar copiar también adjuntos de la cotización
- Frontend: Mostrar toast con link a nueva SO creada
Estimación Detallada
| Tarea | Horas |
|---|---|
| Backend | 2.5 |
| Frontend | 1.5 |
| Testing | 2 |
| Code Review | 1 |
| TOTAL | 7 horas = 5 SP |
Definition of Done
- Código implementado según ET
- Tests pasando (>80%)
- Code review aprobado
- Conversión funciona correctamente
- Validaciones aplicadas
- Número secuencial generado
- Estado de cotización actualizado
- Mensaje en chatter creado
- Swagger docs actualizado
- QA validado
- PO aprobado