import type { Request, Response, NextFunction } from 'express'; import { z } from 'zod'; import * as asignacionesService from '../services/asignaciones.service.js'; import { getEntidadesVisibles } from '../utils/entidades-visibles.js'; import { AppError } from '../middlewares/error.middleware.js'; /** * Valida que el auxiliar pertenezca al supervisor (o que el caller sea owner). * Owner puede asignar a cualquier auxiliar del tenant. */ async function validarAuxiliarDelSupervisor( pool: import('pg').Pool, supervisorUserId: string, auxiliarUserId: string, callerRole: string, ): Promise { if (callerRole === 'owner') return; const { rows } = await pool.query( `SELECT 1 FROM auxiliar_supervisores WHERE auxiliar_user_id = $1 AND supervisor_user_id = $2 LIMIT 1`, [auxiliarUserId, supervisorUserId], ); if (rows.length === 0) { throw new AppError(403, 'El auxiliar no pertenece a tu equipo'); } } // ── Obligaciones ── export async function asignarObligacion(req: Request, res: Response, next: NextFunction) { try { const obligacionId = String(req.params.obligacionId); const schema = z.object({ auxiliarUserId: z.string().uuid() }); const { auxiliarUserId } = schema.parse(req.body); await validarAuxiliarDelSupervisor( req.tenantPool!, req.user!.userId, auxiliarUserId, req.user!.role, ); await asignacionesService.asignarObligacion( req.tenantPool!, obligacionId, auxiliarUserId, req.user!.userId, ); res.json({ message: 'Obligación asignada' }); } catch (err: any) { if (err instanceof z.ZodError) return next(new AppError(400, err.errors[0].message)); next(err); } } export async function desasignarObligacion(req: Request, res: Response, next: NextFunction) { try { const obligacionId = String(req.params.obligacionId); await asignacionesService.desasignarObligacion(req.tenantPool!, obligacionId); res.json({ message: 'Asignación de obligación eliminada' }); } catch (err) { next(err); } } // ── Tareas ── export async function asignarTarea(req: Request, res: Response, next: NextFunction) { try { const tareaId = String(req.params.id); const schema = z.object({ auxiliarUserId: z.string().uuid() }); const { auxiliarUserId } = schema.parse(req.body); await validarAuxiliarDelSupervisor( req.tenantPool!, req.user!.userId, auxiliarUserId, req.user!.role, ); await asignacionesService.asignarTarea( req.tenantPool!, tareaId, auxiliarUserId, req.user!.userId, ); res.json({ message: 'Tarea asignada' }); } catch (err: any) { if (err instanceof z.ZodError) return next(new AppError(400, err.errors[0].message)); next(err); } } export async function desasignarTarea(req: Request, res: Response, next: NextFunction) { try { const tareaId = String(req.params.id); await asignacionesService.desasignarTarea(req.tenantPool!, tareaId); res.json({ message: 'Asignación de tarea eliminada' }); } catch (err) { next(err); } } // ── Listados ── export async function listPorSupervisor(req: Request, res: Response, next: NextFunction) { try { const data = await asignacionesService.getAsignacionesPorSupervisor( req.tenantPool!, req.user!.userId, req.user!.role, ); res.json(data); } catch (err) { next(err); } } export async function listPorAuxiliar(req: Request, res: Response, next: NextFunction) { try { const data = await asignacionesService.getAsignacionesPorAuxiliar( req.tenantPool!, req.user!.userId, ); res.json(data); } catch (err) { next(err); } } export async function listSinAsignar(req: Request, res: Response, next: NextFunction) { try { const entidadIds = await getEntidadesVisibles(req.tenantPool!, req.user!.userId, req.user!.role); const [obligaciones, tareas] = await Promise.all([ asignacionesService.getObligacionesSinAsignar(req.tenantPool!, entidadIds), asignacionesService.getTareasSinAsignar(req.tenantPool!, entidadIds), ]); res.json({ obligaciones, tareas }); } catch (err) { next(err); } }