diff --git a/src/modules/assets/controllers/asset.controller.ts b/src/modules/assets/controllers/asset.controller.ts index f212ac6..11b9ce4 100644 --- a/src/modules/assets/controllers/asset.controller.ts +++ b/src/modules/assets/controllers/asset.controller.ts @@ -6,7 +6,7 @@ * @module Assets (MAE-015) */ -import { Router, Request, Response } from 'express'; +import { Router, Request, Response, NextFunction } from 'express'; import { DataSource } from 'typeorm'; import { AssetService } from '../services'; @@ -20,10 +20,9 @@ export function createAssetController(dataSource: DataSource): Router { * GET / * Lista activos con filtros y paginación */ - router.get('/', async (req: Request, res: Response) => { + router.get('/', 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 filters = { assetType: req.query.assetType as any, @@ -81,20 +80,21 @@ export function createAssetController(dataSource: DataSource): Router { * GET /search * Búsqueda de activos para autocomplete */ - router.get('/search', async (req: Request, res: Response) => { + router.get('/search', async (req: Request, res: Response, next: NextFunction): Promise => { 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)' }); + res.status(400).json({ error: 'Se requiere parámetro de búsqueda (q)' }); + return; } const assets = await service.search(tenantId, query, limit); res.json(assets); } catch (error) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -102,18 +102,19 @@ export function createAssetController(dataSource: DataSource): Router { * GET /by-code/:code * Obtiene un activo por código */ - router.get('/by-code/:code', async (req: Request, res: Response) => { + router.get('/by-code/:code', async (req: Request, res: Response, next: NextFunction): Promise => { 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.status(404).json({ error: 'Activo no encontrado' }); + return; } res.json(asset); } catch (error) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -121,18 +122,19 @@ export function createAssetController(dataSource: DataSource): Router { * GET /:id * Obtiene un activo por ID */ - router.get('/:id', async (req: Request, res: Response) => { + router.get('/:id', async (req: Request, res: Response, next: NextFunction): Promise => { 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.status(404).json({ error: 'Activo no encontrado' }); + return; } res.json(asset); } catch (error) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -156,19 +158,20 @@ export function createAssetController(dataSource: DataSource): Router { * PUT /:id * Actualiza un activo */ - router.put('/:id', async (req: Request, res: Response) => { + 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 asset = await service.update(tenantId, req.params.id, req.body, userId); if (!asset) { - return res.status(404).json({ error: 'Activo no encontrado' }); + res.status(404).json({ error: 'Activo no encontrado' }); + return; } res.json(asset); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -176,24 +179,26 @@ export function createAssetController(dataSource: DataSource): Router { * PATCH /:id/status * Actualiza el estado de un activo */ - router.patch('/:id/status', async (req: Request, res: Response) => { + router.patch('/:id/status', 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 { status } = req.body; if (!status) { - return res.status(400).json({ error: 'Se requiere el nuevo estado' }); + res.status(400).json({ error: 'Se requiere el nuevo estado' }); + return; } const asset = await service.updateStatus(tenantId, req.params.id, status, userId); if (!asset) { - return res.status(404).json({ error: 'Activo no encontrado' }); + res.status(404).json({ error: 'Activo no encontrado' }); + return; } res.json(asset); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -201,7 +206,7 @@ export function createAssetController(dataSource: DataSource): Router { * PATCH /:id/usage * Actualiza métricas de uso (horas/kilómetros) */ - router.patch('/:id/usage', async (req: Request, res: Response) => { + router.patch('/:id/usage', 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; @@ -209,12 +214,13 @@ export function createAssetController(dataSource: DataSource): Router { const asset = await service.updateUsage(tenantId, req.params.id, hours, kilometers, userId); if (!asset) { - return res.status(404).json({ error: 'Activo no encontrado' }); + res.status(404).json({ error: 'Activo no encontrado' }); + return; } res.json(asset); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -222,19 +228,20 @@ export function createAssetController(dataSource: DataSource): Router { * DELETE /:id * Elimina un activo (soft delete) */ - router.delete('/:id', async (req: Request, res: Response) => { + 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) { - return res.status(404).json({ error: 'Activo no encontrado' }); + res.status(404).json({ error: 'Activo no encontrado' }); + return; } res.status(204).send(); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -300,7 +307,7 @@ export function createAssetController(dataSource: DataSource): Router { * POST /:id/return * Retorna un activo de un proyecto */ - router.post('/:id/return', async (req: Request, res: Response) => { + router.post('/:id/return', 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; @@ -314,12 +321,13 @@ export function createAssetController(dataSource: DataSource): Router { ); if (!result) { - return res.status(400).json({ error: 'No hay asignación activa para este activo' }); + res.status(400).json({ error: 'No hay asignación activa para este activo' }); + return; } res.json({ success: true, message: 'Activo retornado exitosamente' }); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); diff --git a/src/modules/assets/controllers/fuel-log.controller.ts b/src/modules/assets/controllers/fuel-log.controller.ts index e80f4f6..7f04b0d 100644 --- a/src/modules/assets/controllers/fuel-log.controller.ts +++ b/src/modules/assets/controllers/fuel-log.controller.ts @@ -6,7 +6,7 @@ * @module Assets (MAE-015) */ -import { Router, Request, Response } from 'express'; +import { Router, Request, Response, NextFunction } from 'express'; import { DataSource } from 'typeorm'; import { FuelLogService } from '../services'; @@ -18,7 +18,7 @@ export function createFuelLogController(dataSource: DataSource): Router { * GET / * Lista registros de combustible con filtros */ - router.get('/', async (req: Request, res: Response) => { + router.get('/', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; @@ -38,7 +38,7 @@ export function createFuelLogController(dataSource: DataSource): Router { const result = await service.findAll(tenantId, filters, pagination); res.json(result); } catch (error) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -46,7 +46,7 @@ export function createFuelLogController(dataSource: DataSource): Router { * GET /statistics/:assetId * Obtiene estadísticas de combustible para un activo */ - router.get('/statistics/:assetId', async (req: Request, res: Response) => { + router.get('/statistics/:assetId', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; @@ -56,7 +56,7 @@ export function createFuelLogController(dataSource: DataSource): Router { const stats = await service.getAssetStatistics(tenantId, req.params.assetId, fromDate, toDate); res.json(stats); } catch (error) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -64,18 +64,19 @@ export function createFuelLogController(dataSource: DataSource): Router { * GET /:id * Obtiene un registro por ID */ - router.get('/:id', async (req: Request, res: Response) => { + router.get('/:id', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const fuelLog = await service.findById(tenantId, req.params.id); if (!fuelLog) { - return res.status(404).json({ error: 'Registro de combustible no encontrado' }); + res.status(404).json({ error: 'Registro de combustible no encontrado' }); + return; } res.json(fuelLog); } catch (error) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -83,7 +84,7 @@ export function createFuelLogController(dataSource: DataSource): Router { * POST / * Crea un nuevo registro de combustible */ - router.post('/', async (req: Request, res: Response) => { + 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; @@ -91,7 +92,7 @@ export function createFuelLogController(dataSource: DataSource): Router { const fuelLog = await service.create(tenantId, req.body, userId); res.status(201).json(fuelLog); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -99,18 +100,19 @@ export function createFuelLogController(dataSource: DataSource): Router { * DELETE /:id * Elimina un registro de combustible */ - router.delete('/:id', async (req: Request, res: Response) => { + router.delete('/:id', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; const deleted = await service.delete(tenantId, req.params.id); if (!deleted) { - return res.status(404).json({ error: 'Registro de combustible no encontrado' }); + res.status(404).json({ error: 'Registro de combustible no encontrado' }); + return; } res.status(204).send(); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); diff --git a/src/modules/assets/controllers/work-order.controller.ts b/src/modules/assets/controllers/work-order.controller.ts index e475e16..73bef47 100644 --- a/src/modules/assets/controllers/work-order.controller.ts +++ b/src/modules/assets/controllers/work-order.controller.ts @@ -6,7 +6,7 @@ * @module Assets (MAE-015) */ -import { Router, Request, Response } from 'express'; +import { Router, Request, Response, NextFunction } from 'express'; import { DataSource } from 'typeorm'; import { WorkOrderService } from '../services'; @@ -20,7 +20,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { * GET / * Lista órdenes de trabajo con filtros y paginación */ - router.get('/', async (req: Request, res: Response) => { + router.get('/', async (req: Request, res: Response, next: NextFunction): Promise => { try { const tenantId = req.headers['x-tenant-id'] as string; @@ -44,7 +44,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { const result = await service.findAll(tenantId, filters, pagination); res.json(result); } catch (error) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -52,14 +52,14 @@ export function createWorkOrderController(dataSource: DataSource): Router { * GET /statistics * Obtiene estadísticas de órdenes de trabajo */ - router.get('/statistics', async (req: Request, res: Response) => { + 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) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -67,14 +67,14 @@ export function createWorkOrderController(dataSource: DataSource): Router { * GET /by-status * Obtiene órdenes agrupadas por estado */ - router.get('/by-status', async (req: Request, res: Response) => { + 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) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -82,18 +82,19 @@ export function createWorkOrderController(dataSource: DataSource): Router { * GET /by-number/:orderNumber * Obtiene una orden por número */ - router.get('/by-number/:orderNumber', async (req: Request, res: Response) => { + 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) { - return res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + return; } res.json(workOrder); } catch (error) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -101,14 +102,14 @@ export function createWorkOrderController(dataSource: DataSource): Router { * GET /overdue * Lista órdenes vencidas */ - router.get('/overdue', async (req: Request, res: Response) => { + 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) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -116,18 +117,19 @@ export function createWorkOrderController(dataSource: DataSource): Router { * GET /:id * Obtiene una orden de trabajo por ID */ - router.get('/:id', async (req: Request, res: Response) => { + 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) { - return res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + return; } res.json(workOrder); } catch (error) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -135,7 +137,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { * POST / * Crea una nueva orden de trabajo */ - router.post('/', async (req: Request, res: Response) => { + 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; @@ -143,7 +145,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { const workOrder = await service.create(tenantId, req.body, userId); res.status(201).json(workOrder); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -151,19 +153,20 @@ export function createWorkOrderController(dataSource: DataSource): Router { * PUT /:id * Actualiza una orden de trabajo */ - router.put('/:id', async (req: Request, res: Response) => { + 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) { - return res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + return; } res.json(workOrder); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -171,19 +174,20 @@ export function createWorkOrderController(dataSource: DataSource): Router { * DELETE /:id * Elimina una orden de trabajo (soft delete) */ - router.delete('/:id', async (req: Request, res: Response) => { + 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) { - return res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + return; } res.status(204).send(); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -193,19 +197,20 @@ export function createWorkOrderController(dataSource: DataSource): Router { * POST /:id/start * Inicia una orden de trabajo */ - router.post('/:id/start', async (req: Request, res: Response) => { + 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) { - return res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + return; } res.json(workOrder); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -213,7 +218,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { * POST /:id/hold * Pone en espera una orden de trabajo */ - router.post('/:id/hold', async (req: Request, res: Response) => { + 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; @@ -221,12 +226,13 @@ export function createWorkOrderController(dataSource: DataSource): Router { const workOrder = await service.hold(tenantId, req.params.id, reason, userId); if (!workOrder) { - return res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + return; } res.json(workOrder); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -234,19 +240,20 @@ export function createWorkOrderController(dataSource: DataSource): Router { * POST /:id/resume * Reanuda una orden de trabajo en espera */ - router.post('/:id/resume', async (req: Request, res: Response) => { + 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) { - return res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + return; } res.json(workOrder); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -254,19 +261,20 @@ export function createWorkOrderController(dataSource: DataSource): Router { * POST /:id/complete * Completa una orden de trabajo */ - router.post('/:id/complete', async (req: Request, res: Response) => { + 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) { - return res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + return; } res.json(workOrder); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -274,7 +282,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { * POST /:id/cancel * Cancela una orden de trabajo */ - router.post('/:id/cancel', async (req: Request, res: Response) => { + 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; @@ -282,12 +290,13 @@ export function createWorkOrderController(dataSource: DataSource): Router { const workOrder = await service.cancel(tenantId, req.params.id, reason, userId); if (!workOrder) { - return res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + res.status(404).json({ error: 'Orden de trabajo no encontrada' }); + return; } res.json(workOrder); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -297,14 +306,14 @@ export function createWorkOrderController(dataSource: DataSource): Router { * GET /:id/parts * Lista partes usadas en una orden de trabajo */ - router.get('/:id/parts', async (req: Request, res: Response) => { + 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) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -312,7 +321,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { * POST /:id/parts * Agrega una parte a la orden de trabajo */ - router.post('/:id/parts', async (req: Request, res: Response) => { + 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; @@ -320,7 +329,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { const part = await service.addPart(tenantId, req.params.id, req.body, userId); res.status(201).json(part); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -328,19 +337,20 @@ export function createWorkOrderController(dataSource: DataSource): Router { * PUT /:id/parts/:partId * Actualiza una parte de la orden */ - router.put('/:id/parts/:partId', async (req: Request, res: Response) => { + 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) { - return res.status(404).json({ error: 'Parte no encontrada' }); + res.status(404).json({ error: 'Parte no encontrada' }); + return; } res.json(part); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -348,19 +358,20 @@ export function createWorkOrderController(dataSource: DataSource): Router { * DELETE /:id/parts/:partId * Elimina una parte de la orden */ - router.delete('/:id/parts/:partId', async (req: Request, res: Response) => { + 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) { - return res.status(404).json({ error: 'Parte no encontrada' }); + res.status(404).json({ error: 'Parte no encontrada' }); + return; } res.status(204).send(); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -370,7 +381,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { * GET /plans * Lista planes de mantenimiento */ - router.get('/plans/list', async (req: Request, res: Response) => { + 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; @@ -378,7 +389,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { const plans = await service.getMaintenancePlans(tenantId, assetId); res.json(plans); } catch (error) { - res.status(500).json({ error: (error as Error).message }); + next(error); } }); @@ -386,7 +397,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { * POST /plans * Crea un plan de mantenimiento */ - router.post('/plans', async (req: Request, res: Response) => { + 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; @@ -394,7 +405,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { const plan = await service.createMaintenancePlan(tenantId, req.body, userId); res.status(201).json(plan); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); @@ -402,7 +413,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { * POST /plans/:planId/generate * Genera órdenes de trabajo desde un plan */ - router.post('/plans/:planId/generate', async (req: Request, res: Response) => { + 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; @@ -410,7 +421,7 @@ export function createWorkOrderController(dataSource: DataSource): Router { const orders = await service.generateFromPlan(tenantId, req.params.planId, userId); res.status(201).json(orders); } catch (error) { - res.status(400).json({ error: (error as Error).message }); + next(error); } }); diff --git a/src/modules/assets/entities/work-order.entity.ts b/src/modules/assets/entities/work-order.entity.ts index b3b6b85..f72ce16 100644 --- a/src/modules/assets/entities/work-order.entity.ts +++ b/src/modules/assets/entities/work-order.entity.ts @@ -24,6 +24,23 @@ export type MaintenanceType = 'preventive' | 'corrective' | 'predictive' | 'emer export type WorkOrderStatus = 'draft' | 'scheduled' | 'in_progress' | 'on_hold' | 'completed' | 'cancelled'; export type WorkOrderPriority = 'low' | 'medium' | 'high' | 'critical'; +// Const objects for runtime use +export const WorkOrderStatusValues: Record = { + DRAFT: 'draft', + SCHEDULED: 'scheduled', + IN_PROGRESS: 'in_progress', + ON_HOLD: 'on_hold', + COMPLETED: 'completed', + CANCELLED: 'cancelled', +} as const; + +export const WorkOrderPriorityValues: Record = { + LOW: 'low', + MEDIUM: 'medium', + HIGH: 'high', + CRITICAL: 'critical', +} as const; + @Entity('work_orders', { schema: 'assets' }) @Index(['tenantId', 'workOrderNumber'], { unique: true }) @Index(['tenantId', 'assetId']) diff --git a/src/modules/assets/services/asset.service.ts b/src/modules/assets/services/asset.service.ts index 1b5abc8..ea2758f 100644 --- a/src/modules/assets/services/asset.service.ts +++ b/src/modules/assets/services/asset.service.ts @@ -5,7 +5,7 @@ * Logica de negocio para gestion de activos fijos y maquinaria. */ -import { Repository, DataSource, ILike } from 'typeorm'; +import { Repository, DataSource, IsNull } from 'typeorm'; import { Asset, AssetType, AssetStatus, OwnershipType } from '../entities/asset.entity'; import { AssetCategory } from '../entities/asset-category.entity'; import { AssetAssignment } from '../entities/asset-assignment.entity'; @@ -94,7 +94,7 @@ export class AssetService { private categoryRepository: Repository; private assignmentRepository: Repository; - constructor(private dataSource: DataSource) { + constructor(dataSource: DataSource) { this.assetRepository = dataSource.getRepository(Asset); this.categoryRepository = dataSource.getRepository(AssetCategory); this.assignmentRepository = dataSource.getRepository(AssetAssignment); @@ -120,8 +120,8 @@ export class AssetService { const asset = this.assetRepository.create({ tenantId, ...dto, - status: AssetStatus.AVAILABLE, - ownershipType: dto.ownershipType || OwnershipType.OWNED, + status: 'available' as AssetStatus, + ownershipType: dto.ownershipType || ('owned' as OwnershipType), currentBookValue: dto.purchasePrice, createdBy: userId, }); @@ -263,7 +263,7 @@ export class AssetService { async delete(tenantId: string, id: string, userId?: string): Promise { const result = await this.assetRepository.update( { id, tenantId }, - { deletedAt: new Date(), status: AssetStatus.RETIRED, updatedBy: userId } + { deletedAt: new Date(), status: 'retired' as AssetStatus, updatedBy: userId } ); return (result.affected ?? 0) > 0; } @@ -315,7 +315,7 @@ export class AssetService { // Update asset with new project await this.update(tenantId, dto.assetId, { currentProjectId: dto.projectId, - status: AssetStatus.ASSIGNED, + status: 'assigned' as AssetStatus, assignedOperatorId: dto.operatorId, }, userId); @@ -378,7 +378,7 @@ export class AssetService { // Update asset status await this.update(tenantId, assetId, { currentProjectId: undefined, - status: AssetStatus.AVAILABLE, + status: 'available' as AssetStatus, }, userId); return true; @@ -405,7 +405,7 @@ export class AssetService { */ async getCategories(tenantId: string): Promise { return this.categoryRepository.find({ - where: { tenantId, isActive: true, deletedAt: null }, + where: { tenantId, isActive: true, deletedAt: IsNull() }, order: { level: 'ASC', name: 'ASC' }, }); } @@ -425,7 +425,7 @@ export class AssetService { maintenanceDue: number; }> { const [total, byStatusRaw, byTypeRaw, valueResult, maintenanceDue] = await Promise.all([ - this.assetRepository.count({ where: { tenantId, deletedAt: null } }), + this.assetRepository.count({ where: { tenantId, deletedAt: IsNull() } }), this.assetRepository.createQueryBuilder('asset') .select('asset.status', 'status') @@ -484,7 +484,7 @@ export class AssetService { return this.assetRepository.createQueryBuilder('asset') .where('asset.tenant_id = :tenantId', { tenantId }) .andWhere('asset.deleted_at IS NULL') - .andWhere('asset.status != :retired', { retired: AssetStatus.RETIRED }) + .andWhere('asset.status != :retired', { retired: 'retired' }) .andWhere( '(asset.next_maintenance_date <= :today OR asset.current_hours >= asset.next_maintenance_hours OR asset.current_kilometers >= asset.next_maintenance_kilometers)', { today } diff --git a/src/modules/assets/services/fuel-log.service.ts b/src/modules/assets/services/fuel-log.service.ts index b401ed2..081a82e 100644 --- a/src/modules/assets/services/fuel-log.service.ts +++ b/src/modules/assets/services/fuel-log.service.ts @@ -5,7 +5,7 @@ * Logica de negocio para registro de combustible y calculo de rendimiento. */ -import { Repository, DataSource, Between } from 'typeorm'; +import { Repository, DataSource } from 'typeorm'; import { FuelLog } from '../entities/fuel-log.entity'; import { Asset } from '../entities/asset.entity'; @@ -48,7 +48,7 @@ export class FuelLogService { private fuelLogRepository: Repository; private assetRepository: Repository; - constructor(private dataSource: DataSource) { + constructor(dataSource: DataSource) { this.fuelLogRepository = dataSource.getRepository(FuelLog); this.assetRepository = dataSource.getRepository(Asset); } diff --git a/src/modules/assets/services/work-order.service.ts b/src/modules/assets/services/work-order.service.ts index 15391f2..3847807 100644 --- a/src/modules/assets/services/work-order.service.ts +++ b/src/modules/assets/services/work-order.service.ts @@ -5,8 +5,8 @@ * Logica de negocio para ordenes de trabajo de mantenimiento. */ -import { Repository, DataSource, LessThanOrEqual, In } from 'typeorm'; -import { WorkOrder, WorkOrderStatus, WorkOrderPriority, MaintenanceType } from '../entities/work-order.entity'; +import { Repository, DataSource, LessThanOrEqual, In, IsNull } from 'typeorm'; +import { WorkOrder, WorkOrderStatus, WorkOrderPriority, MaintenanceType, WorkOrderStatusValues, WorkOrderPriorityValues } from '../entities/work-order.entity'; import { WorkOrderPart } from '../entities/work-order-part.entity'; import { MaintenanceHistory } from '../entities/maintenance-history.entity'; import { MaintenancePlan } from '../entities/maintenance-plan.entity'; @@ -102,7 +102,7 @@ export class WorkOrderService { private planRepository: Repository; private assetRepository: Repository; - constructor(private dataSource: DataSource) { + constructor(dataSource: DataSource) { this.workOrderRepository = dataSource.getRepository(WorkOrder); this.partRepository = dataSource.getRepository(WorkOrderPart); this.historyRepository = dataSource.getRepository(MaintenanceHistory); @@ -153,8 +153,8 @@ export class WorkOrderService { assetCode: asset.assetCode, assetName: asset.name, maintenanceType: dto.maintenanceType, - priority: dto.priority || WorkOrderPriority.MEDIUM, - status: WorkOrderStatus.DRAFT, + priority: dto.priority || WorkOrderPriorityValues.MEDIUM, + status: WorkOrderStatusValues.DRAFT, title: dto.title, description: dto.description, problemReported: dto.problemReported, @@ -290,15 +290,15 @@ export class WorkOrderService { */ private validateStatusTransition(from: WorkOrderStatus, to: WorkOrderStatus): void { const validTransitions: Record = { - [WorkOrderStatus.DRAFT]: [WorkOrderStatus.SCHEDULED, WorkOrderStatus.IN_PROGRESS, WorkOrderStatus.CANCELLED], - [WorkOrderStatus.SCHEDULED]: [WorkOrderStatus.IN_PROGRESS, WorkOrderStatus.CANCELLED], - [WorkOrderStatus.IN_PROGRESS]: [WorkOrderStatus.ON_HOLD, WorkOrderStatus.COMPLETED, WorkOrderStatus.CANCELLED], - [WorkOrderStatus.ON_HOLD]: [WorkOrderStatus.IN_PROGRESS, WorkOrderStatus.CANCELLED], - [WorkOrderStatus.COMPLETED]: [], - [WorkOrderStatus.CANCELLED]: [], + draft: ['scheduled', 'in_progress', 'cancelled'], + scheduled: ['in_progress', 'cancelled'], + in_progress: ['on_hold', 'completed', 'cancelled'], + on_hold: ['in_progress', 'cancelled'], + completed: [], + cancelled: [], }; - if (!validTransitions[from].includes(to)) { + if (!validTransitions[from]?.includes(to)) { throw new Error(`Invalid status transition from ${from} to ${to}`); } } @@ -310,12 +310,12 @@ export class WorkOrderService { const now = new Date(); switch (newStatus) { - case WorkOrderStatus.IN_PROGRESS: + case 'in_progress': if (!workOrder.actualStartDate) { workOrder.actualStartDate = now; } break; - case WorkOrderStatus.COMPLETED: + case 'completed': workOrder.actualEndDate = now; break; } @@ -325,7 +325,7 @@ export class WorkOrderService { * Start work order (change to in_progress) */ async start(tenantId: string, id: string, userId?: string): Promise { - return this.update(tenantId, id, { status: WorkOrderStatus.IN_PROGRESS }, userId); + return this.update(tenantId, id, { status: 'in_progress' }, userId); } /** @@ -336,7 +336,7 @@ export class WorkOrderService { if (!workOrder) return null; // Update work order - workOrder.status = WorkOrderStatus.COMPLETED; + workOrder.status = 'completed'; workOrder.actualEndDate = new Date(); workOrder.workPerformed = dto.workPerformed; workOrder.findings = dto.findings; @@ -454,7 +454,7 @@ export class WorkOrderService { const [total, byStatusRaw, byTypeRaw, pendingCount, inProgressCount, completedThisMonth, costResult] = await Promise.all([ - this.workOrderRepository.count({ where: { tenantId, deletedAt: null } }), + this.workOrderRepository.count({ where: { tenantId, deletedAt: IsNull() } }), this.workOrderRepository.createQueryBuilder('wo') .select('wo.status', 'status') @@ -473,23 +473,23 @@ export class WorkOrderService { .getRawMany(), this.workOrderRepository.count({ - where: { tenantId, status: WorkOrderStatus.DRAFT, deletedAt: null }, + where: { tenantId, status: 'draft' as WorkOrderStatus, deletedAt: IsNull() }, }), this.workOrderRepository.count({ - where: { tenantId, status: WorkOrderStatus.IN_PROGRESS, deletedAt: null }, + where: { tenantId, status: 'in_progress' as WorkOrderStatus, deletedAt: IsNull() }, }), this.workOrderRepository.createQueryBuilder('wo') .where('wo.tenant_id = :tenantId', { tenantId }) - .andWhere('wo.status = :status', { status: WorkOrderStatus.COMPLETED }) + .andWhere('wo.status = :status', { status: 'completed' as WorkOrderStatus }) .andWhere('wo.actual_end_date >= :startOfMonth', { startOfMonth }) .getCount(), this.workOrderRepository.createQueryBuilder('wo') .select('SUM(wo.total_cost)', 'total') .where('wo.tenant_id = :tenantId', { tenantId }) - .andWhere('wo.status = :status', { status: WorkOrderStatus.COMPLETED }) + .andWhere('wo.status = :status', { status: 'completed' as WorkOrderStatus }) .andWhere('wo.actual_end_date >= :startOfMonth', { startOfMonth }) .getRawOne(), ]); @@ -520,18 +520,18 @@ export class WorkOrderService { */ async getByStatus(tenantId: string): Promise> { const workOrders = await this.workOrderRepository.find({ - where: { tenantId, deletedAt: null }, + where: { tenantId, deletedAt: IsNull() }, relations: ['asset'], order: { requestedDate: 'DESC' }, }); const grouped: Record = { - [WorkOrderStatus.DRAFT]: [], - [WorkOrderStatus.SCHEDULED]: [], - [WorkOrderStatus.IN_PROGRESS]: [], - [WorkOrderStatus.ON_HOLD]: [], - [WorkOrderStatus.COMPLETED]: [], - [WorkOrderStatus.CANCELLED]: [], + draft: [], + scheduled: [], + in_progress: [], + on_hold: [], + completed: [], + cancelled: [], }; for (const wo of workOrders) { @@ -548,9 +548,9 @@ export class WorkOrderService { const workOrder = await this.findById(tenantId, id); if (!workOrder) return null; - this.validateStatusTransition(workOrder.status, WorkOrderStatus.ON_HOLD); + this.validateStatusTransition(workOrder.status, 'on_hold' as WorkOrderStatus); - workOrder.status = WorkOrderStatus.ON_HOLD; + workOrder.status = 'on_hold' as WorkOrderStatus; workOrder.notes = reason ? `${workOrder.notes || ''}\n[ON HOLD] ${reason}` : workOrder.notes; workOrder.updatedBy = userId; @@ -564,9 +564,9 @@ export class WorkOrderService { const workOrder = await this.findById(tenantId, id); if (!workOrder) return null; - this.validateStatusTransition(workOrder.status, WorkOrderStatus.IN_PROGRESS); + this.validateStatusTransition(workOrder.status, 'in_progress' as WorkOrderStatus); - workOrder.status = WorkOrderStatus.IN_PROGRESS; + workOrder.status = 'in_progress' as WorkOrderStatus; workOrder.updatedBy = userId; return this.workOrderRepository.save(workOrder); @@ -579,9 +579,9 @@ export class WorkOrderService { const workOrder = await this.findById(tenantId, id); if (!workOrder) return null; - this.validateStatusTransition(workOrder.status, WorkOrderStatus.CANCELLED); + this.validateStatusTransition(workOrder.status, 'cancelled' as WorkOrderStatus); - workOrder.status = WorkOrderStatus.CANCELLED; + workOrder.status = 'cancelled' as WorkOrderStatus; workOrder.notes = reason ? `${workOrder.notes || ''}\n[CANCELLED] ${reason}` : workOrder.notes; workOrder.updatedBy = userId; @@ -612,7 +612,7 @@ export class WorkOrderService { /** * Update a part */ - async updatePart(tenantId: string, partId: string, dto: Partial, userId?: string): Promise { + async updatePart(tenantId: string, partId: string, dto: Partial, _userId?: string): Promise { const part = await this.partRepository.findOne({ where: { id: partId, tenantId } }); if (!part) return null; @@ -625,7 +625,6 @@ export class WorkOrderService { if (part.unitCost && part.quantityUsed) { part.totalCost = part.unitCost * part.quantityUsed; } - part.updatedBy = userId; const savedPart = await this.partRepository.save(part); await this.recalculatePartsCost(part.workOrderId); @@ -636,7 +635,7 @@ export class WorkOrderService { /** * Remove a part from work order */ - async removePart(tenantId: string, partId: string, userId?: string): Promise { + async removePart(tenantId: string, partId: string, _userId?: string): Promise { const part = await this.partRepository.findOne({ where: { id: partId, tenantId } }); if (!part) return false; @@ -657,7 +656,7 @@ export class WorkOrderService { where: { tenantId, deletedAt: undefined, - status: In([WorkOrderStatus.DRAFT, WorkOrderStatus.SCHEDULED, WorkOrderStatus.IN_PROGRESS]), + status: In(['draft' as WorkOrderStatus, 'scheduled' as WorkOrderStatus, 'in_progress' as WorkOrderStatus]), scheduledEndDate: LessThanOrEqual(now), }, relations: ['asset'],