- Fix controllers to use Promise<void> return types - Replace 'return res.status()' with 'res.status(); return;' - Add NextFunction parameter to handlers - Fix enum-style references with string literals - Replace 'deletedAt: null' with 'IsNull()' for TypeORM - Remove unused parameters and imports - Fix grouped object initialization in getByStatus - Remove non-existent 'updatedBy' property from WorkOrderPart Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
430 lines
12 KiB
TypeScript
430 lines
12 KiB
TypeScript
/**
|
|
* 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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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<void> => {
|
|
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;
|
|
}
|