Se agregó el inicio de sesion de google
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// src/excel/excel.controller.ts
|
||||
import { Controller, Post, Get, UseGuards, Request, Res, UploadedFile, UseInterceptors, HttpStatus, Param, ParseIntPipe, Body, BadRequestException } from '@nestjs/common';
|
||||
import { Controller, Post, Get, UseGuards, Request, Res, UploadedFile, UseInterceptors, HttpStatus, Param, ParseIntPipe, Body, BadRequestException, Delete } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { ApiBearerAuth } from '@nestjs/swagger';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
@@ -69,6 +69,7 @@ export class ExcelController {
|
||||
@ExcelDocumentation.loadExcel()
|
||||
async loadExcel(@Request() req, @UploadedFile() file: Express.Multer.File) {
|
||||
const origen = req.user.origen; // ahora sí existe
|
||||
|
||||
if (origen != 'LOAD') {
|
||||
await this.movimientoService.log(
|
||||
origen,
|
||||
@@ -78,6 +79,7 @@ export class ExcelController {
|
||||
throw new Error('Origen no permitido para carga.');
|
||||
}
|
||||
try {
|
||||
|
||||
const result = await this.excelService.loadFile(file.buffer);
|
||||
//Actualizar el estado de los movimientos
|
||||
if (!result) {
|
||||
@@ -120,6 +122,24 @@ export class ExcelController {
|
||||
});
|
||||
|
||||
await this.excelService.enviarCargaMasiva();
|
||||
|
||||
|
||||
await this.excelService.enviarInforme('AT',"Carga de datos en Servicios PCpuma",
|
||||
`Se ha hecho una carga en el sistema \n ${result.conteoTiposAt}`
|
||||
)
|
||||
|
||||
await this.excelService.enviarInforme('RED',"Carga de datos en Servicios PCpuma",
|
||||
`Se ha hecho una carga en el sistema \n ${result.conteoTiposRed}`
|
||||
)
|
||||
|
||||
await this.excelService.enviarInforme('SOLICITA',"Carga de datos en Servicios PCpuma",
|
||||
`Se ha hecho una carga en el sistema \n ${result.conteoTiposCorreo}`
|
||||
)
|
||||
|
||||
await this.excelService.enviarInforme('CORREO',"Carga de datos en Servicios PCpuma",
|
||||
`Se ha hecho una carga en el sistema \n ${result.conteoTiposCorreo}`
|
||||
)
|
||||
|
||||
|
||||
|
||||
return result
|
||||
@@ -300,6 +320,11 @@ export class ExcelController {
|
||||
|
||||
}
|
||||
|
||||
// @Delete()
|
||||
// async borrar(){
|
||||
// return await this.excelService.borrarUsers()
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+242
-166
@@ -198,178 +198,244 @@ export class ExcelService {
|
||||
* Valida y luego persiste usuarios y relaciones básicas
|
||||
*/
|
||||
async loadFile(buffer: Buffer) {
|
||||
const file = await this.parseFile(buffer);
|
||||
const status = this.validateRows(file);
|
||||
const errs = status.errors;
|
||||
const rows = status.rowsGood;
|
||||
const file = await this.parseFile(buffer);
|
||||
const status = this.validateRows(file);
|
||||
const errs = status.errors;
|
||||
const rows = status.rowsGood;
|
||||
|
||||
if (rows.length === 0) {
|
||||
throw new BadRequestException('No se encontraron filas válidas para cargar. ' + errs);
|
||||
}
|
||||
if (rows.length === 0) {
|
||||
throw new BadRequestException(
|
||||
'No se encontraron filas válidas para cargar. ' + errs,
|
||||
);
|
||||
}
|
||||
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
const conteoTiposRed: Record<string, number> = {};
|
||||
const conteoTiposAt: Record<string, number> = {};
|
||||
const conteoTiposSolicita: Record<string, number> = {};
|
||||
const conteoTiposCorreo: Record<string, number> = {};
|
||||
|
||||
try {
|
||||
const movimiento = await this.movimientoService.logger(
|
||||
queryRunner,
|
||||
'LOAD',
|
||||
'LOADING',
|
||||
undefined,
|
||||
'CARGA MASIVA DE USUARIOS'
|
||||
);
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
let contador = 0;
|
||||
try {
|
||||
const movimiento = await this.movimientoService.logger(
|
||||
queryRunner,
|
||||
'LOAD',
|
||||
'LOADING',
|
||||
undefined,
|
||||
'CARGA MASIVA DE USUARIOS',
|
||||
);
|
||||
|
||||
for (const r of rows) {
|
||||
// 🔹 1. Pre-cargar cuentas/rfc ya existentes
|
||||
const cuentas = rows.map((r) => r.cuenta).filter(Boolean);
|
||||
const rfcs = rows.map((r) => r.rfc).filter(Boolean);
|
||||
|
||||
const whereCond: Array<{ num_cuenta?: string; rfc?: string }> = [{ num_cuenta: r.cuenta }];
|
||||
if (r.rfc) {
|
||||
whereCond.push({ rfc: r.rfc });
|
||||
}
|
||||
const existentes = await queryRunner.manager.find(Usuario, {
|
||||
where: [{ num_cuenta: In(cuentas) }, { rfc: In(rfcs) }],
|
||||
select: ['num_cuenta', 'rfc'],
|
||||
});
|
||||
|
||||
const userExists = await queryRunner.manager.findOne(Usuario, {
|
||||
where: whereCond,
|
||||
});
|
||||
const setCuentas = new Set(existentes.map((u) => u.num_cuenta));
|
||||
const setRfcs = new Set(existentes.map((u) => u.rfc));
|
||||
|
||||
// 🔹 2. Pre-cargar catálogos
|
||||
const generosUnicos = [...new Set(rows.map((r) => r.sexo).filter(Boolean))];
|
||||
const carrerasUnicasClave = [
|
||||
...new Set(rows.map((r) => r.clave).filter(Boolean)),
|
||||
];
|
||||
const carrerasUnicasNombre = [
|
||||
...new Set(rows.map((r) => r.nomCarr).filter(Boolean)),
|
||||
];
|
||||
const tiposUnicos = [...new Set(rows.map((r) => r.tipo?.trim()).filter(Boolean))];
|
||||
|
||||
const [generosExist, carrerasExist, tiposExist] = await Promise.all([
|
||||
queryRunner.manager.find(Genero, { where: { genero: In(generosUnicos) } }),
|
||||
queryRunner.manager.find(Carrera, {
|
||||
where: [{ clave: In(carrerasUnicasClave) }, { carrera: In(carrerasUnicasNombre) }],
|
||||
}),
|
||||
queryRunner.manager.find(TipoUsuario, {
|
||||
where: { tipo_usuario: In(tiposUnicos) },
|
||||
}),
|
||||
]);
|
||||
|
||||
if (userExists) {
|
||||
errs.push(`Cuenta ${r.cuenta} ya existe, se omite.`);
|
||||
contador++;
|
||||
continue;
|
||||
}
|
||||
const mapGenero = new Map(generosExist.map((g) => [g.genero, g]));
|
||||
const mapCarreraClave = new Map(carrerasExist.map((c) => [c.clave, c]));
|
||||
const mapCarreraNom = new Map(carrerasExist.map((c) => [c.carrera, c]));
|
||||
const mapTipo = new Map(tiposExist.map((t) => [t.tipo_usuario, t]));
|
||||
|
||||
if (!r.tipo || !r.nombres || !r.apellidopa || !r.apellidoma) {
|
||||
errs.push(`Fila con cuenta ${r.cuenta} tiene campos faltantes.`);
|
||||
contador++;
|
||||
continue;
|
||||
}
|
||||
// 🔹 3. Arreglos para inserciones masivas
|
||||
const usuarios: Usuario[] = [];
|
||||
const carrerasUsuario: CarreraUsuario[] = [];
|
||||
const usuariosTipos: UsuarioTipoUsuario[] = [];
|
||||
const serviciosActivos: ServActivos[] = [];
|
||||
|
||||
let genero = await queryRunner.manager.findOne(Genero, { where: { genero: r.sexo } });
|
||||
if (!genero) {
|
||||
genero = queryRunner.manager.create(Genero, { genero: r.sexo });
|
||||
await queryRunner.manager.save(genero);
|
||||
}
|
||||
let contador = 0;
|
||||
|
||||
let carrera
|
||||
if (r.clave || r.nomCarr) {
|
||||
if (!r.clave) {
|
||||
carrera = await queryRunner.manager.findOne(Carrera, { where: { carrera: r.nomCarr } });
|
||||
|
||||
|
||||
} else if (!r.nomCarr) {
|
||||
|
||||
carrera = await queryRunner.manager.findOne(Carrera, { where: { clave: r.clave } });
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (!carrera) {
|
||||
carrera = queryRunner.manager.create(Carrera, {
|
||||
clave: r.clave ?? "",
|
||||
carrera: r.nomCarr.slice(0, 100),
|
||||
});
|
||||
|
||||
await queryRunner.manager.save(carrera);
|
||||
}
|
||||
|
||||
} else {
|
||||
carrera = null;
|
||||
}
|
||||
|
||||
let tipo = await queryRunner.manager.findOne(TipoUsuario, {
|
||||
where: { tipo_usuario: r.tipo.trim() },
|
||||
});
|
||||
if (!tipo) {
|
||||
tipo = queryRunner.manager.create(TipoUsuario, { tipo_usuario: r.tipo.trim() });
|
||||
await queryRunner.manager.save(tipo);
|
||||
}
|
||||
|
||||
const generacion = /^[0-9]{4}$/.test(r.gen?.toString().trim())
|
||||
? parseInt(r.gen.toString().trim(), 10)
|
||||
: null;
|
||||
|
||||
const usuario = queryRunner.manager.create(Usuario, {
|
||||
num_cuenta: r.cuenta,
|
||||
nombre: r.nombres,
|
||||
a_paterno: r.apellidopa,
|
||||
a_materno: r.apellidoma,
|
||||
rfc: r.rfc,
|
||||
fecha_nacimiento: r.fechnac,
|
||||
generacion,
|
||||
genero,
|
||||
movimiento,
|
||||
});
|
||||
const user = await queryRunner.manager.save(usuario);
|
||||
|
||||
if (carrera) {
|
||||
const carreraUsuario = queryRunner.manager.create(CarreraUsuario, {
|
||||
usuario: user,
|
||||
carrera,
|
||||
});
|
||||
await queryRunner.manager.save(carreraUsuario);
|
||||
}
|
||||
|
||||
const usuarioTipo = queryRunner.manager.create(UsuarioTipoUsuario, {
|
||||
usuario: user,
|
||||
tipoUsuario: tipo,
|
||||
});
|
||||
await queryRunner.manager.save(usuarioTipo);
|
||||
|
||||
const servData = {
|
||||
usuario: user,
|
||||
RedStatus: 'Inactivo',
|
||||
ATStatus: 'Inactivo',
|
||||
CorreoStatus: 'Inactivo',
|
||||
PrestamosStatus: 'Inactivo',
|
||||
};
|
||||
|
||||
switch (r.tipo.trim()) {
|
||||
case 'Diplomado':
|
||||
Object.assign(servData, { Correo: true });
|
||||
break;
|
||||
case 'Extra Largo':
|
||||
Object.assign(servData, { Correo: true, Prestamos: true });
|
||||
break;
|
||||
case 'Servicio Social':
|
||||
Object.assign(servData, { Correo: true, AT: true, Red: true });
|
||||
break;
|
||||
case 'Idiomas R (UNAM)':
|
||||
case 'Idiomas Sabatino':
|
||||
Object.assign(servData, { Correo: true, Red: true });
|
||||
break;
|
||||
case 'Reinscrito':
|
||||
case 'Posgrado':
|
||||
case 'Intercambio UNAM':
|
||||
case 'Movilidad':
|
||||
case 'Ampliación de Conocimiento':
|
||||
case 'Licenciatura':
|
||||
case 'Profesor':
|
||||
Object.assign(servData, { Correo: true, AT: true, Red: true, Prestamos: true });
|
||||
break;
|
||||
case 'Trabajadores':
|
||||
Object.assign(servData, { Red: true });
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
const serv = queryRunner.manager.create(ServActivos, servData);
|
||||
await queryRunner.manager.save(serv);
|
||||
for (const r of rows) {
|
||||
if (setCuentas.has(r.cuenta) || (r.rfc && setRfcs.has(r.rfc))) {
|
||||
errs.push(`Cuenta ${r.cuenta} ya existe, se omite.`);
|
||||
contador++;
|
||||
continue;
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
return { inserted: rows.length - contador, id_movimiento: movimiento.id_mov, errors: errs };
|
||||
} catch (err) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw err;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
if (!r.tipo || !r.nombres || !r.apellidopa || !r.apellidoma) {
|
||||
errs.push(`Fila con cuenta ${r.cuenta} tiene campos faltantes.`);
|
||||
contador++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Genero
|
||||
let genero = mapGenero.get(r.sexo);
|
||||
if (!genero) {
|
||||
genero = queryRunner.manager.create(Genero, { genero: r.sexo });
|
||||
mapGenero.set(r.sexo, genero);
|
||||
}
|
||||
|
||||
// Carrera
|
||||
let carrera: Carrera|undefined;
|
||||
if (r.clave && mapCarreraClave.has(r.clave)) {
|
||||
carrera = mapCarreraClave.get(r.clave);
|
||||
} else if (r.nomCarr && mapCarreraNom.has(r.nomCarr)) {
|
||||
carrera = mapCarreraNom.get(r.nomCarr);
|
||||
} else if (r.clave || r.nomCarr) {
|
||||
carrera = queryRunner.manager.create(Carrera, {
|
||||
clave: r.clave ?? '',
|
||||
carrera: r.nomCarr?.slice(0, 100) ?? '',
|
||||
});
|
||||
mapCarreraClave.set(carrera.clave, carrera);
|
||||
mapCarreraNom.set(carrera.carrera, carrera);
|
||||
}
|
||||
|
||||
// Tipo
|
||||
let tipo = mapTipo.get(r.tipo.trim());
|
||||
if (!tipo) {
|
||||
tipo = queryRunner.manager.create(TipoUsuario, {
|
||||
tipo_usuario: r.tipo.trim(),
|
||||
});
|
||||
mapTipo.set(r.tipo.trim(), tipo);
|
||||
}
|
||||
|
||||
const generacion =
|
||||
/^[0-9]{4}$/.test(r.gen?.toString().trim()) ?
|
||||
parseInt(r.gen.toString().trim(), 10) : null;
|
||||
|
||||
const usuario = queryRunner.manager.create(Usuario, {
|
||||
num_cuenta: r.cuenta,
|
||||
nombre: r.nombres,
|
||||
a_paterno: r.apellidopa,
|
||||
a_materno: r.apellidoma,
|
||||
rfc: r.rfc,
|
||||
fecha_nacimiento: r.fechnac,
|
||||
generacion,
|
||||
genero,
|
||||
movimiento,
|
||||
});
|
||||
|
||||
usuarios.push(usuario);
|
||||
|
||||
if (carrera) {
|
||||
const carreraUsuario = queryRunner.manager.create(CarreraUsuario, {
|
||||
usuario,
|
||||
carrera,
|
||||
});
|
||||
carrerasUsuario.push(carreraUsuario);
|
||||
}
|
||||
|
||||
usuariosTipos.push(
|
||||
queryRunner.manager.create(UsuarioTipoUsuario, {
|
||||
usuario,
|
||||
tipoUsuario: tipo,
|
||||
}),
|
||||
);
|
||||
|
||||
// Servicios activos
|
||||
const servData: Partial<ServActivos> = {
|
||||
usuario,
|
||||
RedStatus: 'Inactivo',
|
||||
ATStatus: 'Inactivo',
|
||||
CorreoStatus: 'Inactivo',
|
||||
PrestamosStatus: 'Inactivo',
|
||||
};
|
||||
|
||||
switch (r.tipo.trim()) {
|
||||
case 'Diplomado':
|
||||
Object.assign(servData, { Correo: true });
|
||||
conteoTiposCorreo[r.tipo] = (conteoTiposCorreo[r.tipo] ?? 0) + 1;
|
||||
break;
|
||||
case 'Extra Largo':
|
||||
Object.assign(servData, { Correo: true, Prestamos: true });
|
||||
conteoTiposCorreo[r.tipo] = (conteoTiposCorreo[r.tipo] ?? 0) + 1;
|
||||
conteoTiposSolicita[r.tipo] = (conteoTiposSolicita[r.tipo] ?? 0) + 1;
|
||||
break;
|
||||
case 'Servicio Social':
|
||||
Object.assign(servData, { Correo: true, AT: true, Red: true });
|
||||
conteoTiposCorreo[r.tipo] = (conteoTiposCorreo[r.tipo] ?? 0) + 1;
|
||||
conteoTiposAt[r.tipo] = (conteoTiposAt[r.tipo] ?? 0) + 1;
|
||||
conteoTiposRed[r.tipo] = (conteoTiposRed[r.tipo] ?? 0) + 1;
|
||||
break;
|
||||
case 'Idiomas R (UNAM)':
|
||||
case 'Idiomas Sabatino':
|
||||
Object.assign(servData, { Correo: true, Red: true });
|
||||
conteoTiposCorreo[r.tipo] = (conteoTiposCorreo[r.tipo] ?? 0) + 1;
|
||||
conteoTiposRed[r.tipo] = (conteoTiposRed[r.tipo] ?? 0) + 1;
|
||||
break;
|
||||
case 'Reinscrito':
|
||||
case 'Posgrado':
|
||||
case 'Intercambio UNAM':
|
||||
case 'Movilidad':
|
||||
case 'Ampliación de Conocimiento':
|
||||
case 'Licenciatura':
|
||||
case 'Profesor':
|
||||
Object.assign(servData, {
|
||||
Correo: true,
|
||||
AT: true,
|
||||
Red: true,
|
||||
Prestamos: true,
|
||||
});
|
||||
conteoTiposCorreo[r.tipo] = (conteoTiposCorreo[r.tipo] ?? 0) + 1;
|
||||
conteoTiposRed[r.tipo] = (conteoTiposRed[r.tipo] ?? 0) + 1;
|
||||
conteoTiposAt[r.tipo] = (conteoTiposAt[r.tipo] ?? 0) + 1;
|
||||
conteoTiposSolicita[r.tipo] = (conteoTiposSolicita[r.tipo] ?? 0) + 1;
|
||||
break;
|
||||
case 'Trabajadores':
|
||||
Object.assign(servData, { Red: true });
|
||||
conteoTiposRed[r.tipo] = (conteoTiposRed[r.tipo] ?? 0) + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
serviciosActivos.push(queryRunner.manager.create(ServActivos, servData));
|
||||
}
|
||||
|
||||
// 🔹 4. Guardar en batch
|
||||
await queryRunner.manager.save([...mapGenero.values()]);
|
||||
await queryRunner.manager.save([...mapCarreraClave.values()]);
|
||||
await queryRunner.manager.save([...mapTipo.values()]);
|
||||
|
||||
await queryRunner.manager.save(usuarios);
|
||||
await queryRunner.manager.save(carrerasUsuario);
|
||||
await queryRunner.manager.save(usuariosTipos);
|
||||
await queryRunner.manager.save(serviciosActivos);
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
|
||||
return {
|
||||
inserted: rows.length - contador,
|
||||
id_movimiento: movimiento.id_mov,
|
||||
errors: errs,
|
||||
conteoTiposAt,
|
||||
conteoTiposCorreo,
|
||||
conteoTiposRed,
|
||||
conteoTiposSolicita,
|
||||
};
|
||||
} catch (err) {
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw err;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -571,13 +637,10 @@ export class ExcelService {
|
||||
},
|
||||
servActivo: {
|
||||
RedStatus: 'Inactivo',
|
||||
Red: true, // solo los que tienen el servicio de red activo
|
||||
Red: true, // solo los que tienen el servicio de red
|
||||
}
|
||||
},
|
||||
relations: [
|
||||
'genero',
|
||||
'carreraUsuarios', // relación intermedia
|
||||
'carreraUsuarios.carrera', // carrera asociada a través de la intermedia
|
||||
'usuarioTipos',
|
||||
'usuarioTipos.tipoUsuario',
|
||||
],
|
||||
@@ -737,7 +800,7 @@ export class ExcelService {
|
||||
throw new BadRequestException('No se encontraron usuarios para enviar correo');
|
||||
}
|
||||
|
||||
// Aquí puedes enviar el correo usando MailService
|
||||
// Aquí puedes enviar el correo usnado MailService
|
||||
await this.mailService.sendMail({
|
||||
to: usuarios_red.correo ?? '',
|
||||
subject: 'Carga de Usuarios',
|
||||
@@ -756,12 +819,7 @@ export class ExcelService {
|
||||
text: 'Se ha realizado una carga de usuarios en el sistema.',
|
||||
html: '',
|
||||
});
|
||||
await this.mailService.sendMail({
|
||||
to: usuarios_solicita.correo ?? '',
|
||||
subject: 'Carga de Usuarios',
|
||||
text: 'Se ha realizado una carga de usuarios en el sistema.',
|
||||
html: '',
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -770,6 +828,24 @@ export class ExcelService {
|
||||
|
||||
|
||||
|
||||
async enviarInforme(origen:string, subject:string ,text:string, html:string = "" ):Promise<void>{
|
||||
const user = await this.usuariosDelSistemaRepo.find({where:{origen:{origen}}})
|
||||
|
||||
user.forEach(async(u)=>{
|
||||
await this.mailService.sendMail({
|
||||
to: u.correo ?? '',
|
||||
subject,
|
||||
text,
|
||||
html,
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user