se cambio la descarga de a csv y subida de archivos

This commit is contained in:
evenegas
2025-07-02 10:19:00 -06:00
parent 4510999453
commit 95d4fa506c
2 changed files with 184 additions and 232 deletions
+13 -13
View File
@@ -148,13 +148,13 @@ export class ExcelController {
origen,
'SUCCESS',
undefined,
'descarga de csv',
`size=${csv.length}`
);
return res
.status(HttpStatus.OK)
.header('Content-Type', 'text/tab-separated-values')
.header('Content-Disposition', 'attachment; filename="usuarios_solicita.tsv"')
.header('Content-Type', 'text/csv')
.header('Content-Disposition', 'attachment; filename="usuarios_solicita.csv"')
.send(csv);
} catch (err) {
await this.movimientoService.log(
@@ -166,20 +166,20 @@ export class ExcelController {
.status(HttpStatus.FORBIDDEN)
.json({ statusCode: 403, message: 'Origen no permitido para descarga.' });
}
} else if (origen == 'CORREO') {
} else if (origen == 'CORREO' || origen == 'LOAD') {
try {
const csv = await this.excelService.generateCsvCorreo();
await this.movimientoService.log(
origen,
'SUCCESS',
undefined,
'descarga de csv',
`size=${csv.length}`
);
return res
.status(HttpStatus.OK)
.header('Content-Type', 'text/tab-separated-values')
.header('Content-Disposition', 'attachment; filename="usuarios.tsv"')
.header('Content-Type', 'text/csv')
.header('Content-Disposition', 'attachment; filename="usuarios_correo.csv"')
.send(csv);
} catch (err) {
await this.movimientoService.log(
@@ -199,13 +199,13 @@ export class ExcelController {
origen,
'SUCCESS',
undefined,
'descarga de csv',
`size=${csv.length}`
);
return res
.status(HttpStatus.OK)
.header('Content-Type', 'text/tab-separated-values')
.header('Content-Disposition', 'attachment; filename="usuarios_at.tsv"')
.header('Content-Type', 'text/csv')
.header('Content-Disposition', 'attachment; filename="usuarios_AT.csv"')
.send(csv);
} catch (err) {
await this.movimientoService.log(
@@ -225,13 +225,13 @@ export class ExcelController {
origen,
'SUCCESS',
undefined,
'descarga de csv',
`size=${csv.length}`
);
return res
.status(HttpStatus.OK)
.header('Content-Type', 'text/tab-separated-values')
.header('Content-Disposition', 'attachment; filename="usuarios_red.tsv"')
.header('Content-Type', 'text/csv')
.header('Content-Disposition', 'attachment; filename="usuarios_red.csv"')
.send(csv);
} catch (err) {
await this.movimientoService.log(
+171 -219
View File
@@ -1,7 +1,7 @@
// src/excel/excel.service.ts
import { Injectable, BadRequestException, Logger } from '@nestjs/common';
import { Workbook } from 'exceljs';
import { In, Repository } from 'typeorm';
import { DataSource, In, Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import { CarreraUsuario, TipoUsuario, Usuario, UsuariosDelSistema, UsuarioTipoUsuario } from '../entities/entities';
import { Genero } from '../entities/entities';
@@ -48,7 +48,8 @@ export class ExcelService {
@InjectRepository(UsuariosDelSistema)
private readonly usuariosDelSistemaRepo: Repository<UsuariosDelSistema>,
private readonly movimientoService: MovimientoService,
private readonly mailService: MailService
private readonly mailService: MailService,
private readonly dataSource: DataSource
// … inyecta otros repositorios si los usarás
) { }
@@ -118,7 +119,7 @@ export class ExcelService {
const rowNum = i + 2; // por el encabezado
// Campos que no pueden quedar vacíos
['cuenta', 'nombreCompleto', 'tipo'].forEach(field => {
['cuenta', 'tipo'].forEach(field => {
const raw = (r as any)[field];
const str = raw != null ? raw.toString().trim() : '';
if (!str) {
@@ -144,7 +145,7 @@ export class ExcelService {
seen.add(cuentaStr);
}
// Numérico en cuenta y generación
const genStr = r.gen != null ? r.gen.toString().trim() : '';
@@ -201,215 +202,166 @@ export class ExcelService {
const status = this.validateRows(file);
const errs = status.errors;
const rows = status.rowsGood;
let carga
if (rows.length == 0) {
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 movimiento = await this.movimientoService.log(
try {
const movimiento = await this.movimientoService.logger(
queryRunner,
'LOAD',
'LOADING',
undefined,
'CARGA MASIVA DE USUARIOS'
);
'LOAD',
'LOADING',
undefined,
`CARGA MASIVA DE USUARIOS`,
true, // flag para indicar que es una carga masiva
);
let contador = 0;
for (const r of rows) {
const whereCond: Array<{ num_cuenta?: string; rfc?: string }> = [{ num_cuenta: r.cuenta }];
if (r.rfc) {
whereCond.push({ rfc: r.rfc });
}
let contador: number = 0;
for (const r of rows) {
//0.1
const userExists = await this.usuarioRepo.findOne({
where: { num_cuenta: r.cuenta },
});
if (userExists) {
errs.push(`Cuenta ${r.cuenta} ya existe, se omite.`);
contador++;
continue; // si ya existe, no lo insertamos
}
// 1) Género (crea o reutiliza)
let genero = await this.generoRepo.findOne({ where: { genero: r.sexo } });
if (!genero) {
genero = this.generoRepo.create({ genero: r.sexo });
await this.generoRepo.save(genero);
}
// 2) Carrera (por clave)
let carrera = await this.carreraRepo.findOne({
where: { clave: r.clave },
});
if (!carrera) {
carrera = this.carreraRepo.create({
clave: r.clave,
carrera: r.nomCarr.slice(0, 100),
const userExists = await queryRunner.manager.findOne(Usuario, {
where: whereCond,
});
await this.carreraRepo.save(carrera);
}
let tipo = await this.tipoUsuarioRepo.findOne({
where: { tipo_usuario: r.tipo.trim() },
});
if (!tipo) {
tipo = this.tipoUsuarioRepo.create({
tipo_usuario: r.tipo.trim(),
if (userExists) {
errs.push(`Cuenta ${r.cuenta} ya existe, se omite.`);
contador++;
continue;
}
if (!r.tipo || !r.nombres || !r.apellidopa || !r.apellidoma) {
errs.push(`Fila con cuenta ${r.cuenta} tiene campos faltantes.`);
contador++;
continue;
}
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 carrera
if (r.clave || 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() },
});
await this.tipoUsuarioRepo.save(tipo);
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.trim().split(' ')[0],
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);
}
const generacion = /^[0-9]{4}$/.test(r.gen?.toString().trim())
? parseInt(r.gen.toString().trim(), 10)
: null;
// 3) Usuario
const usuario = this.usuarioRepo.create({
num_cuenta: r.cuenta,
nombre: r.nombres.trim().split(' ')[0], // ajusta si quieres
a_paterno: r.apellidopa,
a_materno: r.apellidoma,
rfc: r.rfc,
fecha_nacimiento: r.fechnac,
generacion: generacion,
genero,
movimiento: { id_mov: movimiento.id_mov }, // Relación con movimiento
});
const user = await this.usuarioRepo.save(usuario);
// 4) Relacion usuariocarrxera (tabla intermedia)
const carreraUsuario = this.carreraUsuarioRepo.create({
usuario,
carrera,
});
await this.carreraUsuarioRepo.save(carreraUsuario);
// 5) Tipo de usuario
const usuarioTipo = this.usuarioTipoUsuarioRepo.create({
usuario,
tipoUsuario: tipo,
});
await this.usuarioTipoUsuarioRepo.save(usuarioTipo);
// 6) ServActivos (si es necesario)
switch (r.tipo.trim()) {
case 'Diplomado':
const servActivosDiplomado = this.servActivosRepo.create({
Correo: true,
usuario: user,
RedStatus: 'Inactivo',
ATStatus: 'Inactivo',
CorreoStatus: 'Inactivo',
PrestamosStatus: 'Inactivo',
});
await this.servActivosRepo.save(servActivosDiplomado);
break;
case 'Extra Largo':
const servActivosExtraLargo = this.servActivosRepo.create({
Prestamos: true,
Correo: true,
usuario: user,
RedStatus: 'Inactivo',
ATStatus: 'Inactivo',
CorreoStatus: 'Inactivo',
PrestamosStatus: 'Inactivo',
});
await this.servActivosRepo.save(servActivosExtraLargo);
break;
case 'Servicio Social':
const servActivosServicio = this.servActivosRepo.create({
Red: true,
AT: true,
Correo: true,
usuario: user,
RedStatus: 'Inactivo',
ATStatus: 'Inactivo',
CorreoStatus: 'Inactivo',
PrestamosStatus: 'Inactivo',
});
await this.servActivosRepo.save(servActivosServicio);
break;
case 'Idiomas R (UNAM)':
case 'Idiomas Sabatino':
const servActivosIdiomas = this.servActivosRepo.create({
Red: true,
Correo: true,
usuario: user,
RedStatus: 'Inactivo',
ATStatus: 'Inactivo',
CorreoStatus: 'Inactivo',
PrestamosStatus: 'Inactivo',
});
await this.servActivosRepo.save(servActivosIdiomas);
break;
case 'Reinscrito':
case 'Posgrado':
case 'Intercambio UNAM':
case 'Movilidad':
case 'Ampliación de Conocimiento':
case 'Licenciatura':
case 'Profesor':
const servActivos = this.servActivosRepo.create({
Red: true,
AT: true,
Correo: true,
Prestamos: true,
usuario: user,
RedStatus: 'Inactivo',
ATStatus: 'Inactivo',
CorreoStatus: 'Inactivo',
PrestamosStatus: 'Inactivo',
});
await this.servActivosRepo.save(servActivos);
break;
case 'Trabajadores':
const servActivosTrabajadores = this.servActivosRepo.create({
Red: true,
usuario: user,
RedStatus: 'Inactivo',
});
await this.servActivosRepo.save(servActivosTrabajadores);
break;
}
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();
}
return { inserted: rows.length - contador, id_movimiento: movimiento.id_mov, errors: errs };
}
// async generateCsv(): Promise<string> {
// const users = await this.usuarioRepo.find({
// relations: ['genero', 'carreraUsuarios'], // si quieres más info
@@ -483,7 +435,7 @@ export class ExcelService {
const header = [
'num_cuenta', 'nombre', 'a_paterno', 'a_materno',
'carreras', 'rfc', 'fecha_nacimiento', 'generacion'
].join('\t') + '\n';
].join(',') + '\n';
const rows = usuarios.map(u => {
const carreras = (u.carreraUsuarios ?? [])
@@ -491,15 +443,15 @@ export class ExcelService {
.join(', '); // une todas las carreras en un solo string
return [
u.num_cuenta,
u.nombre,
u.a_paterno,
u.a_materno,
carreras,
u.num_cuenta ?? '',
u.nombre ?? '',
u.a_paterno ?? '',
u.a_materno ?? '',
carreras ?? '',
u.rfc ?? '',
u.fecha_nacimiento,
u.fecha_nacimiento ?? '',
u.generacion?.toString() ?? ''
].join('\t');
].join(',');
}).join('\n');
const tablaCompleta = header + rows;
@@ -565,7 +517,7 @@ export class ExcelService {
const header = [
'num_cuenta', 'nombre', 'a_paterno', 'a_materno',
'carreras', 'rfc'
].join('\t') + '\n';
].join(',') + '\n';
const rows = usuarios.map(u => {
const carreras = (u.carreraUsuarios ?? [])
@@ -573,13 +525,13 @@ export class ExcelService {
.join(', '); // une todas las carreras en un solo string
return [
u.num_cuenta,
u.nombre,
u.a_paterno,
u.a_materno,
carreras,
u.num_cuenta ?? '',
u.nombre ?? '',
u.a_paterno ?? '',
u.a_materno ?? '',
carreras ?? '',
u.rfc ?? '',
].join('\t');
].join(',');
}).join('\n');
const tablaCompleta = header + rows;
@@ -630,13 +582,13 @@ export class ExcelService {
const header = [
'num_cuenta', 'fecha_nacimiento'
].join('\t') + '\n';
].join(',') + '\n';
const rows = usuarios.map(u => {
return [
u.num_cuenta,
u.fecha_nacimiento,
].join('\t');
].join(',');
}).join('\n');
const tablaCompleta = header + rows;
@@ -686,22 +638,22 @@ export class ExcelService {
const header = [
'num_cuenta', 'nombre', 'a_paterno', 'a_materno',
'carreras', 'fecha_nacimiento', 'generacion'
].join('\t') + '\n';
].join(',') + '\n';
const rows = usuarios.map(u => {
const carreras = (u.carreraUsuarios ?? [])
.map(cu => cu.carrera?.carrera ?? '') // obtenemos solo el nombre
.join(', '); // une todas las carreras en un solo string
.join(','); // une todas las carreras en un solo string
return [
u.num_cuenta,
u.nombre,
u.a_paterno,
u.a_materno,
carreras,
u.fecha_nacimiento,
u.num_cuenta ?? '',
u.nombre ?? '',
u.a_paterno ?? '',
u.a_materno ?? '',
carreras ?? '',
u.fecha_nacimiento ?? '',
u.generacion?.toString() ?? ''
].join('\t');
].join(',');
}).join('\n');
const tablaCompleta = header + rows;