8.5 KiB
US-MGN-005-002-001: Gestionar Almacenes y Ubicaciones
RF Asociado: RF-MGN-005-002 Módulo: MGN-005 - Inventario Básico Epic: Almacenes y Ubicaciones Prioridad: P0 Story Points: 5 Sprint: Sprint 9 Estado: Ready for Development Fecha: 2025-11-24
User Story
Como usuario de inventario, Quiero crear almacenes con ubicaciones jerárquicas (zona → pasillo → anaquel), Para organizar el stock físicamente y facilitar la gestión de inventario.
Descripción Detallada
Los almacenes representan instalaciones físicas donde se almacenan productos. Cada almacén puede tener ubicaciones jerárquicas organizadas en niveles:
- Almacén: Edificio o instalación principal
- Zona: Área dentro del almacén (ej: Zona A, Zona B)
- Pasillo: Pasillo dentro de una zona (ej: Pasillo 1, Pasillo 2)
- Anaquel: Estante específico (ej: Anaquel A1, A2)
Las ubicaciones usan parent_id para crear jerarquía. El stock se registra a nivel de ubicación más específica (quant por ubicación).
Criterios de Aceptación
Escenario 1: Crear almacén principal (Camino Feliz)
Dado que soy usuario con permiso inventory_user, Cuando creo un almacén con name="Almacén Central", code="WH-01", address="Calle 123", Entonces el sistema crea el almacén exitosamente y genera ubicaciones por defecto (Stock, Input, Output).
Escenario 2: Crear ubicaciones jerárquicas
Dado que tengo un almacén "WH-01", Cuando creo ubicación "Zona A" (parent=WH-01), luego "Pasillo 1" (parent=Zona A), luego "Anaquel A1" (parent=Pasillo 1), Entonces el sistema crea jerarquía de 3 niveles correctamente.
Escenario 3: Visualizar árbol de ubicaciones
Dado que un almacén tiene ubicaciones jerárquicas, Cuando consulto el almacén, Entonces el sistema retorna estructura de árbol con todas las ubicaciones anidadas.
Escenario 4: Validar código único de almacén
Dado que ya existe almacén con code="WH-01", Cuando intento crear otro con el mismo código, Entonces el sistema retorna error 400 "El código WH-01 ya está en uso".
Escenario 5: Validar ubicación pertenece a almacén
Dado que intento crear ubicación con parent_id de otro almacén, Cuando envío el request, Entonces el sistema retorna error 400 "La ubicación padre no pertenece al mismo almacén".
Reglas de Negocio
- RN-1: Código de almacén es único por empresa.
- RN-2: Ubicaciones usan parent_id para jerarquía.
- RN-3: Todo almacén tiene ubicaciones por defecto: Stock, Input (recepción), Output (envío).
- RN-4: Stock se registra por ubicación específica (stock_quants.location_id).
- RN-5: Ubicaciones pueden tener tipo: view (virtual), internal (física), supplier, customer, transit.
- RN-6: RLS filtra almacenes por empresa.
Tareas Técnicas
Backend
- Endpoint: POST /api/v1/inventory/warehouses
- Endpoint: GET /api/v1/inventory/warehouses
- Endpoint: GET /api/v1/inventory/warehouses/:id
- Endpoint: POST /api/v1/inventory/warehouses/:id/locations
- Endpoint: GET /api/v1/inventory/locations (tree view)
- Service: WarehouseService.create(createWarehouseDto)
- Service: WarehouseService.createDefaultLocations(warehouseId)
- Service: LocationService.create(createLocationDto)
- Service: LocationService.getTree(warehouseId)
- DTO: CreateWarehouseDto, CreateLocationDto
- Validar código único
- Validar jerarquía de ubicaciones
- Unit tests (>80% coverage)
- Integration tests
- Swagger docs
Frontend
- Componente: CreateWarehouseForm.tsx
- Componente: WarehouseCard.tsx
- Componente: LocationTree.tsx (árbol jerárquico)
- Componente: CreateLocationModal.tsx
- Página: WarehousesPage.tsx (/inventory/warehouses)
- Página: WarehouseDetailsPage.tsx (/inventory/warehouses/:id)
- API client: warehouseApi.create(data), locationApi.create(data)
- State management: useWarehouseStore
- Component tests
- E2E test: "should create warehouse and locations"
Database
- Tabla: inventory.warehouses
- Tabla: inventory.locations
- Índices: idx_warehouses_company_id, idx_warehouses_code
- Índices: idx_locations_warehouse_id, idx_locations_parent_id
- Constraint: uq_warehouses_code_company
- RLS policy: company_isolation_warehouses, company_isolation_locations
Mockups / Wireframes
Formulario Crear Almacén:
┌─────────────────────────────────────────────┐
│ Crear Almacén │
├─────────────────────────────────────────────┤
│ Código: [Input: WH-01] * │
│ Nombre: [Input: Almacén Central] * │
│ Dirección: [TextArea] │
│ │
│ [✓] Crear ubicaciones por defecto │
│ (Stock, Input, Output) │
├─────────────────────────────────────────────┤
│ [Guardar] [Cancelar] │
└─────────────────────────────────────────────┘
Árbol de Ubicaciones:
┌─────────────────────────────────────────────┐
│ Almacén Central (WH-01) │
├─────────────────────────────────────────────┤
│ ▼ Stock │
│ ▼ Zona A │
│ ▼ Pasillo 1 │
│ ▶ Anaquel A1 │
│ ▶ Anaquel A2 │
│ ▶ Pasillo 2 │
│ ▶ Zona B │
│ ▶ Input (Recepción) │
│ ▶ Output (Envío) │
│ │
│ [+ Nueva Ubicación] │
└─────────────────────────────────────────────┘
Casos de Prueba
Funcionales
- TC-001: Crear almacén con ubicaciones por defecto
- TC-002: Crear ubicaciones jerárquicas (3 niveles)
- TC-003: Visualizar árbol de ubicaciones
- TC-004: Error por código de almacén duplicado
- TC-005: Error por ubicación padre de otro almacén
- TC-006: RLS filtra por empresa
No Funcionales
- Performance: < 400ms para crear almacén
- Seguridad: JWT + permiso inventory_user
Dependencias
- US bloqueantes: US-MGN-002-001-001 (Gestión de Empresas)
- Módulos requeridos: MGN-001, MGN-002
- Datos maestros: Ninguno
Notas de Implementación
- Usar patrón de árbol con parent_id (closure table opcional para queries más eficientes)
- Frontend: Usar react-complex-tree o similar para árbol de ubicaciones
- Considerar profundidad máxima de jerarquía (ej: 5 niveles)
- Ubicaciones por defecto: Stock (internal), Input (supplier location), Output (customer location)
Estimación Detallada
| Tarea | Horas |
|---|---|
| Backend | 2.5 |
| Frontend | 3 |
| Testing | 2 |
| Code Review | 0.5 |
| TOTAL | 8 horas = 5 SP |
Definition of Done
- Código implementado según ET
- Tests pasando (>80%)
- Code review aprobado
- Documentación actualizada
- Swagger docs completo
- RLS aplicado
- QA validado
- PO aprobado