1145 lines
43 KiB
YAML
1145 lines
43 KiB
YAML
# 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
|