/** * WorkOrderController - Controlador de Órdenes de Trabajo * * Endpoints para gestión de órdenes de trabajo de mantenimiento. * * @module Assets (MAE-015) */ import { Router, Request, Response, NextFunction } from 'express'; import { DataSource } from 'typeorm'; import { WorkOrderService } from '../services'; export function createWorkOrderController(dataSource: DataSource): Router { const router = Router(); const service = new WorkOrderService(dataSource); // ==================== CRUD DE ÓRDENES ==================== /** * GET / * Lista órdenes de trabajo con filtros y paginación */ router.get('/', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const filters = { assetId: req.query.assetId as string, status: req.query.status as any, priority: req.query.priority as any, maintenanceType: req.query.maintenanceType as any, technicianId: req.query.technicianId as string, projectId: req.query.projectId as string, dateFrom: req.query.dateFrom ? new Date(req.query.dateFrom as string) : undefined, dateTo: req.query.dateTo ? new Date(req.query.dateTo as string) : undefined, search: req.query.search as string, }; 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) { next(error); } }); /** * GET /statistics * Obtiene estadísticas de órdenes de trabajo */ router.get('/statistics', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const stats = await service.getStatistics(tenantId); res.json(stats); } catch (error) { next(error); } }); /** * GET /by-status * Obtiene órdenes agrupadas por estado */ router.get('/by-status', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const grouped = await service.getByStatus(tenantId); res.json(grouped); } catch (error) { next(error); } }); /** * GET /by-number/:orderNumber * Obtiene una orden por número */ router.get('/by-number/:orderNumber', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const workOrder = await service.findByNumber(tenantId, req.params.orderNumber); if (!workOrder) { res.status(404).json({ error: 'Orden de trabajo no encontrada' }); return; } res.json(workOrder); } catch (error) { next(error); } }); /** * GET /overdue * Lista órdenes vencidas */ router.get('/overdue', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const orders = await service.getOverdue(tenantId); res.json(orders); } catch (error) { next(error); } }); /** * GET /:id * Obtiene una orden de trabajo por ID */ router.get('/:id', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const workOrder = await service.findById(tenantId, req.params.id); if (!workOrder) { res.status(404).json({ error: 'Orden de trabajo no encontrada' }); return; } res.json(workOrder); } catch (error) { next(error); } }); /** * POST / * Crea una nueva orden de trabajo */ router.post('/', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const workOrder = await service.create(tenantId, req.body, userId); res.status(201).json(workOrder); } catch (error) { next(error); } }); /** * PUT /:id * Actualiza una orden de trabajo */ router.put('/:id', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const workOrder = await service.update(tenantId, req.params.id, req.body, userId); if (!workOrder) { res.status(404).json({ error: 'Orden de trabajo no encontrada' }); return; } res.json(workOrder); } catch (error) { next(error); } }); /** * DELETE /:id * Elimina una orden de trabajo (soft delete) */ router.delete('/:id', async (req: Request, res: Response, next: NextFunction): Promise => { 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) { res.status(404).json({ error: 'Orden de trabajo no encontrada' }); return; } res.status(204).send(); } catch (error) { next(error); } }); // ==================== WORKFLOW ==================== /** * POST /:id/start * Inicia una orden de trabajo */ router.post('/:id/start', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const workOrder = await service.start(tenantId, req.params.id, userId); if (!workOrder) { res.status(404).json({ error: 'Orden de trabajo no encontrada' }); return; } res.json(workOrder); } catch (error) { next(error); } }); /** * POST /:id/hold * Pone en espera una orden de trabajo */ router.post('/:id/hold', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const { reason } = req.body; const workOrder = await service.hold(tenantId, req.params.id, reason, userId); if (!workOrder) { res.status(404).json({ error: 'Orden de trabajo no encontrada' }); return; } res.json(workOrder); } catch (error) { next(error); } }); /** * POST /:id/resume * Reanuda una orden de trabajo en espera */ router.post('/:id/resume', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const workOrder = await service.resume(tenantId, req.params.id, userId); if (!workOrder) { res.status(404).json({ error: 'Orden de trabajo no encontrada' }); return; } res.json(workOrder); } catch (error) { next(error); } }); /** * POST /:id/complete * Completa una orden de trabajo */ router.post('/:id/complete', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const workOrder = await service.complete(tenantId, req.params.id, req.body, userId); if (!workOrder) { res.status(404).json({ error: 'Orden de trabajo no encontrada' }); return; } res.json(workOrder); } catch (error) { next(error); } }); /** * POST /:id/cancel * Cancela una orden de trabajo */ router.post('/:id/cancel', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const { reason } = req.body; const workOrder = await service.cancel(tenantId, req.params.id, reason, userId); if (!workOrder) { res.status(404).json({ error: 'Orden de trabajo no encontrada' }); return; } res.json(workOrder); } catch (error) { next(error); } }); // ==================== PARTES/REFACCIONES ==================== /** * GET /:id/parts * Lista partes usadas en una orden de trabajo */ router.get('/:id/parts', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const parts = await service.getParts(tenantId, req.params.id); res.json(parts); } catch (error) { next(error); } }); /** * POST /:id/parts * Agrega una parte a la orden de trabajo */ router.post('/:id/parts', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const part = await service.addPart(tenantId, req.params.id, req.body, userId); res.status(201).json(part); } catch (error) { next(error); } }); /** * PUT /:id/parts/:partId * Actualiza una parte de la orden */ router.put('/:id/parts/:partId', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const part = await service.updatePart(tenantId, req.params.partId, req.body, userId); if (!part) { res.status(404).json({ error: 'Parte no encontrada' }); return; } res.json(part); } catch (error) { next(error); } }); /** * DELETE /:id/parts/:partId * Elimina una parte de la orden */ router.delete('/:id/parts/:partId', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const deleted = await service.removePart(tenantId, req.params.partId, userId); if (!deleted) { res.status(404).json({ error: 'Parte no encontrada' }); return; } res.status(204).send(); } catch (error) { next(error); } }); // ==================== PLANES DE MANTENIMIENTO ==================== /** * GET /plans * Lista planes de mantenimiento */ router.get('/plans/list', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const assetId = req.query.assetId as string; const plans = await service.getMaintenancePlans(tenantId, assetId); res.json(plans); } catch (error) { next(error); } }); /** * POST /plans * Crea un plan de mantenimiento */ router.post('/plans', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const plan = await service.createMaintenancePlan(tenantId, req.body, userId); res.status(201).json(plan); } catch (error) { next(error); } }); /** * POST /plans/:planId/generate * Genera órdenes de trabajo desde un plan */ router.post('/plans/:planId/generate', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const userId = (req as any).user?.id; const orders = await service.generateFromPlan(tenantId, req.params.planId, userId); res.status(201).json(orders); } catch (error) { next(error); } }); return router; }