/** * AssetController - Controlador de Activos * * Endpoints para gestión de activos fijos, maquinaria y equipos. * * @module Assets (MAE-015) */ import { Router, Request, Response } from 'express'; import { DataSource } from 'typeorm'; import { AssetService } from '../services'; export function createAssetController(dataSource: DataSource): Router { const router = Router(); const service = new AssetService(dataSource); // ==================== CRUD DE ACTIVOS ==================== /** * GET / * Lista activos con filtros y paginación */ router.get('/', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const filters = { assetType: req.query.assetType as any, status: req.query.status as any, ownershipType: req.query.ownershipType as any, categoryId: req.query.categoryId as string, projectId: req.query.projectId as string, search: req.query.search as string, tags: req.query.tags ? (req.query.tags as string).split(',') : undefined, }; const pagination = { page: parseInt(req.query.page as string) || 1, limit: parseInt(req.query.limit as string) || 20, }; const result = await service.findAll(tenantId, filters, pagination); res.json(result); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * GET /statistics * Obtiene estadísticas de activos */ router.get('/statistics', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const stats = await service.getStatistics(tenantId); res.json(stats); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * GET /maintenance-due * Lista activos que requieren mantenimiento */ router.get('/maintenance-due', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const assets = await service.getAssetsNeedingMaintenance(tenantId); res.json(assets); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * GET /search * Búsqueda de activos para autocomplete */ router.get('/search', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const query = req.query.q as string; const limit = parseInt(req.query.limit as string) || 10; if (!query) { return res.status(400).json({ error: 'Se requiere parámetro de búsqueda (q)' }); } const assets = await service.search(tenantId, query, limit); res.json(assets); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * GET /by-code/:code * Obtiene un activo por código */ router.get('/by-code/:code', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const asset = await service.findByCode(tenantId, req.params.code); if (!asset) { return res.status(404).json({ error: 'Activo no encontrado' }); } res.json(asset); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * GET /:id * Obtiene un activo por ID */ router.get('/:id', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const asset = await service.findById(tenantId, req.params.id); if (!asset) { return res.status(404).json({ error: 'Activo no encontrado' }); } res.json(asset); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * POST / * Crea un nuevo activo */ router.post('/', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const asset = await service.create(tenantId, req.body, userId); res.status(201).json(asset); } catch (error) { res.status(400).json({ error: (error as Error).message }); } }); /** * PUT /:id * Actualiza un activo */ router.put('/:id', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const asset = await service.update(tenantId, req.params.id, req.body, userId); if (!asset) { return res.status(404).json({ error: 'Activo no encontrado' }); } res.json(asset); } catch (error) { res.status(400).json({ error: (error as Error).message }); } }); /** * PATCH /:id/status * Actualiza el estado de un activo */ router.patch('/:id/status', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const { status } = req.body; if (!status) { return res.status(400).json({ error: 'Se requiere el nuevo estado' }); } const asset = await service.updateStatus(tenantId, req.params.id, status, userId); if (!asset) { return res.status(404).json({ error: 'Activo no encontrado' }); } res.json(asset); } catch (error) { res.status(400).json({ error: (error as Error).message }); } }); /** * PATCH /:id/usage * Actualiza métricas de uso (horas/kilómetros) */ router.patch('/:id/usage', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const { hours, kilometers } = req.body; const asset = await service.updateUsage(tenantId, req.params.id, hours, kilometers, userId); if (!asset) { return res.status(404).json({ error: 'Activo no encontrado' }); } res.json(asset); } catch (error) { res.status(400).json({ error: (error as Error).message }); } }); /** * DELETE /:id * Elimina un activo (soft delete) */ router.delete('/:id', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const deleted = await service.delete(tenantId, req.params.id, userId); if (!deleted) { return res.status(404).json({ error: 'Activo no encontrado' }); } res.status(204).send(); } catch (error) { res.status(400).json({ error: (error as Error).message }); } }); // ==================== ASIGNACIONES ==================== /** * GET /:id/assignment * Obtiene la asignación actual de un activo */ router.get('/:id/assignment', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const assignment = await service.getCurrentAssignment(tenantId, req.params.id); res.json(assignment); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * GET /:id/assignments * Obtiene el historial de asignaciones de un activo */ router.get('/:id/assignments', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const pagination = { page: parseInt(req.query.page as string) || 1, limit: parseInt(req.query.limit as string) || 20, }; const history = await service.getAssignmentHistory(tenantId, req.params.id, pagination); res.json(history); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * POST /:id/assign * Asigna un activo a un proyecto */ router.post('/:id/assign', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const dto = { ...req.body, assetId: req.params.id, }; const assignment = await service.assignToProject(tenantId, dto, userId); res.status(201).json(assignment); } catch (error) { res.status(400).json({ error: (error as Error).message }); } }); /** * POST /:id/return * Retorna un activo de un proyecto */ router.post('/:id/return', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const { endDate } = req.body; const result = await service.returnFromProject( tenantId, req.params.id, endDate ? new Date(endDate) : new Date(), userId ); if (!result) { return res.status(400).json({ error: 'No hay asignación activa para este activo' }); } res.json({ success: true, message: 'Activo retornado exitosamente' }); } catch (error) { res.status(400).json({ error: (error as Error).message }); } }); // ==================== CATEGORÍAS ==================== /** * GET /categories * Lista categorías de activos */ router.get('/categories/list', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const categories = await service.getCategories(tenantId); res.json(categories); } catch (error) { res.status(500).json({ error: (error as Error).message }); } }); /** * POST /categories * Crea una nueva categoría */ router.post('/categories', async (req: Request, res: Response) => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const category = await service.createCategory(tenantId, req.body, userId); res.status(201).json(category); } catch (error) { res.status(400).json({ error: (error as Error).message }); } }); return router; }