/** * Proyecto Controller * API endpoints para gestión de proyectos * * @module Construction * @prefix /api/v1/proyectos */ import { Router, Request, Response, NextFunction } from 'express'; import { ProyectoService, CreateProyectoDto, UpdateProyectoDto } from '../services/proyecto.service'; const router = Router(); const proyectoService = new ProyectoService(); /** * GET /api/v1/proyectos * Lista todos los proyectos del tenant */ router.get('/', async (req: Request, res: Response, next: NextFunction) => { try { const tenantId = req.headers['x-tenant-id'] as string; if (!tenantId) { return res.status(400).json({ error: 'X-Tenant-Id header required' }); } const { estadoProyecto, ciudad } = req.query; const proyectos = await proyectoService.findAll({ tenantId, estadoProyecto: estadoProyecto as any, ciudad: ciudad as string, }); return res.json({ success: true, data: proyectos, count: proyectos.length, }); } catch (error) { return next(error); } }); /** * GET /api/v1/proyectos/statistics * Estadísticas de proyectos */ router.get('/statistics', async (req: Request, res: Response, next: NextFunction) => { try { const tenantId = req.headers['x-tenant-id'] as string; if (!tenantId) { return res.status(400).json({ error: 'X-Tenant-Id header required' }); } const stats = await proyectoService.getStatistics(tenantId); return res.json({ success: true, data: stats }); } catch (error) { return next(error); } }); /** * GET /api/v1/proyectos/:id * Obtiene un proyecto por ID */ router.get('/:id', async (req: Request, res: Response, next: NextFunction) => { try { const tenantId = req.headers['x-tenant-id'] as string; if (!tenantId) { return res.status(400).json({ error: 'X-Tenant-Id header required' }); } const proyecto = await proyectoService.findById(req.params.id, tenantId); if (!proyecto) { return res.status(404).json({ error: 'Proyecto no encontrado' }); } return res.json({ success: true, data: proyecto }); } catch (error) { return next(error); } }); /** * POST /api/v1/proyectos * Crea un nuevo proyecto */ router.post('/', async (req: Request, res: Response, next: NextFunction) => { try { const tenantId = req.headers['x-tenant-id'] as string; if (!tenantId) { return res.status(400).json({ error: 'X-Tenant-Id header required' }); } const data: CreateProyectoDto = { ...req.body, tenantId, createdById: (req as any).user?.id, }; // Validate required fields if (!data.codigo || !data.nombre) { return res.status(400).json({ error: 'codigo y nombre son requeridos' }); } // Check if codigo already exists const existing = await proyectoService.findByCodigo(data.codigo, tenantId); if (existing) { return res.status(409).json({ error: 'Ya existe un proyecto con ese código' }); } const proyecto = await proyectoService.create(data); return res.status(201).json({ success: true, data: proyecto }); } catch (error) { return next(error); } }); /** * PATCH /api/v1/proyectos/:id * Actualiza un proyecto */ router.patch('/:id', async (req: Request, res: Response, next: NextFunction) => { try { const tenantId = req.headers['x-tenant-id'] as string; if (!tenantId) { return res.status(400).json({ error: 'X-Tenant-Id header required' }); } const data: UpdateProyectoDto = req.body; const proyecto = await proyectoService.update(req.params.id, tenantId, data); if (!proyecto) { return res.status(404).json({ error: 'Proyecto no encontrado' }); } return res.json({ success: true, data: proyecto }); } catch (error) { return next(error); } }); /** * DELETE /api/v1/proyectos/:id * Elimina un proyecto */ router.delete('/:id', async (req: Request, res: Response, next: NextFunction) => { try { const tenantId = req.headers['x-tenant-id'] as string; if (!tenantId) { return res.status(400).json({ error: 'X-Tenant-Id header required' }); } const deleted = await proyectoService.delete(req.params.id, tenantId); if (!deleted) { return res.status(404).json({ error: 'Proyecto no encontrado' }); } return res.json({ success: true, message: 'Proyecto eliminado' }); } catch (error) { return next(error); } }); export default router;