448 lines
30 KiB
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
|