feat: recordatorios periódicos; supervisor invita auxiliares; owner edita usuarios; precios planes y MSI
This commit is contained in:
@@ -13,6 +13,8 @@ const createRecordatorioSchema = z.object({
|
||||
fechaLimite: z.string().min(8), // ISO date o yyyy-mm-dd
|
||||
notas: z.string().max(2000).optional(),
|
||||
privado: z.boolean().optional(),
|
||||
recurrencia: z.enum(['unica', 'mensual', 'bimestral', 'trimestral', 'anual']).default('unica'),
|
||||
fechaFin: z.string().min(8).optional(),
|
||||
});
|
||||
|
||||
const updateRecordatorioSchema = z.object({
|
||||
@@ -107,7 +109,7 @@ export async function createRecordatorio(req: Request, res: Response, next: Next
|
||||
const evento = await recordatoriosService.createRecordatorio(
|
||||
req.tenantPool!,
|
||||
req.user!.userId,
|
||||
{ ...data, tipo: 'custom', recurrencia: 'unica' }
|
||||
{ ...data, tipo: 'custom' }
|
||||
);
|
||||
|
||||
res.status(201).json(evento);
|
||||
|
||||
@@ -260,7 +260,11 @@ export async function removeAuxiliar(req: Request, res: Response, next: NextFunc
|
||||
// Supervisores available (for dropdown)
|
||||
export async function getSupervisores(req: Request, res: Response, next: NextFunction) {
|
||||
try {
|
||||
const supervisores = await carteraService.getSupervisores(req.tenantPool!, req.user!.tenantId);
|
||||
const allSupervisores = await carteraService.getSupervisores(req.tenantPool!, req.user!.tenantId);
|
||||
// Un supervisor solo se ve a si mismo en el dropdown (no puede asignar a otro supervisor)
|
||||
const supervisores = isSupervisor(req)
|
||||
? allSupervisores.filter(s => s.userId === req.user!.userId)
|
||||
: allSupervisores;
|
||||
return res.json({ data: supervisores });
|
||||
} catch (err) { return next(err); }
|
||||
}
|
||||
|
||||
@@ -70,14 +70,24 @@ export async function inviteUsuario(req: Request, res: Response, next: NextFunct
|
||||
}
|
||||
const data = inviteSchema.parse(req.body);
|
||||
|
||||
// Los supervisores solo pueden invitar clientes
|
||||
if (req.user!.role === 'supervisor' && data.role !== 'cliente') {
|
||||
throw new AppError(403, 'Los supervisores solo pueden invitar clientes');
|
||||
// Los supervisores solo pueden invitar clientes y auxiliares
|
||||
if (req.user!.role === 'supervisor' && !['cliente', 'auxiliar'].includes(data.role)) {
|
||||
throw new AppError(403, 'Los supervisores solo pueden invitar clientes y auxiliares');
|
||||
}
|
||||
|
||||
// Validate: auxiliar requires a supervisor
|
||||
if (data.role === 'auxiliar' && !data.supervisorUserId) {
|
||||
throw new AppError(400, 'Debes asignar un supervisor al auxiliar');
|
||||
// Un supervisor que invita un auxiliar se asigna a sí mismo por defecto
|
||||
if (req.user!.role === 'supervisor') {
|
||||
data.supervisorUserId = req.user!.userId;
|
||||
} else {
|
||||
throw new AppError(400, 'Debes asignar un supervisor al auxiliar');
|
||||
}
|
||||
}
|
||||
|
||||
// Un supervisor solo puede asignar auxiliares a sí mismo
|
||||
if (req.user!.role === 'supervisor' && data.role === 'auxiliar' && data.supervisorUserId !== req.user!.userId) {
|
||||
throw new AppError(403, 'Solo puedes asignar auxiliares a tu propia supervisión');
|
||||
}
|
||||
|
||||
const usuario = await usuariosService.inviteUsuario(req.user!.tenantId, data);
|
||||
|
||||
Reference in New Issue
Block a user