diff --git a/src/app.module.ts b/src/app.module.ts index af3a5de..20b4306 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -41,7 +41,7 @@ import { UsuariosModule } from './usuarios/usuarios.module'; logger: 'advanced-console', retryAttempts: 5, retryDelay: 2000, - dropSchema: true, // solo para desarrollo + dropSchema: false, // solo para desarrollo }; }, }), diff --git a/src/auth/auth.documentation.ts b/src/auth/auth.documentation.ts index afe30cd..537a7f6 100644 --- a/src/auth/auth.documentation.ts +++ b/src/auth/auth.documentation.ts @@ -77,7 +77,7 @@ export class AuthDocumentation { properties: { email: { type: 'string', format: 'email', example: 'nuevo@dominio.com' }, password: { type: 'string', example: 'Password123' }, - origen: { type: 'string', example: 'redes', description: 'Origen del usuario' } + origen: { type: 'string', example: 'RED', description: 'Origen del usuario' } } } @@ -91,7 +91,7 @@ export class AuthDocumentation { id_usuario: { type: 'number', example: 1 }, correo: { type: 'string', example: 'nuevo@dominio.com' }, - origen: { type: 'string', example: 'redes' } + origen: { type: 'string', example: 'RED' } } diff --git a/src/entities/entities.ts b/src/entities/entities.ts index 45097ae..27d6c61 100644 --- a/src/entities/entities.ts +++ b/src/entities/entities.ts @@ -1,4 +1,5 @@ -import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, JoinColumn, PrimaryColumn } from 'typeorm'; +import { ServActivos } from 'src/usuarios/entities/servActivos.entitie'; +import { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, JoinColumn, PrimaryColumn, OneToOne } from 'typeorm'; @Entity({ name: 'origen' }) export class Origen { @@ -42,6 +43,44 @@ export class Carrera { carreraUsuarios: CarreraUsuario[]; } +@Entity({ name: 'movimiento' }) +export class Movimiento { + @PrimaryGeneratedColumn({ name: 'id_mov', type: 'int' }) + id_mov: number; + + @Column({ name: 'fecha_mov', type: 'datetime' }) + fecha_mov: Date; + + @Column({ type: 'varchar', length: 100 }) + status: string; + + @Column({ + type: 'varchar', + length: 200, + nullable: true, // permite NULL + }) + observaciones?: string; + + @Column({ type: 'bit' }) + flag: boolean; + + @Column({ + type: 'varchar', + length: 200, + nullable: true, // permite NULL + }) + reporte?: string; + + @OneToMany(() => Usuario, usuario => usuario.movimiento) + usuario: Usuario[]; + + + @ManyToOne(() => Origen, origen => origen.movimientos) + @JoinColumn({ name: 'id_origen' }) + origen: Origen; +} + + @Entity({ name: 'usuario' }) export class Usuario { @PrimaryGeneratedColumn({ name: 'id_usuario', type: 'int' }) @@ -89,7 +128,17 @@ export class Usuario { @OneToMany(() => UsuarioTipoUsuario, utu => utu.usuario) usuarioTipos: UsuarioTipoUsuario[]; + + @ManyToOne(() => Movimiento, movimiento => movimiento.usuario) + @JoinColumn({ name: 'id_movimiento' }) + movimiento: Movimiento; + + @OneToOne(() => ServActivos, (servActivo) => servActivo.usuario,) + servActivo: ServActivos; } + + + @Entity({ name: 'carrera_usuario' }) export class CarreraUsuario { @PrimaryGeneratedColumn({ name: 'id_carrera_usuario', type: 'int' }) @@ -152,6 +201,12 @@ export class TipoUsuario { usuariosTipos: UsuarioTipoUsuario[]; } + + + + + + @Entity({ name: 'usuario_tipo_usuario' }) export class UsuarioTipoUsuario { @PrimaryGeneratedColumn({ name: 'id_user_tipo_usuario', type: 'int' }) @@ -166,36 +221,6 @@ export class UsuarioTipoUsuario { usuario: Usuario; } -@Entity({ name: 'movimiento' }) -export class Movimiento { - @PrimaryGeneratedColumn({ name: 'id_mov', type: 'int' }) - id_mov: number; - @Column({ name: 'fecha_mov', type: 'datetime' }) - fecha_mov: Date; +export { ServActivos }; - @Column({ type: 'varchar', length: 100 }) - status: string; - - @Column({ - type: 'varchar', - length: 200, - nullable: true, // permite NULL - }) - observaciones?: string; - - @Column({ type: 'bit' }) - flag: boolean; - - @Column({ - type: 'varchar', - length: 200, - nullable: true, // permite NULL - }) - reporte?: string; - - - @ManyToOne(() => Origen, origen => origen.movimientos) - @JoinColumn({ name: 'id_origen' }) - origen: Origen; -} diff --git a/src/excel/excel.controller.ts b/src/excel/excel.controller.ts index 1a1fcc5..c1ad4ae 100644 --- a/src/excel/excel.controller.ts +++ b/src/excel/excel.controller.ts @@ -1,5 +1,5 @@ // src/excel/excel.controller.ts -import { Controller, Post, Get, UseGuards, Request, Res, UploadedFile, UseInterceptors, HttpStatus } from '@nestjs/common'; +import { Controller, Post, Get, UseGuards, Request, Res, UploadedFile, UseInterceptors, HttpStatus, Param, ParseIntPipe } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; import { ApiBearerAuth } from '@nestjs/swagger'; import { FileInterceptor } from '@nestjs/platform-express'; @@ -22,7 +22,8 @@ export class ExcelController { @ExcelDocumentation.verifyExcel() async verifyExcel(@UploadedFile() file: Express.Multer.File, @Request() req) { const userId: number = req.user.userId; // ahora sí existe - const errors = await this.excelService.validateFile(file.buffer); + const status = await this.excelService.validateFile(file.buffer); + const errors = status.errors; await this.movimientoService.log( 'VERIFY', @@ -47,14 +48,17 @@ export class ExcelController { } try { const result = await this.excelService.loadFile(file.buffer); - await this.movimientoService.log( - - 'LOAD', + //Actualizar el estado de los movimientos + if (!result) { + throw new Error('No se pudo procesar el archivo.'); + } + await this.movimientoService.updateStatus( + result.id_movimiento, 'SUCCESS', - undefined, `inserted=${result.inserted}` ); - return result; + return result + } catch (err) { await this.movimientoService.log( @@ -189,4 +193,28 @@ export class ExcelController { } } + + //Agregarlo en su propio controller + @Get('movimientos') + async getMovimientos() { + const movimientos = await this.movimientoService.findAll(); + return movimientos; + } + + @Post('activar-servicios/:id_movimiento') + async activarServicios( + @Param('id_movimiento', ParseIntPipe) id_movimiento: number, + @Request() req, + ): Promise<{ message: string }> { + const origen = req.user.origen; + + if (!origen) { + throw new Error('No se encontró origen en el JWT'); + } + + await this.excelService.activarServicios(id_movimiento, origen); + + return { message: 'Servicios activados correctamente' }; + } + } \ No newline at end of file diff --git a/src/excel/excel.module.ts b/src/excel/excel.module.ts index 4351d3f..984a73b 100644 --- a/src/excel/excel.module.ts +++ b/src/excel/excel.module.ts @@ -4,10 +4,12 @@ import { ExcelController } from './excel.controller'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Usuario, Genero, Carrera, CarreraUsuario, UsuarioTipoUsuario, TipoUsuario } from '../entities/entities'; import { MovimientoModule } from 'src/movimiento/movimiento.module'; +import { ServActivos } from 'src/usuarios/entities/servActivos.entitie'; + @Module({ imports: [ - TypeOrmModule.forFeature([Usuario, Genero, Carrera, CarreraUsuario, UsuarioTipoUsuario, TipoUsuario]), + TypeOrmModule.forFeature([Usuario, Genero, Carrera, CarreraUsuario, UsuarioTipoUsuario, TipoUsuario, ServActivos]), MovimientoModule ], providers: [ExcelService], diff --git a/src/excel/excel.service.ts b/src/excel/excel.service.ts index 04c33f4..189e1e5 100644 --- a/src/excel/excel.service.ts +++ b/src/excel/excel.service.ts @@ -6,6 +6,8 @@ import { InjectRepository } from '@nestjs/typeorm'; import { CarreraUsuario, TipoUsuario, Usuario, UsuarioTipoUsuario } from '../entities/entities'; import { Genero } from '../entities/entities'; import { Carrera } from '../entities/entities'; +import { MovimientoService } from 'src/movimiento/movimiento.service'; +import { ServActivos } from 'src/usuarios/entities/servActivos.entitie'; export interface UsuarioRow { cuenta: string; @@ -40,6 +42,10 @@ export class ExcelService { private readonly usuarioTipoUsuarioRepo: Repository, @InjectRepository(TipoUsuario) private readonly tipoUsuarioRepo: Repository, + @InjectRepository(ServActivos) + private readonly servActivosRepo: Repository, + private readonly movimientoService: MovimientoService, + // … inyecta otros repositorios si los usarás ) { } @@ -101,23 +107,35 @@ export class ExcelService { validateRows(rows: UsuarioRow[]) { const errors: string[] = []; const seen = new Set(); + const rowsGood: UsuarioRow[] = []; + let flagError: boolean = false; rows.forEach((r, i) => { const rowNum = i + 2; // por el encabezado // Campos que no pueden quedar vacíos - ['cuenta', 'nombreCompleto'].forEach(field => { + ['cuenta', 'nombreCompleto', 'tipo'].forEach(field => { const raw = (r as any)[field]; const str = raw != null ? raw.toString().trim() : ''; if (!str) { errors.push(`Fila ${rowNum}: campo "${field}" está vacío.`); + flagError = true; } }); + // El número de cuenta debe ser de nueve dígitos para los alumnos + if (r.cuenta && !/^\d{9}$/.test(r.cuenta.toString().trim()) && r.tipo.trim() === 'Licenciatura') { + errors.push(`Fila ${rowNum}: cuenta "${r.cuenta}" debe tener 9 dígitos.`); + flagError = true; + } + + + // Duplicados de "cuenta" const cuentaStr = r.cuenta != null ? r.cuenta.toString().trim() : ''; if (seen.has(cuentaStr)) { errors.push(`Fila ${rowNum}: cuenta duplicada "${cuentaStr}".`); + flagError = true; } else { seen.add(cuentaStr); } @@ -128,6 +146,7 @@ export class ExcelService { const genStr = r.gen != null ? r.gen.toString().trim() : ''; if (genStr && !/^[0-9]{4}$/.test(genStr)) { errors.push(`Fila ${rowNum}: generación inválida "${genStr}".`); + flagError = true; } @@ -135,16 +154,29 @@ export class ExcelService { const fechaStr = r.fechnac != null ? r.fechnac.toString().trim() : ''; if (!/^[0-9]{8}$/.test(fechaStr)) { errors.push(`Fila ${rowNum}: fecha de nacimiento inválida "${fechaStr}".`); + flagError = true; } // RFC alfanumérico const rfcStr = r.rfc != null ? r.rfc.toString().trim() : ''; if (rfcStr && !/^[A-Z0-9]+$/.test(rfcStr)) { errors.push(`Fila ${rowNum}: RFC contiene caracteres no válidos.`); + flagError = true; } + + if (!flagError) { + rowsGood.push(r) + } else { + flagError = false; // reset para la siguiente fila + } + + + }); - return errors; + console.log(rowsGood); + + return { errors, rowsGood }; } @@ -153,21 +185,42 @@ export class ExcelService { */ async validateFile(buffer: Buffer) { const rows = await this.parseFile(buffer); - const errs = this.validateRows(rows); - return errs; + const status = this.validateRows(rows); + return status; } /** * Valida y luego persiste usuarios y relaciones básicas */ async loadFile(buffer: Buffer) { - const rows = await this.parseFile(buffer); - const errs = this.validateRows(rows); - if (errs.length) { - throw new BadRequestException({ errors: errs }); - } + const file = await this.parseFile(buffer); + const status = this.validateRows(file); + const errs = status.errors; + const rows = status.rowsGood; + + const movimiento = await this.movimientoService.log( + + 'LOAD', + 'LOADING', + undefined + ); + + + + 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.`); + 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) { @@ -200,6 +253,8 @@ export class ExcelService { 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, @@ -210,9 +265,10 @@ export class ExcelService { fecha_nacimiento: r.fechnac, generacion: generacion, genero, + movimiento: { id_mov: movimiento.id_mov }, // Relación con movimiento }); - await this.usuarioRepo.save(usuario); + const user = await this.usuarioRepo.save(usuario); @@ -231,14 +287,50 @@ export class ExcelService { }); await this.usuarioTipoUsuarioRepo.save(usuarioTipo); + // 6) ServActivos (si es necesario) + switch (r.tipo.trim()) { + + + 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; + } + } - - return { inserted: rows.length }; + return { inserted: rows.length, id_movimiento: movimiento.id_mov, errors: errs }; } + + // async generateCsv(): Promise { // const users = await this.usuarioRepo.find({ // relations: ['genero', 'carreraUsuarios'], // si quieres más info @@ -293,6 +385,9 @@ export class ExcelService { tipo_usuario: In(tipos), }, }, + servActivo: { + CorreoStatus: 'Inactivo', // solo los que no tienen servicio activo + } }, relations: [ 'genero', @@ -355,6 +450,9 @@ export class ExcelService { tipo_usuario: In(tipos), }, }, + servActivo: { + PrestamosStatus: 'Inactivo', // solo los que no tienen servicio activo + } }, relations: [ 'genero', @@ -433,6 +531,9 @@ export class ExcelService { tipo_usuario: In(tipos), }, }, + servActivo: { + RedStatus: 'Inactivo', + } }, relations: [ 'genero', @@ -483,6 +584,9 @@ export class ExcelService { tipo_usuario: In(tipos), }, }, + servActivo: { + ATStatus: 'Inactivo', + }, }, relations: [ 'genero', @@ -523,17 +627,52 @@ export class ExcelService { + //Funcion que se le debe hacer su propio módulo + async activarServicios(id_movimiento: number, origen: string): Promise { + if (!['AT', 'SOLICITA', 'CORREO', 'RED'].includes(origen)) { + throw new BadRequestException(`Origen ${origen} no válido`); + } + const usuarios = await this.usuarioRepo.find({ + where: { movimiento: { id_mov: id_movimiento } }, + }); + if (!usuarios || usuarios.length === 0) { + throw new BadRequestException('No se encontró el usuario asociado al movimiento'); + } + for (const usuario of usuarios) { + const servActivos = await this.servActivosRepo.findOne({ + where: { usuario: { id_usuario: usuario.id_usuario } }, + }); + if (!servActivos) { + throw new BadRequestException( + `No se encontraron servicios activos para el usuario con ID ${usuario.id_usuario}`, + ); + } + const timestamp = 'Activo ' + new Date().toISOString(); + if (origen === 'AT' && servActivos.AT) { + servActivos.ATStatus = timestamp; + } else if (origen === 'RED' && servActivos.Red) { + servActivos.RedStatus = timestamp; + } else if (origen === 'CORREO' && servActivos.Correo) { + servActivos.CorreoStatus = timestamp; + } else if (origen === 'SOLICITA' && servActivos.Prestamos) { + servActivos.PrestamosStatus = timestamp; + } else { + throw new BadRequestException( + `Origen ${origen} no válido o servicio no activo para el usuario con ID ${usuario.id_usuario}`, + ); + } + await this.servActivosRepo.save(servActivos); + console.log(`Servicio ${servActivos}`); + } - - - + } diff --git a/src/movimiento/movimiento.service.ts b/src/movimiento/movimiento.service.ts index 45168f6..f91e5fd 100644 --- a/src/movimiento/movimiento.service.ts +++ b/src/movimiento/movimiento.service.ts @@ -20,19 +20,50 @@ export class MovimientoService { observaciones?: string, reporte?: string, flag = false, - ) { + ): Promise { // 1) Obtén el origen o falla si no existe const origin = await this.origenRepo.findOne({ where: { origen } }); if (!origin) throw new Error(`Origen desconocido: ${origen}`); - // 2) Inserta directamente usando los campos de FK - await this.movRepo.insert({ + // 2) Crea el objeto movimiento + const movimiento = this.movRepo.create({ fecha_mov: new Date(), status, - observaciones, // aquí usas el valor que vino como parámetro - reporte, // idem + observaciones, + reporte, flag, origen: { id_origen: origin.id_origen }, }); + + // 3) Guarda y devuelve el ID + const saved = await this.movRepo.save(movimiento); + return saved; } + + + /** Actualiza el status de un movimiento existente */ + async updateStatus(id_movimiento: number, nuevoStatus: string, observaciones?: string): Promise { + const movimiento = await this.movRepo.findOne({ where: { id_mov: id_movimiento } }); + + if (!movimiento) { + throw new Error(`Movimiento con ID ${id_movimiento} no encontrado`); + } + + movimiento.status = nuevoStatus; + movimiento.observaciones = observaciones || movimiento.observaciones; // opcional: actualizar observaciones + movimiento.fecha_mov = new Date(); // opcional: actualizar fecha + + await this.movRepo.save(movimiento); + } + + async findAll(): Promise { + return await this.movRepo.find({ where: { origen: { origen: 'LOAD' } } }); + } + + + + + + + } diff --git a/src/usuarios/entities/servActivos.entitie.ts b/src/usuarios/entities/servActivos.entitie.ts new file mode 100644 index 0000000..d3980ba --- /dev/null +++ b/src/usuarios/entities/servActivos.entitie.ts @@ -0,0 +1,39 @@ +import { Usuario } from "src/entities/entities"; +import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from "typeorm"; + +@Entity({ name: 'servActivos' }) +export class ServActivos { + @PrimaryGeneratedColumn({ name: 'id_servActivos' }) + id_servActivos: number; + + @OneToOne(() => Usuario, (usuario) => usuario.servActivo, { onDelete: 'CASCADE' }) + @JoinColumn({ name: 'id_usuario' }) + usuario: Usuario; + + + @Column({ type: 'boolean', default: false }) + Red: boolean; + + @Column({ type: 'boolean', default: false }) + Prestamos: boolean; + + @Column({ type: 'boolean', default: false }) + AT: boolean; + + @Column({ type: 'boolean', default: false }) + Correo: boolean; + + @Column({ type: 'varchar', length: 40, nullable: true }) + RedStatus: string; + + @Column({ type: 'varchar', length: 40, nullable: true }) + PrestamosStatus: string; + + @Column({ type: 'varchar', length: 40, nullable: true }) + ATStatus: string; + + @Column({ type: 'varchar', length: 40, nullable: true }) + CorreoStatus: string; + + +} diff --git a/src/usuarios/usuarios.controller.ts b/src/usuarios/usuarios.controller.ts index 0c80045..a0d1e65 100644 --- a/src/usuarios/usuarios.controller.ts +++ b/src/usuarios/usuarios.controller.ts @@ -14,7 +14,7 @@ export class UsuariosController { @Get(':noCuenta') findOne(@Param('noCuenta') noCuenta: string) { - return this.usuariosService.findOne(noCuenta); + return this.usuariosService.findOneStatus(noCuenta); } // @UseGuards(AuthGuard('jwt')) // @ApiBearerAuth('bearer') diff --git a/src/usuarios/usuarios.module.ts b/src/usuarios/usuarios.module.ts index 92f81aa..1741048 100644 --- a/src/usuarios/usuarios.module.ts +++ b/src/usuarios/usuarios.module.ts @@ -4,13 +4,16 @@ import { UsuariosController } from './usuarios.controller'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Carrera, CarreraUsuario, Genero, Usuario } from 'src/entities/entities'; import { MovimientoModule } from 'src/movimiento/movimiento.module'; +import { ServActivos } from './entities/servActivos.entitie'; @Module({ imports: [ - TypeOrmModule.forFeature([Usuario, Genero, Carrera, CarreraUsuario]), + TypeOrmModule.forFeature([Usuario, Genero, Carrera, CarreraUsuario, ServActivos]), MovimientoModule + ], controllers: [UsuariosController], providers: [UsuariosService], + exports: [TypeOrmModule.forFeature([ServActivos])], }) export class UsuariosModule { } diff --git a/src/usuarios/usuarios.service.ts b/src/usuarios/usuarios.service.ts index 37bfb32..251251c 100644 --- a/src/usuarios/usuarios.service.ts +++ b/src/usuarios/usuarios.service.ts @@ -4,6 +4,7 @@ import { UpdateUsuarioDto } from './dto/update-usuario.dto'; import { InjectRepository } from '@nestjs/typeorm'; import { CarreraUsuario, Usuario } from 'src/entities/entities'; import { Repository } from 'typeorm'; +import { ServActivos } from './entities/servActivos.entitie'; @Injectable() export class UsuariosService { @@ -12,47 +13,82 @@ export class UsuariosService { private readonly usuarioRepository: Repository, @InjectRepository(CarreraUsuario) private readonly carreraUsuarioRepository: Repository, + @InjectRepository(ServActivos) + private readonly servActivosRepository: Repository, ) { } altaIndividual(createUsuarioDto: CreateUsuarioDto) { return 'This action adds a new usuario'; } - async findOne(noCuenta: string) { + async findOneStatus(noCuenta: string) { const usuario = await this.usuarioRepository.findOne({ - relations: [ - 'genero', - 'carreraUsuarios', - 'carreraUsuarios.carrera', - ], where: { num_cuenta: noCuenta }, - + select: ['id_usuario'], }); + + + + if (!usuario) { throw new NotFoundException('Usuario no encontrado'); } + const servActivo = await this.servActivosRepository.findOne({ + where: { usuario: { id_usuario: usuario.id_usuario } }, + + }); + if (!servActivo) { + throw new NotFoundException('Servicios no encontrados para el usuario'); + } + + + // Transformar a formato plano - const resultadoPlano = { - num_cuenta: usuario.num_cuenta, - nombre: usuario.nombre, - a_paterno: usuario.a_paterno, - a_materno: usuario.a_materno, - fecha_nacimiento: usuario.fecha_nacimiento, - generacion: usuario.generacion, - genero: usuario.genero?.genero ?? '', - carreras: (usuario.carreraUsuarios ?? []) - .map(cu => cu.carrera?.carrera) - .filter(Boolean) - .join(', '), // todas las carreras como string plano - }; + let resultadoPlano = {}; + + if (servActivo.AT) { + resultadoPlano = { + AT: servActivo.ATStatus + } + } + if (servActivo.Red) { + resultadoPlano = { + ...resultadoPlano, + Red: servActivo.RedStatus + } + } + if (servActivo.Prestamos) { + resultadoPlano = { + ...resultadoPlano, + Prestamos: servActivo.PrestamosStatus + } + } + if (servActivo.Correo) { + resultadoPlano = { + ...resultadoPlano, + Correo: servActivo.CorreoStatus + } + } + + // Si no hay servicios activos, retornar un objeto vacío + if (Object.keys(resultadoPlano).length === 0) { + resultadoPlano = { + Servicios: 'No hay servicios activos para este usuario' + }; + } + console.log('Resultado plano:', resultadoPlano); return [resultadoPlano]; // importante: frontend espera array + + } + + update(id: number, updateUsuarioDto: UpdateUsuarioDto) { return `This action updates a #${id} usuario`; }