# US-MGN-007-002-001: Crear Sales Order desde Cotización **RF Asociado:** [RF-MGN-007-002](../../02-modelado/requerimientos-funcionales/mgn-007/RF-MGN-007-002-conversión-a-órdenes-de-venta.md) **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: 1. Valida que cotización esté en estado sent o draft 2. Crea nueva SO copiando todos los datos 3. Genera número secuencial SO/YYYY/NNNN 4. Cambia estado de cotización a sale (convertida) 5. 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 1. **TC-001:** Convertir cotización sent exitosamente 2. **TC-002:** Convertir cotización draft exitosamente 3. **TC-003:** Error al convertir cotización ya convertida 4. **TC-004:** Error al convertir cotización cancelada 5. **TC-005:** Todas las líneas se copian correctamente 6. **TC-006:** Totales coinciden entre cotización y SO 7. **TC-007:** Número secuencial se genera correctamente 8. **TC-008:** quotation_id se vincula correctamente 9. **TC-009:** Estado de cotización cambia a sale 10. **TC-010:** Mensaje se crea en chatter ### No Funcionales 1. **Performance:** < 500ms para convertir cotización con 20 líneas 2. **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 --- ## Referencias - [RF-MGN-007-002](../../02-modelado/requerimientos-funcionales/mgn-007/RF-MGN-007-002-conversión-a-órdenes-de-venta.md) - [ET Backend](../../02-modelado/especificaciones-tecnicas/backend/mgn-007/ET-BACKEND-MGN-007-002-conversion-ordenes.md) - [ET Frontend](../../02-modelado/especificaciones-tecnicas/frontend/mgn-007/ET-FRONTEND-MGN-007-002-conversion-ordenes.md) - [Traceability](../../02-modelado/trazabilidad/TRACEABILITY-MGN-007.yaml) - [Schema](../../02-modelado/database-design/schemas/sales-schema-ddl.sql)