erp-core/docs/04-modelado/trazabilidad/TRACEABILITY-MGN-006.yaml

448 lines
30 KiB
YAML

# TRACEABILITY-MGN-006.yaml
# Matriz de Trazabilidad - MGN-006: Compras Básico
# Fecha: 2025-11-24
# Versión: 1.0
module:
id: MGN-006
name: "Compras Básico"
description: "RFQ, órdenes de compra, workflow de aprobación, recepciones y facturación"
priority: P0/P1
story_points: 42
status: Diseñado
metadata:
total_rf: 6
total_et_backend: 6
total_et_frontend: 6
total_tables: 6
total_tests: 120
coverage: 100%
requirements:
- rf_id: RF-MGN-006-001
rf_title: "Solicitudes de Cotización (RFQ)"
rf_file: "requerimientos-funcionales/mgn-006/RF-MGN-006-001-solicitudes-de-cotización-rfq.md"
priority: P1
story_points: 5
et_backend:
file: "especificaciones-tecnicas/backend/mgn-006/ET-BACKEND-MGN-006-001-solicitudes-de-cotización-rfq.md"
endpoints:
- {method: POST, path: "/api/v1/purchases/rfq", description: "Crear RFQ"}
- {method: GET, path: "/api/v1/purchases/rfq", description: "Listar RFQs"}
- {method: GET, path: "/api/v1/purchases/rfq/:id", description: "Obtener RFQ"}
- {method: PUT, path: "/api/v1/purchases/rfq/:id", description: "Actualizar RFQ"}
- {method: POST, path: "/api/v1/purchases/rfq/:id/send", description: "Enviar RFQ a proveedores"}
services:
- {name: "RfqService", file: "src/modules/purchases/services/rfq.service.ts", methods: [create, findAll, findOne, update, send]}
controllers:
- {name: "RfqController", file: "src/modules/purchases/controllers/rfq.controller.ts"}
dtos:
- {name: "CreateRfqDto", file: "src/modules/purchases/dto/create-rfq.dto.ts"}
- {name: "UpdateRfqDto", file: "src/modules/purchases/dto/update-rfq.dto.ts"}
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-006/ET-FRONTEND-MGN-006-001-solicitudes-de-cotización-rfq.md"
routes:
- {path: "/purchases/rfq", component: "RfqPage"}
- {path: "/purchases/rfq/create", component: "CreateRfqPage"}
- {path: "/purchases/rfq/:id", component: "ViewRfqPage"}
components:
- {name: "RfqTable", file: "src/widgets/rfq-table/ui/RfqTable.tsx", type: widget}
- {name: "CreateRfqForm", file: "src/features/create-rfq/ui/CreateRfqForm.tsx", type: feature}
- {name: "RfqCard", file: "src/entities/rfq/ui/RfqCard.tsx", type: entity}
api_client:
- {name: "rfqApi", file: "src/entities/rfq/api/rfq.api.ts", methods: [getAll, getById, create, update, send]}
state_management:
- {name: "useRfqStore", file: "src/entities/rfq/model/rfq.store.ts", type: zustand}
database_tables:
- {schema: purchases, table: rfqs, file: "database-design/schemas/purchases-schema-ddl.sql", operations: [SELECT, INSERT, UPDATE], indices: [idx_rfqs_company_id, idx_rfqs_state], rls_policy: company_isolation_rfqs}
- {schema: purchases, table: rfq_lines, file: "database-design/schemas/purchases-schema-ddl.sql", operations: [SELECT, INSERT, UPDATE, DELETE], indices: [idx_rfq_lines_rfq_id, idx_rfq_lines_product_id], rls_policy: company_isolation_rfq_lines}
tests:
backend:
unit_tests:
- file: "src/modules/purchases/services/rfq.service.spec.ts"
test_cases: ["should create RFQ", "should send RFQ to suppliers", "should update RFQ", "should validate RFQ lines", "should soft delete RFQ"]
integration_tests:
- file: "test/purchases/rfq.controller.e2e-spec.ts"
test_cases: ["POST /api/v1/purchases/rfq should create", "GET /api/v1/purchases/rfq should return all", "POST /api/v1/purchases/rfq/:id/send should send", "should enforce company isolation", "should require authentication"]
frontend:
component_tests:
- file: "src/widgets/rfq-table/ui/RfqTable.test.tsx"
test_cases: ["should render table", "should filter by state"]
- file: "src/features/create-rfq/ui/CreateRfqForm.test.tsx"
test_cases: ["should validate fields", "should submit form"]
e2e_tests:
- file: "e2e/purchases/rfq.spec.ts"
test_cases: ["should create RFQ successfully", "should send RFQ to suppliers", "should edit RFQ"]
acceptance_criteria:
- {id: AC-001, description: "Usuario puede crear RFQ con líneas de productos", status: Pending, test_reference: "test/purchases/rfq.controller.e2e-spec.ts:28"}
- {id: AC-002, description: "Usuario puede enviar RFQ a múltiples proveedores", status: Pending, test_reference: "src/modules/purchases/services/rfq.service.spec.ts:65"}
- {id: AC-003, description: "RFQ puede convertirse en orden de compra", status: Pending, test_reference: "test/purchases/rfq.controller.e2e-spec.ts:95"}
business_rules:
- {id: RN-001, description: "RFQ tiene estados: draft, sent, cancelled", implementation: "database-design/schemas/purchases-schema-ddl.sql:rfqs.state", test_reference: "src/modules/purchases/services/rfq.service.spec.ts:48"}
- {id: RN-002, description: "RFQ puede enviarse a múltiples proveedores", implementation: "src/modules/purchases/services/rfq.service.ts:send()", test_reference: "src/modules/purchases/services/rfq.service.spec.ts:78"}
- {id: RN-003, description: "RFQ puede convertirse en orden de compra", implementation: "src/modules/purchases/services/rfq.service.ts:convertToPO()", test_reference: "src/modules/purchases/services/rfq.service.spec.ts:108"}
dependencies:
rf_dependencies: [RF-MGN-003-001, RF-MGN-005-001]
module_dependencies: [MGN-003, MGN-005]
external_dependencies: []
- rf_id: RF-MGN-006-002
rf_title: "Gestión de Órdenes de Compra"
rf_file: "requerimientos-funcionales/mgn-006/RF-MGN-006-002-gestión-de-órdenes-de-compra.md"
priority: P0
story_points: 13
et_backend:
file: "especificaciones-tecnicas/backend/mgn-006/ET-BACKEND-MGN-006-002-gestión-de-órdenes-de-compra.md"
endpoints:
- {method: POST, path: "/api/v1/purchases/orders", description: "Crear orden de compra"}
- {method: GET, path: "/api/v1/purchases/orders", description: "Listar órdenes"}
- {method: GET, path: "/api/v1/purchases/orders/:id", description: "Obtener orden"}
- {method: PUT, path: "/api/v1/purchases/orders/:id", description: "Actualizar orden"}
- {method: POST, path: "/api/v1/purchases/orders/:id/confirm", description: "Confirmar orden"}
services:
- {name: "PurchaseOrderService", file: "src/modules/purchases/services/purchase-order.service.ts", methods: [create, findAll, findOne, update, confirm]}
controllers:
- {name: "PurchaseOrderController", file: "src/modules/purchases/controllers/purchase-order.controller.ts"}
dtos:
- {name: "CreatePurchaseOrderDto", file: "src/modules/purchases/dto/create-purchase-order.dto.ts"}
- {name: "UpdatePurchaseOrderDto", file: "src/modules/purchases/dto/update-purchase-order.dto.ts"}
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-006/ET-FRONTEND-MGN-006-002-gestión-de-órdenes-de-compra.md"
routes:
- {path: "/purchases/orders", component: "PurchaseOrdersPage"}
- {path: "/purchases/orders/create", component: "CreatePurchaseOrderPage"}
- {path: "/purchases/orders/:id", component: "ViewPurchaseOrderPage"}
components:
- {name: "PurchaseOrdersTable", file: "src/widgets/purchase-orders-table/ui/PurchaseOrdersTable.tsx", type: widget}
- {name: "CreatePurchaseOrderForm", file: "src/features/create-purchase-order/ui/CreatePurchaseOrderForm.tsx", type: feature}
- {name: "PurchaseOrderCard", file: "src/entities/purchase-order/ui/PurchaseOrderCard.tsx", type: entity}
api_client:
- {name: "purchaseOrderApi", file: "src/entities/purchase-order/api/purchase-order.api.ts", methods: [getAll, getById, create, update, confirm]}
state_management:
- {name: "usePurchaseOrderStore", file: "src/entities/purchase-order/model/purchase-order.store.ts", type: zustand}
database_tables:
- {schema: purchases, table: purchase_orders, file: "database-design/schemas/purchases-schema-ddl.sql", operations: [SELECT, INSERT, UPDATE], indices: [idx_purchase_orders_company_id, idx_purchase_orders_partner_id, idx_purchase_orders_state], rls_policy: company_isolation_purchase_orders}
- {schema: purchases, table: purchase_order_lines, file: "database-design/schemas/purchases-schema-ddl.sql", operations: [SELECT, INSERT, UPDATE, DELETE], indices: [idx_purchase_order_lines_order_id, idx_purchase_order_lines_product_id], rls_policy: company_isolation_purchase_order_lines}
tests:
backend:
unit_tests:
- file: "src/modules/purchases/services/purchase-order.service.spec.ts"
test_cases: ["should create purchase order", "should calculate totals with taxes", "should confirm order", "should generate picking", "should cancel order"]
integration_tests:
- file: "test/purchases/purchase-order.controller.e2e-spec.ts"
test_cases: ["POST /api/v1/purchases/orders should create", "GET /api/v1/purchases/orders should return all", "POST /api/v1/purchases/orders/:id/confirm should confirm", "should enforce company isolation", "should require authentication"]
frontend:
component_tests:
- file: "src/widgets/purchase-orders-table/ui/PurchaseOrdersTable.test.tsx"
test_cases: ["should render table", "should filter by state"]
- file: "src/features/create-purchase-order/ui/CreatePurchaseOrderForm.test.tsx"
test_cases: ["should validate fields", "should calculate totals", "should submit form"]
e2e_tests:
- file: "e2e/purchases/purchase-orders.spec.ts"
test_cases: ["should create order successfully", "should confirm order", "should show generated picking"]
acceptance_criteria:
- {id: AC-001, description: "Usuario puede crear órdenes de compra con líneas y impuestos", status: Pending, test_reference: "test/purchases/purchase-order.controller.e2e-spec.ts:28"}
- {id: AC-002, description: "Confirmar orden genera picking de recepción", status: Pending, test_reference: "src/modules/purchases/services/purchase-order.service.spec.ts:85"}
- {id: AC-003, description: "Órdenes tienen estados: draft, sent, done, cancelled", status: Pending, test_reference: "test/purchases/purchase-order.controller.e2e-spec.ts:125"}
business_rules:
- {id: RN-001, description: "Orden tiene estados: draft, sent, done, cancelled", implementation: "database-design/schemas/purchases-schema-ddl.sql:purchase_orders.state", test_reference: "src/modules/purchases/services/purchase-order.service.spec.ts:58"}
- {id: RN-002, description: "Confirmar orden genera picking de recepción", implementation: "src/modules/purchases/services/purchase-order.service.ts:confirm()", test_reference: "src/modules/purchases/services/purchase-order.service.spec.ts:92"}
- {id: RN-003, description: "Solo órdenes draft pueden modificarse", implementation: "src/modules/purchases/services/purchase-order.service.ts:update()", test_reference: "src/modules/purchases/services/purchase-order.service.spec.ts:118"}
dependencies:
rf_dependencies: [RF-MGN-003-001, RF-MGN-005-001, RF-MGN-004-004]
module_dependencies: [MGN-003, MGN-004, MGN-005]
external_dependencies: []
- rf_id: RF-MGN-006-003
rf_title: "Workflow de Aprobación de Compras"
rf_file: "requerimientos-funcionales/mgn-006/RF-MGN-006-003-workflow-de-aprobación-de-compras.md"
priority: P1
story_points: 5
et_backend:
file: "especificaciones-tecnicas/backend/mgn-006/ET-BACKEND-MGN-006-003-workflow-de-aprobación-de-compras.md"
endpoints:
- {method: POST, path: "/api/v1/purchases/orders/:id/request-approval", description: "Solicitar aprobación"}
- {method: POST, path: "/api/v1/purchases/orders/:id/approve", description: "Aprobar orden"}
- {method: POST, path: "/api/v1/purchases/orders/:id/reject", description: "Rechazar orden"}
- {method: GET, path: "/api/v1/purchases/approvals/pending", description: "Órdenes pendientes de aprobación"}
services:
- {name: "PurchaseApprovalService", file: "src/modules/purchases/services/purchase-approval.service.ts", methods: [requestApproval, approve, reject, getPendingApprovals]}
controllers:
- {name: "PurchaseApprovalController", file: "src/modules/purchases/controllers/purchase-approval.controller.ts"}
dtos:
- {name: "ApprovalRequestDto", file: "src/modules/purchases/dto/approval-request.dto.ts"}
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-006/ET-FRONTEND-MGN-006-003-workflow-de-aprobación-de-compras.md"
routes:
- {path: "/purchases/approvals", component: "PurchaseApprovalsPage"}
components:
- {name: "PendingApprovalsTable", file: "src/widgets/pending-approvals-table/ui/PendingApprovalsTable.tsx", type: widget}
- {name: "ApprovalButtons", file: "src/features/approval-buttons/ui/ApprovalButtons.tsx", type: feature}
api_client:
- {name: "purchaseApprovalApi", file: "src/entities/purchase-approval/api/purchase-approval.api.ts", methods: [requestApproval, approve, reject, getPending]}
state_management:
- {name: "usePurchaseApprovalStore", file: "src/entities/purchase-approval/model/purchase-approval.store.ts", type: zustand}
database_tables:
- {schema: purchases, table: purchase_orders, file: "database-design/schemas/purchases-schema-ddl.sql", operations: [SELECT, UPDATE], indices: [idx_purchase_orders_state, idx_purchase_orders_approver_id], rls_policy: company_isolation_purchase_orders}
- {schema: purchases, table: approval_logs, file: "database-design/schemas/purchases-schema-ddl.sql", operations: [SELECT, INSERT], indices: [idx_approval_logs_order_id, idx_approval_logs_approver_id], rls_policy: company_isolation_approval_logs}
tests:
backend:
unit_tests:
- file: "src/modules/purchases/services/purchase-approval.service.spec.ts"
test_cases: ["should request approval", "should approve order", "should reject order", "should validate approval permissions", "should send notifications"]
integration_tests:
- file: "test/purchases/purchase-approval.controller.e2e-spec.ts"
test_cases: ["POST /api/v1/purchases/orders/:id/request-approval should request", "POST /api/v1/purchases/orders/:id/approve should approve", "POST /api/v1/purchases/orders/:id/reject should reject", "GET /api/v1/purchases/approvals/pending should return pending", "should enforce approval permissions"]
frontend:
component_tests:
- file: "src/widgets/pending-approvals-table/ui/PendingApprovalsTable.test.tsx"
test_cases: ["should render table", "should show approval actions"]
- file: "src/features/approval-buttons/ui/ApprovalButtons.test.tsx"
test_cases: ["should call approve", "should call reject"]
e2e_tests:
- file: "e2e/purchases/approvals.spec.ts"
test_cases: ["should request approval successfully", "should approve order", "should reject order with reason"]
acceptance_criteria:
- {id: AC-001, description: "Órdenes de compra sobre monto límite requieren aprobación", status: Pending, test_reference: "test/purchases/purchase-approval.controller.e2e-spec.ts:28"}
- {id: AC-002, description: "Solo usuarios con permiso pueden aprobar órdenes", status: Pending, test_reference: "src/modules/purchases/services/purchase-approval.service.spec.ts:65"}
- {id: AC-003, description: "Sistema registra historial de aprobaciones", status: Pending, test_reference: "test/purchases/purchase-approval.controller.e2e-spec.ts:95"}
business_rules:
- {id: RN-001, description: "Órdenes sobre límite configurado requieren aprobación", implementation: "src/modules/purchases/services/purchase-approval.service.ts:requiresApproval()", test_reference: "src/modules/purchases/services/purchase-approval.service.spec.ts:48"}
- {id: RN-002, description: "Solo usuarios con rol de aprobador pueden aprobar", implementation: "src/modules/purchases/guards/approval.guard.ts", test_reference: "src/modules/purchases/services/purchase-approval.service.spec.ts:78"}
- {id: RN-003, description: "Aprobaciones se registran en approval_logs", implementation: "database-design/schemas/purchases-schema-ddl.sql:approval_logs", test_reference: "src/modules/purchases/services/purchase-approval.service.spec.ts:108"}
dependencies:
rf_dependencies: [RF-MGN-006-002, RF-MGN-001-002, RF-MGN-014-002]
module_dependencies: [MGN-001, MGN-014]
external_dependencies: []
- rf_id: RF-MGN-006-004
rf_title: "Recepciones de Compras"
rf_file: "requerimientos-funcionales/mgn-006/RF-MGN-006-004-recepciones-de-compras.md"
priority: P0
story_points: 8
et_backend:
file: "especificaciones-tecnicas/backend/mgn-006/ET-BACKEND-MGN-006-004-recepciones-de-compras.md"
endpoints:
- {method: GET, path: "/api/v1/purchases/orders/:id/receipts", description: "Listar recepciones de orden"}
- {method: POST, path: "/api/v1/purchases/orders/:id/receipts/:pickingId/validate", description: "Validar recepción"}
services:
- {name: "PurchaseReceiptService", file: "src/modules/purchases/services/purchase-receipt.service.ts", methods: [findByOrder, validate]}
controllers:
- {name: "PurchaseReceiptController", file: "src/modules/purchases/controllers/purchase-receipt.controller.ts"}
dtos: []
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-006/ET-FRONTEND-MGN-006-004-recepciones-de-compras.md"
routes:
- {path: "/purchases/orders/:id/receipts", component: "PurchaseReceiptsPage"}
components:
- {name: "PurchaseReceiptsTable", file: "src/widgets/purchase-receipts-table/ui/PurchaseReceiptsTable.tsx", type: widget}
- {name: "ValidateReceiptButton", file: "src/features/validate-receipt/ui/ValidateReceiptButton.tsx", type: feature}
api_client:
- {name: "purchaseReceiptApi", file: "src/entities/purchase-receipt/api/purchase-receipt.api.ts", methods: [getByOrder, validate]}
state_management:
- {name: "usePurchaseReceiptStore", file: "src/entities/purchase-receipt/model/purchase-receipt.store.ts", type: zustand}
database_tables:
- {schema: inventory, table: pickings, file: "database-design/schemas/inventory-schema-ddl.sql", operations: [SELECT, UPDATE], indices: [idx_pickings_purchase_order_id], rls_policy: company_isolation_pickings}
tests:
backend:
unit_tests:
- file: "src/modules/purchases/services/purchase-receipt.service.spec.ts"
test_cases: ["should find receipts by order", "should validate receipt", "should update order received qty", "should mark order as done when fully received"]
integration_tests:
- file: "test/purchases/purchase-receipt.controller.e2e-spec.ts"
test_cases: ["GET /api/v1/purchases/orders/:id/receipts should return receipts", "POST /api/v1/purchases/orders/:id/receipts/:pickingId/validate should validate", "should update purchase order state", "should enforce company isolation"]
frontend:
component_tests:
- file: "src/widgets/purchase-receipts-table/ui/PurchaseReceiptsTable.test.tsx"
test_cases: ["should render table", "should show validate button"]
e2e_tests:
- file: "e2e/purchases/receipts.spec.ts"
test_cases: ["should view receipts for order", "should validate receipt successfully", "should show updated order state"]
acceptance_criteria:
- {id: AC-001, description: "Usuario puede ver recepciones vinculadas a orden de compra", status: Pending, test_reference: "test/purchases/purchase-receipt.controller.e2e-spec.ts:28"}
- {id: AC-002, description: "Validar recepción actualiza cantidad recibida en orden", status: Pending, test_reference: "src/modules/purchases/services/purchase-receipt.service.spec.ts:65"}
- {id: AC-003, description: "Orden se marca como done cuando se recibe completamente", status: Pending, test_reference: "test/purchases/purchase-receipt.controller.e2e-spec.ts:95"}
business_rules:
- {id: RN-001, description: "Recepciones son pickings vinculados a orden de compra", implementation: "database-design/schemas/inventory-schema-ddl.sql:pickings.purchase_order_id", test_reference: "src/modules/purchases/services/purchase-receipt.service.spec.ts:48"}
- {id: RN-002, description: "Validar recepción actualiza qty_received en orden", implementation: "src/modules/purchases/services/purchase-receipt.service.ts:validate()", test_reference: "src/modules/purchases/services/purchase-receipt.service.spec.ts:78"}
- {id: RN-003, description: "Orden pasa a done cuando qty_received = qty_ordered", implementation: "src/modules/purchases/services/purchase-receipt.service.ts:checkOrderCompletion()", test_reference: "src/modules/purchases/services/purchase-receipt.service.spec.ts:108"}
dependencies:
rf_dependencies: [RF-MGN-006-002, RF-MGN-005-004]
module_dependencies: [MGN-005]
external_dependencies: []
- rf_id: RF-MGN-006-005
rf_title: "Facturación de Proveedores desde Compras"
rf_file: "requerimientos-funcionales/mgn-006/RF-MGN-006-005-facturación-de-proveedores-desde-compras.md"
priority: P0
story_points: 8
et_backend:
file: "especificaciones-tecnicas/backend/mgn-006/ET-BACKEND-MGN-006-005-facturación-de-proveedores-desde-compras.md"
endpoints:
- {method: POST, path: "/api/v1/purchases/orders/:id/create-invoice", description: "Crear factura desde orden"}
- {method: GET, path: "/api/v1/purchases/orders/:id/invoices", description: "Listar facturas de orden"}
services:
- {name: "PurchaseInvoiceService", file: "src/modules/purchases/services/purchase-invoice.service.ts", methods: [createFromOrder, findByOrder]}
controllers:
- {name: "PurchaseInvoiceController", file: "src/modules/purchases/controllers/purchase-invoice.controller.ts"}
dtos:
- {name: "CreateInvoiceFromOrderDto", file: "src/modules/purchases/dto/create-invoice-from-order.dto.ts"}
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-006/ET-FRONTEND-MGN-006-005-facturación-de-proveedores-desde-compras.md"
routes:
- {path: "/purchases/orders/:id/invoices", component: "PurchaseInvoicesPage"}
components:
- {name: "PurchaseInvoicesTable", file: "src/widgets/purchase-invoices-table/ui/PurchaseInvoicesTable.tsx", type: widget}
- {name: "CreateInvoiceFromOrderButton", file: "src/features/create-invoice-from-order/ui/CreateInvoiceFromOrderButton.tsx", type: feature}
api_client:
- {name: "purchaseInvoiceApi", file: "src/entities/purchase-invoice/api/purchase-invoice.api.ts", methods: [createFromOrder, getByOrder]}
state_management:
- {name: "usePurchaseInvoiceStore", file: "src/entities/purchase-invoice/model/purchase-invoice.store.ts", type: zustand}
database_tables:
- {schema: financial, table: invoices, file: "database-design/schemas/financial-schema-ddl.sql", operations: [SELECT, INSERT], indices: [idx_invoices_purchase_order_id], rls_policy: company_isolation_invoices}
tests:
backend:
unit_tests:
- file: "src/modules/purchases/services/purchase-invoice.service.spec.ts"
test_cases: ["should create invoice from order", "should copy lines from order", "should calculate taxes", "should link invoice to order"]
integration_tests:
- file: "test/purchases/purchase-invoice.controller.e2e-spec.ts"
test_cases: ["POST /api/v1/purchases/orders/:id/create-invoice should create", "GET /api/v1/purchases/orders/:id/invoices should return invoices", "should enforce company isolation"]
frontend:
component_tests:
- file: "src/widgets/purchase-invoices-table/ui/PurchaseInvoicesTable.test.tsx"
test_cases: ["should render table", "should show linked invoices"]
e2e_tests:
- file: "e2e/purchases/invoices.spec.ts"
test_cases: ["should create invoice from order successfully", "should view invoices linked to order"]
acceptance_criteria:
- {id: AC-001, description: "Usuario puede crear factura de proveedor desde orden de compra", status: Pending, test_reference: "test/purchases/purchase-invoice.controller.e2e-spec.ts:28"}
- {id: AC-002, description: "Factura copia líneas y montos de orden de compra", status: Pending, test_reference: "src/modules/purchases/services/purchase-invoice.service.spec.ts:65"}
- {id: AC-003, description: "Factura queda vinculada a orden de compra", status: Pending, test_reference: "test/purchases/purchase-invoice.controller.e2e-spec.ts:95"}
business_rules:
- {id: RN-001, description: "Factura copia datos de orden de compra", implementation: "src/modules/purchases/services/purchase-invoice.service.ts:createFromOrder()", test_reference: "src/modules/purchases/services/purchase-invoice.service.spec.ts:48"}
- {id: RN-002, description: "Factura se vincula a orden con purchase_order_id", implementation: "database-design/schemas/financial-schema-ddl.sql:invoices.purchase_order_id", test_reference: "src/modules/purchases/services/purchase-invoice.service.spec.ts:78"}
- {id: RN-003, description: "Orden permite múltiples facturas (facturación parcial)", implementation: "src/modules/purchases/services/purchase-invoice.service.ts:allowMultipleInvoices", test_reference: "src/modules/purchases/services/purchase-invoice.service.spec.ts:108"}
dependencies:
rf_dependencies: [RF-MGN-006-002, RF-MGN-004-006]
module_dependencies: [MGN-004]
external_dependencies: []
- rf_id: RF-MGN-006-006
rf_title: "Reportes de Compras"
rf_file: "requerimientos-funcionales/mgn-006/RF-MGN-006-006-reportes-de-compras.md"
priority: P1
story_points: 3
et_backend:
file: "especificaciones-tecnicas/backend/mgn-006/ET-BACKEND-MGN-006-006-reportes-de-compras.md"
endpoints:
- {method: GET, path: "/api/v1/purchases/reports/summary", description: "Reporte resumen de compras"}
- {method: GET, path: "/api/v1/purchases/reports/by-supplier", description: "Compras por proveedor"}
- {method: GET, path: "/api/v1/purchases/reports/by-product", description: "Compras por producto"}
services:
- {name: "PurchaseReportService", file: "src/modules/purchases/services/purchase-report.service.ts", methods: [generateSummary, generateBySupplier, generateByProduct]}
controllers:
- {name: "PurchaseReportController", file: "src/modules/purchases/controllers/purchase-report.controller.ts"}
dtos:
- {name: "ReportPeriodDto", file: "src/modules/purchases/dto/report-period.dto.ts"}
et_frontend:
file: "especificaciones-tecnicas/frontend/mgn-006/ET-FRONTEND-MGN-006-006-reportes-de-compras.md"
routes:
- {path: "/purchases/reports", component: "PurchaseReportsPage"}
components:
- {name: "PurchaseSummaryReport", file: "src/widgets/purchase-summary-report/ui/PurchaseSummaryReport.tsx", type: widget}
- {name: "PurchasesBySupplierChart", file: "src/widgets/purchases-by-supplier-chart/ui/PurchasesBySupplierChart.tsx", type: widget}
api_client:
- {name: "purchaseReportApi", file: "src/entities/purchase-report/api/purchase-report.api.ts", methods: [getSummary, getBySupplier, getByProduct]}
state_management:
- {name: "usePurchaseReportStore", file: "src/entities/purchase-report/model/purchase-report.store.ts", type: zustand}
database_tables:
- {schema: purchases, table: purchase_orders, file: "database-design/schemas/purchases-schema-ddl.sql", operations: [SELECT], indices: [idx_purchase_orders_order_date, idx_purchase_orders_partner_id], rls_policy: company_isolation_purchase_orders}
tests:
backend:
unit_tests:
- file: "src/modules/purchases/services/purchase-report.service.spec.ts"
test_cases: ["should generate summary report", "should generate by supplier report", "should filter by date range", "should calculate totals"]
integration_tests:
- file: "test/purchases/purchase-report.controller.e2e-spec.ts"
test_cases: ["GET /api/v1/purchases/reports/summary should return summary", "GET /api/v1/purchases/reports/by-supplier should return by supplier", "should enforce company isolation"]
frontend:
component_tests:
- file: "src/widgets/purchase-summary-report/ui/PurchaseSummaryReport.test.tsx"
test_cases: ["should render report", "should show totals"]
e2e_tests:
- file: "e2e/purchases/reports.spec.ts"
test_cases: ["should generate summary report", "should filter by date range", "should export to PDF"]
acceptance_criteria:
- {id: AC-001, description: "Usuario puede generar reporte resumen de compras por período", status: Pending, test_reference: "test/purchases/purchase-report.controller.e2e-spec.ts:28"}
- {id: AC-002, description: "Usuario puede ver compras agrupadas por proveedor", status: Pending, test_reference: "src/modules/purchases/services/purchase-report.service.spec.ts:65"}
- {id: AC-003, description: "Reportes pueden exportarse a PDF y Excel", status: Pending, test_reference: "e2e/purchases/reports.spec.ts:85"}
business_rules:
- {id: RN-001, description: "Reportes filtran por rango de fechas", implementation: "src/modules/purchases/services/purchase-report.service.ts:filterByPeriod()", test_reference: "src/modules/purchases/services/purchase-report.service.spec.ts:48"}
- {id: RN-002, description: "Reportes calculan totales en moneda de empresa", implementation: "src/modules/purchases/services/purchase-report.service.ts:calculateTotals()", test_reference: "src/modules/purchases/services/purchase-report.service.spec.ts:78"}
- {id: RN-003, description: "Reportes respetan company isolation", implementation: "src/modules/purchases/services/purchase-report.service.ts:findAll()", test_reference: "src/modules/purchases/services/purchase-report.service.spec.ts:108"}
dependencies:
rf_dependencies: [RF-MGN-006-002]
module_dependencies: []
external_dependencies:
- {name: "pdfkit", version: "^0.13.0"}
- {name: "xlsx", version: "^0.18.5"}
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: 6
total_test_cases: 120
estimated_duration_sprints: 3