Se corrigió envio de correos y creacion de controller servicio

This commit is contained in:
2025-10-21 14:54:31 -06:00
parent 86b0096350
commit de24bdeec0
11 changed files with 709 additions and 432 deletions
+39
View File
@@ -0,0 +1,39 @@
import {
Controller,
Post,
Body,
Get,
Query,
UnauthorizedException,
} from '@nestjs/common';
import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
// ===================== LOGIN =====================
@Post('login')
async login(
@Body('usuario') usuario: string,
@Body('password') password: string,
) {
try {
const token = await this.authService.login(usuario, password);
return token; // { access_token: '...' }
} catch (error) {
throw new UnauthorizedException(error.message);
}
}
// ===================== VERIFICAR TOKEN =====================
@Get('verify')
async verify(@Query('token') token: string) {
try {
const payload = await this.authService.jwtVerificar(token);
return { valid: true, payload };
} catch (error) {
throw new UnauthorizedException(error.message);
}
}
}
-254
View File
@@ -18,44 +18,11 @@ import * as argon2 from 'argon2';
@Injectable()
export class AuthService {
private readonly carreras: Record<string, number> = {
'LIC. EN ACTUARIA': 64,
'LIC. EN ARQUITECTURA': 70,
'LIC. EN CIENCIAS POLITICAS Y ADMON PUB': 67,
'LIC. EN CIENCIAS POLITICAS Y ADMON.PUBL.': 67,
'LIC. EN COMUNICACION': 67,
'LIC. EN DERECHO': 68,
'LIC. EN DERECHO (SUA)': 67,
'LIC. EN DISEÑO GRAFICO': 70,
'LIC. EN ECONOMIA': 66,
'LIC. EN ENSEÑANZA DE INGLES': 69,
'LIC. EN FILOSOFIA': 70,
'LIC. EN HISTORIA': 70,
'LIC. EN INGENIERIA CIVIL': 70,
'LIC. EN LENGUA Y LITERATURA HISPANICAS': 70,
'LIC. EN MAT. APLICADAS Y COMPUTACION': 66,
'LIC. EN MATEMATICAS APLICADAS Y COMP.': 66,
'LIC. EN PEDAGOGÍA': 70,
'LIC. EN PERIODISMO Y COMUNICACION COL.': 70,
'LIC. EN RELACIONES INTERNACIONALES': 70,
'LIC. EN RELACIONES INTERNACIONALES (SUA)': 70,
'LIC. EN SOCIOLOGIA': 68,
'LIC. ENSEÑANZA DE ALEMÁN (LENG. EXTRANJS': 70,
'LIC. ENSEÑANZA DE ESPAÑOL(LENG. EXTRANJ)': 70,
'LIC. ENSEÑANZA DE INGLÉS(LENG. EXTRANJE)': 70,
'LIC. ENSEÑANZA DE ITALIANO(LENG. EXTRANJ': 70,
};
constructor(
private readonly jwtService: JwtService,
@InjectRepository(Usuario)
private readonly userRepo: Repository<Usuario>,
@InjectRepository(Carrera)
private readonly carreraRepo: Repository<Carrera>,
@InjectRepository(Servicio)
private readonly servicioRepo: Repository<Servicio>,
private readonly gmail: gmail,
) {}
async jwtVerificar(token: string) {
@@ -117,225 +84,4 @@ export class AuthService {
);
return token;
}
async escolares(numeroDeCuenta: string) {
let response;
try {
response = await axios.post(
`${process.env.ESCOLARES}${numeroDeCuenta}`,
{ password: process.env.ESCOLARES_PASS },
{
headers: {
'Content-Type': 'application/json', // Tipo de contenido
Authorization: `Bearer ${process.env.API_TOKEN}`, // Header de auth
},
},
);
} catch (error) {
throw new UnauthorizedException(
'No se pudo conectar con el servicio de escolares',
);
}
if (
!response.data.nombre ||
!response.data.carrconst ||
!response.data.avance
) {
throw new Error(
'El alumno no cumple con los requisitos para realizar el Servicio Social. Si cree que esto es erróneo comunícate al Departamento de Servicio Social y Bolsa de Trabajo.',
);
}
interface AlumnoDTO {
nombre: string;
creditos: number;
carrera: string;
idCarrera?: number;
}
let alumno: AlumnoDTO = {
nombre: response.data.nombre.trim(),
creditos: response.data.avance,
carrera: response.data.carrconst.trim(),
};
if (alumno.creditos < this.carreras[alumno.carrera]) {
throw new Error('Este alumno no cuenta con los créditos necesarios.');
}
while (alumno.nombre.search('‘') != -1 && alumno.nombre.search('Ã') != -1) {
alumno.nombre = alumno.nombre.replace('Ã', 'Ñ');
alumno.nombre = alumno.nombre.replace('‘', '');
}
let carrera = await this.carreraRepo.findOne({
where: { carrera: alumno.carrera },
});
if (!carrera) {
let carr = this.carreraRepo.create({ carrera: alumno.carrera });
carrera = await this.carreraRepo.save(carr);
}
alumno.idCarrera = carrera.idCarrera;
let alum = await this.userRepo.findOne({
where: { usuario: numeroDeCuenta },
});
if (!alum) {
let nuevoUsuario = this.userRepo.create({
usuario: numeroDeCuenta,
nombre: alumno.nombre,
activo: true,
tipoUsuario: { idTipoUsuario: 3 },
});
alum = await this.userRepo.save(nuevoUsuario);
}
return { ...alumno, idUsuario: alum.idUsuario };
}
async newPasswordAlumno(idServicio: number) {
const password = this.generarPassword();
// Buscar el servicio junto con el usuario
const servicio = await this.servicioRepo.findOne({
where: { idServicio },
relations: ['usuario'], // asegura que traiga la relación
});
if (!servicio) {
throw new NotFoundException('No existe este Servicio Social.');
}
let usuario = servicio.usuario;
if (usuario.tipoUsuario.idTipoUsuario !== 3) {
throw new BadRequestException('Este usuario no es de tipo alumno.');
}
// Preparar correo
let correo = preRegistro(password, usuario.nombre);
// Enviar correo
await this.gmail.sendMail({
subject: correo.subject,
to: servicio.correo,
text: correo.msj,
});
// Actualizar contraseña en la DB
usuario.password = await this.encriptar(password);
await this.userRepo.save(usuario);
return {
message: 'Se envió un correo con una contraseña nueva al alumno.',
};
}
async newPasswordResponsable(idUsuario: number) {
const password = this.generarPassword();
// Buscar usuario
const usuario = await this.userRepo.findOne({ where: { idUsuario } });
if (!usuario) {
throw new NotFoundException('No existe este Usuario.');
}
if (usuario.tipoUsuario.idTipoUsuario !== 2) {
throw new BadRequestException('Este usuario no es de tipo responsable.');
}
// Preparar correo
const correo = enviarSec(password, usuario.usuario, usuario.nombre);
// Enviar correo
await this.gmail.sendMail({
subject: correo.subject,
to: usuario.usuario,
text: correo.msj,
});
// Actualizar contraseña en la DB
usuario.password = await this.encriptar(password);
await this.userRepo.save(usuario);
return {
message: 'Se envió un correo con una contraseña nueva al responsable.',
};
}
async findResponsable(idUsuario: number) {
return await this.userRepo.findOne({ where: { idUsuario } });
}
async findResponsables(pagina: number, nombre: string, correo: string) {
const [responsables, count] = await this.userRepo.findAndCount({
where: {
usuario: Like(`%${correo}%`),
nombre: Like(`%${nombre}%`),
tipoUsuario: { idTipoUsuario: 2 },
},
select: ['idUsuario', 'usuario', 'nombre', 'activo'],
take: 25,
skip: 25 * (pagina - 1),
});
return { count: count, responsables: responsables };
}
async actualizarResponsable(
idUsuario: number,
correo?: string,
nombre?: string,
) {
const dataUpdate: Partial<Usuario> = {};
// Buscar responsable
const responsable = await this.userRepo.findOne({ where: { idUsuario } });
if (!responsable) {
throw new NotFoundException(
'No existe este responsable en la base de datos.',
);
}
if (responsable.tipoUsuario.idTipoUsuario !== 2) {
throw new BadRequestException(
'Este usuario no es un responsable de programa.',
);
}
if (correo) {
const yaUsado = await this.userRepo.findOne({
where: { usuario: correo, idUsuario: Not(idUsuario) },
});
if (yaUsado) {
throw new BadRequestException(
'No se puede asignar este correo a esta cuenta porque está siendo usado por otro responsable.',
);
}
dataUpdate.usuario = correo;
}
// Validar nombre si se envía
if (nombre) {
dataUpdate.nombre = nombre;
}
// Verificar que haya algo para actualizar
if (Object.keys(dataUpdate).length === 0) {
throw new BadRequestException('No se ha enviado nada para actualizar.');
}
// Actualizar en la base
await this.userRepo.update(idUsuario, dataUpdate);
return {
message:
'Se actualizó la información de este responsable de programas correctamente.',
};
}
}
+5 -3
View File
@@ -4,6 +4,7 @@ import {
IsOptional,
IsISO8601,
ValidateNested,
IsDate,
} from 'class-validator';
import { Type } from 'class-transformer';
@@ -33,10 +34,11 @@ export class SendMailDto {
text: string;
@IsString()
html: string;
@IsOptional()
html?: string;
@IsISO8601()
fecha_recibido: string;
@IsDate()
fecha_recibido: Date;
@IsOptional()
@IsArray()
+4 -4
View File
@@ -12,7 +12,7 @@ import { Usuario } from 'src/usuario/entities/usuario.entity';
import { ValidacionService } from 'src/helpers.services/validacion.service';
import { AuthService } from 'src/auth/auth.service';
import { gmail } from 'src/helpers.services/gmail.service';
import { SendCorreoDto } from 'src/helpers.services/dto/send-email.dto';
import { SendMailDto } from 'src/helpers.services/dto/send-email.dto';
@Injectable()
export class ProgramaService {
@@ -83,11 +83,11 @@ export class ProgramaService {
},
});
const sendCorreoDto: SendCorreoDto = {
const sendCorreoDto: SendMailDto = {
to: prog.correo,
subject: correoInfo.subject,
text: correoInfo.msj,
html: '',
fecha_recibido: new Date(),
};
if (!process.env.TOKEN_GMAIL) {
@@ -97,7 +97,7 @@ export class ProgramaService {
try {
responsable = await this.usuarioRepo.save(responsable);
await this.gmail.sendMail(sendCorreoDto);
await this.gmail.enviarCorreo(sendCorreoDto);
mensaje += `Responsable creado con ID ${responsable.idUsuario} y correo ${responsable.usuario}\n`;
} catch (err) {
mensaje += `Error creando responsable: ${err.message}\n`;
+3 -3
View File
@@ -19,15 +19,15 @@ export class UpdateServicioDto {
@IsOptional()
@IsDateString({}, { message: 'fechaInicio debe ser una fecha válida' })
fechaInicio?: string;
fechaInicio?: Date;
@IsOptional()
@IsDateString({}, { message: 'fechaFin debe ser una fecha válida' })
fechaFin?: string;
fechaFin?: Date;
@IsOptional()
@IsDateString({}, { message: 'fechaNacimiento debe ser una fecha válida' })
fechaNacimiento?: string;
fechaNacimiento?: Date;
@IsOptional()
@IsString({ message: 'direccion debe ser texto' })
+142 -18
View File
@@ -1,34 +1,158 @@
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import {
Controller,
Get,
Post,
Put,
Body,
Query,
UploadedFile,
UploadedFiles,
UseGuards,
UseInterceptors,
Res,
ParseIntPipe,
} from '@nestjs/common';
import {
FileInterceptor,
FileFieldsInterceptor,
} from '@nestjs/platform-express';
import { ServicioService } from './servicio.service';
import { CreateServicioDto } from './dto/create-servicio.dto';
import { UpdateServicioDto } from './dto/update-servicio.dto';
import { AuthGuard } from '@nestjs/passport';
import { CrearServicioDto } from './dto/create-servicio.dto';
import { Response } from 'express';
@Controller('servicio')
export class ServicioController {
constructor(private readonly servicioService: ServicioService) {}
@Post()
create(@Body() createServicioDto: CreateServicioDto) {
return this.servicioService.create(createServicioDto);
// ------------------ POST ------------------
@Post('nuevo')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('cartaAceptacion'))
async crearServicio(
@UploadedFile() file: Express.Multer.File,
@Body('alumno') alumnoJson: string,
) {
const dto: CrearServicioDto = JSON.parse(alumnoJson);
return await this.servicioService.registrarNuevoServicio(dto, file);
}
@Get()
findAll() {
return this.servicioService.findAll();
// ------------------ GET ------------------
@Get('admin')
@UseGuards(AuthGuard('jwt'))
async obtenerAdmin(@Query() query: any) {
return this.servicioService.obtenerDetalleServicio(query.idServicio);
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.servicioService.findOne(+id);
@Get('alumno')
@UseGuards(AuthGuard('jwt'))
async obtenerAlumno(@Query('idUsuario', ParseIntPipe) idUsuario: number) {
return this.servicioService.obtenerServicioAlumno(idUsuario);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateServicioDto: UpdateServicioDto) {
return this.servicioService.update(+id, updateServicioDto);
@Get('gustavo_baz_prada')
@UseGuards(AuthGuard('jwt'))
async gustavoBaz(
@Query('year', ParseIntPipe) year: number,
@Res() res: Response,
) {
const path = await this.servicioService.generarGustavoBazPrada(year);
return res.download(path);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.servicioService.remove(+id);
@Get('reporte')
@UseGuards(AuthGuard('jwt'))
async generarReporte(@Query() query: any, @Res() res: Response) {
const path = await this.servicioService.generarGustavoBazPrada(query.year);
return res.download(path);
}
// ------------------ PUT ------------------
@Put('cancelar')
@UseGuards(AuthGuard('jwt'))
async cancelar(@Body() body: { idServicio: number; mensaje: string }) {
return this.servicioService.cancelarServicio(body);
}
@Put('carta_aceptacion')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('cartaAceptacion'))
async subirCartaAceptacion(
@UploadedFile() file: Express.Multer.File,
@Body('data') dataJson: string,
) {
const body = JSON.parse(dataJson);
return this.servicioService.cartaTermino(body.idServicio, file);
}
@Put('carta_termino')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('cartaTermino'))
async subirCartaTermino(
@UploadedFile() file: Express.Multer.File,
@Body('data') dataJson: string,
) {
const body = JSON.parse(dataJson);
return this.servicioService.subirCartaTermino(body.idServicio, file);
}
@Put('informe_global')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('informeGlobal'))
async subirInformeGlobal(
@UploadedFile() file: Express.Multer.File,
@Body('data') dataJson: string,
) {
const body = JSON.parse(dataJson);
return this.servicioService.subirInformeGlobal(body.idServicio, file);
}
@Put('liberacion')
@UseGuards(AuthGuard('jwt'))
async liberarServicio(
@Body('idServicio', ParseIntPipe) idServicio: number,
@Body('vistoBuenoAcatlan') vistoBuenoAcatlan: boolean,
) {
return this.servicioService.liberarServicio(idServicio, vistoBuenoAcatlan);
}
@Put('rechazar_aceptacion')
@UseGuards(AuthGuard('jwt'))
async rechazarAceptacion(
@Body() body: { idServicio: number; mensaje: string },
) {
return this.servicioService.rechazar(body.idServicio, body.mensaje);
}
@Put('rechazar_informe')
@UseGuards(AuthGuard('jwt'))
async rechazarInforme(@Body() body: { idServicio: number; mensaje: string }) {
return this.servicioService.rechazarInforme(body.idServicio, body.mensaje);
}
@Put('rechazar_termino')
@UseGuards(AuthGuard('jwt'))
async rechazarTermino(@Body() body: { idServicio: number; mensaje: string }) {
return this.servicioService.rechazarTermino(body.idServicio, body.mensaje);
}
@Put('update')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(
FileFieldsInterceptor([
{ name: 'cartaAceptacion', maxCount: 1 },
{ name: 'cartaTermino', maxCount: 1 },
{ name: 'informeGlobal', maxCount: 1 },
]),
)
async actualizarServicio(
@UploadedFiles() files: Record<string, Express.Multer.File[]>,
@Body('data') dataJson: string,
) {
const data = JSON.parse(dataJson);
return this.servicioService.update(data, files);
}
}
+231 -66
View File
@@ -11,7 +11,6 @@ import { Servicio } from './entities/servicio.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity';
import { ValidacionService } from 'src/helpers.services/validacion.service';
import { gmail } from 'src/helpers.services/gmail.service';
import { SendCorreoDto } from 'src/helpers.services/dto/send-email.dto';
import { DriveService } from 'src/drive/drive.service';
import moment from 'moment';
import { ArchivoService } from 'src/helpers.services/archivo.service';
@@ -22,6 +21,8 @@ import { AuthService } from 'src/auth/auth.service';
import { RegistroValidadoDto } from './dto/registro-validado.dto';
import * as fs from 'fs';
import { ServiciosAdminDto } from './dto/servicios-admin.dto';
import { ServiciosResponsableDto } from './dto/servicios-responsable.dto';
import { SendMailDto } from 'src/helpers.services/dto/send-email.dto';
@Injectable()
export class ServicioService {
@@ -211,7 +212,7 @@ export class ServicioService {
const correoAlumno = canceladoAlumno(mensaje, alumno.nombre);
const correoResponsable = canceladoResponsable(mensaje, alumno.nombre);
const sendCorreoAlumnoDto: SendCorreoDto = {
const sendCorreoAlumnoDto: SendMailDto = {
to: servicio.correo,
subject: correoAlumno.subject,
fecha_recibido: new Date(),
@@ -220,7 +221,7 @@ export class ServicioService {
adjuntos: undefined,
};
const sendCorreoResponsableDto: SendCorreoDto = {
const sendCorreoResponsableDto: SendMailDto = {
to: responsable.usuario,
subject: correoResponsable.subject,
fecha_recibido: new Date(),
@@ -234,17 +235,11 @@ export class ServicioService {
}
if (servicio.correo) {
await this.gmailService.send(
sendCorreoAlumnoDto,
process.env.TOKEN_GMAIL,
);
await this.gmailService.enviarCorreo(sendCorreoAlumnoDto);
}
if (responsable.usuario) {
await this.gmailService.send(
sendCorreoResponsableDto,
process.env.TOKEN_GMAIL,
);
await this.gmailService.enviarCorreo(sendCorreoResponsableDto);
}
// Desactivar al usuario (alumno)
@@ -588,16 +583,12 @@ export class ServicioService {
const correoAlumno = terminoValidado(servicio.usuario.nombre);
if (servicio.correo) {
await this.gmailService.sendMail(
{
to: servicio.correo,
subject: correoAlumno.subject,
text: correoAlumno.msj,
html: '',
fecha_recibido: new Date(),
},
process.env.TOKEN_GMAIL,
);
await this.gmailService.enviarCorreo({
to: servicio.correo,
subject: correoAlumno.subject,
text: correoAlumno.msj,
fecha_recibido: new Date(),
});
}
break;
@@ -763,11 +754,12 @@ export class ServicioService {
emailResponsable = servicio.programa.usuario.usuario; // correo del responsable
// Enviar correo al alumno
await this.gmailService.enviarCorreo(
correoAlumno.subject,
servicio.correo,
correoAlumno.msj,
);
await this.gmailService.enviarCorreo({
subject: correoAlumno.subject,
to: servicio.correo,
text: correoAlumno.msj,
fecha_recibido: new Date(),
});
break;
case 2:
@@ -796,11 +788,12 @@ export class ServicioService {
}
// Enviar correo al responsable
await this.gmailService.enviarCorreo(
correoResponsable.subject,
emailResponsable,
correoResponsable.msj,
);
await this.gmailService.enviarCorreo({
subject: correoResponsable.subject,
to: emailResponsable,
text: correoResponsable.msj,
fecha_recibido: new Date(),
});
// Actualizar estado del servicio
await this.servicioRepo.update(idServicio, {
@@ -836,11 +829,12 @@ export class ServicioService {
);
emailResponsable = servicio.programa.usuario.usuario; // correo responsable
await this.gmailService.enviarCorreo(
correoAlumno.subject,
servicio.correo,
correoAlumno.msj,
);
await this.gmailService.enviarCorreo({
subject: correoAlumno.subject,
to: servicio.correo,
text: correoAlumno.msj,
fecha_recibido: new Date(),
});
break;
}
@@ -874,11 +868,12 @@ export class ServicioService {
}
// Enviar correo al responsable
await this.gmailService.enviarCorreo(
correoResponsable.subject,
emailResponsable,
correoResponsable.msj,
);
await this.gmailService.enviarCorreo({
subject: correoResponsable.subject,
to: emailResponsable,
text: correoResponsable.msj,
fecha_recibido: new Date(),
});
// Actualizar estado del servicio
await this.servicioRepo.update(idServicio, {
@@ -916,11 +911,12 @@ export class ServicioService {
);
emailResponsable = servicio.programa.usuario.usuario;
await this.gmailService.enviarCorreo(
correoAlumno.subject,
servicio.correo,
correoAlumno.msj,
);
await this.gmailService.enviarCorreo({
subject: correoAlumno.subject,
to: servicio.correo,
text: correoAlumno.msj,
fecha_recibido: new Date(),
});
break;
}
@@ -954,11 +950,12 @@ export class ServicioService {
}
// Enviar correo al responsable
await this.gmailService.enviarCorreo(
correoResponsable.subject,
emailResponsable,
correoResponsable.msj,
);
await this.gmailService.enviarCorreo({
subject: correoResponsable.subject,
to: emailResponsable,
text: correoResponsable.msj,
fecha_recibido: new Date(),
});
// Actualizar el servicio
await this.servicioRepo.update(idServicio, {
@@ -988,11 +985,12 @@ export class ServicioService {
case 1:
correoData = preRegistro(password, servicio.usuario.nombre);
idUsuario = servicio.usuario.idUsuario;
await this.gmailService.enviarCorreo(
correoData.subject,
servicio.correo,
correoData.msj,
);
await this.gmailService.enviarCorreo({
subject: correoData.subject,
to: servicio.correo,
text: correoData.msj,
fecha_recibido: new Date(),
});
break;
case 2:
@@ -1061,11 +1059,12 @@ export class ServicioService {
servicio.usuario.nombre,
);
emailResponsable = servicio.programa.usuario.usuario;
await this.gmailService.enviarCorreo(
correoAlumno.subject,
servicio.correo,
correoAlumno.msj,
);
await this.gmailService.enviarCorreo({
subject: correoAlumno.subject,
to: servicio.correo,
text: correoAlumno.msj,
fecha_recibido: new Date(),
});
break;
case 1:
@@ -1097,11 +1096,12 @@ export class ServicioService {
throw new BadRequestException('Id status no válido.');
}
await this.gmailService.enviarCorreo(
correoResponsable.subject,
emailResponsable,
correoResponsable.msj,
);
await this.gmailService.enviarCorreo({
subject: correoResponsable.subject,
to: emailResponsable,
text: correoResponsable.msj,
fecha_recibido: new Date(),
});
// Determinar estatus según modo
const tempStatus = process.env.MODE === 'pruebas' ? 4 : 3;
@@ -1202,4 +1202,169 @@ export class ServicioService {
return { count, serviciosAdmin: servicios };
}
async obtenerServiciosResponsable(dto: ServiciosResponsableDto) {
const {
idUsuario,
idStatus,
nombre = '',
numeroCuenta = '',
pagina = 1,
} = dto;
// 1️⃣ Validar que el usuario exista y sea responsable
const responsable = await this.usuarioRepo.findOne({
where: { idUsuario },
});
if (!responsable) throw new NotFoundException('No existe este usuario.');
if (responsable.tipoUsuario.idTipoUsuario !== 2)
throw new BadRequestException('No es un usuario tipo responsable.');
// 2️⃣ Crear el query builder
const query = this.servicioRepo
.createQueryBuilder('servicio')
.innerJoinAndSelect('servicio.usuario', 'usuario')
.innerJoinAndSelect('servicio.programa', 'programa')
.innerJoinAndSelect('servicio.carrera', 'carrera')
.innerJoinAndSelect('servicio.status', 'status')
.where('programa.idUsuario = :idUsuario', { idUsuario })
.andWhere('usuario.usuario LIKE :numeroCuenta', {
numeroCuenta: `%${numeroCuenta}%`,
})
.andWhere('usuario.nombre LIKE :nombre', { nombre: `%${nombre}%` });
// 3️⃣ Filtro de estatus
if (idStatus) {
query.andWhere('status.idStatus = :idStatus', { idStatus });
} else {
query.andWhere('status.idStatus != :status', { status: 10 });
}
// 4️⃣ Orden, paginación y selección de campos
query
.select([
'servicio.idServicio',
'servicio.fechaInicio',
'servicio.fechaFin',
'servicio.cartaTermino',
'servicio.createdAt',
'servicio.idCuestionarioPrograma',
'servicio.idCuestionarioPrograma2',
'usuario.idUsuario',
'usuario.usuario',
'usuario.nombre',
'carrera.idCarrera',
'carrera.carrera',
'status.idStatus',
'status.status',
])
.orderBy('servicio.updatedAt', 'DESC')
.skip(25 * (pagina - 1))
.take(25);
// 5️⃣ Ejecutar y obtener datos + total
const [rows, count] = await query.getManyAndCount();
// 6️⃣ Retornar resultado en el mismo formato que antes
return { count, serviciosResponsable: rows };
}
async update(
body: UpdateServicioDto,
files: Record<string, Express.Multer.File[]>,
) {
const idServicio = body.idServicio;
const servicio = await this.servicioRepo.findOne({ where: { idServicio } });
if (!servicio)
throw new NotFoundException(
'Este Servicio Social no existe en la base de datos.',
);
let dataUpdate: Partial<Servicio> = {};
// ---- 📁 Subida de archivos a Drive ----
try {
if (files?.['cartaAceptacion']?.[0]?.filename) {
{
const result = await this.driveService.uploadFile(
`./server/uploads/${files['cartaAceptacion'][0].filename}`,
`Carta_Aceptacion.pdf`,
'application/pdf',
servicio.carpeta,
);
dataUpdate.cartaAceptacion = result === null ? undefined : result;
}
}
} catch (err) {}
try {
if (files?.['cartaTermino']?.[0]?.filename) {
{
const result = await this.driveService.uploadFile(
`./server/uploads/${files['cartaTermino'][0].filename}`,
`Carta_Termino.pdf`,
'application/pdf',
servicio.carpeta,
);
dataUpdate.cartaTermino = result === null ? undefined : result;
}
}
} catch (err) {}
try {
if (files?.['informeGlobal']?.[0]?.filename) {
const result = await this.driveService.uploadFile(
`./server/uploads/${files['informeGlobal'][0].filename}`,
`Informe_Global.pdf`,
'application/pdf',
servicio.carpeta,
);
dataUpdate.informeGlobal = result === null ? undefined : result;
}
} catch (err) {}
// ---- 📆 Validaciones de campos del body ----
if (body.correo) dataUpdate.correo = body.correo;
if (body.fechaInicio) dataUpdate.fechaInicio = body.fechaInicio;
if (body.fechaFin) dataUpdate.fechaFin = body.fechaFin;
if (body.fechaNacimiento) dataUpdate.fechaNacimiento = body.fechaNacimiento;
if (body.direccion)
dataUpdate.direccion = this.validacionService.validarAlfanumerico(
body.direccion,
'dirección',
false,
200,
);
if (body.telefono)
dataUpdate.telefono = this.validacionService.validarNumero(
body.telefono,
'teléfono',
true,
15,
);
if (this.validacionService.validarObjetoVacio(dataUpdate)) {
throw new BadRequestException(
'No se envió nada para actualizar este Servicio Social.',
);
}
// ---- 🧱 Actualización en la base ----
await this.servicioRepo
.createQueryBuilder()
.update(Servicio)
.set(dataUpdate)
.where('idServicio = :idServicio', { idServicio })
.execute();
// ---- ✅ Validar si se requiere "pre-término" ----
if (dataUpdate.cartaTermino || dataUpdate.informeGlobal) {
await this.validacionService.validarPreTermino(idServicio);
}
return {
message: 'Se guardaron correctamente los cambios de este servicio.',
};
}
}
+9 -26
View File
@@ -1,4 +1,12 @@
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
} from '@nestjs/common';
import { StatusService } from './status.service';
import { CreateStatusDto } from './dto/create-status.dto';
import { UpdateStatusDto } from './dto/update-status.dto';
@@ -6,29 +14,4 @@ import { UpdateStatusDto } from './dto/update-status.dto';
@Controller('status')
export class StatusController {
constructor(private readonly statusService: StatusService) {}
@Post()
create(@Body() createStatusDto: CreateStatusDto) {
return this.statusService.create(createStatusDto);
}
@Get()
findAll() {
return this.statusService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.statusService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateStatusDto: UpdateStatusDto) {
return this.statusService.update(+id, updateStatusDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.statusService.remove(+id);
}
}
+1 -21
View File
@@ -3,24 +3,4 @@ import { CreateStatusDto } from './dto/create-status.dto';
import { UpdateStatusDto } from './dto/update-status.dto';
@Injectable()
export class StatusService {
create(createStatusDto: CreateStatusDto) {
return 'This action adds a new status';
}
findAll() {
return `This action returns all status`;
}
findOne(id: number) {
return `This action returns a #${id} status`;
}
update(id: number, updateStatusDto: UpdateStatusDto) {
return `This action updates a #${id} status`;
}
remove(id: number) {
return `This action removes a #${id} status`;
}
}
export class StatusService {}
+9 -26
View File
@@ -1,4 +1,12 @@
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
} from '@nestjs/common';
import { UsuarioService } from './usuario.service';
import { CreateUsuarioDto } from './dto/create-usuario.dto';
import { UpdateUsuarioDto } from './dto/update-usuario.dto';
@@ -6,29 +14,4 @@ import { UpdateUsuarioDto } from './dto/update-usuario.dto';
@Controller('usuario')
export class UsuarioController {
constructor(private readonly usuarioService: UsuarioService) {}
@Post()
create(@Body() createUsuarioDto: CreateUsuarioDto) {
return this.usuarioService.create(createUsuarioDto);
}
@Get()
findAll() {
return this.usuarioService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.usuarioService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateUsuarioDto: UpdateUsuarioDto) {
return this.usuarioService.update(+id, updateUsuarioDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.usuarioService.remove(+id);
}
}
+266 -11
View File
@@ -1,26 +1,281 @@
import { Injectable } from '@nestjs/common';
import {
BadRequestException,
Injectable,
NotFoundException,
UnauthorizedException,
} from '@nestjs/common';
import { CreateUsuarioDto } from './dto/create-usuario.dto';
import { UpdateUsuarioDto } from './dto/update-usuario.dto';
import axios from 'axios';
import { InjectRepository } from '@nestjs/typeorm';
import { Usuario } from './entities/usuario.entity';
import { Like, Not, Repository } from 'typeorm';
import { Carrera } from 'src/carrera/entities/carrera.entity';
import { Servicio } from 'src/servicio/entities/servicio.entity';
import { gmail } from 'src/helpers.services/gmail.service';
import { AuthService } from 'src/auth/auth.service';
@Injectable()
export class UsuarioService {
create(createUsuarioDto: CreateUsuarioDto) {
return 'This action adds a new usuario';
private readonly carreras: Record<string, number> = {
'LIC. EN ACTUARIA': 64,
'LIC. EN ARQUITECTURA': 70,
'LIC. EN CIENCIAS POLITICAS Y ADMON PUB': 67,
'LIC. EN CIENCIAS POLITICAS Y ADMON.PUBL.': 67,
'LIC. EN COMUNICACION': 67,
'LIC. EN DERECHO': 68,
'LIC. EN DERECHO (SUA)': 67,
'LIC. EN DISEÑO GRAFICO': 70,
'LIC. EN ECONOMIA': 66,
'LIC. EN ENSEÑANZA DE INGLES': 69,
'LIC. EN FILOSOFIA': 70,
'LIC. EN HISTORIA': 70,
'LIC. EN INGENIERIA CIVIL': 70,
'LIC. EN LENGUA Y LITERATURA HISPANICAS': 70,
'LIC. EN MAT. APLICADAS Y COMPUTACION': 66,
'LIC. EN MATEMATICAS APLICADAS Y COMP.': 66,
'LIC. EN PEDAGOGÍA': 70,
'LIC. EN PERIODISMO Y COMUNICACION COL.': 70,
'LIC. EN RELACIONES INTERNACIONALES': 70,
'LIC. EN RELACIONES INTERNACIONALES (SUA)': 70,
'LIC. EN SOCIOLOGIA': 68,
'LIC. ENSEÑANZA DE ALEMÁN (LENG. EXTRANJS': 70,
'LIC. ENSEÑANZA DE ESPAÑOL(LENG. EXTRANJ)': 70,
'LIC. ENSEÑANZA DE INGLÉS(LENG. EXTRANJE)': 70,
'LIC. ENSEÑANZA DE ITALIANO(LENG. EXTRANJ': 70,
};
constructor(
@InjectRepository(Usuario)
private readonly userRepo: Repository<Usuario>,
@InjectRepository(Carrera)
private readonly carreraRepo: Repository<Carrera>,
@InjectRepository(Servicio)
private readonly servicioRepo: Repository<Servicio>,
private readonly gmail: gmail,
private readonly authService: AuthService,
) {}
async escolares(numeroDeCuenta: string) {
let response;
try {
response = await axios.post(
`${process.env.ESCOLARES}${numeroDeCuenta}`,
{ password: process.env.ESCOLARES_PASS },
{
headers: {
'Content-Type': 'application/json', // Tipo de contenido
Authorization: `Bearer ${process.env.API_TOKEN}`, // Header de auth
},
},
);
} catch (error) {
throw new UnauthorizedException(
'No se pudo conectar con el servicio de escolares',
);
}
if (
!response.data.nombre ||
!response.data.carrconst ||
!response.data.avance
) {
throw new Error(
'El alumno no cumple con los requisitos para realizar el Servicio Social. Si cree que esto es erróneo comunícate al Departamento de Servicio Social y Bolsa de Trabajo.',
);
}
interface AlumnoDTO {
nombre: string;
creditos: number;
carrera: string;
idCarrera?: number;
}
let alumno: AlumnoDTO = {
nombre: response.data.nombre.trim(),
creditos: response.data.avance,
carrera: response.data.carrconst.trim(),
};
if (alumno.creditos < this.carreras[alumno.carrera]) {
throw new Error('Este alumno no cuenta con los créditos necesarios.');
}
while (alumno.nombre.search('‘') != -1 && alumno.nombre.search('Ã') != -1) {
alumno.nombre = alumno.nombre.replace('Ã', 'Ñ');
alumno.nombre = alumno.nombre.replace('‘', '');
}
let carrera = await this.carreraRepo.findOne({
where: { carrera: alumno.carrera },
});
if (!carrera) {
let carr = this.carreraRepo.create({ carrera: alumno.carrera });
carrera = await this.carreraRepo.save(carr);
}
alumno.idCarrera = carrera.idCarrera;
let alum = await this.userRepo.findOne({
where: { usuario: numeroDeCuenta },
});
if (!alum) {
let nuevoUsuario = this.userRepo.create({
usuario: numeroDeCuenta,
nombre: alumno.nombre,
activo: true,
tipoUsuario: { idTipoUsuario: 3 },
});
alum = await this.userRepo.save(nuevoUsuario);
}
return { ...alumno, idUsuario: alum.idUsuario };
}
findAll() {
return `This action returns all usuario`;
async newPasswordAlumno(idServicio: number) {
const password = this.authService.generarPassword();
// Buscar el servicio junto con el usuario
const servicio = await this.servicioRepo.findOne({
where: { idServicio },
relations: ['usuario'], // asegura que traiga la relación
});
if (!servicio) {
throw new NotFoundException('No existe este Servicio Social.');
}
let usuario = servicio.usuario;
if (usuario.tipoUsuario.idTipoUsuario !== 3) {
throw new BadRequestException('Este usuario no es de tipo alumno.');
}
// Preparar correo
let correo = preRegistro(password, usuario.nombre);
// Enviar correo
await this.gmail.enviarCorreo({
subject: correo.subject,
to: servicio.correo,
text: correo.msj,
fecha_recibido: new Date(),
});
// Actualizar contraseña en la DB
usuario.password = await this.authService.encriptar(password);
await this.userRepo.save(usuario);
return {
message: 'Se envió un correo con una contraseña nueva al alumno.',
};
}
findOne(id: number) {
return `This action returns a #${id} usuario`;
async newPasswordResponsable(idUsuario: number) {
const password = this.authService.generarPassword();
// Buscar usuario
const usuario = await this.userRepo.findOne({ where: { idUsuario } });
if (!usuario) {
throw new NotFoundException('No existe este Usuario.');
}
if (usuario.tipoUsuario.idTipoUsuario !== 2) {
throw new BadRequestException('Este usuario no es de tipo responsable.');
}
// Preparar correo
const correo = enviarSec(password, usuario.usuario, usuario.nombre);
// Enviar correo
await this.gmail.enviarCorreo({
subject: correo.subject,
to: usuario.usuario,
text: correo.msj,
fecha_recibido: new Date(),
});
// Actualizar contraseña en la DB
usuario.password = await this.authService.encriptar(password);
await this.userRepo.save(usuario);
return {
message: 'Se envió un correo con una contraseña nueva al responsable.',
};
}
update(id: number, updateUsuarioDto: UpdateUsuarioDto) {
return `This action updates a #${id} usuario`;
async findResponsable(idUsuario: number) {
return await this.userRepo.findOne({ where: { idUsuario } });
}
remove(id: number) {
return `This action removes a #${id} usuario`;
async findResponsables(pagina: number, nombre: string, correo: string) {
const [responsables, count] = await this.userRepo.findAndCount({
where: {
usuario: Like(`%${correo}%`),
nombre: Like(`%${nombre}%`),
tipoUsuario: { idTipoUsuario: 2 },
},
select: ['idUsuario', 'usuario', 'nombre', 'activo'],
take: 25,
skip: 25 * (pagina - 1),
});
return { count: count, responsables: responsables };
}
async actualizarResponsable(
idUsuario: number,
correo?: string,
nombre?: string,
) {
const dataUpdate: Partial<Usuario> = {};
// Buscar responsable
const responsable = await this.userRepo.findOne({ where: { idUsuario } });
if (!responsable) {
throw new NotFoundException(
'No existe este responsable en la base de datos.',
);
}
if (responsable.tipoUsuario.idTipoUsuario !== 2) {
throw new BadRequestException(
'Este usuario no es un responsable de programa.',
);
}
if (correo) {
const yaUsado = await this.userRepo.findOne({
where: { usuario: correo, idUsuario: Not(idUsuario) },
});
if (yaUsado) {
throw new BadRequestException(
'No se puede asignar este correo a esta cuenta porque está siendo usado por otro responsable.',
);
}
dataUpdate.usuario = correo;
}
// Validar nombre si se envía
if (nombre) {
dataUpdate.nombre = nombre;
}
// Verificar que haya algo para actualizar
if (Object.keys(dataUpdate).length === 0) {
throw new BadRequestException('No se ha enviado nada para actualizar.');
}
// Actualizar en la base
await this.userRepo.update(idUsuario, dataUpdate);
return {
message:
'Se actualizó la información de este responsable de programas correctamente.',
};
}
}