From 9d513c34391586444912d01c2eae3ab42340de9f Mon Sep 17 00:00:00 2001 From: Emilio Date: Fri, 21 Nov 2025 11:51:30 -0600 Subject: [PATCH] cambio de inf en los reportes --- mariadb/aria_log.00000001 | Bin 5152768 -> 5160960 bytes mariadb/aria_log_control | Bin 52 -> 52 bytes src/auth/auth.controller.ts | 35 +++++----- src/auth/auth.service.ts | 13 ++++ src/equipo/equipo.controller.ts | 7 +- src/equipo/equipo.service.ts | 89 ++++++++++++++++++------- src/usuarios/dto/update-usuario.dto.ts | 31 ++++++++- src/usuarios/usuarios.service.ts | 50 +++++++++++++- 8 files changed, 180 insertions(+), 45 deletions(-) diff --git a/mariadb/aria_log.00000001 b/mariadb/aria_log.00000001 index 63de30cc6a014f6b5ccae7dcfc8fab31934e48f4..1e1098e43e28a79698bcf80ead17df0ec2adc9fe 100644 GIT binary patch delta 2288 zcmYk*O;F5n9LMp0yB^9?v9d@)l;=gU5jMh76!N?Xix5H*l3jcVA%wL(KOi%mOlHj3 z12Y{Q%y4k%7~{~vAu|pRdsq*9dH+tonb-F>-}mL`d;iwt^94;lMad+y25GQ{XsDtU zqhYdWxJD>eaf;VSjgnQPm7p=QX{^R+yb?7*Nt&o+P10mdQHrK&no^af>6)RLnx)yA zqq(wcp5|+T(v_iwS|o=uwOC7(rKMV?<#H-pE3{HMTBX%mBbU}{opP0@_1f?{KNRqP z{gfLM8w%tbh7pdO0{^$njD!fJ)dMyH3JIG4UczQT7oiZ)OV|Q1H(>czfSs@n;3jMb zR1AOCze>00&_=z(d#rs3q(L1PMiee!@P0wFy=G0ZzgJKncME@DmOK zdI*OA1BAnX#AZ|#16+h7fHJ~SKnvj*;3J^~@SAWPkkW#x6MzE3NkAo`6wpaH1^7ZZ z{U4IU|d2j8x;_EzF@ac%9yg!ZJV+;VhtrP!4!QI0yJnr~t&ZVflGL7U2S* zm~atLN2mn6BU}RfAXEWt?Wnp8$RT(Er34?Kfp7)zfp8V@i*OB)+<__q@(9-f6@(jr z4nj5HGvOv6M7RY=dxompfI>nIz)QFT=px(&^b+m?%+ImB7GNjb2e=6j0M&$tfNnw^ zppWnfV0nS6#{dW63BW_}18NCR0YO3l&`+obSUXYG0B{l-0VRYcfS=F|=pnQK1_-Ty W#FwaQ1Got75%}qhj(?S1W%~nlyQ<9q delta 176 zcmV~$OEy9Q007X_qa;cQsgQpqzdp#X5TdfCH@JInT849sm{^jDkxBQ4;dc=JdmNH8z}0BB1Ez5oCK delta 27 gcmXppnII?jG+ARlBLl this.flattenObject(item)) - : [this.flattenObject(obj)]; - return this.exportToClient(objectParse, res); + const data = await this.generar_reporte(); + return this.exportarReporteAExcel(data, res); + } + + async exportarReporteAExcel(data: any, res: Response) { + const workbook = new ExcelJS.Workbook(); + + for (const key of Object.keys(data)) { + const sheetName = key.substring(0, 30); // Excel limita a 30 caracteres + const sheet = workbook.addWorksheet(sheetName); + + const rows = data[key]; + + if (!Array.isArray(rows) || rows.length === 0) { + sheet.addRow([`(sin datos)`]); + continue; + } + + // Obtener columnas automáticamente + const headers = Object.keys(rows[0]); + + // Crear encabezado + sheet.addRow(headers); + + // Agregar filas + for (const row of rows) { + sheet.addRow(headers.map((h) => row[h])); + } + + // Estilo básico + sheet.getRow(1).font = { bold: true }; + sheet.getRow(1).alignment = { horizontal: 'center' }; + sheet.columns.forEach((col) => { + col.width = 35; + }); + } + + // Enviar el archivo al cliente + res.setHeader( + 'Content-Type', + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + ); + res.setHeader('Content-Disposition', 'attachment; filename=reporte.xlsx'); + + await workbook.xlsx.write(res); + res.end(); } async createReport() { @@ -441,10 +482,8 @@ export class EquipoService { .createQueryBuilder('e') .innerJoin('e.tipoEquipo', 't') .where('t.tipo_equipo IN (:...tipo_equipo)', { tipo_equipo }) - .select(['t.tipo_equipo AS tipo_equipo']) - .addSelect('COUNT(*) AS total') - .groupBy('t.tipo_equipo') - .getRawMany(); + .select('COUNT(*) AS total') + .getRawOne(); return count; } catch (error) { @@ -489,27 +528,21 @@ export class EquipoService { .groupBy('e.antiguedad') .getRawMany(); - interface Porcentaje { - antiguedad: any; - total: any; - porcentaje: string; - } - return { escritorios: countEscritorios.map((i) => ({ antiguedad: i.antiguedad, total: i.total, - porcentaje: ((i.total / totalEscrtitorio) * 100).toFixed(2), + porcentaje: ((i.total / totalEscrtitorio.total) * 100).toFixed(2), })), portatiles: countPortatiles.map((i) => ({ antiguedad: i.antiguedad, total: i.total, - porcentaje: ((i.total / totalPortatiles) * 100).toFixed(2), + porcentaje: ((i.total / totalPortatiles.total) * 100).toFixed(2), })), altoRendimiento: countAltoRendimiento.map((i) => ({ antiguedad: i.antiguedad, total: i.total, - porcentaje: ((i.total / totalAltoRendimiento) * 100).toFixed(2), + porcentaje: ((i.total / totalAltoRendimiento.total) * 100).toFixed(2), })), }; } catch (error) { @@ -596,20 +629,25 @@ export class EquipoService { }> { try { // Helper para obtener total por tipos de equipo (count simple, sin group by) - const getTotalPorTipos = async (tipos: string[]): Promise => { + const getTotalPorTipos = async (tipos: string[]): Promise => { if (!tipos || tipos.length === 0) return 0; return this.equipoRepository .createQueryBuilder('e') - .innerJoin('e.tipoEquipo', 't') - .where('t.tipo_equipo IN (:...tipos)', { tipos }) - .getCount(); + .innerJoin('e.periferico', 'p') + .where('p.periferico IN (:...tipos)', { tipos }) + .select('COUNT(*) AS total') + .getRawOne(); }; // Ejecutar ambos totales en paralelo - const [totalImpresion, totalDigitalizacion] = await Promise.all([ + + const [totalI, totalD] = await Promise.all([ getTotalPorTipos(impresion), getTotalPorTipos(digitalizacion), ]); + const totalImpresion = totalI.total; + + const totalDigitalizacion = totalD.total; // Obtener conteos por antiguedad para impresion const countImpresionPromise = this.equipoRepository @@ -641,6 +679,11 @@ export class EquipoService { countDigitalizacionPromise, ]); + console.log('I', totalImpresion); + console.log('D', totalDigitalizacion); + console.log('I', countImpresion); + console.log('D', countDigitalizacion); + // Tipo para salida interface Porcentaje { antiguedad: any; diff --git a/src/usuarios/dto/update-usuario.dto.ts b/src/usuarios/dto/update-usuario.dto.ts index a2b8fbc..56453fa 100644 --- a/src/usuarios/dto/update-usuario.dto.ts +++ b/src/usuarios/dto/update-usuario.dto.ts @@ -1,4 +1,29 @@ -import { PartialType } from '@nestjs/mapped-types'; -import { CreateUsuarioDto } from './create-usuario.dto'; +import { DefaultValuePipe } from '@nestjs/common'; +import { Transform } from 'class-transformer'; +import { + IsInt, + IsNotEmpty, + IsOptional, + IsString, + MinLength, +} from 'class-validator'; -export class UpdateUsuarioDto extends PartialType(CreateUsuarioDto) {} +export class UpdateUsuarioDto { + @IsString() + @IsNotEmpty() + id_User: number; + + @IsString() + @IsOptional() + nombre?: string; + + @IsString() + @MinLength(5) + @Transform(({ value }) => value.trim()) + @IsOptional() + contraseña?: string; + + @IsInt() + @IsOptional() + tipoUsuario?: number; +} diff --git a/src/usuarios/usuarios.service.ts b/src/usuarios/usuarios.service.ts index 2465391..105c35b 100644 --- a/src/usuarios/usuarios.service.ts +++ b/src/usuarios/usuarios.service.ts @@ -1,8 +1,13 @@ -import { Injectable } from '@nestjs/common'; +import { + BadRequestException, + Injectable, + NotFoundException, +} from '@nestjs/common'; import { CreateUsuarioDto } from './dto/create-usuario.dto'; import { InjectRepository } from '@nestjs/typeorm'; import { Tipo_Usuario, Usuario } from './entities/usuario.entity'; import { Repository } from 'typeorm'; +import * as bcrypt from 'bcryptjs'; @Injectable() export class UsuariosService { @@ -13,6 +18,49 @@ export class UsuariosService { @InjectRepository(Tipo_Usuario) private readonly tipoUsuarioRepository: Repository, ) {} + + async actualizarUsuario( + id: number, + datos: { nombre?: string; contraseña?: string; id_tipo_usuario?: number }, + ) { + // Buscar al usuario + const usuario = await this.usuarioRepository.findOne({ + where: { id_usuario: id }, + relations: ['tipoUsuario'], + }); + + if (!usuario) { + throw new NotFoundException('Usuario no encontrado'); + } + + // Actualizar nombre + if (datos.nombre) { + usuario.nombre = datos.nombre; + } + + // Actualizar contraseña con hash + if (datos.contraseña) { + const hashed = await bcrypt.hash(datos.contraseña, 10); + usuario.contraseña = hashed; + } + + // Actualizar tipo de usuario + if (datos.id_tipo_usuario) { + const tipo = await this.tipoUsuarioRepository.findOne({ + where: { id_tipo_usuario: datos.id_tipo_usuario }, + }); + + if (!tipo) { + throw new BadRequestException('El tipo de usuario no existe'); + } + + usuario.tipoUsuario = tipo; + } + + // Guardar cambios + return await this.usuarioRepository.save(usuario); + } + async create(createUsuarioDto: CreateUsuarioDto) { let user = await this.usuarioRepository.create({ nombre: createUsuarioDto.nombre,