# TRACEABILITY-MGN-007.yaml # Matriz de Trazabilidad - MGN-007: Ventas Básico # Fecha: 2025-11-24 # Versión: 1.0 module: id: MGN-007 name: "Ventas Básico" description: "Gestión completa del ciclo de ventas: cotizaciones, órdenes, entregas, facturación y reportes" priority: P0 story_points: 45 status: Diseñado metadata: total_rf: 6 total_et_backend: 6 total_et_frontend: 6 total_tables: 5 total_tests: 120 coverage: 100% requirements: - rf_id: RF-MGN-007-001 rf_title: "Gestión de Cotizaciones" rf_file: "requerimientos-funcionales/mgn-007/RF-MGN-007-001-gestión-de-cotizaciones.md" priority: P0 story_points: 8 et_backend: file: "especificaciones-tecnicas/backend/mgn-007/ET-BACKEND-MGN-007-001-gestión-de-cotizaciones.md" endpoints: - method: POST path: /api/v1/sales/quotations description: "Crear nueva cotización" - method: GET path: /api/v1/sales/quotations description: "Listar todas las cotizaciones" - method: GET path: /api/v1/sales/quotations/:id description: "Obtener cotización por ID" - method: PUT path: /api/v1/sales/quotations/:id description: "Actualizar cotización" - method: DELETE path: /api/v1/sales/quotations/:id description: "Eliminar cotización (soft delete)" services: - name: "QuotationService" file: "src/modules/sales/services/quotation.service.ts" methods: - create - findAll - findOne - update - remove - validateBusinessRules - calculateTotals controllers: - name: "QuotationController" file: "src/modules/sales/controllers/quotation.controller.ts" dtos: - name: "CreateQuotationDto" file: "src/modules/sales/dto/create-quotation.dto.ts" - name: "UpdateQuotationDto" file: "src/modules/sales/dto/update-quotation.dto.ts" - name: "QuotationResponseDto" file: "src/modules/sales/dto/quotation-response.dto.ts" - name: "FilterQuotationDto" file: "src/modules/sales/dto/filter-quotation.dto.ts" et_frontend: file: "especificaciones-tecnicas/frontend/mgn-007/ET-FRONTEND-MGN-007-001-gestión-de-cotizaciones.md" routes: - path: "/sales/quotations" component: "QuotationsPage" - path: "/sales/quotations/create" component: "CreateQuotationPage" - path: "/sales/quotations/:id/edit" component: "EditQuotationPage" - path: "/sales/quotations/:id" component: "ViewQuotationPage" components: - name: "QuotationsTable" file: "src/widgets/quotations-table/ui/QuotationsTable.tsx" type: widget - name: "CreateQuotationForm" file: "src/features/create-quotation/ui/CreateQuotationForm.tsx" type: feature - name: "QuotationCard" file: "src/entities/quotation/ui/QuotationCard.tsx" type: entity - name: "QuotationPage" file: "src/pages/sales/QuotationPage.tsx" type: page api_client: - name: "quotationApi" file: "src/entities/quotation/api/quotation.api.ts" methods: - getAll - getById - create - update - delete state_management: - name: "useQuotationStore" file: "src/entities/quotation/model/quotation.store.ts" type: zustand - name: "useQuotations" file: "src/entities/quotation/api/quotation.queries.ts" type: react-query database_tables: - schema: sales table: quotations file: "database-design/schemas/sales-schema-ddl.sql" operations: - SELECT - INSERT - UPDATE - DELETE (soft) indices: - idx_quotations_tenant_id - idx_quotations_partner_id - idx_quotations_state - idx_quotations_date rls_policy: tenant_isolation_quotations - schema: sales table: quotation_lines file: "database-design/schemas/sales-schema-ddl.sql" operations: - SELECT - INSERT - UPDATE - DELETE indices: - idx_quotation_lines_quotation_id - idx_quotation_lines_product_id rls_policy: tenant_isolation_quotation_lines tests: backend: unit_tests: - file: "src/modules/sales/services/quotation.service.spec.ts" test_cases: - "should create quotation with valid data" - "should throw error when partner not found" - "should calculate totals correctly" - "should find all quotations for tenant" - "should update quotation successfully" integration_tests: - file: "test/sales/quotation.controller.e2e-spec.ts" test_cases: - "POST /api/v1/sales/quotations should create quotation" - "GET /api/v1/sales/quotations should return all quotations" - "GET /api/v1/sales/quotations/:id should return quotation" - "PUT /api/v1/sales/quotations/:id should update quotation" - "DELETE /api/v1/sales/quotations/:id should soft delete quotation" - "should enforce tenant isolation" - "should require authentication" - "should check permissions" frontend: component_tests: - file: "src/widgets/quotations-table/ui/QuotationsTable.test.tsx" test_cases: - "should render table with quotations" - "should handle pagination" - "should call delete on button click" - file: "src/features/create-quotation/ui/CreateQuotationForm.test.tsx" test_cases: - "should validate required fields" - "should submit valid form" - "should show error messages" e2e_tests: - file: "e2e/sales/quotations.spec.ts" test_cases: - "should create quotation successfully" - "should edit quotation successfully" - "should delete quotation with confirmation" - "should enforce permissions" acceptance_criteria: - id: AC-001 description: "Usuario puede crear cotizaciones con líneas de productos" status: Pending test_reference: "test/sales/quotation.controller.e2e-spec.ts:30" - id: AC-002 description: "Sistema calcula automáticamente subtotales, impuestos y totales" status: Pending test_reference: "src/modules/sales/services/quotation.service.spec.ts:58" - id: AC-003 description: "Cotizaciones soportan múltiples monedas con conversión" status: Pending test_reference: "test/sales/quotation.controller.e2e-spec.ts:92" business_rules: - id: RN-001 description: "Cotización debe tener al menos una línea de producto" implementation: "src/modules/sales/services/quotation.service.ts:validateQuotationLines()" test_reference: "src/modules/sales/services/quotation.service.spec.ts:82" - id: RN-002 description: "Fecha de validez debe ser posterior a fecha de cotización" implementation: "src/modules/sales/services/quotation.service.ts:validateDates()" test_reference: "src/modules/sales/services/quotation.service.spec.ts:105" - id: RN-003 description: "Estado inicial de cotización debe ser 'draft'" implementation: "src/modules/sales/services/quotation.service.ts:create()" test_reference: "src/modules/sales/services/quotation.service.spec.ts:128" dependencies: rf_dependencies: - RF-MGN-003-001 - RF-MGN-005-001 module_dependencies: - MGN-001 - MGN-003 - MGN-005 external_dependencies: - name: "@nestjs/common" version: "^10.0.0" - name: "ant-design" version: "^5.0.0" - rf_id: RF-MGN-007-002 rf_title: "Conversión a Órdenes de Venta" rf_file: "requerimientos-funcionales/mgn-007/RF-MGN-007-002-conversión-a-órdenes-de-venta.md" priority: P0 story_points: 5 et_backend: file: "especificaciones-tecnicas/backend/mgn-007/ET-BACKEND-MGN-007-002-conversión-a-órdenes-de-venta.md" endpoints: - method: POST path: /api/v1/sales/quotations/:id/confirm description: "Confirmar cotización y crear orden de venta" - method: POST path: /api/v1/sales/quotations/:id/cancel description: "Cancelar cotización" - method: POST path: /api/v1/sales/quotations/:id/send description: "Enviar cotización por email" services: - name: "QuotationService" file: "src/modules/sales/services/quotation.service.ts" methods: - confirmQuotation - cancelQuotation - sendQuotation - createSalesOrder controllers: - name: "QuotationController" file: "src/modules/sales/controllers/quotation.controller.ts" dtos: - name: "ConfirmQuotationDto" file: "src/modules/sales/dto/confirm-quotation.dto.ts" et_frontend: file: "especificaciones-tecnicas/frontend/mgn-007/ET-FRONTEND-MGN-007-002-conversión-a-órdenes-de-venta.md" routes: - path: "/sales/quotations/:id/confirm" component: "ConfirmQuotationPage" components: - name: "ConfirmQuotationButton" file: "src/features/confirm-quotation/ui/ConfirmQuotationButton.tsx" type: feature - name: "QuotationActions" file: "src/widgets/quotation-actions/ui/QuotationActions.tsx" type: widget api_client: - name: "quotationApi" file: "src/entities/quotation/api/quotation.api.ts" methods: - confirm - cancel - send state_management: - name: "useQuotationStore" file: "src/entities/quotation/model/quotation.store.ts" type: zustand database_tables: - schema: sales table: quotations file: "database-design/schemas/sales-schema-ddl.sql" operations: - SELECT - UPDATE indices: - idx_quotations_state rls_policy: tenant_isolation_quotations - schema: sales table: sales_orders file: "database-design/schemas/sales-schema-ddl.sql" operations: - INSERT - SELECT indices: - idx_sales_orders_quotation_id rls_policy: tenant_isolation_sales_orders tests: backend: unit_tests: - file: "src/modules/sales/services/quotation.service.spec.ts" test_cases: - "should confirm quotation and create sales order" - "should validate stock availability before confirmation" - "should cancel quotation successfully" - "should send quotation email with PDF" - "should throw error if quotation already confirmed" integration_tests: - file: "test/sales/quotation.controller.e2e-spec.ts" test_cases: - "POST /api/v1/sales/quotations/:id/confirm should create sales order" - "POST /api/v1/sales/quotations/:id/cancel should cancel quotation" - "POST /api/v1/sales/quotations/:id/send should send email" - "should prevent double confirmation" - "should enforce permissions" frontend: component_tests: - file: "src/features/confirm-quotation/ui/ConfirmQuotationButton.test.tsx" test_cases: - "should show confirmation dialog" - "should call confirm API" - "should redirect to sales order" e2e_tests: - file: "e2e/sales/quotation-confirm.spec.ts" test_cases: - "should confirm quotation successfully" - "should create sales order from quotation" - "should send quotation email" - "should prevent confirming cancelled quotation" acceptance_criteria: - id: AC-001 description: "Confirmar cotización crea automáticamente orden de venta" status: Pending test_reference: "test/sales/quotation.controller.e2e-spec.ts:142" - id: AC-002 description: "Sistema envía email con PDF de cotización" status: Pending test_reference: "src/modules/sales/services/quotation.service.spec.ts:168" - id: AC-003 description: "Cotización confirmada no puede ser modificada" status: Pending test_reference: "test/sales/quotation.controller.e2e-spec.ts:185" business_rules: - id: RN-001 description: "Solo cotizaciones en estado 'sent' pueden ser confirmadas" implementation: "src/modules/sales/services/quotation.service.ts:validateConfirmation()" test_reference: "src/modules/sales/services/quotation.service.spec.ts:195" - id: RN-002 description: "Al confirmar, estado cambia a 'sale' y se crea sales_order" implementation: "src/modules/sales/services/quotation.service.ts:confirmQuotation()" test_reference: "src/modules/sales/services/quotation.service.spec.ts:218" - id: RN-003 description: "Orden de venta hereda todos los datos de la cotización" implementation: "src/modules/sales/services/quotation.service.ts:createSalesOrder()" test_reference: "src/modules/sales/services/quotation.service.spec.ts:242" dependencies: rf_dependencies: - RF-MGN-007-001 module_dependencies: - MGN-001 - MGN-014 external_dependencies: - name: "nodemailer" version: "^6.9.0" - name: "puppeteer" version: "^21.0.0" - rf_id: RF-MGN-007-003 rf_title: "Gestión de Órdenes de Venta" rf_file: "requerimientos-funcionales/mgn-007/RF-MGN-007-003-gestión-de-órdenes-de-venta.md" priority: P0 story_points: 13 et_backend: file: "especificaciones-tecnicas/backend/mgn-007/ET-BACKEND-MGN-007-003-gestión-de-órdenes-de-venta.md" endpoints: - method: POST path: /api/v1/sales/orders description: "Crear nueva orden de venta" - method: GET path: /api/v1/sales/orders description: "Listar todas las órdenes de venta" - method: GET path: /api/v1/sales/orders/:id description: "Obtener orden de venta por ID" - method: PUT path: /api/v1/sales/orders/:id description: "Actualizar orden de venta" - method: DELETE path: /api/v1/sales/orders/:id description: "Cancelar orden de venta" services: - name: "SalesOrderService" file: "src/modules/sales/services/sales-order.service.ts" methods: - create - findAll - findOne - update - cancel - validateBusinessRules controllers: - name: "SalesOrderController" file: "src/modules/sales/controllers/sales-order.controller.ts" dtos: - name: "CreateSalesOrderDto" file: "src/modules/sales/dto/create-sales-order.dto.ts" - name: "UpdateSalesOrderDto" file: "src/modules/sales/dto/update-sales-order.dto.ts" - name: "SalesOrderResponseDto" file: "src/modules/sales/dto/sales-order-response.dto.ts" - name: "FilterSalesOrderDto" file: "src/modules/sales/dto/filter-sales-order.dto.ts" et_frontend: file: "especificaciones-tecnicas/frontend/mgn-007/ET-FRONTEND-MGN-007-003-gestión-de-órdenes-de-venta.md" routes: - path: "/sales/orders" component: "SalesOrdersPage" - path: "/sales/orders/create" component: "CreateSalesOrderPage" - path: "/sales/orders/:id/edit" component: "EditSalesOrderPage" - path: "/sales/orders/:id" component: "ViewSalesOrderPage" components: - name: "SalesOrdersTable" file: "src/widgets/sales-orders-table/ui/SalesOrdersTable.tsx" type: widget - name: "CreateSalesOrderForm" file: "src/features/create-sales-order/ui/CreateSalesOrderForm.tsx" type: feature - name: "SalesOrderCard" file: "src/entities/sales-order/ui/SalesOrderCard.tsx" type: entity - name: "SalesOrderPage" file: "src/pages/sales/SalesOrderPage.tsx" type: page api_client: - name: "salesOrderApi" file: "src/entities/sales-order/api/sales-order.api.ts" methods: - getAll - getById - create - update - cancel state_management: - name: "useSalesOrderStore" file: "src/entities/sales-order/model/sales-order.store.ts" type: zustand - name: "useSalesOrders" file: "src/entities/sales-order/api/sales-order.queries.ts" type: react-query database_tables: - schema: sales table: sales_orders file: "database-design/schemas/sales-schema-ddl.sql" operations: - SELECT - INSERT - UPDATE - DELETE (soft) indices: - idx_sales_orders_tenant_id - idx_sales_orders_partner_id - idx_sales_orders_state - idx_sales_orders_date rls_policy: tenant_isolation_sales_orders - schema: sales table: sales_order_lines file: "database-design/schemas/sales-schema-ddl.sql" operations: - SELECT - INSERT - UPDATE - DELETE indices: - idx_sales_order_lines_order_id - idx_sales_order_lines_product_id rls_policy: tenant_isolation_sales_order_lines tests: backend: unit_tests: - file: "src/modules/sales/services/sales-order.service.spec.ts" test_cases: - "should create sales order with valid data" - "should reserve stock on order confirmation" - "should calculate delivery dates based on lead time" - "should find all orders for tenant" - "should update order successfully" integration_tests: - file: "test/sales/sales-order.controller.e2e-spec.ts" test_cases: - "POST /api/v1/sales/orders should create order" - "GET /api/v1/sales/orders should return all orders" - "GET /api/v1/sales/orders/:id should return order" - "PUT /api/v1/sales/orders/:id should update order" - "DELETE /api/v1/sales/orders/:id should cancel order" - "should enforce tenant isolation" - "should require authentication" - "should check permissions" frontend: component_tests: - file: "src/widgets/sales-orders-table/ui/SalesOrdersTable.test.tsx" test_cases: - "should render table with orders" - "should handle pagination" - "should filter by state" - file: "src/features/create-sales-order/ui/CreateSalesOrderForm.test.tsx" test_cases: - "should validate required fields" - "should submit valid form" - "should show error messages" e2e_tests: - file: "e2e/sales/sales-orders.spec.ts" test_cases: - "should create sales order successfully" - "should edit sales order successfully" - "should cancel sales order with confirmation" - "should enforce permissions" acceptance_criteria: - id: AC-001 description: "Usuario puede crear órdenes de venta con líneas de productos" status: Pending test_reference: "test/sales/sales-order.controller.e2e-spec.ts:35" - id: AC-002 description: "Sistema reserva stock automáticamente al confirmar orden" status: Pending test_reference: "src/modules/sales/services/sales-order.service.spec.ts:72" - id: AC-003 description: "Órdenes soportan entregas parciales y facturación parcial" status: Pending test_reference: "test/sales/sales-order.controller.e2e-spec.ts:118" business_rules: - id: RN-001 description: "Orden debe tener al menos una línea de producto" implementation: "src/modules/sales/services/sales-order.service.ts:validateOrderLines()" test_reference: "src/modules/sales/services/sales-order.service.spec.ts:95" - id: RN-002 description: "Estado inicial de orden debe ser 'draft' o 'sale'" implementation: "src/modules/sales/services/sales-order.service.ts:create()" test_reference: "src/modules/sales/services/sales-order.service.spec.ts:142" - id: RN-003 description: "No se puede cancelar orden con entregas o facturas confirmadas" implementation: "src/modules/sales/services/sales-order.service.ts:cancel()" test_reference: "src/modules/sales/services/sales-order.service.spec.ts:168" dependencies: rf_dependencies: - RF-MGN-007-002 - RF-MGN-005-003 module_dependencies: - MGN-001 - MGN-003 - MGN-005 external_dependencies: - name: "@nestjs/common" version: "^10.0.0" - rf_id: RF-MGN-007-004 rf_title: "Entregas de Ventas" rf_file: "requerimientos-funcionales/mgn-007/RF-MGN-007-004-entregas-de-ventas.md" priority: P0 story_points: 8 et_backend: file: "especificaciones-tecnicas/backend/mgn-007/ET-BACKEND-MGN-007-004-entregas-de-ventas.md" endpoints: - method: POST path: /api/v1/sales/deliveries description: "Crear nueva entrega" - method: GET path: /api/v1/sales/deliveries description: "Listar todas las entregas" - method: GET path: /api/v1/sales/deliveries/:id description: "Obtener entrega por ID" - method: PUT path: /api/v1/sales/deliveries/:id description: "Actualizar entrega" - method: POST path: /api/v1/sales/deliveries/:id/validate description: "Validar entrega" services: - name: "DeliveryService" file: "src/modules/sales/services/delivery.service.ts" methods: - create - findAll - findOne - update - validate - validateBusinessRules controllers: - name: "DeliveryController" file: "src/modules/sales/controllers/delivery.controller.ts" dtos: - name: "CreateDeliveryDto" file: "src/modules/sales/dto/create-delivery.dto.ts" - name: "UpdateDeliveryDto" file: "src/modules/sales/dto/update-delivery.dto.ts" - name: "DeliveryResponseDto" file: "src/modules/sales/dto/delivery-response.dto.ts" - name: "ValidateDeliveryDto" file: "src/modules/sales/dto/validate-delivery.dto.ts" et_frontend: file: "especificaciones-tecnicas/frontend/mgn-007/ET-FRONTEND-MGN-007-004-entregas-de-ventas.md" routes: - path: "/sales/deliveries" component: "DeliveriesPage" - path: "/sales/deliveries/create" component: "CreateDeliveryPage" - path: "/sales/deliveries/:id/edit" component: "EditDeliveryPage" - path: "/sales/deliveries/:id" component: "ViewDeliveryPage" components: - name: "DeliveriesTable" file: "src/widgets/deliveries-table/ui/DeliveriesTable.tsx" type: widget - name: "CreateDeliveryForm" file: "src/features/create-delivery/ui/CreateDeliveryForm.tsx" type: feature - name: "DeliveryCard" file: "src/entities/delivery/ui/DeliveryCard.tsx" type: entity - name: "DeliveryPage" file: "src/pages/sales/DeliveryPage.tsx" type: page api_client: - name: "deliveryApi" file: "src/entities/delivery/api/delivery.api.ts" methods: - getAll - getById - create - update - validate state_management: - name: "useDeliveryStore" file: "src/entities/delivery/model/delivery.store.ts" type: zustand - name: "useDeliveries" file: "src/entities/delivery/api/delivery.queries.ts" type: react-query database_tables: - schema: sales table: deliveries file: "database-design/schemas/sales-schema-ddl.sql" operations: - SELECT - INSERT - UPDATE - DELETE (soft) indices: - idx_deliveries_tenant_id - idx_deliveries_sales_order_id - idx_deliveries_state rls_policy: tenant_isolation_deliveries - schema: inventory table: stock_moves file: "database-design/schemas/inventory-schema-ddl.sql" operations: - SELECT - INSERT - UPDATE indices: - idx_stock_moves_picking_id rls_policy: tenant_isolation_stock_moves tests: backend: unit_tests: - file: "src/modules/sales/services/delivery.service.spec.ts" test_cases: - "should create delivery from sales order" - "should validate delivery and update stock" - "should support partial deliveries" - "should find all deliveries for tenant" - "should update delivery successfully" integration_tests: - file: "test/sales/delivery.controller.e2e-spec.ts" test_cases: - "POST /api/v1/sales/deliveries should create delivery" - "GET /api/v1/sales/deliveries should return all deliveries" - "GET /api/v1/sales/deliveries/:id should return delivery" - "PUT /api/v1/sales/deliveries/:id should update delivery" - "POST /api/v1/sales/deliveries/:id/validate should validate delivery" - "should enforce tenant isolation" - "should require authentication" - "should check permissions" frontend: component_tests: - file: "src/widgets/deliveries-table/ui/DeliveriesTable.test.tsx" test_cases: - "should render table with deliveries" - "should handle pagination" - "should filter by state" - file: "src/features/create-delivery/ui/CreateDeliveryForm.test.tsx" test_cases: - "should validate required fields" - "should submit valid form" - "should show error messages" e2e_tests: - file: "e2e/sales/deliveries.spec.ts" test_cases: - "should create delivery successfully" - "should validate delivery successfully" - "should support partial deliveries" - "should enforce permissions" acceptance_criteria: - id: AC-001 description: "Usuario puede crear entregas desde órdenes de venta" status: Pending test_reference: "test/sales/delivery.controller.e2e-spec.ts:42" - id: AC-002 description: "Validar entrega actualiza stock automáticamente" status: Pending test_reference: "src/modules/sales/services/delivery.service.spec.ts:78" - id: AC-003 description: "Sistema soporta entregas parciales con backorders" status: Pending test_reference: "test/sales/delivery.controller.e2e-spec.ts:125" business_rules: - id: RN-001 description: "Entrega debe estar asociada a una orden de venta" implementation: "src/modules/sales/services/delivery.service.ts:validateSalesOrder()" test_reference: "src/modules/sales/services/delivery.service.spec.ts:102" - id: RN-002 description: "Al validar entrega, stock se reduce automáticamente" implementation: "src/modules/sales/services/delivery.service.ts:validate()" test_reference: "src/modules/sales/services/delivery.service.spec.ts:148" - id: RN-003 description: "Cantidad entregada no puede exceder cantidad ordenada" implementation: "src/modules/sales/services/delivery.service.ts:validateQuantities()" test_reference: "src/modules/sales/services/delivery.service.spec.ts:175" dependencies: rf_dependencies: - RF-MGN-007-003 - RF-MGN-005-003 module_dependencies: - MGN-001 - MGN-005 external_dependencies: - name: "@nestjs/common" version: "^10.0.0" - rf_id: RF-MGN-007-005 rf_title: "Facturación de Clientes desde Ventas" rf_file: "requerimientos-funcionales/mgn-007/RF-MGN-007-005-facturación-de-clientes-desde-ventas.md" priority: P0 story_points: 8 et_backend: file: "especificaciones-tecnicas/backend/mgn-007/ET-BACKEND-MGN-007-005-facturación-de-clientes-desde-ventas.md" endpoints: - method: POST path: /api/v1/sales/orders/:id/create-invoice description: "Crear factura desde orden de venta" - method: GET path: /api/v1/sales/invoices description: "Listar facturas de cliente" - method: GET path: /api/v1/sales/invoices/:id description: "Obtener factura por ID" - method: POST path: /api/v1/sales/invoices/:id/validate description: "Validar factura" - method: POST path: /api/v1/sales/invoices/:id/cancel description: "Cancelar factura" services: - name: "SalesInvoiceService" file: "src/modules/sales/services/sales-invoice.service.ts" methods: - createFromOrder - findAll - findOne - validate - cancel - validateBusinessRules controllers: - name: "SalesInvoiceController" file: "src/modules/sales/controllers/sales-invoice.controller.ts" dtos: - name: "CreateSalesInvoiceDto" file: "src/modules/sales/dto/create-sales-invoice.dto.ts" - name: "SalesInvoiceResponseDto" file: "src/modules/sales/dto/sales-invoice-response.dto.ts" - name: "ValidateSalesInvoiceDto" file: "src/modules/sales/dto/validate-sales-invoice.dto.ts" et_frontend: file: "especificaciones-tecnicas/frontend/mgn-007/ET-FRONTEND-MGN-007-005-facturación-de-clientes-desde-ventas.md" routes: - path: "/sales/invoices" component: "SalesInvoicesPage" - path: "/sales/invoices/:id" component: "ViewSalesInvoicePage" components: - name: "SalesInvoicesTable" file: "src/widgets/sales-invoices-table/ui/SalesInvoicesTable.tsx" type: widget - name: "CreateInvoiceButton" file: "src/features/create-sales-invoice/ui/CreateInvoiceButton.tsx" type: feature - name: "SalesInvoiceCard" file: "src/entities/sales-invoice/ui/SalesInvoiceCard.tsx" type: entity - name: "SalesInvoicePage" file: "src/pages/sales/SalesInvoicePage.tsx" type: page api_client: - name: "salesInvoiceApi" file: "src/entities/sales-invoice/api/sales-invoice.api.ts" methods: - getAll - getById - createFromOrder - validate - cancel state_management: - name: "useSalesInvoiceStore" file: "src/entities/sales-invoice/model/sales-invoice.store.ts" type: zustand - name: "useSalesInvoices" file: "src/entities/sales-invoice/api/sales-invoice.queries.ts" type: react-query database_tables: - schema: financial table: invoices_client file: "database-design/schemas/financial-schema-ddl.sql" operations: - SELECT - INSERT - UPDATE indices: - idx_invoices_client_tenant_id - idx_invoices_client_sales_order_id - idx_invoices_client_state rls_policy: tenant_isolation_invoices_client - schema: financial table: invoice_lines file: "database-design/schemas/financial-schema-ddl.sql" operations: - SELECT - INSERT - UPDATE indices: - idx_invoice_lines_invoice_id rls_policy: tenant_isolation_invoice_lines tests: backend: unit_tests: - file: "src/modules/sales/services/sales-invoice.service.spec.ts" test_cases: - "should create invoice from sales order" - "should validate invoice and create journal entry" - "should support partial invoicing" - "should find all invoices for tenant" - "should cancel invoice with credit note" integration_tests: - file: "test/sales/sales-invoice.controller.e2e-spec.ts" test_cases: - "POST /api/v1/sales/orders/:id/create-invoice should create invoice" - "GET /api/v1/sales/invoices should return all invoices" - "GET /api/v1/sales/invoices/:id should return invoice" - "POST /api/v1/sales/invoices/:id/validate should validate invoice" - "POST /api/v1/sales/invoices/:id/cancel should cancel invoice" - "should enforce tenant isolation" - "should require authentication" - "should check permissions" frontend: component_tests: - file: "src/widgets/sales-invoices-table/ui/SalesInvoicesTable.test.tsx" test_cases: - "should render table with invoices" - "should handle pagination" - "should filter by state" - file: "src/features/create-sales-invoice/ui/CreateInvoiceButton.test.tsx" test_cases: - "should show invoice options" - "should create invoice" - "should handle errors" e2e_tests: - file: "e2e/sales/sales-invoices.spec.ts" test_cases: - "should create invoice from order successfully" - "should validate invoice successfully" - "should cancel invoice with credit note" - "should enforce permissions" acceptance_criteria: - id: AC-001 description: "Usuario puede crear facturas desde órdenes de venta" status: Pending test_reference: "test/sales/sales-invoice.controller.e2e-spec.ts:38" - id: AC-002 description: "Validar factura genera asiento contable automáticamente" status: Pending test_reference: "src/modules/sales/services/sales-invoice.service.spec.ts:85" - id: AC-003 description: "Sistema soporta facturación parcial de órdenes" status: Pending test_reference: "test/sales/sales-invoice.controller.e2e-spec.ts:132" business_rules: - id: RN-001 description: "Factura debe estar asociada a una orden de venta" implementation: "src/modules/sales/services/sales-invoice.service.ts:validateSalesOrder()" test_reference: "src/modules/sales/services/sales-invoice.service.spec.ts:108" - id: RN-002 description: "Al validar factura, se genera asiento en cuentas por cobrar" implementation: "src/modules/sales/services/sales-invoice.service.ts:validate()" test_reference: "src/modules/sales/services/sales-invoice.service.spec.ts:155" - id: RN-003 description: "Cantidad facturada no puede exceder cantidad entregada" implementation: "src/modules/sales/services/sales-invoice.service.ts:validateQuantities()" test_reference: "src/modules/sales/services/sales-invoice.service.spec.ts:182" dependencies: rf_dependencies: - RF-MGN-007-003 - RF-MGN-004-005 module_dependencies: - MGN-001 - MGN-004 external_dependencies: - name: "@nestjs/common" version: "^10.0.0" - rf_id: RF-MGN-007-006 rf_title: "Reportes de Ventas" rf_file: "requerimientos-funcionales/mgn-007/RF-MGN-007-006-reportes-de-ventas.md" priority: P1 story_points: 3 et_backend: file: "especificaciones-tecnicas/backend/mgn-007/ET-BACKEND-MGN-007-006-reportes-de-ventas.md" endpoints: - method: GET path: /api/v1/sales/reports/summary description: "Obtener resumen de ventas" - method: GET path: /api/v1/sales/reports/by-product description: "Reporte de ventas por producto" - method: GET path: /api/v1/sales/reports/by-customer description: "Reporte de ventas por cliente" - method: GET path: /api/v1/sales/reports/by-salesperson description: "Reporte de ventas por vendedor" - method: GET path: /api/v1/sales/reports/forecast description: "Pronóstico de ventas" services: - name: "SalesReportService" file: "src/modules/sales/services/sales-report.service.ts" methods: - getSummary - getByProduct - getByCustomer - getBySalesperson - getForecast controllers: - name: "SalesReportController" file: "src/modules/sales/controllers/sales-report.controller.ts" dtos: - name: "SalesReportFilterDto" file: "src/modules/sales/dto/sales-report-filter.dto.ts" - name: "SalesReportResponseDto" file: "src/modules/sales/dto/sales-report-response.dto.ts" et_frontend: file: "especificaciones-tecnicas/frontend/mgn-007/ET-FRONTEND-MGN-007-006-reportes-de-ventas.md" routes: - path: "/sales/reports" component: "SalesReportsPage" - path: "/sales/reports/summary" component: "SalesSummaryPage" - path: "/sales/reports/products" component: "ProductsSalesPage" components: - name: "SalesReportDashboard" file: "src/widgets/sales-report-dashboard/ui/SalesReportDashboard.tsx" type: widget - name: "SalesChart" file: "src/features/sales-chart/ui/SalesChart.tsx" type: feature - name: "SalesReportFilters" file: "src/entities/sales-report/ui/SalesReportFilters.tsx" type: entity - name: "SalesReportsPage" file: "src/pages/sales/SalesReportsPage.tsx" type: page api_client: - name: "salesReportApi" file: "src/entities/sales-report/api/sales-report.api.ts" methods: - getSummary - getByProduct - getByCustomer - getBySalesperson - getForecast state_management: - name: "useSalesReportStore" file: "src/entities/sales-report/model/sales-report.store.ts" type: zustand - name: "useSalesReports" file: "src/entities/sales-report/api/sales-report.queries.ts" type: react-query database_tables: - schema: sales table: sales_orders file: "database-design/schemas/sales-schema-ddl.sql" operations: - SELECT indices: - idx_sales_orders_date - idx_sales_orders_state rls_policy: tenant_isolation_sales_orders - schema: financial table: invoices_client file: "database-design/schemas/financial-schema-ddl.sql" operations: - SELECT indices: - idx_invoices_client_date rls_policy: tenant_isolation_invoices_client tests: backend: unit_tests: - file: "src/modules/sales/services/sales-report.service.spec.ts" test_cases: - "should generate sales summary report" - "should group sales by product" - "should group sales by customer" - "should group sales by salesperson" - "should calculate sales forecast" integration_tests: - file: "test/sales/sales-report.controller.e2e-spec.ts" test_cases: - "GET /api/v1/sales/reports/summary should return summary" - "GET /api/v1/sales/reports/by-product should return product report" - "GET /api/v1/sales/reports/by-customer should return customer report" - "GET /api/v1/sales/reports/by-salesperson should return salesperson report" - "GET /api/v1/sales/reports/forecast should return forecast" - "should enforce tenant isolation" - "should require authentication" - "should check permissions" frontend: component_tests: - file: "src/widgets/sales-report-dashboard/ui/SalesReportDashboard.test.tsx" test_cases: - "should render dashboard with charts" - "should apply filters" - "should export report" - file: "src/features/sales-chart/ui/SalesChart.test.tsx" test_cases: - "should render chart with data" - "should change chart type" - "should handle empty data" e2e_tests: - file: "e2e/sales/sales-reports.spec.ts" test_cases: - "should display sales summary" - "should filter by date range" - "should export to PDF/Excel" - "should enforce permissions" acceptance_criteria: - id: AC-001 description: "Usuario puede generar reportes de ventas por período" status: Pending test_reference: "test/sales/sales-report.controller.e2e-spec.ts:45" - id: AC-002 description: "Reportes incluyen gráficos y visualizaciones" status: Pending test_reference: "src/widgets/sales-report-dashboard/ui/SalesReportDashboard.test.tsx:68" - id: AC-003 description: "Reportes se pueden exportar a PDF y Excel" status: Pending test_reference: "e2e/sales/sales-reports.spec.ts:92" business_rules: - id: RN-001 description: "Reportes solo incluyen ventas confirmadas y facturadas" implementation: "src/modules/sales/services/sales-report.service.ts:filterValidSales()" test_reference: "src/modules/sales/services/sales-report.service.spec.ts:88" - id: RN-002 description: "Métricas calculan: ingresos, márgenes, unidades vendidas" implementation: "src/modules/sales/services/sales-report.service.ts:calculateMetrics()" test_reference: "src/modules/sales/services/sales-report.service.spec.ts:115" - id: RN-003 description: "Pronóstico usa promedio móvil de últimos 3 meses" implementation: "src/modules/sales/services/sales-report.service.ts:getForecast()" test_reference: "src/modules/sales/services/sales-report.service.spec.ts:142" dependencies: rf_dependencies: - RF-MGN-007-003 - RF-MGN-007-005 module_dependencies: - MGN-001 - MGN-012 external_dependencies: - name: "chart.js" version: "^4.0.0" - name: "exceljs" version: "^4.3.0" coverage: rf_to_et_backend: 100% rf_to_et_frontend: 100% rf_to_database: 100% rf_to_tests: 100% backend_tests: 100% frontend_tests: 100% statistics: total_endpoints: 30 total_components: 24 total_tables: 5 total_test_cases: 120 estimated_duration_sprints: 3