1011 lines
42 KiB
YAML
1011 lines
42 KiB
YAML
# TRACEABILITY-MGN-005.yaml
|
|
# Matriz de Trazabilidad - MGN-005: Inventario Básico
|
|
# Fecha: 2025-11-24
|
|
# Versión: 1.0
|
|
|
|
module:
|
|
id: MGN-005
|
|
name: "Inventario Básico"
|
|
description: "Productos, almacenes, movimientos de stock, pickings, trazabilidad y valoración"
|
|
priority: P0
|
|
story_points: 66
|
|
status: Diseñado
|
|
|
|
metadata:
|
|
total_rf: 7
|
|
total_et_backend: 7
|
|
total_et_frontend: 7
|
|
total_tables: 10
|
|
total_tests: 140
|
|
coverage: 100%
|
|
|
|
requirements:
|
|
- rf_id: RF-MGN-005-001
|
|
rf_title: "Gestión de Productos"
|
|
rf_file: "requerimientos-funcionales/mgn-005/RF-MGN-005-001-gestión-de-productos.md"
|
|
priority: P0
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-005/ET-BACKEND-MGN-005-001-gestión-de-productos.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/inventory/products
|
|
description: "Crear producto"
|
|
- method: GET
|
|
path: /api/v1/inventory/products
|
|
description: "Listar productos"
|
|
- method: GET
|
|
path: /api/v1/inventory/products/:id
|
|
description: "Obtener producto por ID"
|
|
- method: PUT
|
|
path: /api/v1/inventory/products/:id
|
|
description: "Actualizar producto"
|
|
- method: DELETE
|
|
path: /api/v1/inventory/products/:id
|
|
description: "Desactivar producto"
|
|
services:
|
|
- name: "ProductService"
|
|
file: "src/modules/inventory/services/product.service.ts"
|
|
methods: [create, findAll, findOne, update, remove]
|
|
controllers:
|
|
- name: "ProductController"
|
|
file: "src/modules/inventory/controllers/product.controller.ts"
|
|
dtos:
|
|
- name: "CreateProductDto"
|
|
file: "src/modules/inventory/dto/create-product.dto.ts"
|
|
- name: "UpdateProductDto"
|
|
file: "src/modules/inventory/dto/update-product.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-005/ET-FRONTEND-MGN-005-001-gestión-de-productos.md"
|
|
routes:
|
|
- path: "/inventory/products"
|
|
component: "ProductsPage"
|
|
- path: "/inventory/products/create"
|
|
component: "CreateProductPage"
|
|
- path: "/inventory/products/:id/edit"
|
|
component: "EditProductPage"
|
|
- path: "/inventory/products/:id"
|
|
component: "ViewProductPage"
|
|
components:
|
|
- name: "ProductsTable"
|
|
file: "src/widgets/products-table/ui/ProductsTable.tsx"
|
|
type: widget
|
|
- name: "CreateProductForm"
|
|
file: "src/features/create-product/ui/CreateProductForm.tsx"
|
|
type: feature
|
|
- name: "ProductCard"
|
|
file: "src/entities/product/ui/ProductCard.tsx"
|
|
type: entity
|
|
api_client:
|
|
- name: "productApi"
|
|
file: "src/entities/product/api/product.api.ts"
|
|
methods: [getAll, getById, create, update, delete]
|
|
state_management:
|
|
- name: "useProductStore"
|
|
file: "src/entities/product/model/product.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: inventory
|
|
table: products
|
|
file: "database-design/schemas/inventory-schema-ddl.sql"
|
|
operations: [SELECT, INSERT, UPDATE, DELETE (soft)]
|
|
indices: [idx_products_company_id, idx_products_category_id, idx_products_type]
|
|
rls_policy: company_isolation_products
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/inventory/services/product.service.spec.ts"
|
|
test_cases:
|
|
- "should create product with valid data"
|
|
- "should validate product type (storable, consumable, service)"
|
|
- "should throw error when SKU already exists"
|
|
- "should update product successfully"
|
|
- "should soft delete product"
|
|
integration_tests:
|
|
- file: "test/inventory/product.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/inventory/products should create product"
|
|
- "GET /api/v1/inventory/products should return all products"
|
|
- "GET /api/v1/inventory/products/:id should return product"
|
|
- "PUT /api/v1/inventory/products/:id should update product"
|
|
- "DELETE /api/v1/inventory/products/:id should soft delete product"
|
|
- "should enforce company isolation"
|
|
- "should require authentication"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/products-table/ui/ProductsTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with products"
|
|
- "should handle pagination"
|
|
- "should filter by category"
|
|
- file: "src/features/create-product/ui/CreateProductForm.test.tsx"
|
|
test_cases:
|
|
- "should validate required fields"
|
|
- "should validate SKU format"
|
|
- "should submit valid form"
|
|
e2e_tests:
|
|
- file: "e2e/inventory/products.spec.ts"
|
|
test_cases:
|
|
- "should create product successfully"
|
|
- "should edit product successfully"
|
|
- "should delete product with confirmation"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede crear productos con SKU, nombre, tipo (storable, consumable, service)"
|
|
status: Pending
|
|
test_reference: "test/inventory/product.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "SKU es único por empresa"
|
|
status: Pending
|
|
test_reference: "src/modules/inventory/services/product.service.spec.ts:65"
|
|
- id: AC-003
|
|
description: "Productos tienen categoría, UoM y cuentas contables"
|
|
status: Pending
|
|
test_reference: "test/inventory/product.controller.e2e-spec.ts:95"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "SKU único por empresa"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:CONSTRAINT uq_products_sku_company"
|
|
test_reference: "src/modules/inventory/services/product.service.spec.ts:48"
|
|
- id: RN-002
|
|
description: "Productos tienen tipos: storable, consumable, service"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:products.type"
|
|
test_reference: "src/modules/inventory/services/product.service.spec.ts:78"
|
|
- id: RN-003
|
|
description: "Solo productos storable tienen stock"
|
|
implementation: "src/modules/inventory/services/product.service.ts:validateType()"
|
|
test_reference: "src/modules/inventory/services/product.service.spec.ts:108"
|
|
|
|
dependencies:
|
|
rf_dependencies: [RF-MGN-003-004, RF-MGN-003-005]
|
|
module_dependencies: [MGN-003]
|
|
external_dependencies: []
|
|
|
|
- rf_id: RF-MGN-005-002
|
|
rf_title: "Gestión de Almacenes y Ubicaciones"
|
|
rf_file: "requerimientos-funcionales/mgn-005/RF-MGN-005-002-gestión-de-almacenes-y-ubicaciones.md"
|
|
priority: P0
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-005/ET-BACKEND-MGN-005-002-gestión-de-almacenes-y-ubicaciones.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/inventory/warehouses
|
|
description: "Crear almacén"
|
|
- method: GET
|
|
path: /api/v1/inventory/warehouses
|
|
description: "Listar almacenes"
|
|
- method: GET
|
|
path: /api/v1/inventory/warehouses/:id
|
|
description: "Obtener almacén por ID"
|
|
- method: PUT
|
|
path: /api/v1/inventory/warehouses/:id
|
|
description: "Actualizar almacén"
|
|
- method: POST
|
|
path: /api/v1/inventory/warehouses/:id/locations
|
|
description: "Crear ubicación en almacén"
|
|
services:
|
|
- name: "WarehouseService"
|
|
file: "src/modules/inventory/services/warehouse.service.ts"
|
|
methods: [create, findAll, findOne, update, createLocation]
|
|
controllers:
|
|
- name: "WarehouseController"
|
|
file: "src/modules/inventory/controllers/warehouse.controller.ts"
|
|
dtos:
|
|
- name: "CreateWarehouseDto"
|
|
file: "src/modules/inventory/dto/create-warehouse.dto.ts"
|
|
- name: "CreateLocationDto"
|
|
file: "src/modules/inventory/dto/create-location.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-005/ET-FRONTEND-MGN-005-002-gestión-de-almacenes-y-ubicaciones.md"
|
|
routes:
|
|
- path: "/inventory/warehouses"
|
|
component: "WarehousesPage"
|
|
- path: "/inventory/warehouses/:id"
|
|
component: "WarehouseDetailsPage"
|
|
components:
|
|
- name: "WarehousesTable"
|
|
file: "src/widgets/warehouses-table/ui/WarehousesTable.tsx"
|
|
type: widget
|
|
- name: "CreateWarehouseForm"
|
|
file: "src/features/create-warehouse/ui/CreateWarehouseForm.tsx"
|
|
type: feature
|
|
- name: "LocationTree"
|
|
file: "src/widgets/location-tree/ui/LocationTree.tsx"
|
|
type: widget
|
|
api_client:
|
|
- name: "warehouseApi"
|
|
file: "src/entities/warehouse/api/warehouse.api.ts"
|
|
methods: [getAll, getById, create, update, createLocation]
|
|
state_management:
|
|
- name: "useWarehouseStore"
|
|
file: "src/entities/warehouse/model/warehouse.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: inventory
|
|
table: warehouses
|
|
file: "database-design/schemas/inventory-schema-ddl.sql"
|
|
operations: [SELECT, INSERT, UPDATE]
|
|
indices: [idx_warehouses_company_id]
|
|
rls_policy: company_isolation_warehouses
|
|
- schema: inventory
|
|
table: locations
|
|
file: "database-design/schemas/inventory-schema-ddl.sql"
|
|
operations: [SELECT, INSERT, UPDATE]
|
|
indices: [idx_locations_warehouse_id, idx_locations_parent_id]
|
|
rls_policy: company_isolation_locations
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/inventory/services/warehouse.service.spec.ts"
|
|
test_cases:
|
|
- "should create warehouse"
|
|
- "should create location in warehouse"
|
|
- "should create hierarchical locations"
|
|
- "should validate location belongs to warehouse"
|
|
- "should update warehouse"
|
|
integration_tests:
|
|
- file: "test/inventory/warehouse.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/inventory/warehouses should create warehouse"
|
|
- "GET /api/v1/inventory/warehouses should return all warehouses"
|
|
- "POST /api/v1/inventory/warehouses/:id/locations should create location"
|
|
- "should enforce company isolation"
|
|
- "should require authentication"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/warehouses-table/ui/WarehousesTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with warehouses"
|
|
- file: "src/widgets/location-tree/ui/LocationTree.test.tsx"
|
|
test_cases:
|
|
- "should render location tree"
|
|
- "should expand/collapse nodes"
|
|
e2e_tests:
|
|
- file: "e2e/inventory/warehouses.spec.ts"
|
|
test_cases:
|
|
- "should create warehouse successfully"
|
|
- "should create location in warehouse"
|
|
- "should show location hierarchy"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede crear almacenes con ubicaciones jerárquicas"
|
|
status: Pending
|
|
test_reference: "test/inventory/warehouse.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "Ubicaciones soportan jerarquía (zona → pasillo → anaquel)"
|
|
status: Pending
|
|
test_reference: "src/modules/inventory/services/warehouse.service.spec.ts:65"
|
|
- id: AC-003
|
|
description: "Stock se almacena por ubicación"
|
|
status: Pending
|
|
test_reference: "test/inventory/warehouse.controller.e2e-spec.ts:95"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Almacenes pertenecen a una empresa"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:warehouses.company_id"
|
|
test_reference: "src/modules/inventory/services/warehouse.service.spec.ts:48"
|
|
- id: RN-002
|
|
description: "Ubicaciones soportan jerarquía con parent_id"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:locations.parent_id"
|
|
test_reference: "src/modules/inventory/services/warehouse.service.spec.ts:78"
|
|
- id: RN-003
|
|
description: "Stock se registra por ubicación específica"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:stock_quants.location_id"
|
|
test_reference: "src/modules/inventory/services/warehouse.service.spec.ts:108"
|
|
|
|
dependencies:
|
|
rf_dependencies: [RF-MGN-002-001]
|
|
module_dependencies: [MGN-002]
|
|
external_dependencies: []
|
|
|
|
- rf_id: RF-MGN-005-003
|
|
rf_title: "Movimientos de Stock"
|
|
rf_file: "requerimientos-funcionales/mgn-005/RF-MGN-005-003-movimientos-de-stock.md"
|
|
priority: P0
|
|
story_points: 13
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-005/ET-BACKEND-MGN-005-003-movimientos-de-stock.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/inventory/stock-moves
|
|
description: "Crear movimiento de stock"
|
|
- method: GET
|
|
path: /api/v1/inventory/stock-moves
|
|
description: "Listar movimientos"
|
|
- method: GET
|
|
path: /api/v1/inventory/stock-moves/:id
|
|
description: "Obtener movimiento por ID"
|
|
- method: POST
|
|
path: /api/v1/inventory/stock-moves/:id/validate
|
|
description: "Validar movimiento (actualiza stock)"
|
|
- method: POST
|
|
path: /api/v1/inventory/stock-moves/:id/cancel
|
|
description: "Cancelar movimiento"
|
|
services:
|
|
- name: "StockMoveService"
|
|
file: "src/modules/inventory/services/stock-move.service.ts"
|
|
methods: [create, findAll, findOne, validate, cancel, updateQuants]
|
|
controllers:
|
|
- name: "StockMoveController"
|
|
file: "src/modules/inventory/controllers/stock-move.controller.ts"
|
|
dtos:
|
|
- name: "CreateStockMoveDto"
|
|
file: "src/modules/inventory/dto/create-stock-move.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-005/ET-FRONTEND-MGN-005-003-movimientos-de-stock.md"
|
|
routes:
|
|
- path: "/inventory/stock-moves"
|
|
component: "StockMovesPage"
|
|
- path: "/inventory/stock-moves/:id"
|
|
component: "ViewStockMovePage"
|
|
components:
|
|
- name: "StockMovesTable"
|
|
file: "src/widgets/stock-moves-table/ui/StockMovesTable.tsx"
|
|
type: widget
|
|
- name: "CreateStockMoveForm"
|
|
file: "src/features/create-stock-move/ui/CreateStockMoveForm.tsx"
|
|
type: feature
|
|
- name: "StockMoveCard"
|
|
file: "src/entities/stock-move/ui/StockMoveCard.tsx"
|
|
type: entity
|
|
api_client:
|
|
- name: "stockMoveApi"
|
|
file: "src/entities/stock-move/api/stock-move.api.ts"
|
|
methods: [getAll, getById, create, validate, cancel]
|
|
state_management:
|
|
- name: "useStockMoveStore"
|
|
file: "src/entities/stock-move/model/stock-move.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: inventory
|
|
table: stock_moves
|
|
file: "database-design/schemas/inventory-schema-ddl.sql"
|
|
operations: [SELECT, INSERT, UPDATE]
|
|
indices: [idx_stock_moves_product_id, idx_stock_moves_location_from, idx_stock_moves_location_to, idx_stock_moves_state]
|
|
rls_policy: company_isolation_stock_moves
|
|
- schema: inventory
|
|
table: stock_quants
|
|
file: "database-design/schemas/inventory-schema-ddl.sql"
|
|
operations: [SELECT, INSERT, UPDATE]
|
|
indices: [idx_stock_quants_product_location]
|
|
rls_policy: company_isolation_stock_quants
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/inventory/services/stock-move.service.spec.ts"
|
|
test_cases:
|
|
- "should create stock move"
|
|
- "should validate stock move (draft → done)"
|
|
- "should update stock quants on validation"
|
|
- "should cancel stock move"
|
|
- "should reverse quants on cancellation"
|
|
integration_tests:
|
|
- file: "test/inventory/stock-move.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/inventory/stock-moves should create move"
|
|
- "GET /api/v1/inventory/stock-moves should return all moves"
|
|
- "POST /api/v1/inventory/stock-moves/:id/validate should validate move"
|
|
- "POST /api/v1/inventory/stock-moves/:id/cancel should cancel move"
|
|
- "should enforce company isolation"
|
|
- "should require authentication"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/stock-moves-table/ui/StockMovesTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with moves"
|
|
- "should filter by state"
|
|
- file: "src/features/create-stock-move/ui/CreateStockMoveForm.test.tsx"
|
|
test_cases:
|
|
- "should validate required fields"
|
|
- "should submit valid form"
|
|
e2e_tests:
|
|
- file: "e2e/inventory/stock-moves.spec.ts"
|
|
test_cases:
|
|
- "should create stock move successfully"
|
|
- "should validate move successfully"
|
|
- "should show updated stock after validation"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede crear movimientos de stock entre ubicaciones"
|
|
status: Pending
|
|
test_reference: "test/inventory/stock-move.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "Validar movimiento actualiza stock en ubicaciones origen y destino"
|
|
status: Pending
|
|
test_reference: "src/modules/inventory/services/stock-move.service.spec.ts:85"
|
|
- id: AC-003
|
|
description: "Movimientos tienen estados: draft, done, cancelled"
|
|
status: Pending
|
|
test_reference: "test/inventory/stock-move.controller.e2e-spec.ts:105"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Movimientos tienen estados: draft, done, cancelled"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:stock_moves.state"
|
|
test_reference: "src/modules/inventory/services/stock-move.service.spec.ts:58"
|
|
- id: RN-002
|
|
description: "Al validar movimiento se actualizan stock_quants"
|
|
implementation: "src/modules/inventory/services/stock-move.service.ts:updateQuants()"
|
|
test_reference: "src/modules/inventory/services/stock-move.service.spec.ts:92"
|
|
- id: RN-003
|
|
description: "Cancelar movimiento revierte cambios en quants"
|
|
implementation: "src/modules/inventory/services/stock-move.service.ts:cancel()"
|
|
test_reference: "src/modules/inventory/services/stock-move.service.spec.ts:118"
|
|
|
|
dependencies:
|
|
rf_dependencies: [RF-MGN-005-001, RF-MGN-005-002]
|
|
module_dependencies: []
|
|
external_dependencies: []
|
|
|
|
- rf_id: RF-MGN-005-004
|
|
rf_title: "Pickings (Albaranes de Entrada/Salida)"
|
|
rf_file: "requerimientos-funcionales/mgn-005/RF-MGN-005-004-pickings-albaranes-de-entrada-salida.md"
|
|
priority: P0
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-005/ET-BACKEND-MGN-005-004-pickings-albaranes-de-entrada-salida.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/inventory/pickings
|
|
description: "Crear picking"
|
|
- method: GET
|
|
path: /api/v1/inventory/pickings
|
|
description: "Listar pickings"
|
|
- method: GET
|
|
path: /api/v1/inventory/pickings/:id
|
|
description: "Obtener picking por ID"
|
|
- method: POST
|
|
path: /api/v1/inventory/pickings/:id/validate
|
|
description: "Validar picking (genera movimientos)"
|
|
- method: POST
|
|
path: /api/v1/inventory/pickings/:id/cancel
|
|
description: "Cancelar picking"
|
|
services:
|
|
- name: "PickingService"
|
|
file: "src/modules/inventory/services/picking.service.ts"
|
|
methods: [create, findAll, findOne, validate, cancel, generateStockMoves]
|
|
controllers:
|
|
- name: "PickingController"
|
|
file: "src/modules/inventory/controllers/picking.controller.ts"
|
|
dtos:
|
|
- name: "CreatePickingDto"
|
|
file: "src/modules/inventory/dto/create-picking.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-005/ET-FRONTEND-MGN-005-004-pickings-albaranes-de-entrada-salida.md"
|
|
routes:
|
|
- path: "/inventory/pickings"
|
|
component: "PickingsPage"
|
|
- path: "/inventory/pickings/create"
|
|
component: "CreatePickingPage"
|
|
- path: "/inventory/pickings/:id"
|
|
component: "ViewPickingPage"
|
|
components:
|
|
- name: "PickingsTable"
|
|
file: "src/widgets/pickings-table/ui/PickingsTable.tsx"
|
|
type: widget
|
|
- name: "CreatePickingForm"
|
|
file: "src/features/create-picking/ui/CreatePickingForm.tsx"
|
|
type: feature
|
|
- name: "PickingCard"
|
|
file: "src/entities/picking/ui/PickingCard.tsx"
|
|
type: entity
|
|
api_client:
|
|
- name: "pickingApi"
|
|
file: "src/entities/picking/api/picking.api.ts"
|
|
methods: [getAll, getById, create, validate, cancel]
|
|
state_management:
|
|
- name: "usePickingStore"
|
|
file: "src/entities/picking/model/picking.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: inventory
|
|
table: pickings
|
|
file: "database-design/schemas/inventory-schema-ddl.sql"
|
|
operations: [SELECT, INSERT, UPDATE]
|
|
indices: [idx_pickings_picking_type_id, idx_pickings_state, idx_pickings_company_id]
|
|
rls_policy: company_isolation_pickings
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/inventory/services/picking.service.spec.ts"
|
|
test_cases:
|
|
- "should create picking"
|
|
- "should validate picking (generates stock moves)"
|
|
- "should handle receipt (incoming) picking"
|
|
- "should handle delivery (outgoing) picking"
|
|
- "should cancel picking"
|
|
integration_tests:
|
|
- file: "test/inventory/picking.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/inventory/pickings should create picking"
|
|
- "GET /api/v1/inventory/pickings should return all pickings"
|
|
- "POST /api/v1/inventory/pickings/:id/validate should validate picking"
|
|
- "POST /api/v1/inventory/pickings/:id/cancel should cancel picking"
|
|
- "should enforce company isolation"
|
|
- "should require authentication"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/pickings-table/ui/PickingsTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with pickings"
|
|
- "should filter by type"
|
|
- file: "src/features/create-picking/ui/CreatePickingForm.test.tsx"
|
|
test_cases:
|
|
- "should validate required fields"
|
|
- "should submit valid form"
|
|
e2e_tests:
|
|
- file: "e2e/inventory/pickings.spec.ts"
|
|
test_cases:
|
|
- "should create picking successfully"
|
|
- "should validate picking successfully"
|
|
- "should show generated stock moves"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede crear pickings de entrada (recepciones)"
|
|
status: Pending
|
|
test_reference: "test/inventory/picking.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "Usuario puede crear pickings de salida (entregas)"
|
|
status: Pending
|
|
test_reference: "test/inventory/picking.controller.e2e-spec.ts:58"
|
|
- id: AC-003
|
|
description: "Validar picking genera movimientos de stock automáticamente"
|
|
status: Pending
|
|
test_reference: "src/modules/inventory/services/picking.service.spec.ts:85"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Pickings tienen tipos: incoming (recepción), outgoing (entrega), internal (transferencia)"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:picking_types"
|
|
test_reference: "src/modules/inventory/services/picking.service.spec.ts:58"
|
|
- id: RN-002
|
|
description: "Validar picking genera stock_moves automáticamente"
|
|
implementation: "src/modules/inventory/services/picking.service.ts:generateStockMoves()"
|
|
test_reference: "src/modules/inventory/services/picking.service.spec.ts:92"
|
|
- id: RN-003
|
|
description: "Pickings tienen estados: draft, confirmed, done, cancelled"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:pickings.state"
|
|
test_reference: "src/modules/inventory/services/picking.service.spec.ts:118"
|
|
|
|
dependencies:
|
|
rf_dependencies: [RF-MGN-005-002, RF-MGN-005-003]
|
|
module_dependencies: []
|
|
external_dependencies: []
|
|
|
|
- rf_id: RF-MGN-005-005
|
|
rf_title: "Trazabilidad (Lotes y Números de Serie)"
|
|
rf_file: "requerimientos-funcionales/mgn-005/RF-MGN-005-005-trazabilidad-lotes-y-números-de-serie.md"
|
|
priority: P1
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-005/ET-BACKEND-MGN-005-005-trazabilidad-lotes-y-números-de-serie.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/inventory/lots
|
|
description: "Crear lote"
|
|
- method: GET
|
|
path: /api/v1/inventory/lots
|
|
description: "Listar lotes"
|
|
- method: GET
|
|
path: /api/v1/inventory/lots/:id
|
|
description: "Obtener lote por ID"
|
|
- method: GET
|
|
path: /api/v1/inventory/lots/:id/trace
|
|
description: "Trazabilidad de lote"
|
|
services:
|
|
- name: "LotService"
|
|
file: "src/modules/inventory/services/lot.service.ts"
|
|
methods: [create, findAll, findOne, trace]
|
|
controllers:
|
|
- name: "LotController"
|
|
file: "src/modules/inventory/controllers/lot.controller.ts"
|
|
dtos:
|
|
- name: "CreateLotDto"
|
|
file: "src/modules/inventory/dto/create-lot.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-005/ET-FRONTEND-MGN-005-005-trazabilidad-lotes-y-números-de-serie.md"
|
|
routes:
|
|
- path: "/inventory/lots"
|
|
component: "LotsPage"
|
|
- path: "/inventory/lots/:id"
|
|
component: "LotDetailsPage"
|
|
components:
|
|
- name: "LotsTable"
|
|
file: "src/widgets/lots-table/ui/LotsTable.tsx"
|
|
type: widget
|
|
- name: "CreateLotForm"
|
|
file: "src/features/create-lot/ui/CreateLotForm.tsx"
|
|
type: feature
|
|
- name: "LotTraceability"
|
|
file: "src/widgets/lot-traceability/ui/LotTraceability.tsx"
|
|
type: widget
|
|
api_client:
|
|
- name: "lotApi"
|
|
file: "src/entities/lot/api/lot.api.ts"
|
|
methods: [getAll, getById, create, trace]
|
|
state_management:
|
|
- name: "useLotStore"
|
|
file: "src/entities/lot/model/lot.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: inventory
|
|
table: lots
|
|
file: "database-design/schemas/inventory-schema-ddl.sql"
|
|
operations: [SELECT, INSERT, UPDATE]
|
|
indices: [idx_lots_product_id, idx_lots_name, idx_lots_company_id]
|
|
rls_policy: company_isolation_lots
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/inventory/services/lot.service.spec.ts"
|
|
test_cases:
|
|
- "should create lot"
|
|
- "should trace lot movements"
|
|
- "should validate lot uniqueness per product"
|
|
- "should handle expiration dates"
|
|
integration_tests:
|
|
- file: "test/inventory/lot.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/inventory/lots should create lot"
|
|
- "GET /api/v1/inventory/lots should return all lots"
|
|
- "GET /api/v1/inventory/lots/:id/trace should return traceability"
|
|
- "should enforce company isolation"
|
|
- "should require authentication"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/lots-table/ui/LotsTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with lots"
|
|
- "should show expiration status"
|
|
- file: "src/widgets/lot-traceability/ui/LotTraceability.test.tsx"
|
|
test_cases:
|
|
- "should render traceability timeline"
|
|
e2e_tests:
|
|
- file: "e2e/inventory/lots.spec.ts"
|
|
test_cases:
|
|
- "should create lot successfully"
|
|
- "should view lot traceability"
|
|
- "should show expired lots"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede crear lotes para productos"
|
|
status: Pending
|
|
test_reference: "test/inventory/lot.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "Sistema registra trazabilidad completa de lotes"
|
|
status: Pending
|
|
test_reference: "src/modules/inventory/services/lot.service.spec.ts:65"
|
|
- id: AC-003
|
|
description: "Lotes tienen fecha de expiración"
|
|
status: Pending
|
|
test_reference: "test/inventory/lot.controller.e2e-spec.ts:95"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Lote/Serial único por producto y empresa"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:CONSTRAINT uq_lots_name_product_company"
|
|
test_reference: "src/modules/inventory/services/lot.service.spec.ts:58"
|
|
- id: RN-002
|
|
description: "Productos pueden requerir trazabilidad por lote o número de serie"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:products.tracking"
|
|
test_reference: "src/modules/inventory/services/lot.service.spec.ts:85"
|
|
- id: RN-003
|
|
description: "Lotes tienen fecha de expiración opcional"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:lots.expiration_date"
|
|
test_reference: "src/modules/inventory/services/lot.service.spec.ts:108"
|
|
|
|
dependencies:
|
|
rf_dependencies: [RF-MGN-005-001, RF-MGN-005-003]
|
|
module_dependencies: []
|
|
external_dependencies: []
|
|
|
|
- rf_id: RF-MGN-005-006
|
|
rf_title: "Valoración de Inventario (FIFO, Promedio)"
|
|
rf_file: "requerimientos-funcionales/mgn-005/RF-MGN-005-006-valoración-de-inventario-fifo,-promedio.md"
|
|
priority: P0
|
|
story_points: 13
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-005/ET-BACKEND-MGN-005-006-valoración-de-inventario-fifo-promedio.md"
|
|
endpoints:
|
|
- method: GET
|
|
path: /api/v1/inventory/valuation/:productId
|
|
description: "Obtener valoración de producto"
|
|
- method: GET
|
|
path: /api/v1/inventory/valuation/report
|
|
description: "Reporte de valoración de inventario"
|
|
- method: POST
|
|
path: /api/v1/inventory/valuation/recalculate
|
|
description: "Recalcular valoración"
|
|
services:
|
|
- name: "ValuationService"
|
|
file: "src/modules/inventory/services/valuation.service.ts"
|
|
methods: [calculateValuation, generateReport, recalculate]
|
|
controllers:
|
|
- name: "ValuationController"
|
|
file: "src/modules/inventory/controllers/valuation.controller.ts"
|
|
dtos:
|
|
- name: "ValuationReportDto"
|
|
file: "src/modules/inventory/dto/valuation-report.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-005/ET-FRONTEND-MGN-005-006-valoración-de-inventario-fifo-promedio.md"
|
|
routes:
|
|
- path: "/inventory/valuation"
|
|
component: "ValuationPage"
|
|
- path: "/inventory/valuation/:productId"
|
|
component: "ProductValuationPage"
|
|
components:
|
|
- name: "ValuationTable"
|
|
file: "src/widgets/valuation-table/ui/ValuationTable.tsx"
|
|
type: widget
|
|
- name: "ValuationChart"
|
|
file: "src/widgets/valuation-chart/ui/ValuationChart.tsx"
|
|
type: widget
|
|
api_client:
|
|
- name: "valuationApi"
|
|
file: "src/entities/valuation/api/valuation.api.ts"
|
|
methods: [getByProduct, getReport, recalculate]
|
|
state_management:
|
|
- name: "useValuationStore"
|
|
file: "src/entities/valuation/model/valuation.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: inventory
|
|
table: stock_valuation_layers
|
|
file: "database-design/schemas/inventory-schema-ddl.sql"
|
|
operations: [SELECT, INSERT, UPDATE]
|
|
indices: [idx_valuation_layers_product_id, idx_valuation_layers_company_id]
|
|
rls_policy: company_isolation_valuation_layers
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/inventory/services/valuation.service.spec.ts"
|
|
test_cases:
|
|
- "should calculate FIFO valuation"
|
|
- "should calculate average cost valuation"
|
|
- "should update valuation on stock move"
|
|
- "should generate valuation report"
|
|
- "should recalculate valuation"
|
|
integration_tests:
|
|
- file: "test/inventory/valuation.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "GET /api/v1/inventory/valuation/:productId should return valuation"
|
|
- "GET /api/v1/inventory/valuation/report should return report"
|
|
- "POST /api/v1/inventory/valuation/recalculate should recalculate"
|
|
- "should enforce company isolation"
|
|
- "should require authentication"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/valuation-table/ui/ValuationTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with valuations"
|
|
- "should show total valuation"
|
|
- file: "src/widgets/valuation-chart/ui/ValuationChart.test.tsx"
|
|
test_cases:
|
|
- "should render valuation chart"
|
|
e2e_tests:
|
|
- file: "e2e/inventory/valuation.spec.ts"
|
|
test_cases:
|
|
- "should view product valuation"
|
|
- "should generate valuation report"
|
|
- "should recalculate valuation"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Sistema calcula valoración de inventario con método FIFO"
|
|
status: Pending
|
|
test_reference: "src/modules/inventory/services/valuation.service.spec.ts:58"
|
|
- id: AC-002
|
|
description: "Sistema calcula valoración de inventario con método Promedio"
|
|
status: Pending
|
|
test_reference: "src/modules/inventory/services/valuation.service.spec.ts:85"
|
|
- id: AC-003
|
|
description: "Valoración se actualiza automáticamente con movimientos de stock"
|
|
status: Pending
|
|
test_reference: "test/inventory/valuation.controller.e2e-spec.ts:68"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Métodos de valoración: FIFO (First In First Out), Average Cost"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:products.cost_method"
|
|
test_reference: "src/modules/inventory/services/valuation.service.spec.ts:48"
|
|
- id: RN-002
|
|
description: "Valoración se actualiza automáticamente en cada movimiento"
|
|
implementation: "src/modules/inventory/services/valuation.service.ts:updateValuation()"
|
|
test_reference: "src/modules/inventory/services/valuation.service.spec.ts:92"
|
|
- id: RN-003
|
|
description: "Stock valuation layers registran historia de costos"
|
|
implementation: "database-design/schemas/inventory-schema-ddl.sql:stock_valuation_layers"
|
|
test_reference: "src/modules/inventory/services/valuation.service.spec.ts:118"
|
|
|
|
dependencies:
|
|
rf_dependencies: [RF-MGN-005-001, RF-MGN-005-003]
|
|
module_dependencies: []
|
|
external_dependencies: []
|
|
|
|
- rf_id: RF-MGN-005-007
|
|
rf_title: "Inventario Físico y Ajustes"
|
|
rf_file: "requerimientos-funcionales/mgn-005/RF-MGN-005-007-inventario-físico-y-ajustes.md"
|
|
priority: P0
|
|
story_points: 8
|
|
|
|
et_backend:
|
|
file: "especificaciones-tecnicas/backend/mgn-005/ET-BACKEND-MGN-005-007-inventario-físico-y-ajustes.md"
|
|
endpoints:
|
|
- method: POST
|
|
path: /api/v1/inventory/adjustments
|
|
description: "Crear ajuste de inventario"
|
|
- method: GET
|
|
path: /api/v1/inventory/adjustments
|
|
description: "Listar ajustes"
|
|
- method: GET
|
|
path: /api/v1/inventory/adjustments/:id
|
|
description: "Obtener ajuste por ID"
|
|
- method: POST
|
|
path: /api/v1/inventory/adjustments/:id/validate
|
|
description: "Validar ajuste (actualiza stock)"
|
|
- method: POST
|
|
path: /api/v1/inventory/adjustments/:id/cancel
|
|
description: "Cancelar ajuste"
|
|
services:
|
|
- name: "InventoryAdjustmentService"
|
|
file: "src/modules/inventory/services/inventory-adjustment.service.ts"
|
|
methods: [create, findAll, findOne, validate, cancel]
|
|
controllers:
|
|
- name: "InventoryAdjustmentController"
|
|
file: "src/modules/inventory/controllers/inventory-adjustment.controller.ts"
|
|
dtos:
|
|
- name: "CreateInventoryAdjustmentDto"
|
|
file: "src/modules/inventory/dto/create-inventory-adjustment.dto.ts"
|
|
|
|
et_frontend:
|
|
file: "especificaciones-tecnicas/frontend/mgn-005/ET-FRONTEND-MGN-005-007-inventario-físico-y-ajustes.md"
|
|
routes:
|
|
- path: "/inventory/adjustments"
|
|
component: "AdjustmentsPage"
|
|
- path: "/inventory/adjustments/create"
|
|
component: "CreateAdjustmentPage"
|
|
- path: "/inventory/adjustments/:id"
|
|
component: "ViewAdjustmentPage"
|
|
components:
|
|
- name: "AdjustmentsTable"
|
|
file: "src/widgets/adjustments-table/ui/AdjustmentsTable.tsx"
|
|
type: widget
|
|
- name: "CreateAdjustmentForm"
|
|
file: "src/features/create-adjustment/ui/CreateAdjustmentForm.tsx"
|
|
type: feature
|
|
- name: "AdjustmentCard"
|
|
file: "src/entities/adjustment/ui/AdjustmentCard.tsx"
|
|
type: entity
|
|
api_client:
|
|
- name: "inventoryAdjustmentApi"
|
|
file: "src/entities/adjustment/api/adjustment.api.ts"
|
|
methods: [getAll, getById, create, validate, cancel]
|
|
state_management:
|
|
- name: "useAdjustmentStore"
|
|
file: "src/entities/adjustment/model/adjustment.store.ts"
|
|
type: zustand
|
|
|
|
database_tables:
|
|
- schema: inventory
|
|
table: inventory_adjustments
|
|
file: "database-design/schemas/inventory-schema-ddl.sql"
|
|
operations: [SELECT, INSERT, UPDATE]
|
|
indices: [idx_adjustments_company_id, idx_adjustments_state]
|
|
rls_policy: company_isolation_adjustments
|
|
|
|
tests:
|
|
backend:
|
|
unit_tests:
|
|
- file: "src/modules/inventory/services/inventory-adjustment.service.spec.ts"
|
|
test_cases:
|
|
- "should create inventory adjustment"
|
|
- "should validate adjustment (generates stock moves)"
|
|
- "should handle positive adjustments (increases stock)"
|
|
- "should handle negative adjustments (decreases stock)"
|
|
- "should cancel adjustment"
|
|
integration_tests:
|
|
- file: "test/inventory/inventory-adjustment.controller.e2e-spec.ts"
|
|
test_cases:
|
|
- "POST /api/v1/inventory/adjustments should create adjustment"
|
|
- "GET /api/v1/inventory/adjustments should return all adjustments"
|
|
- "POST /api/v1/inventory/adjustments/:id/validate should validate adjustment"
|
|
- "POST /api/v1/inventory/adjustments/:id/cancel should cancel adjustment"
|
|
- "should enforce company isolation"
|
|
- "should require authentication"
|
|
frontend:
|
|
component_tests:
|
|
- file: "src/widgets/adjustments-table/ui/AdjustmentsTable.test.tsx"
|
|
test_cases:
|
|
- "should render table with adjustments"
|
|
- "should filter by state"
|
|
- file: "src/features/create-adjustment/ui/CreateAdjustmentForm.test.tsx"
|
|
test_cases:
|
|
- "should validate required fields"
|
|
- "should submit valid form"
|
|
e2e_tests:
|
|
- file: "e2e/inventory/adjustments.spec.ts"
|
|
test_cases:
|
|
- "should create adjustment successfully"
|
|
- "should validate adjustment successfully"
|
|
- "should show updated stock"
|
|
|
|
acceptance_criteria:
|
|
- id: AC-001
|
|
description: "Usuario puede crear ajustes de inventario por diferencias en conteo físico"
|
|
status: Pending
|
|
test_reference: "test/inventory/inventory-adjustment.controller.e2e-spec.ts:28"
|
|
- id: AC-002
|
|
description: "Validar ajuste genera movimientos de stock automáticamente"
|
|
status: Pending
|
|
test_reference: "src/modules/inventory/services/inventory-adjustment.service.spec.ts:68"
|
|
- id: AC-003
|
|
description: "Ajustes pueden ser positivos (incremento) o negativos (decremento)"
|
|
status: Pending
|
|
test_reference: "test/inventory/inventory-adjustment.controller.e2e-spec.ts:95"
|
|
|
|
business_rules:
|
|
- id: RN-001
|
|
description: "Ajustes generan movimientos de stock automáticamente"
|
|
implementation: "src/modules/inventory/services/inventory-adjustment.service.ts:validate()"
|
|
test_reference: "src/modules/inventory/services/inventory-adjustment.service.spec.ts:58"
|
|
- id: RN-002
|
|
description: "Ajustes positivos incrementan stock, negativos decrementan"
|
|
implementation: "src/modules/inventory/services/inventory-adjustment.service.ts:generateStockMoves()"
|
|
test_reference: "src/modules/inventory/services/inventory-adjustment.service.spec.ts:85"
|
|
- id: RN-003
|
|
description: "Ajustes validados generan asientos contables de valoración"
|
|
implementation: "src/modules/inventory/services/inventory-adjustment.service.ts:generateJournalEntry()"
|
|
test_reference: "src/modules/inventory/services/inventory-adjustment.service.spec.ts:108"
|
|
|
|
dependencies:
|
|
rf_dependencies: [RF-MGN-005-001, RF-MGN-005-002, RF-MGN-005-003]
|
|
module_dependencies: []
|
|
external_dependencies: []
|
|
|
|
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: 35
|
|
total_components: 28
|
|
total_tables: 10
|
|
total_test_cases: 140
|
|
estimated_duration_sprints: 4
|