- Migracion 046: tablas obligacion_asignaciones y tarea_asignaciones - Servicio y controller de asignaciones (CRUD + listados) - Fix: enviar correo welcome al invitar usuario nuevo - Fix: quitar JOIN users de queries tenant (usar Prisma en BD central) - Fix: req.params.obligacionId correcto en asignaciones controller - Fix: orden rutas estaticas antes de dinamicas en cartera.routes - Fix: owner/cfo ven todas las asignaciones en getAsignacionesPorSupervisor - Fix: validar que entidad pertenezca a cartera padre en subcartera - Nuevo endpoint GET /carteras/asignaciones/sin-asignar - Nuevo endpoint GET /tareas/mis-tareas
138 lines
4.1 KiB
TypeScript
138 lines
4.1 KiB
TypeScript
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<void> {
|
|
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); }
|
|
}
|