se cambió la estructura

This commit is contained in:
2026-02-05 11:52:35 -06:00
parent 9ad749782f
commit 8104fd35ff
+313 -203
View File
@@ -502,200 +502,327 @@ export class ExcelService {
}
async loadFile2(buffer: Buffer, user: string) {
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,
);
}
let conteoTiposRed = 0;
let conteoTiposAt = 0;
let conteoTiposSolicita = 0;
let conteoTiposCorreo = 0;
let contador = 0;
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
let idsUsuariosFinales: number[] = [];
try {
/* ===================== MOVIMIENTO ===================== */
const movimiento = await this.movimientoService.logger(
queryRunner,
'LOAD',
'LOADING',
'Carga masiva de usuarios hecha por: ' + user,
'CARGA MASIVA DE USUARIOS',
);
/* ===================== EXISTENTES ===================== */
const cuentas = rows.map(r => r.cuenta).filter(Boolean);
const rfcs = rows.map(r => r.rfc).filter(Boolean);
const existentes = await queryRunner.manager.find(Usuario, {
where: [{ num_cuenta: In(cuentas) }, { rfc: In(rfcs) }],
select: ['id_usuario', 'num_cuenta', 'rfc'],
});
const setCuentas = new Set(existentes.map(u => u.num_cuenta));
const setRfcs = new Set(existentes.map(u => u.rfc));
/* ===================== CATÁLOGOS ===================== */
const generosUnicos = [...new Set(rows.map(r => r.sexo).filter(Boolean))];
const clavesCarrera = [...new Set(rows.map(r => r.clave).filter(Boolean))];
const nombresCarrera = [...new Set(rows.map(r => r.nomCarr).filter(Boolean))];
const tiposUnicos = [...new Set(rows.map(r => r.tipo?.trim()).filter(Boolean))];
const [generos, carreras, tipos] = await Promise.all([
queryRunner.manager.find(Genero, { where: { genero: In(generosUnicos) } }),
queryRunner.manager.find(Carrera, {
where: [{ clave: In(clavesCarrera) }, { carrera: In(nombresCarrera) }],
}),
queryRunner.manager.find(TipoUsuario, {
where: { tipo_usuario: In(tiposUnicos) },
}),
]);
const mapGenero = new Map(generos.map(g => [g.genero, g]));
const mapCarreraClave = new Map(carreras.map(c => [c.clave, c]));
const mapCarreraNombre = new Map(carreras.map(c => [c.carrera, c]));
const mapTipo = new Map(tipos.map(t => [t.tipo_usuario, t]));
/* ===================== ARRAYS ===================== */
const usuarios: Usuario[] = [];
const carrerasUsuario: CarreraUsuario[] = [];
const usuariosTipos: UsuarioTipoUsuario[] = [];
const serviciosActivos: ServActivos[] = [];
for (const r of rows) {
if (setCuentas.has(r.cuenta) || (r.rfc && setRfcs.has(r.rfc))) {
const u = existentes.find(
e => e.num_cuenta === r.cuenta || e.rfc === r.rfc,
);
if (u) idsUsuariosFinales.push(u.id_usuario);
contador++;
continue;
}
if (!r.tipo || !r.nombres || !r.apellidopa || !r.apellidoma) {
errs.push(`Fila con cuenta ${r.cuenta} tiene campos faltantes`);
contador++;
continue;
}
/* ---------- Género ---------- */
let genero = mapGenero.get(r.sexo);
if (!genero) {
genero = queryRunner.manager.create(Genero, { genero: r.sexo });
mapGenero.set(r.sexo, genero);
}
/* ---------- Carrera ---------- */
let carrera =
(r.clave && mapCarreraClave.get(r.clave)) ||
(r.nomCarr && mapCarreraNombre.get(r.nomCarr));
if (!carrera && (r.clave || r.nomCarr)) {
carrera = queryRunner.manager.create(Carrera, {
clave: r.clave ?? '',
carrera: r.nomCarr?.slice(0, 100) ?? '',
});
mapCarreraClave.set(carrera.clave, carrera);
mapCarreraNombre.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 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: /^\d{4}$/.test(r.gen?.toString())
? Number(r.gen)
: null,
genero,
movimiento,
});
usuarios.push(usuario);
if (carrera) {
carrerasUsuario.push(
queryRunner.manager.create(CarreraUsuario, { usuario, carrera }),
);
}
usuariosTipos.push(
queryRunner.manager.create(UsuarioTipoUsuario, {
usuario,
tipoUsuario: tipo,
}),
if (rows.length === 0) {
throw new BadRequestException(
'No se encontraron filas válidas para cargar. ' + errs,
);
const sa = queryRunner.manager.create(ServActivos, { usuario });
switch (r.tipo.trim()) {
case 'Diplomado':
sa.Correo = true; conteoTiposCorreo++; break;
case 'Extra Largo':
sa.Correo = true; sa.Prestamos = true;
conteoTiposCorreo++; conteoTiposSolicita++; break;
case 'Servicio Social':
sa.Correo = true; sa.AT = true; sa.Red = true;
conteoTiposCorreo++; conteoTiposAt++; conteoTiposRed++; break;
case 'Idiomas R (UNAM)':
case 'Idiomas Sabatino':
sa.Correo = true; sa.Red = true;
conteoTiposCorreo++; conteoTiposRed++; break;
default:
sa.Correo = true; sa.AT = true; sa.Red = true; sa.Prestamos = true;
conteoTiposCorreo++; conteoTiposAt++; conteoTiposRed++; conteoTiposSolicita++;
}
serviciosActivos.push(sa);
}
/* ===================== GUARDADOS ===================== */
await queryRunner.manager.save([...mapGenero.values()]);
await queryRunner.manager.save([...mapCarreraClave.values()]);
await queryRunner.manager.save([...mapTipo.values()]);
let conteoTiposRed: number = 0;
let conteoTiposAt: number = 0;
let conteoTiposSolicita: number = 0;
let conteoTiposCorreo: number = 0;
await queryRunner.manager.save(usuarios, { chunk: 500 });
idsUsuariosFinales.push(...usuarios.map(u => u.id_usuario));
const queryRunner = this.dataSource.createQueryRunner();
await queryRunner.connect();
await queryRunner.startTransaction();
let idsUsuarios: number[] =[]
try {
const movimiento = await this.movimientoService.logger(
queryRunner,
'LOAD',
'LOADING',
'Caraga masiva de usuarios hecha por: ' + user,
`CARGA MASIVA DE USUARIOS`,
);
await queryRunner.manager.save(carrerasUsuario, { chunk: 500 });
await queryRunner.manager.save(usuariosTipos, { chunk: 500 });
await queryRunner.manager.save(serviciosActivos, { chunk: 500 });
// 🔹 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);
await queryRunner.commitTransaction();
} catch (e) {
await queryRunner.rollbackTransaction();
throw e;
} finally {
await queryRunner.release();
}
const existentes = await queryRunner.manager.find(Usuario, {
where: [{ num_cuenta: In(cuentas) }, { rfc: In(rfcs) }],
select: ['num_cuenta', 'rfc', 'id_usuario'],
});
/* ===================== UPDATE FUERA ===================== */
await this.servActivosRepo
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) },
}),
]);
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]));
// 🔹 3. Arreglos para inserciones masivas
const usuarios: Usuario[] = [];
const carrerasUsuario: CarreraUsuario[] = [];
const usuariosTipos: UsuarioTipoUsuario[] = [];
const serviciosActivos: ServActivos[] = [];
let contador = 0;
for (const r of rows) {
if (setCuentas.has(r.cuenta) || (r.rfc && setRfcs.has(r.rfc))) {
const use= existentes.find( u => u.num_cuenta === r.cuenta || u.rfc === r.rfc)
if(use?.id_usuario)
idsUsuarios.push(use?.id_usuario)
contador++;
continue;
}
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++;
break;
case 'Extra Largo':
Object.assign(servData, { Correo: true, Prestamos: true });
conteoTiposCorreo++;
conteoTiposSolicita++;
break;
case 'Servicio Social':
Object.assign(servData, { Correo: true, AT: true, Red: true });
conteoTiposCorreo++;
conteoTiposAt++;
conteoTiposRed++;
break;
case 'Idiomas R (UNAM)':
case 'Idiomas Sabatino':
Object.assign(servData, { Correo: true, Red: true });
conteoTiposCorreo++;
conteoTiposRed++;
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++;
conteoTiposRed++;
conteoTiposAt++;
conteoTiposSolicita++;
break;
case 'Trabajadores':
Object.assign(servData, { Red: true });
conteoTiposRed++;
break;
}
serviciosActivos.push(
queryRunner.manager.create(ServActivos, servData),
);
}
// 🔹 4. Guardar en batch (orden correcto)
await queryRunner.manager.save([...mapGenero.values()]);
await queryRunner.manager.save([...mapCarreraClave.values()]);
await queryRunner.manager.save([...mapTipo.values()]);
// 1️⃣ Guardar usuarios primero — asigna IDs
await queryRunner.manager.save(usuarios);
idsUsuarios = usuarios.map(u => u.id_usuario);
idsUsuarios.push(
...existentes.map(e => e.id_usuario)
);
// 2️⃣ Reconstruir relaciones con usuarios ya guardados
// CarreraUsuario
const carrerasUsuarioFinal: CarreraUsuario[] = [];
for (const cu of carrerasUsuario) {
const usuarioConId = usuarios.find(
(u) => u.num_cuenta === cu.usuario.num_cuenta,
);
if (usuarioConId && cu.carrera) {
carrerasUsuarioFinal.push(
queryRunner.manager.create(CarreraUsuario, {
usuario: usuarioConId,
carrera: cu.carrera,
}),
);
}
}
// UsuarioTipoUsuario
const usuariosTiposFinal: UsuarioTipoUsuario[] = [];
for (const ut of usuariosTipos) {
const usuarioConId = usuarios.find(
(u) => u.num_cuenta === ut.usuario.num_cuenta,
);
if (usuarioConId && ut.tipoUsuario) {
usuariosTiposFinal.push(
queryRunner.manager.create(UsuarioTipoUsuario, {
usuario: usuarioConId,
tipoUsuario: ut.tipoUsuario,
}),
);
}
}
// ServActivos
const serviciosActivosFinal: ServActivos[] = [];
for (const sa of serviciosActivos) {
const usuarioConId = usuarios.find(
(u) => u.num_cuenta === sa.usuario.num_cuenta,
);
if (usuarioConId) {
serviciosActivosFinal.push(
queryRunner.manager.create(ServActivos, {
...sa,
usuario: usuarioConId,
}),
);
}
}
// 3️⃣ Guardar relaciones dependientes en orden
await queryRunner.manager.save(carrerasUsuarioFinal);
await queryRunner.manager.save(usuariosTiposFinal);
await queryRunner.manager.save(serviciosActivosFinal);
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();
const subQuery = await this.servActivosRepo.manager
.createQueryBuilder()
.select('1')
.from(Usuario, 'u')
.where('u.id_usuario = sa.id_usuario')
.andWhere('u.id_usuario IN (:...ids)')
.getQuery();
await this.servActivosRepo
.createQueryBuilder('sa')
.update()
.set({
Red: false,
@@ -707,29 +834,12 @@ export class ExcelService {
ATStatus: 'Inactivo',
CorreoStatus: 'Inactivo',
})
.where(
`NOT EXISTS (
SELECT 1
FROM usuario u
WHERE u.id_usuario = servActivos.id_usuario
AND u.id_usuario IN (:...ids)
)`,
{ ids: idsUsuariosFinales },
)
.where(`NOT EXISTS (${subQuery})`)
.setParameters({ ids: idsUsuarios })
.execute();
return {
inserted: rows.length - contador,
errors: errs,
conteoTiposAt,
conteoTiposCorreo,
conteoTiposRed,
conteoTiposSolicita,
};
}
}
}
// async generateCsv(): Promise<string> {
// const users = await this.usuarioRepo.find({