93 Commits

Author SHA1 Message Date
evenegas 47b3933079 se actualizaron dependencia 2026-07-29 11:22:48 -06:00
evenegas 1c6f92658d se actualizaron dependencias y corriguio movimientos 2026-07-28 23:21:30 -06:00
evenegas 08ed18ff4b agregación de distinct 2026-06-23 14:10:56 -06:00
IO 655e45a876 modified graficaPolar to search tecnico and profesor 2026-04-07 12:24:52 -05:00
IO 3c9a28a960 fixed tablaExcel 2026-04-07 12:06:50 -05:00
IO 29ffb0e327 added new ep graficaPolar 2026-03-26 09:52:09 -05:00
Tyrannuss 3dc49766cc se agrego un end point para perifericos 2026-03-23 18:55:29 -06:00
IO 36cf0d217b add where idPeriferico is null 2026-03-23 13:57:31 -05:00
IO 970a6acc0b fixed now can drive Todos 2026-03-20 19:04:53 -05:00
IO cd5f2efc54 added pool limit and modified logging 2026-03-19 16:06:04 -05:00
IO b76c98b7fc modified table 2026-03-19 11:00:09 -05:00
IO a3eea6bf2e added new ep tabla and tablaExcel 2026-03-18 17:02:59 -05:00
IO d2910bbdd7 modified the service grafica 2026-03-13 19:45:52 -05:00
IO 379f6844fb new filter 2026-03-13 19:00:50 -05:00
IO 435c61586d git add new ep grafica and dto 2026-03-13 18:49:04 -05:00
IO 19bde0d891 npm audit fix 2026-03-06 17:45:52 -06:00
IO 0e9e179bd8 modified ranking 2026-01-12 13:44:00 -06:00
IO 4a70ad3aa7 modified ranking 2026-01-12 13:39:46 -06:00
IO 45fdc9fbde added lugar and obsevaciones 2025-12-08 15:15:56 -06:00
evenegas ae1cec3bee Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Censo_api 2025-12-08 11:44:34 -06:00
evenegas 483f8cd203 se agregaron cambios de al reporte 2025-12-08 11:39:46 -06:00
IO 495d611486 added lugar and observaciones 2025-12-05 16:48:14 -06:00
IO a7ee97f2f1 fixed contar_tipoEquipos and get user 2025-12-05 16:43:32 -06:00
IO b3673fbae4 added ranking 2025-12-03 18:49:17 -06:00
IO ba5e95a50a modified date desc 2025-12-02 19:09:35 -06:00
IO 6e4112d9fa created counter 2025-12-02 19:06:07 -06:00
Emilio-11 c321917ed3 se agregaron reportes por estado 2025-11-28 16:04:44 -06:00
IO 62f1776a5e modified update user 2025-11-27 18:48:32 -06:00
evenegas 9c6f9ef3fd Se corrigieron el xlsx, validación de contraseñas y cron 2025-11-26 11:08:37 -06:00
evenegas 7afd16566a Se corrigieron el xlsx, validación de contraseñas y cron 2025-11-26 11:08:25 -06:00
evenegas 7b7892fca7 Se corrigieron el xlsx, validación de contraseñas y cron 2025-11-26 10:59:49 -06:00
evenegas 9b2e675afb Se corrigieron el xlsx, validación de contraseñas y cron 2025-11-26 09:56:35 -06:00
evenegas b737064d93 se agregò endpoint para traer ususarios y funcion diaria 2025-11-25 11:31:22 -06:00
evenegas 99c08b11b5 Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Censo_api into develop 2025-11-25 08:57:58 -06:00
evenegas 806a5ac130 se quitaron console.log 2025-11-25 08:53:23 -06:00
IO 5d8a379a7e change length 2025-11-24 14:58:38 -06:00
evenegas 9d513c3439 cambio de inf en los reportes 2025-11-21 11:51:30 -06:00
evenegas 3472aa911d se cambiò el tamaño del campo serie y se agregò nuevos reportes 2025-11-21 10:25:11 -06:00
evenegas f57d8d114d se cambiò el tamaño del campo serie y se agregò nuevos reportes 2025-11-21 10:24:44 -06:00
evenegas df87cb3c58 se cambiò el tamaño del campo serie y se agregò nuevos reportes 2025-11-21 10:21:29 -06:00
evenegas 519dba72fe se agregaron nuevas tablas 2025-11-20 10:50:13 -06:00
evenegas 882160432d se cambio End point 2025-11-20 09:07:07 -06:00
evenegas 6630dc2f4e se agregó usuarios en procesador 2025-11-19 12:26:17 -06:00
evenegas 75167caee1 se agregó usuarios en procesador 2025-11-19 12:13:29 -06:00
evenegas 2be7270c05 creaciòn de runas nuevas para reportes 2025-11-19 09:59:16 -06:00
evenegas 118d5f1abf se agregaron cambios al meter equipo 2025-11-13 14:15:05 -06:00
evenegas 4905111922 se agregaron cambios al meter equipo 2025-11-13 14:14:20 -06:00
evenegas 6b29c582c7 se agregaron cambios al meter equipo 2025-11-13 14:14:09 -06:00
evenegas 85f62efd7e mov y fecha 2025-11-13 13:27:58 -06:00
evenegas fa1aa3c11b mov y fecha 2025-11-13 12:06:09 -06:00
evenegas 8ebc2deba0 se agregó endpoint para borrar y traer obervaciones 2025-11-12 11:10:35 -06:00
evenegas 1467fe769a Se cambio la longitud de los campos 2025-11-12 10:13:54 -06:00
evenegas d99155186f Se cambio la longitud de los campos 2025-11-12 10:13:33 -06:00
evenegas 5f68509986 Mdificaciòn en actualizar 2025-11-12 10:00:25 -06:00
IO 0e56bdcf61 added useGuard 2025-11-11 17:44:25 -06:00
IO ab452aa4c8 Merge branch 'Axel' of https://repositorio.acatlan.unam.mx/CIDWA/Censo_api into Lino 2025-11-11 17:43:54 -06:00
Tyrannuss df293f6f7c Se creo el controller y service para eliminar equipo por ID 2025-11-11 17:23:35 -06:00
evenegas 764ee7e5d3 cambio de la base 2025-11-11 13:59:10 -06:00
evenegas 01867a2dcd cambio de la base 2025-11-11 13:52:51 -06:00
evenegas 4c758062dd cambio de la base de datos 2025-11-11 13:51:11 -06:00
evenegas 679b1d5f20 cambio de la base de datos 2025-11-11 13:48:45 -06:00
evenegas 61b9dbc3bf se cambiaron relaciones 2025-11-11 11:23:48 -06:00
evenegas 090c5af0b3 crear relaciones en tabla intermedia 2025-11-11 09:18:22 -06:00
evenegas 0cbc8b8f68 se modificó nombre de pc 2025-11-10 17:07:37 -06:00
evenegas c38e7be6a0 se modificó endpoint y base de datps 2025-11-10 10:58:11 -06:00
evenegas 37c721ef87 cambio en req 2025-11-07 13:23:56 -06:00
evenegas 34736b965e cambio en actualizacion 2025-11-07 12:05:50 -06:00
evenegas 339b4dd548 cambio en actualizacion 2025-11-07 12:05:38 -06:00
evenegas 978db238d4 cambios en consultas 2025-11-07 09:50:52 -06:00
evenegas ad4114144c Se hiciero correcones en equipo service 2025-11-06 08:57:41 -06:00
evenegas a59922213d se agregó endpoint de ejemplo 2025-11-05 11:09:19 -06:00
evenegas ef710d8461 se agregó endpoint de ejemplo 2025-11-05 11:06:02 -06:00
evenegas 7e7ee75511 se agregó endpoint de ejemplo 2025-11-05 10:43:08 -06:00
evenegas 5861064c8d se creo reportes 2025-11-04 13:54:39 -06:00
evenegas 91f151d14b Se agregaron 2025-11-04 10:01:02 -06:00
evenegas dec89fd9e7 se agrego el modo pruebas 2025-11-03 10:00:31 -06:00
evenegas 3ac0e9e78c se agrego el modo pruebas 2025-11-03 09:59:03 -06:00
evenegas b853d02be4 nuevo endpoint 2025-10-31 12:11:12 -06:00
evenegas bf2d6d6907 arreglo de errores 2025-10-31 09:56:01 -06:00
evenegas fb44748315 arreglo de errores 2025-10-31 09:44:54 -06:00
evenegas d092980f4a se se agregó una tabla 2025-10-31 09:08:31 -06:00
evenegas 74df68c5e0 se cambió el package.json 2025-10-31 09:02:14 -06:00
evenegas d421a23443 se cambiaron restricciones 2025-10-31 08:30:56 -06:00
evenegas dcd217c26f se cambiaron restricciones 2025-10-31 08:30:49 -06:00
evenegas d514cf8714 bcryptjs 2025-10-30 10:14:46 -06:00
evenegas 4a9c6e5eb7 merge 2025-10-30 10:13:17 -06:00
evenegas 14ac2bd0d1 Se agregaron los perifericos 2025-10-30 10:08:23 -06:00
IO 3317b7bdbe added start 2025-10-29 18:10:16 -06:00
IO e6cafdfb78 change argon2 to bcrypt 2025-10-29 16:52:16 -06:00
stern 7d48df9830 Modificación a script start en package.json para uso de Node 22 2025-10-29 15:32:19 -06:00
evenegas 332575a8e1 Se creo endpoint para crear equipos 2025-10-29 13:55:11 -06:00
evenegas 578c42b959 Se agrego Cors, nombre a FK, trae responsables 2025-10-29 09:47:48 -06:00
evenegas a3bcb0e3aa Se pasaron cambios de cante a develop 2025-10-29 08:55:25 -06:00
25 changed files with 5385 additions and 2583 deletions
+22
View File
@@ -0,0 +1,22 @@
PORT=""
JWT=
DB_HOST=
DB_PORT=
DB_USER=
DB_PASS=
DB_NAME=
#Aqui va la direccion del forntend
CORS_ORIGINS=""
SINCRONIZAR="true"
AUTOLOAD="false"
#Aqui va la direccion de la API de Directorio Funcionarios
API_FUNCIONARIOS=""
MODO="pruebas"
Binary file not shown.
Binary file not shown.
+2673 -2424
View File
File diff suppressed because it is too large Load Diff
+23 -19
View File
@@ -20,16 +20,19 @@
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^11.1.6",
"@nestjs/common": "^11.1.28",
"@nestjs/core": "^11.0.1",
"@nestjs/jwt": "^11.0.1",
"@nestjs/mapped-types": "*",
"@nestjs/jwt": "^11.0.2",
"@nestjs/mapped-types": "^2.1.1",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.0.1",
"@nestjs/typeorm": "^11.0.0",
"argon2": "^0.44.0",
"@nestjs/schedule": "^6.1.3",
"@nestjs/typeorm": "^11.0.3",
"axios": "^1.13.1",
"bcryptjs": "^3.0.2",
"class-transformer": "0.5.1",
"class-validator": "0.14.2",
"exceljs": "^4.4.0",
"mysql2": "^3.15.2",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
@@ -39,31 +42,32 @@
"typeorm": "^0.3.27"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
"@eslint/eslintrc": "^3.3.6",
"@eslint/js": "^9.18.0",
"@nestjs/cli": "^11.0.0",
"@nestjs/config": "^4.0.2",
"@nestjs/schematics": "^11.0.0",
"@nestjs/testing": "^11.0.1",
"@types/express": "^5.0.0",
"@nestjs/testing": "^11.1.28",
"@types/bcrypt": "^6.0.0",
"@types/express": "^5.0.6",
"@types/jest": "^30.0.0",
"@types/node": "^22.10.7",
"@types/node": "^22.20.1",
"@types/passport-jwt": "^4.0.1",
"@types/supertest": "^6.0.2",
"eslint": "^9.18.0",
"eslint": "^10.8.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-prettier": "^5.2.2",
"globals": "^16.0.0",
"jest": "^30.0.0",
"prettier": "^3.4.2",
"eslint-plugin-prettier": "^5.5.6",
"globals": "^16.5.0",
"jest": "^30.4.2",
"prettier": "^3.9.6",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-jest": "^29.2.5",
"ts-loader": "^9.5.2",
"supertest": "^7.2.2",
"ts-jest": "^29.4.12",
"ts-loader": "^9.6.2",
"ts-node": "^10.9.2",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.7.3",
"typescript-eslint": "^8.20.0"
"typescript-eslint": "^8.65.0"
},
"jest": {
"moduleFileExtensions": [
@@ -82,4 +86,4 @@
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
}
+7 -3
View File
@@ -13,6 +13,7 @@ import { AppController } from './app.controller';
ConfigModule.forRoot({
isGlobal: true,
}),
TypeOrmModule.forRoot({
type: 'mariadb',
host: process.env.DB_HOST,
@@ -23,10 +24,13 @@ import { AppController } from './app.controller';
autoLoadEntities: process.env.AUTOLOAD === 'true',
synchronize: process.env.SINCRONIZAR === 'true',
logger: 'advanced-console',
logging: 'all',
logging: ['error', 'warn','log'],
extra: {
connectionLimit: 10,
},
}),
UsuariosModule,
MovimientoModule,
EquipoModule,
AuthModule,
@@ -34,4 +38,4 @@ import { AppController } from './app.controller';
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
export class AppModule { }
+43 -15
View File
@@ -1,27 +1,55 @@
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
import {
Body,
Controller,
Get,
NotFoundException,
Patch,
Post,
Req,
UseGuards,
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { CreateUsuarioDto } from 'src/usuarios/dto/create-usuario.dto';
import { LoginDto } from './dto/login.dto';
import { AuthGuard } from '@nestjs/passport';
import { UpdateUsuarioDto } from 'src/usuarios/dto/update-usuario.dto';
import { not } from 'rxjs/internal/util/not';
@Controller('auth')
export class AuthController {
constructor( private readonly authService:AuthService){}
constructor(private readonly authService: AuthService) {}
@Post("registro")
registro(@Body() registroDto:CreateUsuarioDto ){
return this.authService.registro(registroDto);
@UseGuards(AuthGuard('jwt'))
@Post('registro')
registro(@Body() registroDto: CreateUsuarioDto) {
return this.authService.registro(registroDto);
}
@Post('registro/Masivo')
registroMasivo(@Body() registroDto: CreateUsuarioDto[]) {
return this.authService.registroMasivo(registroDto);
}
@UseGuards(AuthGuard('jwt'))
@Get('buscar')
search() {
return this.authService.findAll();
}
@UseGuards(AuthGuard('jwt'))
@Patch('update')
update(@Body() registroDto: UpdateUsuarioDto, @Req() req: any) {
const id_usuario = req.user.id;
if (!id_usuario) {
throw new NotFoundException('token sin id_usuario');
}
@Post("login")
login(@Body() loginDto:LoginDto){
return this.authService.login(loginDto);
}
return this.authService.update(registroDto, id_usuario);
}
@Post('login')
login(@Body() loginDto: LoginDto) {
return this.authService.login(loginDto);
}
}
+36 -7
View File
@@ -5,10 +5,11 @@ import {
InternalServerErrorException,
} from '@nestjs/common';
import { UsuariosService } from 'src/usuarios/usuarios.service';
import * as argon2 from 'argon2';
import * as bcrypt from 'bcryptjs';
import { LoginDto } from './dto/login.dto';
import { CreateUsuarioDto } from 'src/usuarios/dto/create-usuario.dto';
import { JwtService } from '@nestjs/jwt';
import { UpdateUsuarioDto } from 'src/usuarios/dto/update-usuario.dto';
@Injectable()
export class AuthService {
@@ -17,6 +18,20 @@ export class AuthService {
private readonly jwtService: JwtService,
) {}
async update(registroDto: UpdateUsuarioDto, id_User: number) {
const update_data = await this.usuarioService.actualizarUsuario(id_User, {
nombre: registroDto.nombre,
contraseñaN: registroDto.contraseñaN,
id_tipo_usuario: registroDto.tipoUsuario,
contraseñaV: registroDto.contraseñaV,
});
return update_data;
}
async findAll() {
return await this.usuarioService.usuarios();
}
async registro({ nombre, contraseña, tipoUsuario }: CreateUsuarioDto) {
const usuario = await this.usuarioService.findOneByName(nombre);
if (usuario) {
@@ -24,7 +39,8 @@ export class AuthService {
}
try {
const hashedContraseña = await argon2.hash(contraseña);
const salt = await bcrypt.genSalt(10);
const hashedContraseña = await bcrypt.hash(contraseña, salt);
await this.usuarioService.create({
nombre,
@@ -40,29 +56,42 @@ export class AuthService {
}
}
async registroMasivo(usuarios: CreateUsuarioDto[]) {
let resultados: string[] = [];
for (const usuario of usuarios) {
const creado = await this.registro(usuario);
resultados.push(creado.message);
}
return resultados;
}
async login({ nombre, contraseña }: LoginDto) {
const usuario = await this.usuarioService.findOneByName(nombre);
if (!usuario) {
throw new UnauthorizedException('Usuario no encontrado');
}
const contraseñaValida = await argon2.verify(
usuario.contraseña,
const contraseñaValida = await bcrypt.compare(
contraseña,
usuario.contraseña,
);
if (!contraseñaValida) {
throw new UnauthorizedException('Contraseña invalida');
throw new UnauthorizedException('Contraseña inválida');
}
const dataUser = {
id: usuario.id_usuario,
nombre: usuario.nombre,
tipoUsuario: usuario.tipoUsuario.id_tipo_usuario,
};
const token = await this.jwtService.sign(dataUser);
const token = this.jwtService.sign(dataUser);
return {
token: token,
token,
};
}
}
+63 -1
View File
@@ -1,3 +1,65 @@
import {
IsString,
IsOptional,
IsDateString,
IsInt,
IsNotEmpty,
} from 'class-validator';
export class CreateEquipoDto {
@IsString()
inventario: string;
@IsString()
@IsOptional()
serie?: string;
@IsString()
lugar: string;
@IsDateString()
fechaFactura: Date;
@IsString()
antiguedad: string;
@IsString()
modelo: string;
@IsInt()
id_estado: number;
@IsInt()
id_adscripcion: number;
@IsInt()
id_tipo_equipo: number;
@IsInt()
id_sistema_operativo: number;
@IsInt()
@IsOptional()
id_procesador?: number;
@IsInt()
id_uso: number;
@IsInt()
id_marca: number;
@IsInt()
id_periferico: number;
@IsInt()
@IsOptional()
id_laboratorio?: number;
@IsInt()
@IsOptional()
id_proyecto?: number;
@IsNotEmpty()
@IsOptional()
isImpresora?: Boolean;
}
+66
View File
@@ -0,0 +1,66 @@
import { IsArray, IsOptional, IsString } from "class-validator";
export class FiltrosGraficaDto {
@IsOptional()
@IsArray()
@IsString({ each: true })
adscripciones?: number[]
@IsOptional()
@IsArray()
@IsString({ each: true })
procesadores?: number[]
@IsOptional()
@IsArray()
@IsString({ each: true })
sistemas?: number[]
@IsOptional()
@IsArray()
@IsString({ each: true })
usos?: number[]
@IsOptional()
@IsArray()
@IsString({ each: true })
antiguedad?: string[]
}
export class FiltrosTablaDto {
@IsArray()
@IsString({ each: true })
adscripciones?: string[]
@IsOptional()
@IsArray()
@IsString({ each: true })
procesadores?: string[]
@IsOptional()
@IsArray()
@IsString({ each: true })
sistemas?: string[]
@IsOptional()
@IsArray()
@IsString({ each: true })
usos?: string[]
@IsOptional()
@IsArray()
@IsString({ each: true })
antiguedad?: string[]
}
export class FiltrosPolarDto {
@IsOptional()
@IsArray()
@IsString({ each: true })
adscripciones?: string[]
}
+46 -28
View File
@@ -1,38 +1,56 @@
import { PartialType } from '@nestjs/mapped-types';
import { CreateEquipoDto } from './create-equipo.dto';
import { IsInt, IsOptional, IsString } from 'class-validator';
import { IsInt, IsOptional, IsString } from 'class-validator';
export class UpdateEquipoDto extends PartialType(CreateEquipoDto) {
@IsInt()
@IsOptional()
id_estado?:number
@IsInt()
@IsOptional()
id_adscripcion?: number;
@IsString()
@IsOptional()
serie?: string;
@IsString()
@IsOptional()
lugar?: string;
@IsInt()
@IsOptional()
id_sistema_operativo?: number;
@IsInt()
@IsOptional()
id_procesador?: number;
@IsInt()
@IsOptional()
id_estado?: number;
@IsInt()
@IsOptional()
id_tipo_uso?: number;
@IsInt()
@IsOptional()
id_adscripcion?: number;
@IsString()
@IsOptional()
modelo?: string;
@IsString()
@IsOptional()
lugar?: string;
@IsInt()
@IsOptional()
id_marca?: number;
@IsInt()
@IsOptional()
id_sistema_operativo?: number;
@IsInt()
@IsOptional()
id_procesador?: number;
@IsInt()
@IsOptional()
id_tipo_uso?: number;
@IsString()
@IsOptional()
modelo?: string;
@IsInt()
@IsOptional()
id_marca?: number;
@IsString()
observaciones: string;
@IsInt()
@IsOptional()
id_periferico?: number;
@IsInt()
@IsOptional()
id_proyecto?: number;
@IsInt()
@IsOptional()
id_laboratorio?: number;
}
+55 -13
View File
@@ -1,24 +1,54 @@
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
import {
Column,
Entity,
OneToMany,
PrimaryColumn,
PrimaryGeneratedColumn,
} from 'typeorm';
import { Equipo } from './equipo.entity';
@Entity()
export class Uso {
@PrimaryGeneratedColumn()
id_tipo_uso: number;
@PrimaryGeneratedColumn({ name: 'id_uso' })
id_uso: number;
@Column()
@Column({ type: 'varchar', length: 100 })
tipo_uso: string;
@OneToMany(() => Equipo, (equipo) => equipo.tipoUso)
equipo: Equipo[];
}
@Entity()
export class Laboratorio {
@PrimaryGeneratedColumn()
id_laboratorio: number;
@Column({ type: 'varchar', length: 20 })
laboratorio: string;
@OneToMany(() => Equipo, (equipo) => equipo.laboratorio)
equipo: Equipo[];
}
@Entity()
export class Proyecto {
@PrimaryGeneratedColumn()
id_proyecto: number;
@Column({ type: 'varchar', length: 10 })
proyecto: string;
@OneToMany(() => Equipo, (equipo) => equipo.proyecto)
equipo: Equipo[];
}
@Entity()
export class Marca {
@PrimaryGeneratedColumn()
id_marca: number;
@Column()
tipo_marca: string;
@Column({ type: 'varchar', length: 100 })
marca: string;
@OneToMany(() => Equipo, (equipo) => equipo.marca)
equipo: Equipo[];
@@ -29,7 +59,7 @@ export class Estado {
@PrimaryGeneratedColumn()
id_estado: number;
@Column()
@Column({ type: 'varchar', length: 100 })
estado: string;
@OneToMany(() => Equipo, (equipo) => equipo.estado)
@@ -38,10 +68,10 @@ export class Estado {
@Entity()
export class Adscripcion {
@PrimaryGeneratedColumn()
@PrimaryColumn()
id_adscripcion: number;
@Column()
@Column({ type: 'varchar', length: 300 })
adscripcion: string;
@OneToMany(() => Equipo, (equipo) => equipo.adscripcion)
@@ -53,10 +83,10 @@ export class TipoEquipo {
@PrimaryGeneratedColumn()
id_tipo_de_equipo: number;
@Column()
@Column({ type: 'varchar', length: 200 })
tipo_equipo: string;
@OneToMany(() => Equipo, (equipo) => equipo.tipoEquipo)
@OneToMany(() => Equipo, (eqp) => eqp.tipoEquipo)
equipo: Equipo[];
}
@@ -65,7 +95,7 @@ export class SistemaOperativo {
@PrimaryGeneratedColumn()
id_sistema_operativo: number;
@Column()
@Column({ type: 'varchar', length: 200 })
sistema_operativo: string;
@OneToMany(() => Equipo, (equipo) => equipo.sistemaOperativo)
@@ -77,9 +107,21 @@ export class Procesador {
@PrimaryGeneratedColumn()
id_procesador: number;
@Column()
@Column({ type: 'varchar', length: 200 })
procesador: string;
@OneToMany(() => Equipo, (equipo) => equipo.procesador)
equipo: Equipo[];
}
@Entity()
export class Periferico {
@PrimaryGeneratedColumn()
id_periferico: number;
@Column({ type: 'varchar', length: 100 })
periferico: string;
@OneToMany(() => Equipo, (equipo) => equipo.periferico)
equipo: Equipo[];
}
+38 -11
View File
@@ -1,6 +1,7 @@
import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
@@ -8,8 +9,11 @@ import {
import {
Adscripcion,
Estado,
Laboratorio,
Marca,
Periferico,
Procesador,
Proyecto,
SistemaOperativo,
TipoEquipo,
Uso,
@@ -22,52 +26,75 @@ export class Equipo {
id_equipo: number;
//checar bien aqui si estoy bien
@Column({ length: 200, nullable: true })
@Column({ type: 'varchar', length: 10, nullable: true })
inventario: string;
@Column({ length: 200, nullable: true })
@Column({ type: 'varchar', length: 30, nullable: true })
serie: string;
@Column({ length: 200, nullable: true })
@Column({ type: 'varchar', length: 200, nullable: true })
lugar: string;
@Column({ name: 'fecha_factura' })
fechaFactura: Date;
@Column({ length: 200, nullable: true })
@Column({ type: 'varchar', length: 30, nullable: true })
antiguedad: string;
@Column({ length: 200, nullable: true })
@Column({ type: 'varchar', length: 40, nullable: true })
modelo: string;
//Relaciones
@OneToMany(() => Movimiento, (mov) => mov.equipo)
mov: Movimiento[];
@JoinColumn({ name: 'id_estado' })
@ManyToOne(() => Estado, (estado) => estado.equipo)
estado: Estado;
@JoinColumn({ name: 'id_adscripcion' })
@ManyToOne(() => Adscripcion, (adscripcion) => adscripcion.equipo)
adscripcion: Adscripcion;
@ManyToOne(() => TipoEquipo, (tipoEquipo) => tipoEquipo.equipo)
tipoEquipo: TipoEquipo;
@JoinColumn({ name: 'id_sistema_operativo' })
@ManyToOne(
() => SistemaOperativo,
(sistemaOperativo) => sistemaOperativo.equipo,
)
sistemaOperativo: SistemaOperativo;
@ManyToOne(() => Procesador, (procesador) => procesador.equipo)
@JoinColumn({ name: 'id_tipo_equipo' })
@ManyToOne(() => TipoEquipo, (tipoEquipo) => tipoEquipo.equipo)
tipoEquipo: TipoEquipo;
@JoinColumn({ name: 'id_procesador' })
@ManyToOne(() => Procesador, (procesador) => procesador.equipo, {
nullable: true,
onDelete: 'SET NULL',
})
procesador: Procesador;
@JoinColumn({ name: 'id_uso' })
@ManyToOne(() => Uso, (tipoUso) => tipoUso.equipo)
tipoUso: Uso;
@JoinColumn({ name: 'id_marca' })
@ManyToOne(() => Marca, (marca) => marca.equipo)
marca: Marca;
@OneToMany(() => Equipo, (equipo) => equipo.marca)
equipo: Equipo[];
@JoinColumn({ name: 'id_periferico' })
@ManyToOne(() => Periferico, (Periferico) => Periferico.equipo)
periferico: Periferico;
@JoinColumn({ name: 'id_laboratorio' })
@ManyToOne(() => Laboratorio, (laboratorio) => laboratorio.equipo, {
nullable: true,
})
laboratorio?: Laboratorio | null;
@JoinColumn({ name: 'id_proyecto' })
@ManyToOne(() => Proyecto, (proyecto) => proyecto.equipo, {
nullable: true,
})
proyecto?: Proyecto | null;
}
+409 -5
View File
@@ -7,14 +7,24 @@ import {
UseGuards,
Get,
Query,
Delete,
NotFoundException,
Post,
Res,
Request,
BadRequestException,
} from '@nestjs/common';
import { EquipoService } from './equipo.service';
import { UpdateEquipoDto } from './dto/update-equipo.dto';
import { AuthGuard } from '@nestjs/passport';
import { CreateEquipoDto } from './dto/create-equipo.dto';
import type { Response } from 'express';
import { FiltrosGraficaDto, FiltrosPolarDto, FiltrosTablaDto } from './dto/grafica.dto';
@Controller('equipos')
export class EquipoController {
constructor(private readonly equipoService: EquipoService) {}
constructor(private readonly equipoService: EquipoService) { }
@UseGuards(AuthGuard('jwt'))
@Get('buscar')
async buscarEquipos(
@@ -26,18 +36,110 @@ export class EquipoController {
}
@UseGuards(AuthGuard('jwt'))
@Get('buscar/:inv')
serch(@Param('inv') inv: string) {
return this.equipoService.findEquipo(inv);
@Post('grafica/:tipo')
async grafica(
@Param("tipo") tipo: 'antiguedad' | 'uso' | 'so' | 'procesador',
@Body() filtros: FiltrosGraficaDto
) {
return this.equipoService.grafica(tipo, filtros);
}
@Post('graficaPolar/:tipo')
async graficaPolar(
@Param("tipo") tipo: 'ALUMNO' | 'PROFESOR' | 'ADMINISTRATIVO',
@Body() filtros: FiltrosPolarDto
) {
return this.equipoService.graficaPolar(tipo, filtros);
}
@UseGuards(AuthGuard('jwt'))
@Post('graficaPerifericos/:tipo')
async graficaPerifericos(
@Param("tipo") tipo: 'antiguedad' | 'uso' | 'so' | 'procesador',
@Body() filtros: FiltrosGraficaDto
) {
return this.equipoService.graficaPerifericos(tipo, filtros);
}
@UseGuards(AuthGuard('jwt'))
@Post('tabla')
async tabla(
@Body() filtros: FiltrosTablaDto
) {
return this.equipoService.tabla(filtros);
}
@UseGuards(AuthGuard('jwt'))
@Post('tabla/count')
async tablacount(
@Body() filtros: FiltrosTablaDto
) {
return this.equipoService.tablaCount(filtros);
}
@UseGuards(AuthGuard('jwt'))
@Post('excel/tabla')
async exceltabla(
@Res() res: Response,
@Body() filtros: FiltrosTablaDto
) {
const buffer = await this.equipoService.tablaExcel(filtros);
res.set({
'Content-Type':
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'Content-Disposition': 'attachment; filename=tabla.xlsx',
});
res.send(buffer);
}
@UseGuards(AuthGuard('jwt'))
@Post('excel/tabla/count')
async exceltablacount(
@Res() res: Response,
@Body() filtros: FiltrosGraficaDto
) {
const buffer = await this.equipoService.tablaCountExcel(filtros);
res.set({
'Content-Type':
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'Content-Disposition': 'attachment; filename=tabla.xlsx',
});
res.send(buffer);
}
@UseGuards(AuthGuard('jwt'))
@Get('buscar/:inv')
async serch(@Param('inv') inv: string) {
return await this.equipoService.findEquipo(inv);
}
@UseGuards(AuthGuard('jwt'))
@Post('crear')
async create(@Body() Equipo: CreateEquipoDto, @Request() req) {
return await this.equipoService.create(Equipo, req.user.id);
}
@UseGuards(AuthGuard('jwt'))
@Patch('update/:id')
update(
@Request() req,
@Param('id', ParseIntPipe) id: number,
@Body() equipoNuevo: UpdateEquipoDto,
) {
return this.equipoService.updateEquipo(equipoNuevo, id);
return this.equipoService.updateEquipo(equipoNuevo, id, req.user.id);
}
@UseGuards(AuthGuard('jwt'))
@Delete(':id')
async deleteEquipo(@Param('id', ParseIntPipe) id_equipo: string) {
try {
return await this.equipoService.deleteEquipo(id_equipo);
} catch (error) {
throw new NotFoundException(error.message);
}
}
@UseGuards(AuthGuard('jwt'))
@@ -51,11 +153,13 @@ export class EquipoController {
findMarcas() {
return this.equipoService.findAllMarcas();
}
@UseGuards(AuthGuard('jwt'))
@Get('estados')
findEstados() {
return this.equipoService.findAllEstados();
}
@UseGuards(AuthGuard('jwt'))
@Get('adscripciones')
findAdscripciones() {
@@ -66,6 +170,7 @@ export class EquipoController {
findTiposEquipo() {
return this.equipoService.findAllTiposEquipo();
}
@UseGuards(AuthGuard('jwt'))
@Get('sistemas-operativos')
findSistemasOperativos() {
@@ -76,4 +181,303 @@ export class EquipoController {
findProcesadores() {
return this.equipoService.findAllProcesadores();
}
@UseGuards(AuthGuard('jwt'))
@Get('laboratorios')
findLaboratorios() {
return this.equipoService.findAllLaboratorios();
}
@UseGuards(AuthGuard('jwt'))
@Get('proyectos')
findProyectos() {
return this.equipoService.findAllProyectos();
}
@UseGuards(AuthGuard('jwt'))
@Get('perifericos')
findPerifericos() {
return this.equipoService.findAllPerifericos();
}
@UseGuards(AuthGuard('jwt'))
@Get('procesador-tipo-equipos')
findProcesadorTipoEquipos() {
return this.equipoService.findAllProcesadorTipoEquipos();
}
// @Post('daily')
// async daily() {
// return await this.equipoService.llenadoDeAntiguedad();
// }
@UseGuards(AuthGuard('jwt'))
@Get('reporteXLSX')
async generarReporte(@Res({ passthrough: true }) res: Response) {
try {
await this.equipoService.createEcxel(res);
} catch (error) {
console.error('Error al generar el Excel:', error);
res.status(500).send('Error al generar el archivo Excel');
}
}
@Post('reporte')
async reporte(@Body() estado: string[]) {
try {
return await this.equipoService.generar_reporte(estado);
} catch (error) {
throw new BadRequestException('Error al generar el reporte:', error);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/tipoEquipos_tipoUso')
async reportetipoEquipos_tipoUso(@Body() estado: string[]) {
try {
return await this.equipoService.contar_tipoEquipos_tipoUso(estado);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte tipoEquipos_tipoUso:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/tipoEquipos_sistemasOperativos')
async reportetipoEquipos_sistemasOperativos(@Body() estado: string[]) {
try {
return await Promise.all([
this.equipoService.contar_tipoEquipos_sistemasOperativos(
equpos_pc,
estado,
),
this.equipoService.contar_tipoEquipos_sistemasOperativos(
[estritorio_mac],
estado,
),
this.equipoService.contar_tipoEquipos_sistemasOperativos(
portatiles,
estado,
),
this.equipoService.contar_tipoEquipos_sistemasOperativos(
[portatiles_mac],
estado,
),
this.equipoService.contar_tipoEquipos_sistemasOperativos(
[servidor],
estado,
),
]);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte tipoEquipos_sistemasOperativos:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/tipoEquipos_procesador')
async reportetipoEquipos_procesador(@Body() estado: string[]) {
try {
return await Promise.all([
this.equipoService.contar_tipoEquipos_procesador(equpos_pc, estado),
this.equipoService.contar_tipoEquipos_procesador(
[estritorio_mac],
estado,
),
this.equipoService.contar_tipoEquipos_procesador(portatiles, estado),
this.equipoService.contar_tipoEquipos_procesador(
[portatiles_mac],
estado,
),
this.equipoService.contar_tipoEquipos_procesador([servidor], estado),
]);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte tipoEquipos_procesador:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/tipoEquipos_antiguedad')
async reportetipoEquipos_antiguedad(@Body() estado: string[]) {
try {
return await this.equipoService.contar_tipoEquipos_antiguedad(estado);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte contar_tipoEquipos_antiguedad:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/garantia')
async reportegarantia(@Body() estado: string[]) {
try {
return await Promise.all([
this.equipoService.garantia(escritorio, estado),
this.equipoService.garantia(portatil, estado),
this.equipoService.garantia([servidor], estado),
]);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte garantia:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/equipos_impesion_group/impresoras')
async reporteequipos_impesion_group(@Body() estado: string[]) {
try {
return await this.equipoService.equipos_impesion_group(
impresoras,
estado,
);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte impesion_group impresoras:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/equipos_impesion_group/dijitales')
async reporteequipos_impesion_group_dijitales(@Body() estado: string[]) {
try {
return await this.equipoService.equipos_impesion_group(dijitales, estado);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte impesion_group dijitales:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/contar_periferico_tipoUso/impresoras')
async reporte_contar_periferico_tipoUso_impresoras(@Body() estado: string[]) {
try {
return await this.equipoService.contar_periferico_tipoUso(
impresoras,
estado,
);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte periferico_tipoUso impresoras:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/contar_periferico_tipoUso/dijtales')
async reporte_contar_periferico_tipoUso_dijtales(@Body() estado: string[]) {
try {
return await this.equipoService.contar_periferico_tipoUso(
dijitales,
estado,
);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte periferico_tipoUso dijtales:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/contar_perifericos_antiguedad')
async reporte_contar_perifericos_antiguedad(@Body() estado: string[]) {
try {
return await this.equipoService.contar_perifericos_antiguedad(
impresoras,
dijitales,
estado,
);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte perifericos_antiguedad:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/contar_laboratorio')
async reporte_contar_laboratorio(@Body() estado: string[]) {
try {
return await this.equipoService.laboratorio_equipos(estado);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte perifericos_antiguedad:',
error,
);
}
}
@UseGuards(AuthGuard('jwt'))
@Post('reporte/contar_proyecto')
async reporte_contar_proyecto(@Body() estado: string[]) {
try {
return await this.equipoService.proyecto_equipos(estado);
} catch (error) {
throw new BadRequestException(
'Error al generar el reporte perifericos_antiguedad:',
error,
);
}
}
@Get('ranking/:year')
async ranking(@Param('year') year: string) {
return this.equipoService.ranking(Number(year));
}
}
let escritorio = ['ESCRITORIO PC', 'ESCRITORIO MAC OS ', 'ESCRITORIO LINUX'];
let portatil = [
'PORTÁTILES WINDOWS',
'PORTÁTILES CHROMEBOOK',
'PORTÁTILES MAC OS',
];
let equipos_mac = ['ESCRITORIO MAC OS ', 'PORTÁTILES MAC OS'];
let equpos_pc = ['ESCRITORIO PC', 'ESCRITORIO LINUX'];
let estritorio_mac = 'ESCRITORIO MAC OS ';
let portatiles_mac = 'PORTÁTILES MAC OS';
let portatiles = ['PORTÁTILES WINDOWS', 'PORTÁTILES CHROMEBOOK'];
let tabletas = ['TABLETA ANDROID', 'TABLETA iPAD OS'];
let impresoras = [
'INYECCIÓN TINTA',
'LÁSER DE ALTO VOLUMEN B/N',
'LÁSER DE ALTO VOLUMEN COLOR',
'LÁSER PEQUEÑA B/N',
'LÁSER PEQUEÑA COLOR',
'MATRIZ DE PUNTOS',
'MULTIFUNCIONALES',
'IMPRESORA TÉRMICA',
'IMPRESORA CREDENCIALES',
'PLOTTER',
];
let dijitales = [
'3D',
'DIGITALIZADOR DE CAMA PLANA PARA OFICINA',
'DIGITALIZADOR DE GRAN VOLUMEN',
'DIGITALIZADOR CON ALIMENTADOR DE HOJAS PARA OFICINA',
];
let servidor = 'SERVIDOR';
+30 -3
View File
@@ -3,12 +3,39 @@ import { EquipoService } from './equipo.service';
import { EquipoController } from './equipo.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Equipo } from './entities/equipo.entity';
import { Estado, Marca, Uso, Adscripcion, TipoEquipo, SistemaOperativo, Procesador } from './entities/catalogo.entities';
import {
Estado,
Marca,
Uso,
Adscripcion,
TipoEquipo,
SistemaOperativo,
Procesador,
Periferico,
Proyecto,
Laboratorio,
} from './entities/catalogo.entities';
import { MovimientoModule } from 'src/movimiento/movimiento.module';
@Module({
imports:[TypeOrmModule.forFeature([Equipo, Estado, Marca, Uso, Adscripcion, TipoEquipo, SistemaOperativo, Procesador])],
imports: [
TypeOrmModule.forFeature([
Equipo,
Estado,
Marca,
Uso,
Adscripcion,
TipoEquipo,
SistemaOperativo,
Procesador,
Periferico,
Proyecto,
Laboratorio,
]),
MovimientoModule,
],
controllers: [EquipoController],
providers: [EquipoService],
exports:[EquipoService],
exports: [EquipoService],
})
export class EquipoModule {}
File diff suppressed because it is too large Load Diff
+12 -1
View File
@@ -4,7 +4,18 @@ import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Habilita validación global para que DTOs invalidos devuelvan 400 en lugar de 500
let origen
if (process.env.MODO === 'pruebas') {
origen = '*'
} else {
origen = [process.env.CORS_ORIGINS]
}
app.enableCors({
origin: origen,
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true,
});
app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));
await app.listen(process.env.PORT ?? 3000);
}
+2 -2
View File
@@ -9,7 +9,7 @@ export class CreateMovimientoDto {
@IsNumber()
idEquipo: number;
@IsNotEmpty()
@IsOptional()
@IsString()
observaciones: string;
observaciones?: string;
}
+12 -4
View File
@@ -1,21 +1,29 @@
import { Equipo } from 'src/equipo/entities/equipo.entity';
import { Usuario } from 'src/usuarios/entities/usuario.entity';
import { Entity, PrimaryGeneratedColumn, Column, ManyToOne } from 'typeorm';
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
JoinColumn,
} from 'typeorm';
@Entity('movimientos')
export class Movimiento {
@PrimaryGeneratedColumn()
id_movimiento: number;
@ManyToOne(() => Usuario, user => user.movimiento)
@JoinColumn({ name: 'id_usuario' })
@ManyToOne(() => Usuario, (user) => user.movimiento)
user: Usuario;
@ManyToOne(() => Equipo, equipo=> equipo.mov)
@JoinColumn({ name: 'id_equipo' })
@ManyToOne(() => Equipo, (equipo) => equipo.mov)
equipo: Equipo;
@Column({ name: 'fecha_movimiento' })
fechaMovimiento: Date;
@Column({ type: 'text', nullable: true })
@Column({ type: 'varchar', length: 100, nullable: true })
observaciones: string;
}
+21 -7
View File
@@ -1,18 +1,32 @@
import {
Controller,
Post,
Get,
Body,
Param,
ParseIntPipe,
Patch,
UseGuards,
Req,
NotFoundException,
} from '@nestjs/common';
import { MovimientoService } from './movimiento.service';
import { CreateMovimientoDto } from './dto/create-movimiento.dto';
import { UpdateMovimientoDto } from './dto/update-movimiento.dto';
import { AuthGuard } from '@nestjs/passport';
@Controller('movimientos')
export class MovimientoController {
constructor(private readonly movimientoService: MovimientoService) {}
@UseGuards(AuthGuard('jwt'))
@Get('historial')
buscarEquipoPorToken(@Req() req: any) {
const id_usuario = req.user.id;
if (!id_usuario) {
throw new NotFoundException('token sin id_usuario');
}
return this.movimientoService.searchByIdUser(id_usuario);
}
@UseGuards(AuthGuard('jwt'))
@Get('dia')
EquiposCensadosPorDia() {
return this.movimientoService.countCensosPorDiaUnicos();
}
}
+67 -9
View File
@@ -6,26 +6,84 @@ import { Usuario } from 'src/usuarios/entities/usuario.entity';
import { Equipo } from 'src/equipo/entities/equipo.entity';
import { CreateMovimientoDto } from './dto/create-movimiento.dto';
@Injectable()
export class MovimientoService {
constructor(
@InjectRepository(Movimiento)
private readonly movimientoRepo: Repository<Movimiento>,
) {}
async crear(dto: CreateMovimientoDto) {
const movimiento = this.movimientoRepo.create({
user:{id_usuario:dto.idUsuario},
equipo:{id_equipo:dto.idEquipo},
fechaMovimiento:new Date(),
observaciones:dto.observaciones
const movimiento = this.movimientoRepo.create({
user: { id_usuario: dto.idUsuario },
equipo: { id_equipo: dto.idEquipo },
fechaMovimiento: new Date(),
observaciones: dto.observaciones,
});
return await this.movimientoRepo.save(movimiento);
}
async buscar(idEquipo: number) {
const movimiento = await this.movimientoRepo.findOne({
where: { equipo: { id_equipo: idEquipo } },
order: { fechaMovimiento: 'DESC' },
relations: ['user'],
});
return movimiento;
}
async searchByIdUser(id_usuario: number) {
const movimientos = await this.movimientoRepo
.createQueryBuilder('mov')
.leftJoinAndSelect('mov.equipo', 'equipo')
.leftJoinAndSelect('equipo.tipoEquipo', 'tipoEquipo')
.leftJoinAndSelect('equipo.periferico', 'periferico')
.leftJoin('mov.user', 'user')
.where('user.id_usuario = :id_usuario', { id_usuario })
.orderBy('mov.fechaMovimiento', 'DESC')
.getMany();
if (!movimientos.length) {
throw new NotFoundException('Sin equipos modificados o guardados');
}
const vistos = new Set<string>();
const unicos: Movimiento[] = [];
for (const mov of movimientos) {
const inventario = mov.equipo?.inventario;
if (!vistos.has(inventario)) {
vistos.add(inventario);
unicos.push(mov);
}
}
return unicos.map((mov) => ({
inventario: mov.equipo?.inventario,
tipo_equipo: mov.equipo?.tipoEquipo?.tipo_equipo,
periferico: mov.equipo?.periferico?.periferico,
fechaMovimiento: mov.fechaMovimiento,
lugar: mov.equipo?.lugar,
observaciones: mov.observaciones,
}));
}
async countCensosPorDiaUnicos() {
const result = await this.movimientoRepo
.createQueryBuilder('mov')
.innerJoin('mov.equipo', 'equipo')
.select('DATE(mov.fechaMovimiento)', 'fecha')
.addSelect('COUNT(DISTINCT equipo.inventario)', 'total')
.groupBy('DATE(mov.fechaMovimiento)')
.orderBy('fecha', 'DESC')
.getRawMany();
return result.map((r) => ({
fecha: r.fecha,
total: Number(r.total),
}));
}
}
-2
View File
@@ -1,4 +1,3 @@
import { DefaultValuePipe } from "@nestjs/common"
import { Transform } from "class-transformer"
import { IsInt, IsNotEmpty, IsOptional, IsString, MinLength} from "class-validator"
@@ -18,5 +17,4 @@ contraseña:string
@IsOptional()
tipoUsuario?:number
}
+30 -3
View File
@@ -1,4 +1,31 @@
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()
@IsOptional()
nombre?: string;
@IsString()
@MinLength(5)
@Transform(({ value }) => value.trim())
@IsOptional()
contraseñaN?: string;
@IsInt()
@IsOptional()
tipoUsuario?: number;
@IsString()
@MinLength(5)
@Transform(({ value }) => value.trim())
@IsOptional()
contraseñaV?: string;
}
+5 -3
View File
@@ -2,6 +2,7 @@ import { Movimiento } from 'src/movimiento/entities/movimiento.entity';
import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
@@ -12,7 +13,7 @@ export class Tipo_Usuario {
@PrimaryGeneratedColumn()
id_tipo_usuario: number;
@Column({ length: 50, nullable: true })
@Column({ type: 'varchar', length: 50, nullable: true })
tipo_usuario: string;
@OneToMany(() => Usuario, (usuarios) => usuarios.tipoUsuario)
@@ -24,12 +25,13 @@ export class Usuario {
@PrimaryGeneratedColumn()
id_usuario: number;
@Column({ length: 100, nullable: true })
@Column({ type: 'varchar', length: 100, nullable: true })
nombre: string;
@Column({ nullable: true })
@Column({ type: 'varchar', length: 100, nullable: true })
contraseña: string;
@JoinColumn({ name: 'id_tipo_usuario' })
@ManyToOne(() => Tipo_Usuario, (tipoUsuario) => tipoUsuario.usuarios)
tipoUsuario: Tipo_Usuario;
+68 -1
View File
@@ -1,8 +1,14 @@
import { Injectable } from '@nestjs/common';
import {
BadRequestException,
Injectable,
NotFoundException,
UnauthorizedException,
} 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 +19,67 @@ export class UsuariosService {
@InjectRepository(Tipo_Usuario)
private readonly tipoUsuarioRepository: Repository<Tipo_Usuario>,
) {}
async usuarios() {
return await this.usuarioRepository.find({ relations: ['tipoUsuario'] });
}
async actualizarUsuario(
id: number,
datos: {
nombre?: string;
contraseñaN?: string;
id_tipo_usuario?: number;
contraseñaV?: string;
},
) {
// 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ñaN && datos.contraseñaV) {
const contraseñaValida = await bcrypt.compare(
datos.contraseñaV,
usuario.contraseña,
);
if (!contraseñaValida) {
throw new UnauthorizedException('Contraseña inválida');
}
const hashed = await bcrypt.hash(datos.contraseñaN, 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,