127 Commits

Author SHA1 Message Date
evenegas 1abf4d4e9c Cambio de contraseña 2026-02-26 14:38:21 -06:00
evenegas c73121f7df se agregaron funciones para actualizar usuario 2026-02-26 14:22:33 -06:00
evenegas 1a0af95c4e se agregaron cambios 2026-01-29 12:10:46 -06:00
evenegas 3ef076111e se agregaron cambios 2026-01-29 12:09:40 -06:00
evenegas 313bc7e632 se agregaron cambios 2026-01-29 12:05:08 -06:00
evenegas 29b0f2b3e5 se agregó la relacion 2026-01-29 09:59:42 -06:00
evenegas a387f11302 se agregó la relacion 2026-01-29 09:58:13 -06:00
evenegas e336f0bb86 se agregó la relacion 2026-01-29 09:51:48 -06:00
evenegas 017e6836e9 se agregó la relacion 2026-01-29 09:50:55 -06:00
evenegas 1155735d9d log 2026-01-28 12:16:16 -06:00
evenegas e5f98c9596 se agrgó un log para depurar 2026-01-28 12:03:41 -06:00
evenegas 24717cf931 se agrgó un log para depurar 2026-01-28 11:37:36 -06:00
evenegas 02dd974fd6 enviar diferente el caso especial 2026-01-23 10:18:17 -06:00
evenegas b6e7525dc2 enviar diferente el caso especial 2026-01-23 10:17:25 -06:00
evenegas bc975bc295 gitignore 2026-01-22 14:34:27 -06:00
evenegas cb82dd3354 gitignore 2026-01-22 14:17:34 -06:00
evenegas a18fbe441d gitignore 2026-01-22 14:16:44 -06:00
evenegas c42c87e080 gitignore 2026-01-22 14:16:00 -06:00
evenegas 3bc245e1f7 creditos 2026-01-22 13:31:24 -06:00
evenegas 5c5e7b3f0f creditos 2026-01-22 12:12:59 -06:00
evenegas f50ce6d5f9 creditos 2026-01-22 12:10:38 -06:00
evenegas 7933605cb1 changes 2026-01-21 11:32:07 -06:00
evenegas 963ec5a765 changes 2026-01-21 10:53:53 -06:00
evenegas 308f910bee se agregaron cambios 2026-01-20 10:36:56 -06:00
evenegas 29fdbee734 se agregaron cambios 2026-01-20 10:33:30 -06:00
evenegas ea14bfc96c se agregaron cambios 2026-01-20 10:25:20 -06:00
evenegas 9c677fa479 se cambiaron configuraciones 2026-01-16 16:03:32 -06:00
evenegas 77f685e8d3 changes 2026-01-16 15:39:54 -06:00
evenegas 757fa541d8 ya no es necesario eliminar archivo 2026-01-16 15:08:30 -06:00
evenegas 0ee2f86c75 se agregó un console.log 2026-01-16 14:54:00 -06:00
evenegas 9d8c59d5ca se agregó un console.log 2026-01-16 14:46:19 -06:00
evenegas 592acee975 se agregó un console.log 2026-01-16 14:45:50 -06:00
evenegas c33817ace3 se agregó un console.log 2026-01-16 13:52:52 -06:00
evenegas bcb14ddeba se agregó un console.log 2026-01-16 13:31:44 -06:00
evenegas 0cf0c54879 Se actualizan los campos 2026-01-16 10:21:39 -06:00
evenegas eb787c21e6 busca todos los servicios 2026-01-16 10:15:47 -06:00
evenegas 141afc824a se agregó una relación 2026-01-16 09:58:40 -06:00
evenegas 617f4fe5bb se agregó una relación 2026-01-16 09:40:32 -06:00
evenegas ad7fd3b513 se agregó una relación 2026-01-16 09:06:40 -06:00
evenegas 71a24ca189 se cambio la cuenta de acatlan contigo 2026-01-15 17:54:17 -06:00
evenegas ed5b64afb8 se cambio la cuenta de acatlan contigo 2026-01-15 17:44:13 -06:00
evenegas c0178301ae busca las claves con año actual 2026-01-15 12:23:27 -06:00
evenegas f70aa0f92e busca las claves con año actual 2026-01-15 12:13:16 -06:00
evenegas 8762b51240 Se corrigio cuestionario alumno 2026-01-15 10:52:33 -06:00
IO c9cd073101 ya no se mandan correos 2026-01-14 18:14:32 -06:00
IO 688e13b30c se manda de forma correcta el password 2026-01-14 14:58:11 -06:00
IO 086af154cf se manda de forma correcta el password 2026-01-14 14:40:27 -06:00
IO 400bffa254 se restringio la busqueda 2026-01-14 13:19:49 -06:00
Patriots01 61c164225c Carga masiva completa 2026-01-14 19:03:53 +00:00
Patriots01 151d2e8718 Carga masiva completa 2026-01-14 19:02:47 +00:00
Patriots01 62b71060be modificaciones 2026-01-14 18:38:33 +00:00
IO 43abc72a38 se manda el id cuestionario 2026-01-14 12:24:25 -06:00
IO 04ad4923a5 se manda el id cuestionario 2026-01-14 11:49:07 -06:00
IO c40c82e7ba Cambio de id de status en update 2026-01-14 11:41:15 -06:00
IO 050c2b225c se manda el id cuestionario 2026-01-14 10:59:01 -06:00
IO ccd3781152 se agregò relacion en liberar servicio 2026-01-13 19:42:24 -06:00
IO 6a5d39d122 se agregó la funcion de validar preregistro en los cuestionarios 2026-01-13 19:38:57 -06:00
IO 53c53d62a0 se agregó la funcion de validar preregistro en los cuestionarios 2026-01-13 18:15:46 -06:00
IO 53ba7d45ff se agregó la funcion de validar preregistro en los cuestionarios 2026-01-13 18:00:47 -06:00
IO 0ede1e8372 se hizo la funcion diaria 2026-01-13 17:01:42 -06:00
IO 197b18910d se hizo la funcion diaria 2026-01-13 16:13:05 -06:00
IO cafbce392d se hizo la funcion diaria 2026-01-13 16:06:11 -06:00
IO a6efe25aa3 se hizo la funcion diaria 2026-01-13 16:03:39 -06:00
IO 7b6b29b75f se hizo la funcion diaria 2026-01-13 15:05:46 -06:00
IO 672f3579ac se hizo la funcion diaria 2026-01-13 14:55:58 -06:00
IO 786f56442c se hizo la funcion diaria 2026-01-13 14:49:22 -06:00
IO 90d791347e se hizo la funcion diaria 2026-01-13 14:25:01 -06:00
IO 0aa5180154 se hizo la funcion diaria 2026-01-13 14:23:15 -06:00
IO d6f5f645b8 se cambio el mensaje del correo 2026-01-13 13:20:37 -06:00
santiago 115265381b Modificaciones 2026-01-12 21:38:23 -06:00
evenegas a851d31d3b Se corrigio cuestionario alumno 2026-01-12 11:01:25 -06:00
evenegas 29e4b4a7ad Se corrigio cuestionario alumno 2026-01-12 10:34:39 -06:00
santiago cadb15a9cf Corrigi varios errores que se tenian en la parte de cuestionarios asi como en el envio de archivos, y agregue algunos campos en los dto 2026-01-12 10:14:24 -06:00
santiago 83bbcee4ad Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Iris_back into develop 2026-01-10 09:36:28 -06:00
santiago 8fac3a823a Modificaciones en algunos endpoint 2026-01-10 09:35:58 -06:00
evenegas b7d22cc07e se creo el endpoint de registro 2026-01-09 13:26:46 -06:00
santiago bba0aa12cc Se modificaron varios endpoint que tenian un mal funcionamiento asi como la forma enq ue se reciben los archivos, falta por corregir un endpoint de cuestionario-alumno2 2026-01-08 13:00:15 -06:00
santiago ff6096f603 Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Iris_back into develop 2026-01-08 12:16:54 -06:00
evenegas 53b7d23007 se agrego drive correctamente 2026-01-08 12:14:15 -06:00
evenegas 8f68aadd8a se agrego drive correctamente 2026-01-08 11:33:55 -06:00
evenegas 9386fbc88d se agrego drive correctamente 2026-01-08 11:24:18 -06:00
santiago 7686e26324 Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Iris_back into develop 2026-01-08 11:22:12 -06:00
santiago 8f575aa662 MOdificaciones 2026-01-08 11:21:17 -06:00
evenegas 19303178aa se agrego drive correctamente 2026-01-08 11:13:36 -06:00
santiago 7d3a8ee5ed Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Iris_back into develop 2026-01-08 09:08:04 -06:00
santiago 3aaf9ba0ed Modificaciones en el endpoint de envio carta de aceptacion 2026-01-08 09:06:12 -06:00
evenegas af28549a04 se agrego drive correctamente 2026-01-08 00:03:26 -06:00
evenegas 50487e4859 se agrego drive correctamente 2026-01-07 14:07:39 -06:00
santiago 6c22cfeeba Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Iris_back into develop 2026-01-07 13:43:37 -06:00
santiago bf2367b366 Modificaciones en algunos endpoint y correcion de erroes 2026-01-07 13:43:12 -06:00
evenegas 0cb91d8b9e se agrego drive correctamente 2026-01-07 13:41:11 -06:00
evenegas 2ed657caf2 se agrego drive correctamente 2026-01-07 12:10:45 -06:00
evenegas 016297760f modificaciones 2026-01-07 01:50:01 -06:00
evenegas 5f7ba5ff69 se importop la parte de los correos 2026-01-06 10:03:14 -06:00
evenegas bc4146a07d Se realizaron correcciones 2026-01-06 07:20:11 -06:00
santiago 5567a26ed6 Se modificaron varias cosas y se agrego un endpoint nuevo para hacer pruebas 2025-12-10 10:59:26 -06:00
santiago 7a260169cc Se modificaron varias cosas en los endpoint y en algunas entities asi como en los dto 2025-12-09 09:03:52 -06:00
Tyrannuss 3e67102878 se agregaro un controller para pruebas , no subir en pruebas 2025-12-03 17:44:07 -06:00
Tyrannuss 1c40eca428 Merge remote-tracking branch 'origin/develop' into develop 2025-11-27 17:33:16 -06:00
Tyrannuss f62a868b97 cambios 2025-11-27 17:32:14 -06:00
Patriots01 65e0773337 Correciones en la forma en que se mandan las cosas y las relaciones 2025-11-27 23:31:50 +00:00
Tyrannuss 671f989976 Merge remote-tracking branch 'origin/develop' into develop 2025-11-27 14:44:37 -06:00
Tyrannuss 71e5ca35f9 . 2025-11-27 14:43:58 -06:00
Patriots01 6c0e263bf4 Modificaiones 2025-11-27 20:28:56 +00:00
Patriots01 aff3a44c09 Se modificaron cosas 2025-11-26 22:01:10 +00:00
Patriots01 083cefe84a Modificaciones, se agregaron las relaciones de tipoUsuario 2025-11-26 21:14:13 +00:00
evenegas 340c0d59fd se agregò endpoint para traer ususarios y funcion diaria 2025-11-26 13:57:34 -06:00
evenegas 1b80de961b se agregò endpoint para traer ususarios y funcion diaria 2025-11-26 13:45:25 -06:00
Patriots01 995a3a4e5e Modificaciones en la logica se agregaron las relaciones para traer los tipoUsuario y que no muestre error 2025-11-25 21:53:52 +00:00
Tyrannuss a9b9b39285 nuevo controller para obtener todos los servicios de admin 2025-11-24 16:52:52 -06:00
Tyrannuss f25b60eccf eliminacion de archivo inecesrio 2025-11-24 13:56:19 -06:00
Tyrannuss 0e6d6be3bb se arreglaron servicio y se agrego un controller para obtener todos los servicios 2025-11-20 18:05:36 -06:00
evenegas 324ab9d35a se cambiò query 2025-11-20 11:00:59 -06:00
Tyrannuss a6ee9decec se cambio el tipo de encriptacion y la manera en que se jala el JWT , ya se puede generar un jwt mediante inicio de sesion 2025-11-19 17:24:39 -06:00
Tyrannuss 75f55ba918 se comentaron unas lineass en servicio controller para hacer pruebas 2025-11-19 15:01:37 -06:00
Tyrannuss e5a455bbb1 eliminacion de archivo .sql 2025-11-10 16:50:51 -06:00
Tyrannuss 7b8514468f correccion de entities en caso-especial , cuestionario-programa, servicio, usuario 2025-11-10 16:49:44 -06:00
evenegas 9d9df020db cambio en algunas tablas 2025-11-06 14:01:53 -06:00
evenegas 4770e95420 se cambio el docker 2025-11-06 11:10:05 -06:00
stern c9c0a531f2 Modificación a script start en package.json para uso de Node 22 2025-10-31 12:22:24 -06:00
evenegas cea20f387f Se cambio algunas entidades 2025-10-31 12:18:28 -06:00
Tyrannuss 373b99a57a Controllers 2025-10-30 14:21:07 -06:00
stern 6a871d8fa1 Modificación a script start del package.json para uso de Node 22 2025-10-30 13:24:06 -06:00
evenegas 09e12161bd Se hicieron cambios en el app module 2025-10-30 11:34:55 -06:00
evenegas b771ca2e57 Se hicieron cambios en el app module 2025-10-30 11:32:38 -06:00
evenegas 4427804865 modificaciones sobre .env 2025-10-22 13:44:11 -06:00
evenegas f06decdc43 se corrigieron los modules 2025-10-22 10:30:23 -06:00
70 changed files with 47142 additions and 2132 deletions
+7 -1
View File
@@ -2,7 +2,8 @@
/dist /dist
/node_modules /node_modules
/build /build
/server/uploads
/server
# Logs # Logs
logs logs
*.log *.log
@@ -12,6 +13,8 @@ yarn-debug.log*
yarn-error.log* yarn-error.log*
lerna-debug.log* lerna-debug.log*
/initdb/init.sql
/initdb2
# OS # OS
.DS_Store .DS_Store
@@ -19,6 +22,9 @@ lerna-debug.log*
/coverage /coverage
/.nyc_output /.nyc_output
# Ignorar credenciales sensibles
cred.json
# IDEs and editors # IDEs and editors
/.idea /.idea
.project .project
View File
View File
+11 -26
View File
@@ -1,33 +1,18 @@
version: '3.8'
services: services:
mysql: database:
image: mariadb:latest image: mariadb:latest
container_name: Iris_db container_name: Iris_db
restart: always restart: always
environment: environment:
MYSQL_ROOT_PASSWORD: root MARIADB_ROOT_PASSWORD: root
MYSQL_DATABASE: Iris MARIADB_DATABASE: Iris_testDB
MYSQL_USER: user_crud MARIADB_USER: root
MYSQL_PASSWORD: root MARIADB_PASSWORD: root
ports:
- '3306:3306'
volumes: volumes:
- ./mysql:/var/lib/mysql - mariadb_data:/var/lib/mysql
ports: - ./initdb2:/docker-entrypoint-initdb.dP
- "3307:3306"
nest-app: volumes:
build: mariadb_data:
context: .
dockerfile: Dockerfile
container_name: Iris_app
restart: always
depends_on:
- mysql
environment:
DATABASE_HOST: mysql
DATABASE_PORT: 3306
DATABASE_USER: user_crud
DATABASE_PASSWORD: root
DATABASE_NAME: Iris
ports:
- "3000:3000"
+12
View File
@@ -0,0 +1,12 @@
DB_USERNAME=
DB_DATABASE=
DB_PASSWORD=
DB_PORT=
DB_HOST=
PORT=
JWT=
CARPETA=
MAIL_URL=
MAIL_TOKEN=
ESCOLARES=
ESCOLARES_PASS=
File diff suppressed because one or more lines are too long
+924 -1048
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -8,7 +8,7 @@
"scripts": { "scripts": {
"build": "nest build", "build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start", "start": "source ~/.nvm/nvm.sh && nvm use 22.10.0 && npm install && nest start",
"start:dev": "nest start --watch", "start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch", "start:debug": "nest start --debug --watch",
"start:prod": "node dist/main", "start:prod": "node dist/main",
@@ -28,6 +28,7 @@
"@nestjs/mapped-types": "*", "@nestjs/mapped-types": "*",
"@nestjs/passport": "^11.0.5", "@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.1.6", "@nestjs/platform-express": "^11.1.6",
"@nestjs/schedule": "^6.1.0",
"@nestjs/typeorm": "^11.0.0", "@nestjs/typeorm": "^11.0.0",
"argon2": "^0.44.0", "argon2": "^0.44.0",
"axios": "^1.12.2", "axios": "^1.12.2",
@@ -59,6 +60,7 @@
"@swc/core": "^1.13.5", "@swc/core": "^1.13.5",
"@types/express": "^5.0.3", "@types/express": "^5.0.3",
"@types/jest": "^30.0.0", "@types/jest": "^30.0.0",
"@types/moment": "^2.11.29",
"@types/multer": "^2.0.0", "@types/multer": "^2.0.0",
"@types/node": "^24.5.2", "@types/node": "^24.5.2",
"@types/supertest": "^6.0.3", "@types/supertest": "^6.0.3",
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
numeroCuenta,nombre,licenciatura,correo,fechaInicio,fechaFin,fechaLiberacion,institucion,dependencia,programa,profesor,clave,status
315040357,RAMIREZ AVILA MELANY BETSABE,LIC. EN HISTORIA,424018289@pcpuma.acatlan.unam.mx,29/7/2024,3/12/2025,"",Instituto Nacional De Antropologia E Historia,Museo Nacional De Antropologia,"Investigación, conservación y difusión del patrimonio arqueológico y etnográfico de México","",2024-21/7-1471,Pre-Termino
319304899,ASCENCIO DIAZ ENRIQUE,LIC. EN ACTUARIA,424018289@pcpuma.acatlan.unam.mx,27/10/2024,1/12/2025,"",UNAM,FES ACATLAN,Acatlán Contigo,Act. Jorge Carmona Mendoza,2025-12/145-24,Cancelado
421039812,ARTEAGA GONZALEZ RUBEN MAURICIO,LIC. EN COMUNICACION,424018289@pcpuma.acatlan.unam.mx,29/12/2024,"","",UNAM,FES ACATLAN,Acatlán Contigo,MTRO. SERGIO ARTEMIO GUILLERMO VALENTÍN,2024-12/20-530,Pre-Termino
319302187,PEREZ CENTENO JORGE,LIC. EN CIENCIAS POLITICAS Y ADMON PUB,pcentenojorge15@gmail.com,1/12/2025,8/12/2025,"",UNAM,FES ACATLAN,Acatlán Contigo,Antonio Faustino Torres,2025-12/145-24,Pre-Registro
413076379,JIMENEZ GONZALEZ JAZMIN ALEJANDRA,LIC. EN MATEMATICAS APLICADAS Y COMP.,424018289@pcpuma.acatlan.unam.mx,1/12/2025,4/12/2025,"",UNAM,FES ACATLAN,Acatlán Contigo,xxxx,2025-12/145-24,Carta Aceptación Rechazada
421162888,DREW IBARRA EDUARDO CARLOS,LIC. ENSEÑANZA DE ESPAÑOL(LENG. EXTRANJ),424018289@pcpuma.acatlan.unam.mx,17/12/2025,11/12/2025,"",UNIVERSIDAD NACIONAL AUTONOMA DE MEXICO,ESCUELA DE EXTENSIÓN EN CHICAGO,Enseñanza de Español como Lengua Extranjera,"",2024-12/226-2509,Pre-Termino
419089577,GUTIERREZ GUTIERREZ DIANA ANGELICA,LIC. EN ARQUITECTURA,320154041@pcpuma.acatlan.unam.mx,19/12/2025,18/6/2026,"",UNAM,FES ACATLAN,Titulación por servicio social,LIC. MARCELA MARTÍNEZ RANGEL,2023-12/20-222,Cancelado
422116468,CONTRERAS GONZALEZ ANA CRISTINA,LIC. EN ECONOMIA,424018289@pcpuma.acatlan.unam.mx,28/12/2025,"","",Secretaria De Agricultura Y Desarrollo Rural,Liconsa. S.a De C.v.,SERVICIO SOCIAL LICONSA OFICINA CENTRAL,"",2025-192/1-2689,Pre-Registro
1 numeroCuenta nombre licenciatura correo fechaInicio fechaFin fechaLiberacion institucion dependencia programa profesor clave status
2 315040357 RAMIREZ AVILA MELANY BETSABE LIC. EN HISTORIA 424018289@pcpuma.acatlan.unam.mx 29/7/2024 3/12/2025 Instituto Nacional De Antropologia E Historia Museo Nacional De Antropologia Investigación, conservación y difusión del patrimonio arqueológico y etnográfico de México 2024-21/7-1471 Pre-Termino
3 319304899 ASCENCIO DIAZ ENRIQUE LIC. EN ACTUARIA 424018289@pcpuma.acatlan.unam.mx 27/10/2024 1/12/2025 UNAM FES ACATLAN Acatlán Contigo Act. Jorge Carmona Mendoza 2025-12/145-24 Cancelado
4 421039812 ARTEAGA GONZALEZ RUBEN MAURICIO LIC. EN COMUNICACION 424018289@pcpuma.acatlan.unam.mx 29/12/2024 UNAM FES ACATLAN Acatlán Contigo MTRO. SERGIO ARTEMIO GUILLERMO VALENTÍN 2024-12/20-530 Pre-Termino
5 319302187 PEREZ CENTENO JORGE LIC. EN CIENCIAS POLITICAS Y ADMON PUB pcentenojorge15@gmail.com 1/12/2025 8/12/2025 UNAM FES ACATLAN Acatlán Contigo Antonio Faustino Torres 2025-12/145-24 Pre-Registro
6 413076379 JIMENEZ GONZALEZ JAZMIN ALEJANDRA LIC. EN MATEMATICAS APLICADAS Y COMP. 424018289@pcpuma.acatlan.unam.mx 1/12/2025 4/12/2025 UNAM FES ACATLAN Acatlán Contigo xxxx 2025-12/145-24 Carta Aceptación Rechazada
7 421162888 DREW IBARRA EDUARDO CARLOS LIC. ENSEÑANZA DE ESPAÑOL(LENG. EXTRANJ) 424018289@pcpuma.acatlan.unam.mx 17/12/2025 11/12/2025 UNIVERSIDAD NACIONAL AUTONOMA DE MEXICO ESCUELA DE EXTENSIÓN EN CHICAGO Enseñanza de Español como Lengua Extranjera 2024-12/226-2509 Pre-Termino
8 419089577 GUTIERREZ GUTIERREZ DIANA ANGELICA LIC. EN ARQUITECTURA 320154041@pcpuma.acatlan.unam.mx 19/12/2025 18/6/2026 UNAM FES ACATLAN Titulación por servicio social LIC. MARCELA MARTÍNEZ RANGEL 2023-12/20-222 Cancelado
9 422116468 CONTRERAS GONZALEZ ANA CRISTINA LIC. EN ECONOMIA 424018289@pcpuma.acatlan.unam.mx 28/12/2025 Secretaria De Agricultura Y Desarrollo Rural Liconsa. S.a De C.v. SERVICIO SOCIAL LICONSA OFICINA CENTRAL 2025-192/1-2689 Pre-Registro
+32 -27
View File
@@ -14,51 +14,56 @@ import { ServicioModule } from './servicio/servicio.module';
import { StatusModule } from './status/status.module'; import { StatusModule } from './status/status.module';
import { TipoUsuarioModule } from './tipo-usuario/tipo-usuario.module'; import { TipoUsuarioModule } from './tipo-usuario/tipo-usuario.module';
import { UsuarioModule } from './usuario/usuario.module'; import { UsuarioModule } from './usuario/usuario.module';
import { AuthModule } from './auth/auth.module';
import { DriveModule } from './drive/drive.module';
import { ScheduleModule } from '@nestjs/schedule';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot( ScheduleModule.forRoot(),
{isGlobal: true} ConfigModule.forRoot({ isGlobal: true }),
),
TypeOrmModule.forRoot({ TypeOrmModule.forRoot({
type: 'mysql', type: 'mariadb',
host: process.env.db_host, host: process.env.DB_HOST,
username: process.env.db_username, username: process.env.DB_USERNAME,
database: process.env.db_database, database: process.env.DB_DATABASE,
password: process.env.db_password, password: process.env.DB_PASSWORD,
port: Number(process.env.db_port), port: Number(process.env.DB_PORT),
synchronize: true, synchronize: false, //Lo ponemos en false para no afectar
dropSchema: false, // elimina la base de datos dropSchema: false,
// logging: true, // Habilita los logs para depuración autoLoadEntities: true,
autoLoadEntities: true, // Carga automáticamente las entidades logger: 'advanced-console',
logging: 'all',
}), }),
CarreraModule, CarreraModule,
CasoEspecialModule, CasoEspecialModule,
CuestionarioAlumnoModule, CuestionarioAlumnoModule,
CuestionarioAlumno2Module, CuestionarioAlumno2Module,
CuestionarioProgramaModule, CuestionarioProgramaModule,
CuestionarioPrograma2Module, CuestionarioPrograma2Module,
ProgramaModule, ProgramaModule,
ServicioModule, ServicioModule,
StatusModule, StatusModule,
AuthModule,
TipoUsuarioModule, TipoUsuarioModule,
UsuarioModule, UsuarioModule,
DriveModule,
], ],
controllers: [AppController], controllers: [AppController],
providers: [AppService], providers: [AppService],
}) })
export class AppModule {} export class AppModule { }
+12 -1
View File
@@ -10,7 +10,7 @@ import { AuthService } from './auth.service';
@Controller('auth') @Controller('auth')
export class AuthController { export class AuthController {
constructor(private readonly authService: AuthService) {} constructor(private readonly authService: AuthService) { }
// ===================== LOGIN ===================== // ===================== LOGIN =====================
@Post('login') @Post('login')
@@ -36,4 +36,15 @@ export class AuthController {
throw new UnauthorizedException(error.message); throw new UnauthorizedException(error.message);
} }
} }
// ===================== ACTUALIZAR CONTRASEÑA ADMIN =====================
@Post('update-password')
async updatePassword() {
try {
const newPassword = await this.authService.update_password();
return { message: 'Contraseña actualizada correctamente.', contraseña: newPassword };
} catch (error) {
throw new UnauthorizedException(error.message);
}
}
} }
+14 -5
View File
@@ -1,18 +1,27 @@
import { JwtModule } from '@nestjs/jwt'; import { JwtModule, JwtService } from '@nestjs/jwt';
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { AuthService } from './auth.service'; import { AuthService } from './auth.service';
import { Usuario } from 'src/usuario/entities/usuario.entity'; import { Usuario } from 'src/usuario/entities/usuario.entity';
import { TypeOrmModule } from '@nestjs/typeorm'; import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt.strategy';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({ @Module({
imports: [ imports: [
ConfigModule.forRoot(),
TypeOrmModule.forFeature([Usuario]), TypeOrmModule.forFeature([Usuario]),
JwtModule.register({ JwtModule.registerAsync({
secret: process.env.JWT, // Usa un .env en producción imports: [ConfigModule],
signOptions: { expiresIn: '1h' }, useFactory: async (configService: ConfigService) => ({
secret: configService.get('JWT') || 'clave_temporal_desarrollo',
signOptions: { expiresIn: '1h' },
}),
inject: [ConfigService],
}), }),
], ],
providers: [AuthService], controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [AuthService], exports: [AuthService],
}) })
export class AuthModule {} export class AuthModule {}
+42 -8
View File
@@ -7,7 +7,6 @@ import {
import { JwtService } from '@nestjs/jwt'; import { JwtService } from '@nestjs/jwt';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import axios from 'axios'; import axios from 'axios';
import { bcrypt } from 'bcrypt';
import { Carrera } from 'src/carrera/entities/carrera.entity'; import { Carrera } from 'src/carrera/entities/carrera.entity';
import { gmail } from 'src/helpers.services/gmail.service'; import { gmail } from 'src/helpers.services/gmail.service';
@@ -15,6 +14,7 @@ import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity'; import { Usuario } from 'src/usuario/entities/usuario.entity';
import { Like, Not, Repository } from 'typeorm'; import { Like, Not, Repository } from 'typeorm';
import * as argon2 from 'argon2'; import * as argon2 from 'argon2';
import * as bcrypt from 'bcrypt';
@Injectable() @Injectable()
export class AuthService { export class AuthService {
@@ -23,7 +23,7 @@ export class AuthService {
@InjectRepository(Usuario) @InjectRepository(Usuario)
private readonly userRepo: Repository<Usuario>, private readonly userRepo: Repository<Usuario>,
) {} ) { }
async jwtVerificar(token: string) { async jwtVerificar(token: string) {
try { try {
@@ -48,7 +48,7 @@ export class AuthService {
} }
async comparar(password, dbPassword) { async comparar(password, dbPassword) {
if (await argon2.verify(password, dbPassword)) { if (await bcrypt.compare(password, dbPassword)) {
return true; return true;
} else { } else {
return false; return false;
@@ -56,7 +56,8 @@ export class AuthService {
} }
async encriptar(password) { async encriptar(password) {
return await argon2.hash(password); const saltRounds = 10;
return await bcrypt.hash(password, saltRounds);
} }
async generarPassword() { async generarPassword() {
@@ -72,16 +73,49 @@ export class AuthService {
return password; return password;
} }
async update_password() {
let user = await this.userRepo.findOne({ where: { tipoUsuario: { idTipoUsuario: 1 } } })
if (!user) throw new NotFoundException('No se encontró el usuario administrador.');
const newPassword = await this.generarPassword();
const hashedPassword = await this.encriptar(newPassword);
user.password = hashedPassword;
await this.userRepo.save(user);
return newPassword;
}
async login(usuario: string, password: string) { async login(usuario: string, password: string) {
const user = await this.userRepo.findOne({ where: { usuario } }); const user = await this.userRepo.findOne({
where: { usuario },
relations: ['tipoUsuario'],
});
if (!user) throw new UnauthorizedException('No existe este usuario.'); if (!user) throw new UnauthorizedException('No existe este usuario.');
const match = await this.comparar(password, user.password); const match = await this.comparar(password, user.password);
if (!match) throw new UnauthorizedException('Credenciales inválidas'); if (!match) throw new UnauthorizedException('Credenciales inválidas');
if (!user.activo) throw new Error('Este usuario no esta activo.');
const token = this.jwtCreate( if (!user.activo) throw new BadRequestException('Este usuario no esta activo.');
const token = await this.jwtCreate(
user.idUsuario, user.idUsuario,
user.tipoUsuario.idTipoUsuario, user.tipoUsuario.idTipoUsuario,
); );
return token;
return {
token: token.access_token,
Usuario: {
idUsuario: user.idUsuario,
usuario: user.usuario,
nombre: user.nombre || '',
TipoUsuario: {
idTipoUsuario: user.tipoUsuario.idTipoUsuario,
tipoUsuario: user.tipoUsuario.tipoUsuario || '',
},
},
};
} }
} }
+7 -5
View File
@@ -1,19 +1,21 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport'; import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt'; import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
@Injectable() @Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) { export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() { constructor(private configService: ConfigService) {
super({ super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET, secretOrKey: configService.get('JWT') || 'clave_temporal_desarrollo',
}); });
} }
async validate(payload: any) { async validate(payload: any) {
// payload contiene los datos del usuario que se pusieron al generar el token return {
// ejemplo: { sub: 123, email: "user@example.com" } userId: payload.Usuario,
return { userId: payload.sub, email: payload.email }; tipoUsuario: payload.tipoUsuario,
};
} }
} }
-42
View File
@@ -1,42 +0,0 @@
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
} from '@nestjs/common';
import { CarreraService } from './carrera.service';
import { CreateCarreraDto } from './dto/create-carrera.dto';
import { UpdateCarreraDto } from './dto/update-carrera.dto';
@Controller('carrera')
export class CarreraController {
constructor(private readonly carreraService: CarreraService) {}
@Post()
create(@Body() createCarreraDto: CreateCarreraDto) {
return this.carreraService.create(createCarreraDto);
}
@Get()
findAll() {
return this.carreraService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.carreraService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateCarreraDto: UpdateCarreraDto) {
return this.carreraService.update(+id, updateCarreraDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.carreraService.remove(+id);
}
}
+4 -3
View File
@@ -1,8 +1,9 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Carrera } from './entities/carrera.entity';
@Module({ @Module({
controllers: [], imports: [TypeOrmModule.forFeature([Carrera])],
providers: [], exports: [TypeOrmModule],
exports: [],
}) })
export class CarreraModule {} export class CarreraModule {}
+11 -12
View File
@@ -1,19 +1,18 @@
import { CasoEspecial } from "src/caso-especial/entities/caso-especial.entity"; import { CasoEspecial } from 'src/caso-especial/entities/caso-especial.entity';
import { Servicio } from "src/servicio/entities/servicio.entity"; import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Column, Entity, ManyToMany, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
@Entity('carrera') @Entity('carrera')
export class Carrera { export class Carrera {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn({ name: 'idCarrera' })
idCarrera:number; idCarrera: number;
@Column({type: 'varchar', length: 50, nullable: false}) @Column({ type: 'varchar', length: 50 })
carrera:string; carrera: string;
@OneToMany(()=>Servicio, (servicio)=>servicio.carrera) @OneToMany(() => CasoEspecial, (casoEspecial) => casoEspecial.carrera)
servicio:Servicio[]; casosEspeciales: CasoEspecial[];
@OneToMany(()=>CasoEspecial,(casoEspecial)=>casoEspecial.carrera) @OneToMany(() => Servicio, (servicio) => servicio.carrera)
casoEspecial:CasoEspecial[]; servicios: Servicio[];
} }
+32 -11
View File
@@ -11,6 +11,7 @@ import {
UploadedFile, UploadedFile,
BadRequestException, BadRequestException,
Put, Put,
Query,
} from '@nestjs/common'; } from '@nestjs/common';
import { CasoEspecialService } from './caso-especial.service'; import { CasoEspecialService } from './caso-especial.service';
import { CreateCasoEspecialDto } from './dto/create-caso-especial.dto'; import { CreateCasoEspecialDto } from './dto/create-caso-especial.dto';
@@ -21,13 +22,18 @@ import { diskStorage } from 'multer';
import { extname } from 'path'; import { extname } from 'path';
import * as fs from 'fs'; import * as fs from 'fs';
import { FilterCasoEspecialDto } from './dto/filter-caso-especial.dto'; import { FilterCasoEspecialDto } from './dto/filter-caso-especial.dto';
import { DriveService } from 'src/drive/drive.service';
import { plainToInstance } from 'class-transformer';
@Controller('caso-especial') @Controller('caso-especial')
export class CasoEspecialController { export class CasoEspecialController {
constructor(private readonly casoEspecialService: CasoEspecialService) {} constructor(private readonly casoEspecialService: CasoEspecialService,
private drive: DriveService,
) { }
@Post() @Post()
@UseGuards(AuthGuard('jwt')) // reemplaza verificaToken @UseGuards(AuthGuard('jwt'))
@UseInterceptors( @UseInterceptors(
FileInterceptor('archivos', { FileInterceptor('archivos', {
storage: diskStorage({ storage: diskStorage({
@@ -41,27 +47,42 @@ export class CasoEspecialController {
) )
async subirArchivo( async subirArchivo(
@UploadedFile() file: Express.Multer.File, @UploadedFile() file: Express.Multer.File,
@Body('alumno') alumno: CreateCasoEspecialDto, @Body('alumno') alumno: string, // 👈 STRING
) { ) {
try { try {
const data = await this.casoEspecialService.create(alumno, file.filename); const alumnoParsed = plainToInstance(
CreateCasoEspecialDto,
JSON.parse(alumno),
);
return await this.casoEspecialService.create(
alumnoParsed,
file.filename,
);
} catch (err) {
if (file?.filename) {
fs.unlinkSync(`./server/uploads/${file.filename}`);
}
throw new BadRequestException(err.message);
}
}
@Get('carpeta')
async carpeta() {
try {
const data = await this.drive.folder('123456789');
console.log(data);
return { return {
statusCode: 201, statusCode: 200,
message: 'Archivo subido con éxito',
data, data,
}; };
} catch (err) { } catch (err) {
// Si hay error, eliminar archivo subido
if (file && file.filename) {
fs.unlinkSync(`./server/uploads/${file.filename}`);
}
throw new BadRequestException(err.message); throw new BadRequestException(err.message);
} }
} }
@Get('servicios_especiales') @Get('servicios_especiales')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
async getServiciosEspeciales(@Body() dtoServEsp: FilterCasoEspecialDto) { async getServiciosEspeciales(@Query() dtoServEsp: FilterCasoEspecialDto) {
try { try {
const data = await this.casoEspecialService.findAllFiltered(dtoServEsp); const data = await this.casoEspecialService.findAllFiltered(dtoServEsp);
return { return {
+51 -50
View File
@@ -16,6 +16,12 @@ import { DriveService } from 'src/drive/drive.service';
import { Status } from 'src/status/entities/status.entity'; import { Status } from 'src/status/entities/status.entity';
import { FilterCasoEspecialDto } from './dto/filter-caso-especial.dto'; import { FilterCasoEspecialDto } from './dto/filter-caso-especial.dto';
import { arch } from 'os'; import { arch } from 'os';
import { SendMailDto } from 'src/helpers.services/dto/send-email.dto';
const {
terminoValidado
} = require('./../helpers.services/msjCorreos');
@Injectable() @Injectable()
export class CasoEspecialService { export class CasoEspecialService {
@@ -31,7 +37,7 @@ export class CasoEspecialService {
private validacion: ValidacionService, private validacion: ValidacionService,
private gmail: gmail, private gmail: gmail,
private drive: DriveService, private drive: DriveService,
) {} ) { }
async create(body: CreateCasoEspecialDto, file: string) { async create(body: CreateCasoEspecialDto, file: string) {
const { const {
@@ -51,71 +57,69 @@ export class CasoEspecialService {
motivo = '', motivo = '',
} = body; } = body;
const usuario = await this.usuarioRepository.findOneBy({ const usuario = await this.usuarioRepository.findOne({
idUsuario: idUsuario, where: { idUsuario },
relations: ['tipoUsuario'],
}); });
if (!usuario) if (!usuario)
throw new NotFoundException('Este alumno no existe en la db.'); throw new NotFoundException('Este alumno no existe en la db.');
if (usuario.tipoUsuario.idTipoUsuario !== 3) if (usuario.tipoUsuario.idTipoUsuario !== 3)
throw new BadRequestException('Este usuario no es de tipo Alumno.'); throw new BadRequestException('Este usuario no es de tipo Alumno.');
const carrera = await this.carreraRepository.findOneBy({ const carrera = await this.carreraRepository.findOneBy({ idCarrera });
idCarrera: idCarrera,
});
if (!carrera) if (!carrera)
throw new NotFoundException('Esta carrera no existe en la db.'); throw new NotFoundException('Esta carrera no existe en la db.');
const casoExistente = await this.casoEspecialRepository.findOne({ const casoExistente = await this.casoEspecialRepository.findOne({
where: { usuario: usuario }, where: { usuario: { idUsuario } },
}); });
if (casoExistente) if (casoExistente)
throw new BadRequestException('Este alumno ya tiene un Caso Especial.'); throw new BadRequestException('Este alumno ya tiene un Caso Especial.');
const path = `./server/uploads/${file}`; const path = `./server/uploads/${file}`;
const carpeta = await this.drive.folder(numeroCuenta); const carpeta = await this.drive.folder(numeroCuenta);
if (!carpeta) { if (!carpeta) throw new BadRequestException('No se pudo crear la carpeta en Drive.');
throw new Error('No se pudo crear la carpeta en Drive.');
}
const archivoZip = await this.drive.uploadFile( const archivoZip = await this.drive.uploadFile(
path, path,
`archivos.zip`, 'archivos.zip',
'application/zip', 'application/zip',
carpeta, carpeta,
); );
if (!archivoZip)
throw new BadRequestException('No se pudo subir el archivo a Drive.');
const status = await this.statusRepository.findOne({ const status = await this.statusRepository.findOneBy({ idStatus });
where: { idStatus: idStatus }, if (!status)
}); throw new NotFoundException('Este status no existe en la db.');
if (!status) throw new NotFoundException('Este status no existe en la db.');
if (archivoZip === null)
throw new Error('No se pudo subir el archivo a Drive.');
const nuevoCaso = this.casoEspecialRepository.create({ const nuevoCaso = this.casoEspecialRepository.create({
correo: correo, correo,
telefono: telefono, telefono,
institucion: institucion, institucion,
dependencia: dependencia, dependencia,
motivo: motivo, motivo,
direccion: direccion, direccion,
fechaInicio: fechaInicio, fechaInicio,
fechaFin: fechaFin, fechaFin,
fechaNacimiento: fechaNacimiento, fechaNacimiento,
carpeta: carpeta, carpeta,
archivoZip: archivoZip, archivoZip,
usuario: usuario, usuario,
carrera: carrera, carrera,
status: status, status,
creditos: creditos, creditos,
}); });
await this.casoEspecialRepository.save(nuevoCaso); let caso_especial = await this.casoEspecialRepository.save(nuevoCaso);
console.log('Nuevo Caso Especial creado:', caso_especial);
return { return {
message: 'Se ha registrado el Caso Especial correctamente.', message: 'Se ha registrado el Caso Especial correctamente.',
}; };
} }
async findAllFiltered(dto: FilterCasoEspecialDto) { async findAllFiltered(dto: FilterCasoEspecialDto) {
const { pagina = 1, idStatus, nombre = '', numeroCuenta = '' } = dto; const { pagina = 1, idStatus, nombre = '', numeroCuenta = '' } = dto;
@@ -138,11 +142,11 @@ export class CasoEspecialService {
} }
if (nombre) { if (nombre) {
query.andWhere('usuario.nombre ILIKE :nombre', { nombre: `%${nombre}%` }); query.andWhere('usuario.nombre LIKE :nombre', { nombre: `%${nombre}%` });
} }
if (numeroCuenta) { if (numeroCuenta) {
query.andWhere('usuario.usuario ILIKE :numeroCuenta', { query.andWhere('usuario.usuario LIKE :numeroCuenta', {
numeroCuenta: `%${numeroCuenta}%`, numeroCuenta: `%${numeroCuenta}%`,
}); });
} }
@@ -164,11 +168,11 @@ export class CasoEspecialService {
const caso = await this.casoEspecialRepository.findOne({ const caso = await this.casoEspecialRepository.findOne({
where: { idCasoEspecial }, where: { idCasoEspecial },
relations: ['usuario'], relations: ['usuario', 'status'],
}); });
if (!caso) { if (!caso) {
throw new Error('No existe este Caso Especial.'); throw new BadRequestException('No existe este Caso Especial.');
} }
switch (caso.status.idStatus) { switch (caso.status.idStatus) {
@@ -177,11 +181,10 @@ export class CasoEspecialService {
const correo = terminoValidado(caso.usuario.nombre); const correo = terminoValidado(caso.usuario.nombre);
// Actualiza el status según el caso // Actualiza el status según el caso
update = { caso.status.idStatus = caso.status.idStatus === 11 ? 13 : 14;
idStatus: caso.status.idStatus === 11 ? 13 : 14, await this.casoEspecialRepository.save(caso);
};
let sendcorreo = { let sendMessage: SendMailDto = {
to: caso.correo, to: caso.correo,
subject: correo.subject, subject: correo.subject,
@@ -193,18 +196,16 @@ export class CasoEspecialService {
}; };
// Envía correo // Envía correo
await this.gmail.enviarCorreo(sendcorreo); await this.gmail.enviarCorreo(sendMessage);
break; break;
case 6: case 6:
throw new Error('Este Caso Especial ya se encuentra en Liberación.'); throw new BadRequestException('Este Caso Especial ya se encuentra en Liberación.');
default: default:
throw new Error('Id status no válido.'); throw new BadRequestException('Id status no válido.');
} }
// Actualiza en base de datos
await this.casoEspecialRepository.update({ idCasoEspecial }, update);
return { return {
message: message:
@@ -223,9 +224,9 @@ export class CasoEspecialService {
relations: ['usuario', 'carrera', 'status'], relations: ['usuario', 'carrera', 'status'],
}); });
if (!casoEspecial) { if (!casoEspecial) {
throw new Error('No existe este Caso Especial.'); throw new BadRequestException('No existe este Caso Especial.');
} }
return casoEspecial.createdAt; return casoEspecial;
} }
async updateCasoEspecial(id: number, dto: UpdateCasoEspecialDto) { async updateCasoEspecial(id: number, dto: UpdateCasoEspecialDto) {
@@ -1,3 +1,4 @@
import { Type } from 'class-transformer';
import { import {
IsEmail, IsEmail,
IsNotEmpty, IsNotEmpty,
@@ -13,19 +14,19 @@ import {
} from 'class-validator'; } from 'class-validator';
export class CreateCasoEspecialDto { export class CreateCasoEspecialDto {
@Type(() => Number)
@IsNumber() @IsNumber()
idUsuario: number; idUsuario: number;
@IsNumber() @Type(() => Number)
@IsInt() @IsInt()
idCarrera: number; idCarrera: number;
@Type(() => Number)
@IsNumber() @IsNumber()
@Length(1, 11)
idStatus: number; idStatus: number;
@IsString() @IsString()
@Length(9, 9)
@Matches(/^\d+$/, { message: 'solo puede contener números' }) @Matches(/^\d+$/, { message: 'solo puede contener números' })
numeroCuenta: string; numeroCuenta: string;
@@ -14,15 +14,15 @@ export class UpdateCasoEspecialDto {
@IsOptional() @IsOptional()
@IsDateString() @IsDateString()
fechaInicio?: Date; fechaInicio?: string;
@IsOptional() @IsOptional()
@IsDateString() @IsDateString()
fechaFin?: Date; fechaFin?: string;
@IsOptional() @IsOptional()
@IsDateString() @IsDateString()
fechaNacimiento?: Date; fechaNacimiento?: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@@ -1,63 +1,74 @@
import { Carrera } from "src/carrera/entities/carrera.entity"; import {
import { Status } from "src/status/entities/status.entity"; Entity,
import { Usuario } from "src/usuario/entities/usuario.entity"; PrimaryGeneratedColumn,
import { Column, Entity, ManyToMany, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; Column,
ManyToOne,
JoinColumn,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
import { Carrera } from 'src/carrera/entities/carrera.entity';
import { Status } from 'src/status/entities/status.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity';
@Entity('caso_especial') @Entity('caso_especial')
export class CasoEspecial { export class CasoEspecial {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn({ name: 'idCasoEspecial' })
idCasoEspecial: number; idCasoEspecial: number;
@Column({ type: 'varchar', length: 3 }) @Column({ name: 'creditos', type: 'varchar', length: 3, nullable: false })
creditos: string; creditos: string;
@Column({ type: 'varchar', length: 60 }) @Column({ name: 'correo', type: 'varchar', length: 60, nullable: false })
correo: string; correo: string;
@Column({ type: 'varchar', length: 15 }) @Column({ name: 'telefono', type: 'varchar', length: 15, nullable: false })
telefono: string; telefono: string;
@Column({ type: 'varchar', length: 200, nullable: true, default: null }) @Column({ name: 'institucion', type: 'varchar', length: 200, nullable: true })
institucion: string | null; institucion?: string;
@Column({ type: 'varchar', length: 200, nullable: true, default: null }) @Column({ name: 'dependencia', type: 'varchar', length: 200, nullable: true })
dependencia: string | null; dependencia?: string;
@Column({ type: 'varchar', length: 1, nullable: true, default: null }) @Column({ name: 'motivo', type: 'varchar', length: 1, nullable: true })
motivo: string | null; motivo?: string;
@Column({ type: 'varchar', length: 200 }) @Column({ name: 'direccion', type: 'varchar', length: 200, nullable: false })
direccion: string; direccion: string;
@Column({ type: 'timestamp' }) @Column({ name: 'fechaInicio', type: 'date', nullable: false })
fechaInicio: Date; fechaInicio: string;
@Column({ type: 'timestamp' }) @Column({ name: 'fechaFin', type: 'date', nullable: false })
fechaFin: Date; fechaFin: string;
@Column({ type: 'timestamp' }) @Column({ name: 'fechaNacimiento', type: 'date', nullable: false })
fechaNacimiento: Date; fechaNacimiento: string;
@Column({ type: 'varchar', length: 60 }) @Column({ name: 'carpeta', type: 'varchar', length: 60, nullable: false })
carpeta: string; carpeta: string;
@Column({ type: 'varchar', length: 60 }) @Column({ name: 'archivoZip', type: 'varchar', length: 60, nullable: false })
archivoZip: string; archivoZip: string;
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP' }) @CreateDateColumn({ name: 'createdAt', type: 'datetime' })
updatedAt: Date; createdAt: Date;
@ManyToOne(()=>Usuario, (usuario)=>usuario.casoEspecial) @UpdateDateColumn({ name: 'updatedAt', type: 'datetime' })
usuario:Usuario; updatedAt: Date;
@ManyToOne(()=>Carrera, (carrera)=>carrera.casoEspecial) // ===== Relaciones =====
carrera:Carrera; @ManyToOne(() => Usuario, (usuario) => usuario.casoEspecial) //error en el nombre al llamar caso especial (se cambio)
@JoinColumn({ name: 'idUsuario' })
@ManyToOne(()=>Status, (status)=>status.casoEspecial) usuario: Usuario;
status:Status;
@ManyToOne(() => Carrera, (carrera) => carrera.casosEspeciales)
@JoinColumn({ name: 'idCarrera' })
carrera: Carrera;
@ManyToOne(() => Status, (status) => status.casoEspecial) //error en el nombre al llamar caso especial (se cambio)
@JoinColumn({ name: 'idStatus' })
status: Status;
} }
@@ -1,111 +1,124 @@
import { Servicio } from "src/servicio/entities/servicio.entity"; import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
OneToMany,
} from 'typeorm';
@Entity('cuestionario_alumno') @Entity('cuestionario_alumno')
export class CuestionarioAlumno { export class CuestionarioAlumno {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn({ name: 'idCuestionarioAlumno' })
idCuestionarioAlumno: number; idCuestionarioAlumno: number;
@Column({ type: 'varchar', length: 1 }) @Column({ name: 'sexo', type: 'varchar', length: 1, nullable: false })
sexo: string; sexo: string;
@Column({ type: 'varchar', length: 2 }) @Column({ name: 'edad', type: 'varchar', length: 2, nullable: false })
edad: string; edad: string;
@Column({ type: 'varchar', length: 100 }) @Column({
servicioMedico: string; name: 'servicioMedico',
type: 'varchar',
length: 100,
nullable: false,
})
servicioMedico: string;
@Column() @Column({ name: 'p1', type: 'tinyint', width: 1, nullable: false })
p1: boolean; p1: boolean;
@Column() @Column({ name: 'p2', type: 'tinyint', width: 1, nullable: false })
p2: boolean; p2: boolean;
@Column({ type: 'varchar', length: 150 }) @Column({ name: 'p3', type: 'varchar', length: 150, nullable: false })
p3: string; p3: string;
@Column() @Column({ name: 'p4', type: 'tinyint', width: 1, nullable: false })
p4: boolean; p4: boolean;
@Column({ type: 'varchar', length: 7 }) @Column({ name: 'p5', type: 'varchar', length: 7, nullable: false })
p5: string; p5: string;
@Column() @Column({ name: 'p6', type: 'tinyint', width: 1, nullable: false })
p6: boolean; p6: boolean;
@Column() @Column({ name: 'p7', type: 'tinyint', width: 1, nullable: false })
p7: boolean; p7: boolean;
@Column({ type: 'varchar', length: 5 }) @Column({ name: 'p8', type: 'varchar', length: 5, nullable: false })
p8: string; p8: string;
@Column() @Column({ name: 'p9', type: 'tinyint', width: 1, nullable: false })
p9: boolean; p9: boolean;
@Column() @Column({ name: 'p10', type: 'tinyint', width: 1, nullable: false })
p10: boolean; p10: boolean;
@Column() @Column({ name: 'p11', type: 'tinyint', width: 1, nullable: false })
p11: boolean; p11: boolean;
@Column() @Column({ name: 'p12', type: 'tinyint', width: 1, nullable: false })
p12: boolean; p12: boolean;
@Column() @Column({ name: 'p13', type: 'tinyint', width: 1, nullable: false })
p13: boolean; p13: boolean;
@Column({ type: 'varchar', length: 7 }) @Column({ name: 'p14', type: 'varchar', length: 7, nullable: false })
p14: string; p14: string;
@Column({ type: 'varchar', length: 7 }) @Column({ name: 'p15', type: 'varchar', length: 7, nullable: false })
p15: string; p15: string;
@Column({ type: 'varchar', length: 2 }) @Column({ name: 'p16', type: 'varchar', length: 2, nullable: false })
p16: string; p16: string;
@Column({ type: 'varchar', length: 5 }) @Column({ name: 'p17', type: 'varchar', length: 5, nullable: false })
p17: string; p17: string;
@Column() @Column({ name: 'p18', type: 'tinyint', width: 1, nullable: false })
p18: boolean; p18: boolean;
@Column() @Column({ name: 'p19', type: 'tinyint', width: 1, nullable: false })
p19: boolean; p19: boolean;
@Column() @Column({ name: 'p20', type: 'tinyint', width: 1, nullable: false })
p20: boolean; p20: boolean;
@Column({ type: 'varchar', length: 9 }) @Column({ name: 'p21', type: 'varchar', length: 9, nullable: false })
p21: string; p21: string;
@Column({ type: 'varchar', length: 23 }) @Column({ name: 'p22', type: 'varchar', length: 23, nullable: false })
p22: string; p22: string;
@Column({ type: 'varchar', length: 8 }) @Column({ name: 'p23', type: 'varchar', length: 8, nullable: false })
p23: string; p23: string;
@Column({ type: 'varchar', length: 8 }) @Column({ name: 'p24', type: 'varchar', length: 8, nullable: false })
p24: string; p24: string;
@Column() @Column({ name: 'p25', type: 'tinyint', width: 1, nullable: false })
p25: boolean; p25: boolean;
@Column() @Column({ name: 'p26', type: 'tinyint', width: 1, nullable: false })
p26: boolean; p26: boolean;
@Column({ type: 'varchar', length: 400 }) @Column({ name: 'p27', type: 'varchar', length: 400, nullable: false })
p27: string; p27: string;
@Column({ type: 'varchar', length: 1 }) @Column({ name: 'p28', type: 'varchar', length: 1, nullable: false })
p28: string; p28: string;
@Column({ type: 'varchar', length: 1 }) @Column({ name: 'p29', type: 'varchar', length: 1, nullable: false })
p29: string; p29: string;
@Column({ type: 'varchar', length: 400 }) @Column({ name: 'p30', type: 'varchar', length: 400, nullable: false })
p30: string; p30: string;
@OneToMany(()=>Servicio,(servicio)=>servicio.cuestionarioAlumno2) @CreateDateColumn({ name: 'createdAt', type: 'datetime' })
servicio:Servicio[]; createdAt: Date;
@OneToMany(() => Servicio, (servicio) => servicio.cuestionarioAlumno2)
servicio: Servicio[];
} }
@@ -7,12 +7,15 @@ import {
Param, Param,
Delete, Delete,
UseGuards, UseGuards,
Query,
Res,
} from '@nestjs/common'; } from '@nestjs/common';
import { CuestionarioAlumno2Service } from './cuestionario-alumno2.service'; import { CuestionarioAlumno2Service } from './cuestionario-alumno2.service';
import { CreateCuestionarioAlumno2Dto } from './dto/create-cuestionario-alumno2.dto'; import { CreateCuestionarioAlumno2Dto } from './dto/create-cuestionario-alumno2.dto';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { findCuestionarioAlumno2Dto } from './dto/find.dto'; import { findCuestionarioAlumno2Dto } from './dto/find.dto';
import { Response } from 'express';
@Controller('cuestionario-alumno2') @Controller('cuestionario-alumno2')
export class CuestionarioAlumno2Controller { export class CuestionarioAlumno2Controller {
@@ -23,13 +26,18 @@ export class CuestionarioAlumno2Controller {
@Post() @Post()
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
create(@Body() createCuestionarioAlumno2Dto: CreateCuestionarioAlumno2Dto) { create(@Body() createCuestionarioAlumno2Dto: CreateCuestionarioAlumno2Dto) {
console.log('CreateCuestionarioAlumno2Dto recibido:', createCuestionarioAlumno2Dto);
return this.cuestionarioAlumno2Service.create(createCuestionarioAlumno2Dto); return this.cuestionarioAlumno2Service.create(createCuestionarioAlumno2Dto);
} }
@Get() @Get()
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
findAll(@Body() findDto: findCuestionarioAlumno2Dto) { async findAll(@Query() findDto: findCuestionarioAlumno2Dto, @Res() res: Response) {
return this.cuestionarioAlumno2Service.get(findDto.version, findDto.anio); const path = await this.cuestionarioAlumno2Service.get(findDto.version, findDto.anio);
return res.download(path);
} }
// @Get(':id') // @Get(':id')
@@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common'; import { BadRequestException, Injectable } from '@nestjs/common';
import { CreateCuestionarioAlumno2Dto } from './dto/create-cuestionario-alumno2.dto'; import { CreateCuestionarioAlumno2Dto } from './dto/create-cuestionario-alumno2.dto';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { Servicio } from 'src/servicio/entities/servicio.entity'; import { Servicio } from 'src/servicio/entities/servicio.entity';
@@ -21,7 +21,7 @@ export class CuestionarioAlumno2Service {
private cuestionarioAlumnoRepository: Repository<CuestionarioAlumno>, private cuestionarioAlumnoRepository: Repository<CuestionarioAlumno>,
private validacionService: ValidacionService, private validacionService: ValidacionService,
private archivoService: ArchivoService, private archivoService: ArchivoService,
) {} ) { }
async create(createCuestionarioAlumno2Dto: CreateCuestionarioAlumno2Dto) { async create(createCuestionarioAlumno2Dto: CreateCuestionarioAlumno2Dto) {
let idServicio = createCuestionarioAlumno2Dto.idServicio; let idServicio = createCuestionarioAlumno2Dto.idServicio;
@@ -32,13 +32,14 @@ export class CuestionarioAlumno2Service {
let servicio = await this.servicioRepository.findOne({ let servicio = await this.servicioRepository.findOne({
where: { idServicio }, where: { idServicio },
relations: ['cuestionarioAlumno2', 'status'],
}); });
if (!servicio) { if (!servicio) {
throw new Error('No existe este Servicio Social.'); throw new BadRequestException('No existe este Servicio Social.');
} }
if (servicio.cuestionarioAlumno2.idCuestionarioAlumno2) { if (servicio.cuestionarioAlumno2) {
throw new Error( throw new BadRequestException(
'Este Servicio Social ya cuenta con cuestionario de alumno', 'Este Servicio Social ya cuenta con cuestionario de alumno',
); );
} }
@@ -48,7 +49,7 @@ export class CuestionarioAlumno2Service {
console.log('antes de create ques2'); console.log('antes de create ques2');
if (messageByStatus.hasOwnProperty(servicio.status.idStatus)) { if (messageByStatus.hasOwnProperty(servicio.status.idStatus)) {
throw new Error(messageByStatus[servicio.status.idStatus]); throw new BadRequestException(messageByStatus[servicio.status.idStatus]);
} }
console.log('datos a registrar en la tabla cuestionarioAlumno2', { console.log('datos a registrar en la tabla cuestionarioAlumno2', {
@@ -66,44 +67,87 @@ export class CuestionarioAlumno2Service {
{ cuestionarioAlumno2: respuestasRegistradas }, { cuestionarioAlumno2: respuestasRegistradas },
); );
await this.validacionService.validarPreTermino(idServicio)
return resX; return resX;
} }
async get(version: string, anio: string) { /*
const year = anio; async get(version: string, anio: string) {
const path = `server/uploads/${year}_cuestionario_alumno.csv`; const year = anio;
const path = `server/uploads/${year}_cuestionario_alumno.csv`;
let data = [];
console.log(year);
let temp;
if (version == 'v1') {
temp = await this.cuestionarioAlumnoRepository.find({
where: {
idCuestionarioAlumno: Not(IsNull()),
},
});
}
if (version == 'v2') {
temp = await this.cuestionarioAlumno2Repository.find({
where: {
idCuestionarioAlumno2: Not(IsNull()),
},
});
}
if (!temp) {
throw new BadRequestException();
}
data = temp.map((item) => ({ ...item }));
await this.archivoService.eliminarArchivo(path).catch((err) => {
console.log(err);
});
return this.archivoService.crearArchivo(path, convertArrayToCSV(data));
}
*/
async get(version: string, anio: string): Promise<string> {
const path = `server/uploads/${anio}_cuestionario_alumno.csv`;
let data = [];
console.log(year);
let temp; let temp;
if (version == 'v1') { if (version === 'v1') {
temp = this.cuestionarioAlumnoRepository.find({ temp = await this.cuestionarioAlumnoRepository.find({
where: { where: {
idCuestionarioAlumno: Not(IsNull()), idCuestionarioAlumno: Not(IsNull()),
}, },
}); });
} }
if (version == 'v2') { if (version === 'v2') {
temp = this.cuestionarioAlumno2Repository.find({ temp = await this.cuestionarioAlumno2Repository.find({
where: { where: {
idCuestionarioAlumno2: Not(IsNull()), idCuestionarioAlumno2: Not(IsNull()),
}, },
}); });
} }
if (!temp) { if (!temp || temp.length === 0) {
throw new Error(); throw new BadRequestException('No hay datos para exportar');
} }
data = temp.map((item) => ({ ...item })); const data = temp.map((item) => ({ ...item }));
await this.archivoService.eliminarArchivo(path).catch((err) => { await this.archivoService.eliminarArchivo(path).catch(() => null);
console.log(err);
});
return this.archivoService.crearArchivo(path, convertArrayToCSV(data)); // ✅ Creamos el archivo
await this.archivoService.crearArchivo(
path,
convertArrayToCSV(data),
);
// ✅ SOLO regresamos el path
return path;
} }
} }
@@ -7,7 +7,7 @@ export class CreateCuestionarioAlumno2Dto {
@IsNumber() @IsNumber()
@IsNotEmpty() @IsNotEmpty()
idServicio:number idServicio: number
@IsString() @IsString()
@@ -134,6 +134,10 @@ export class CreateCuestionarioAlumno2Dto {
@IsNotEmpty() @IsNotEmpty()
p12_C_4: string; p12_C_4: string;
@IsString()
@IsNotEmpty()
p12_C_5: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
p12_D_1: string; p12_D_1: string;
@@ -1,145 +1,143 @@
import { Servicio } from "src/servicio/entities/servicio.entity"; import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('cuestionario_alumno_2') @Entity('cuestionario_alumno_2')
export class CuestionarioAlumno2 { export class CuestionarioAlumno2 {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
idCuestionarioAlumno2: number; idCuestionarioAlumno2: number;
@Column({ type: 'varchar', length: 100, nullable: false }) @Column({ type: 'text', nullable: true })
p1: string; p1: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ type: 'varchar', length: 200, nullable: true })
p2: string; p2: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 200, nullable: true })
p4: string; p4: string;
@Column({ type: 'varchar', length: 50, nullable: true }) @Column({ type: 'varchar', length: 500, nullable: true })
p5: string; p5: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 200, nullable: true })
p6: string; p6: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 200, nullable: true })
p7: string; p7: string;
@Column({ type: 'varchar', length: 50, nullable: true }) @Column({ type: 'varchar', length: 500, nullable: true })
p8: string; p8: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 200, nullable: true })
p9: string; p9: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 50, nullable: true })
p3_1: string; p3_1: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 50, nullable: true })
p3_2: string; p3_2: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 50, nullable: true })
p3_3: string; p3_3: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 50, nullable: true })
p3_4: string; p3_4: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p10_1: string; p10_1: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p10_2: string; p10_2: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p10_3: string; p10_3: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p11_1: string; p11_1: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p11_2: string; p11_2: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p11_3: string; p11_3: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p11_4: string; p11_4: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_A_1: string; p12_A_1: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_A_2: string; p12_A_2: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_A_3: string; p12_A_3: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_A_4: string; p12_A_4: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_B_1: string; p12_B_1: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_B_2: string; p12_B_2: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_B_3: string; p12_B_3: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_B_4: string; p12_B_4: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_C_1: string; p12_C_1: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_C_2: string; p12_C_2: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_C_3: string; p12_C_3: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p12_C_4: string; p12_C_4: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 50, nullable: true })
p12_D_1: string; p12_D_1: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 50, nullable: true })
p12_D_2: string; p12_D_2: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 50, nullable: true })
p12_D_3: string; p12_D_3: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 50, nullable: true })
p12_D_4: string; p12_D_4: string;
@Column({ type: 'varchar', length: 20, nullable: false }) @Column({ type: 'varchar', length: 50, nullable: true })
p12_D_5: string; p12_D_5: string;
@Column({ type: 'varchar', length: 100, nullable: false }) @Column({ type: 'varchar', length: 200, nullable: true })
p13: string; p13: string;
@Column({ type: 'varchar', length: 100, nullable: false }) @Column({ type: 'varchar', length: 200, nullable: true })
p14: string; p14: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 200, nullable: true })
p15: string; p15: string;
@Column({ type: 'varchar', length: 50, nullable: true }) @Column({ type: 'varchar', length: 500, nullable: true })
p16: string; p16: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p17_1: string; p17_1: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p17_2: string; p17_2: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ type: 'varchar', length: 20, nullable: true })
p17_3: string; p17_3: string;
@Column({ type: 'varchar', length: 50, nullable: true }) @Column({ type: 'varchar', length: 500, nullable: true })
p18: string; p18: string;
@OneToMany(()=>Servicio,(servicio)=>servicio.cuestionarioAlumno2) @OneToMany(() => Servicio, (servicio) => servicio.cuestionarioAlumno2)
servicio:Servicio[]; servicio: Servicio[];
} }
@@ -1,5 +1,11 @@
import { Servicio } from "src/servicio/entities/servicio.entity"; import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import {
Column,
CreateDateColumn,
Entity,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('cuestionario_programa') @Entity('cuestionario_programa')
export class CuestionarioPrograma { export class CuestionarioPrograma {
@@ -30,6 +36,11 @@ export class CuestionarioPrograma {
@Column({ type: 'varchar', length: 800, nullable: false }) @Column({ type: 'varchar', length: 800, nullable: false })
p7: string; p7: string;
@OneToMany(()=>Servicio,(servicio)=>servicio.cuestionarioPrograma) @CreateDateColumn({ name: 'createdAt', type: 'datetime' })
servicio:Servicio[]; createdAt: Date;
// faltaba createdAT
@OneToMany(() => Servicio, (servicio) => servicio.cuestionarioPrograma)
servicio: Servicio[];
} }
@@ -7,6 +7,7 @@ import { CuestionarioPrograma2 } from './entities/cuestionario-programa2.entity'
import { Servicio } from 'src/servicio/entities/servicio.entity'; import { Servicio } from 'src/servicio/entities/servicio.entity';
import { ValidacionService } from 'src/helpers.services/validacion.service'; import { ValidacionService } from 'src/helpers.services/validacion.service';
import { ArchivoService } from 'src/helpers.services/archivo.service'; import { ArchivoService } from 'src/helpers.services/archivo.service';
import { ServicioService } from 'src/servicio/servicio.service';
@Module({ @Module({
imports: [ imports: [
@@ -9,6 +9,7 @@ import { ValidacionService } from 'src/helpers.services/validacion.service';
import { Servicio } from 'src/servicio/entities/servicio.entity'; import { Servicio } from 'src/servicio/entities/servicio.entity';
import { messageByStatus } from 'src/messageByStatus'; import { messageByStatus } from 'src/messageByStatus';
import { convertArrayToCSV } from 'convert-array-to-csv'; import { convertArrayToCSV } from 'convert-array-to-csv';
import { ServicioService } from 'src/servicio/servicio.service';
@Injectable() @Injectable()
export class CuestionarioPrograma2Service { export class CuestionarioPrograma2Service {
@@ -21,7 +22,8 @@ export class CuestionarioPrograma2Service {
private servicioRepository: Repository<Servicio>, private servicioRepository: Repository<Servicio>,
private archivoService: ArchivoService, private archivoService: ArchivoService,
private validacionService: ValidacionService, private validacionService: ValidacionService,
) {}
) { }
async create(cuestionarioPrograma2: CreateCuestionarioPrograma2Dto) { async create(cuestionarioPrograma2: CreateCuestionarioPrograma2Dto) {
let idServicio = cuestionarioPrograma2.idServicio; let idServicio = cuestionarioPrograma2.idServicio;
@@ -31,21 +33,30 @@ export class CuestionarioPrograma2Service {
let servicio = await this.servicioRepository.findOne({ let servicio = await this.servicioRepository.findOne({
where: { idServicio }, where: { idServicio },
relations: ['cuestionarioPrograma2', 'status'],
}); });
if (!servicio) { if (!servicio) {
throw new Error('No existe este Servicio Social.'); throw new BadRequestException('No existe este Servicio Social.');
} }
if (servicio.cuestionarioPrograma2.idCuestionarioPrograma2) {
throw new Error( if (servicio.cuestionarioPrograma2) {
'Este Servicio Social ya cuenta con cuestionario de alumno', throw new BadRequestException(
'Este Servicio Social ya cuenta con cuestionario de programa',
); );
} }
// Esta validacion esta incorrecta la cambie por la linea de arriva
// if (servicio.cuestionarioPrograma2.idCuestionarioPrograma2) {
// throw new BadRequestException(
// 'Este Servicio Social ya cuenta con cuestionario de alumno',
// );
// }
console.log('el servicio social si existe', servicio); console.log('el servicio social si existe', servicio);
console.log('antes de create ques2'); console.log('antes de create ques2');
if (messageByStatus.hasOwnProperty(servicio.status.idStatus)) if (messageByStatus.hasOwnProperty(servicio.status.idStatus))
throw new Error(messageByStatus[servicio.status.idStatus]); throw new BadRequestException(messageByStatus[servicio.status.idStatus]);
console.log('despues de registrar cuestionario', cuestionario); console.log('despues de registrar cuestionario', cuestionario);
@@ -61,7 +72,7 @@ export class CuestionarioPrograma2Service {
); );
console.log(x); console.log(x);
await this.validacionService.validarPreTermino(idServicio)
return respuestasRegistradas; return respuestasRegistradas;
} }
@@ -1,142 +1,142 @@
import { import {
IsNotEmpty, IsNotEmpty,
IsNumber, IsNumber,
IsOptional, IsOptional,
IsString, IsString,
Length, Length,
} from 'class-validator'; } from 'class-validator';
export class CreateCuestionarioPrograma2Dto {
@IsNumber() export class CreateCuestionarioPrograma2Dto {
@IsNotEmpty()
idServicio:number @IsNumber()
@IsNotEmpty()
idServicio: number
// p1, p2, p4, p5, p6 // p1, p2, p4, p5, p6
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 100) @Length(1, 500)
p1: string; p1: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 200)
p2: string; p2: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 100) @Length(1, 500)
p4: string; p4: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 5) @Length(1, 200)
p5: string; p5: string;
@IsOptional() @IsOptional()
@IsString() @IsString()
@Length(1, 100) @Length(1, 500)
p6?: string; p6?: string;
// Sección p3_A // Sección p3_A
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_A_1: string; p3_A_1: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_A_2: string; p3_A_2: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_A_3: string; p3_A_3: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_A_4: string; p3_A_4: string;
// Sección p3_B // Sección p3_B
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_B_1: string; p3_B_1: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_B_2: string; p3_B_2: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_B_3: string; p3_B_3: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_B_4: string; p3_B_4: string;
// Sección p3_C // Sección p3_C
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_C_1: string; p3_C_1: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_C_2: string; p3_C_2: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_C_3: string; p3_C_3: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_C_4: string; p3_C_4: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_C_5: string; p3_C_5: string;
// Sección p3_D // Sección p3_D
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_D_1: string; p3_D_1: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_D_2: string; p3_D_2: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_D_3: string; p3_D_3: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_D_4: string; p3_D_4: string;
@IsString() @IsString()
@IsNotEmpty() @IsNotEmpty()
@Length(1, 50) @Length(1, 50)
p3_D_5: string; p3_D_5: string;
// Sección p3_E // Elimine esta tabla por que no existe en el cuestionario => Verificar si se ocupa en otro cuestionario ?
@IsString() // Sección p3_E
@IsNotEmpty() // @IsString()
@Length(1, 50) // @IsNotEmpty()
p3_E_1: string; // @Length(1, 50)
} // p3_E_1: string;
}
@@ -1,90 +1,95 @@
import { Servicio } from "src/servicio/entities/servicio.entity"; import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import {
Column,
CreateDateColumn,
Entity,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('cuestionario_programa_2') @Entity('cuestionario_programa_2')
export class CuestionarioPrograma2 { export class CuestionarioPrograma2 {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn({ name: 'idCuestionarioPrograma2' })
idCuestionarioPrograma2: number; idCuestionarioPrograma2: number;
// Sección p3_A_* @CreateDateColumn({ name: 'createdAt', type: 'datetime', nullable: true })
@Column({ type: 'varchar', length: 50, nullable: false }) createdAt?: Date;
p3_A_1: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_A_1', type: 'varchar', length: 50, nullable: true })
p3_A_2: string; p3_A_1?: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_A_2', type: 'varchar', length: 50, nullable: true })
p3_A_3: string; p3_A_2?: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_A_3', type: 'varchar', length: 50, nullable: true })
p3_A_4: string; p3_A_3?: string;
// Sección p3_B_* @Column({ name: 'p3_A_4', type: 'varchar', length: 50, nullable: true })
@Column({ type: 'varchar', length: 50, nullable: false }) p3_A_4?: string;
p3_B_1: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_B_1', type: 'varchar', length: 50, nullable: true })
p3_B_2: string; p3_B_1?: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_B_2', type: 'varchar', length: 50, nullable: true })
p3_B_3: string; p3_B_2?: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_B_3', type: 'varchar', length: 50, nullable: true })
p3_B_4: string; p3_B_3?: string;
// Sección p3_C_* @Column({ name: 'p3_B_4', type: 'varchar', length: 50, nullable: true })
@Column({ type: 'varchar', length: 50, nullable: false }) p3_B_4?: string;
p3_C_1: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_C_1', type: 'varchar', length: 50, nullable: true })
p3_C_2: string; p3_C_1?: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_C_2', type: 'varchar', length: 50, nullable: true })
p3_C_3: string; p3_C_2?: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_C_3', type: 'varchar', length: 50, nullable: true })
p3_C_4: string; p3_C_3?: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_C_4', type: 'varchar', length: 50, nullable: true })
p3_C_5: string; p3_C_4?: string;
// Sección p3_D_* @Column({ name: 'p3_C_5', type: 'varchar', length: 50, nullable: true })
@Column({ type: 'varchar', length: 50, nullable: false }) p3_C_5?: string;
p3_D_1: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_D_1', type: 'varchar', length: 50, nullable: true })
p3_D_2: string; p3_D_1?: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_D_2', type: 'varchar', length: 50, nullable: true })
p3_D_3: string; p3_D_2?: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_D_3', type: 'varchar', length: 50, nullable: true })
p3_D_4: string; p3_D_3?: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p3_D_4', type: 'varchar', length: 50, nullable: true })
p3_D_5: string; p3_D_4?: string;
// Sección p3_E_* @Column({ name: 'p3_D_5', type: 'varchar', length: 50, nullable: true })
@Column({ type: 'varchar', length: 50, nullable: false }) p3_D_5?: string;
p3_E_1: string;
// p1, p2, p4, p5, p6 @Column({ name: 'p3_E_1', type: 'varchar', length: 50, nullable: true })
@Column({ type: 'varchar', length: 100, nullable: false }) p3_E_1?: string;
p1: string;
@Column({ type: 'varchar', length: 50, nullable: false }) @Column({ name: 'p1', type: 'varchar', length: 500, nullable: true })
p2: string; p1?: string;
@Column({ type: 'varchar', length: 100, nullable: false }) @Column({ name: 'p2', type: 'varchar', length: 200, nullable: true })
p4: string; p2?: string;
@Column({ type: 'varchar', length: 5, nullable: false }) @Column({ name: 'p4', type: 'varchar', length: 500, nullable: true })
p5: string; p4?: string;
@Column({ type: 'varchar', length: 100, nullable: true }) @Column({ name: 'p5', type: 'varchar', length: 200, nullable: true })
p5?: string;
@Column({ name: 'p6', type: 'varchar', length: 500, nullable: true })
p6?: string; p6?: string;
@OneToMany(()=>Servicio,(servicio)=>servicio.cuestionarioAlumno2) @Column({ name: 'p7', type: 'varchar', length: 200, nullable: true })
servicio:Servicio[]; p7?: string;
@OneToMany(() => Servicio, (servicio) => servicio.cuestionarioAlumno2)
servicio: Servicio[];
} }
+87
View File
@@ -0,0 +1,87 @@
// src/drive/drive.controller.ts
import {
BadRequestException,
Body,
Controller,
Delete,
Get,
Param,
Post,
Query,
} from '@nestjs/common';
import { DriveService } from './drive.service';
import * as fs from 'fs';
@Controller('drive-test')
export class DriveController {
constructor(private readonly driveService: DriveService) { }
// ✅ Listar archivos/carpetas
@Get('list')
async list(@Query('pageToken') pageToken?: string) {
return this.driveService.list(pageToken || '');
}
// ✅ Crear carpeta dentro de CARPETA
@Post('mkdir')
async createFolder(@Body('name') name: string) {
if (!name) {
throw new BadRequestException('El nombre es obligatorio');
}
const folderId = await this.driveService.mkDir(name);
return { folderId };
}
// ✅ Obtener carpeta (buscar o crear)
@Get('folder/:numeroCuenta')
async getOrCreateFolder(
@Param('numeroCuenta') numeroCuenta: string,
) {
const folderId = await this.driveService.folder(numeroCuenta);
return { folderId };
}
// ✅ Subir archivo (archivo dummy para pruebas)
@Post('upload')
async uploadFile(
@Body('parent') parent: string,
) {
if (!parent) {
throw new BadRequestException('Parent es obligatorio');
}
// Archivo temporal para prueba
const tempPath = './temp-test.txt';
fs.writeFileSync(tempPath, 'Archivo de prueba DriveService');
const fileId = await this.driveService.uploadFile(
tempPath,
'test-drive.txt',
'text/plain',
parent,
);
return { fileId };
}
// ✅ Mover archivo (update)
@Post('update')
async updateFile(
@Body('fileId') fileId: string,
@Body('addParents') addParents: string,
@Body('removeParents') removeParents: string,
) {
if (!fileId || !addParents || !removeParents) {
throw new BadRequestException('Datos incompletos');
}
return this.driveService.update(fileId, addParents, removeParents);
}
// ✅ Eliminar archivo
@Delete('delete/:fileId')
async deleteFile(@Param('fileId') fileId: string) {
await this.driveService.deleteFile(fileId);
return { message: 'Archivo eliminado correctamente' };
}
}
+3 -2
View File
@@ -1,9 +1,10 @@
// src/drive/drive.module.ts
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { DriveService } from './drive.service'; import { DriveService } from './drive.service';
import { DriveController } from './drive.controller';
@Module({ @Module({
controllers: [DriveController],
providers: [DriveService], providers: [DriveService],
exports: [DriveService], exports: [DriveService],
}) })
export class DriveModule {} export class DriveModule { }
+76 -55
View File
@@ -1,9 +1,11 @@
// src/drive/drive.service.ts // src/drive/drive.service.ts
import { Injectable, InternalServerErrorException } from '@nestjs/common'; import {
BadRequestException,
Injectable,
InternalServerErrorException,
} from '@nestjs/common';
import { google } from 'googleapis'; import { google } from 'googleapis';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path';
import { error } from 'console';
@Injectable() @Injectable()
export class DriveService { export class DriveService {
@@ -11,30 +13,32 @@ export class DriveService {
private loadCredentials() { private loadCredentials() {
try { try {
const credenciales = JSON.parse( const oAuth2Client = new google.auth.OAuth2(
fs.readFileSync(path.resolve('./cred.json'), 'utf8'), process.env.CLIENT_ID,
process.env.CLIENT_SECRET,
); );
credenciales.private_key = credenciales.private_key.replace(/\\n/g, '\n');
return new google.auth.JWT({ oAuth2Client.setCredentials({
email: credenciales.client_email, refresh_token: process.env.REFRESH_TOKEN,
key: credenciales.private_key, });
scopes: ['https://www.googleapis.com/auth/drive'],
}); return oAuth2Client;
} catch (err) { } catch (err) {
console.error('❌ Error cargando credenciales:', err); throw new InternalServerErrorException(
throw new InternalServerErrorException('Error al cargar credenciales'); 'Error al cargar credenciales de Google Drive',
);
} }
} }
async list(pageToken = '') { async list(pageToken = '') {
const authClient = await this.loadCredentials(); const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient }); const drive = google.drive({ version: 'v3', auth: authClient });
try { try {
const res = await drive.files.list({ const res = await drive.files.list({
corpora: 'user', corpora: 'user',
supportsTeamDrives: true, supportsAllDrives: true,
includeTeamDriveItems: true, includeItemsFromAllDrives: true,
spaces: 'drive', spaces: 'drive',
pageSize: 1000, pageSize: 1000,
pageToken, pageToken,
@@ -50,28 +54,36 @@ export class DriveService {
async deleteFile(fileId: string) { async deleteFile(fileId: string) {
if (fileId === process.env.CARPETA) { if (fileId === process.env.CARPETA) {
throw new Error('No se puede eliminar este archivo/carpeta.'); throw new BadRequestException(
'No se puede eliminar este archivo/carpeta.',
);
} }
const authClient = await this.loadCredentials(); console.log('Eliminando archivo de Drive, ID:', fileId);
const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient }); const drive = google.drive({ version: 'v3', auth: authClient });
try { try {
return await drive.files.delete({ fileId }); return await drive.files.delete({ fileId });
} catch (err) { } catch (err) {
throw new InternalServerErrorException( console.error('Error al eliminar archivo de Drive:', err.message);
'Error al eliminar archivo: ' + err.message, return 0;
);
} }
} }
async uploadFile(filePath: string, name: string, mimeType: string, parent: string) { async uploadFile(
const authClient = await this.loadCredentials(); filePath: string,
name: string,
mimeType: string,
parent: string,
) {
const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient }); const drive = google.drive({ version: 'v3', auth: authClient });
const requestBody = { const requestBody = {
name, name,
parents: [parent], parents: [parent],
}; };
const media = { const media = {
mimeType, mimeType,
body: fs.createReadStream(filePath), body: fs.createReadStream(filePath),
@@ -82,9 +94,12 @@ export class DriveService {
requestBody, requestBody,
media, media,
fields: 'id', fields: 'id',
supportsAllDrives: true,
}); });
fs.unlinkSync(filePath); fs.unlinkSync(filePath);
return res.data.id; console.log('Archivo subido a Drive, ID:', res.data.id);
return res.data.id!;
} catch (err) { } catch (err) {
throw new InternalServerErrorException( throw new InternalServerErrorException(
'Error al subir archivo: ' + err.message, 'Error al subir archivo: ' + err.message,
@@ -93,36 +108,35 @@ export class DriveService {
} }
async mkDir(name: string): Promise<string> { async mkDir(name: string): Promise<string> {
const authClient = await this.loadCredentials(); if (!name) {
throw new BadRequestException('Nombre de carpeta inválido');
}
if (!process.env.CARPETA) {
throw new InternalServerErrorException('CARPETA no configurada');
}
const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient }); const drive = google.drive({ version: 'v3', auth: authClient });
if(!name){
throw new Error()
}
if(!process.env.CARPETA){
throw new Error()
}
const requestBody = { const requestBody = {
name, name,
mimeType: 'application/vnd.google-apps.folder', mimeType: 'application/vnd.google-apps.folder',
parents: [process.env.CARPETA], parents: [process.env.CARPETA],
}; };
try { try {
const res = await drive.files.create({ const res = await drive.files.create({
requestBody, requestBody,
fields: 'id', fields: 'id',
supportsAllDrives: true,
}); });
if(!res.data.id){ if (!res.data.id) {
throw new Error() throw new BadRequestException('No se pudo crear la carpeta');
} }
return res.data.id; return res.data.id;
} catch (err) { } catch (err) {
throw new InternalServerErrorException( throw new InternalServerErrorException(
'Error al crear carpeta: ' + err.message, 'Error al crear carpeta: ' + err.message,
@@ -131,7 +145,7 @@ export class DriveService {
} }
async update(fileId: string, addParents: string, removeParents: string) { async update(fileId: string, addParents: string, removeParents: string) {
const authClient = await this.loadCredentials(); const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient }); const drive = google.drive({ version: 'v3', auth: authClient });
try { try {
@@ -139,6 +153,7 @@ export class DriveService {
fileId, fileId,
addParents, addParents,
removeParents, removeParents,
supportsAllDrives: true,
}); });
return res.data; return res.data;
} catch (err) { } catch (err) {
@@ -148,24 +163,30 @@ export class DriveService {
} }
} }
async folder(numeroCuenta: string) { async folder(numeroCuenta: string): Promise<string> {
let pageToken = ''; if (!process.env.CARPETA) {
do { throw new InternalServerErrorException('CARPETA no configurada');
const res = await this.list(pageToken); }
if(!res.files){
throw new Error() const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient });
const res = await drive.files.list({
q: `
mimeType='application/vnd.google-apps.folder'
and name='${numeroCuenta}'
and '${process.env.CARPETA}' in parents
and trashed=false
`,
fields: 'files(id, name)',
supportsAllDrives: true,
includeItemsFromAllDrives: true,
});
if (res.data.files?.length) {
return res.data.files[0].id!;
} }
for (const file of res.files) {
if (
file.name === numeroCuenta &&
file.mimeType === 'application/vnd.google-apps.folder'
) {
return file.id;
}
}
pageToken = res.nextPageToken || '';
} while (pageToken);
return this.mkDir(numeroCuenta); return this.mkDir(numeroCuenta);
} }
} }
+14 -1
View File
@@ -11,7 +11,19 @@ const preRegistro = (password, nombre) => {
subject: 'Notificación de Pre-Registro.', subject: 'Notificación de Pre-Registro.',
msj: `Estimado(a) ${nombre}: msj: `Estimado(a) ${nombre}:
Te informamos que la dependencia en donde decidiste realizar tu servicio social ya te registró en nuestro sistema de servicio social, por lo que para completar el proceso debes ingresar en un periodo máximo de 10 días hábiles a partir de la recepción de este mensaje, a la página https://iris.acatlan.unam.mx/ con tu número de cuenta y contraseña: ${password}, para llenar un formulario con tus datos.
Te informamos que la dependencia en donde decidiste realizar tu servicio social
ya te registró en nuestro sistema de servicio social, por lo que para completar
el proceso deberás:
-Ingresar en un periodo máximo de 10 días hábiles a partir de la recepción de este mensaje,
a la página https://iris.acatlan.unam.mx/
-Tu usuario será tu número de cuenta
-Tu contraseña es: ${password}
-Deberás llenar un formulario con tus datos
En caso de no realizar dicho registro, tu trámite queda inconcluso y no nos hacemos responsables por las horas de servicio social prestadas en cualquier dependencia. En caso de no realizar dicho registro, tu trámite queda inconcluso y no nos hacemos responsables por las horas de servicio social prestadas en cualquier dependencia.
@@ -272,6 +284,7 @@ ${atentamente}
}; };
}; };
// Quitamos el module para que podamos exportar normal anteriormente module.export
module.exports = { module.exports = {
preRegistro, preRegistro,
preRegistroRechazadoAlumno, preRegistroRechazadoAlumno,
+58 -37
View File
@@ -1,4 +1,4 @@
import { Injectable } from "@nestjs/common"; import { BadRequestException, Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm"; import { InjectRepository } from "@nestjs/typeorm";
import moment from "moment"; import moment from "moment";
import { Servicio } from "src/servicio/entities/servicio.entity"; import { Servicio } from "src/servicio/entities/servicio.entity";
@@ -8,49 +8,49 @@ import validator from "validator";
@Injectable() @Injectable()
export class ValidacionService { export class ValidacionService {
constructor( constructor(
@InjectRepository(Servicio) @InjectRepository(Servicio)
private servicioRepository:Repository<Servicio>, private servicioRepository: Repository<Servicio>,
){} ) { }
caracterEspecial(char) { caracterEspecial(char) {
const charset = [' ', '.', ',', ':', ';', '?', '¿', '!', '¡', '(', ')', '"', "'", '-', '_', '/', '#', '%', '\n']; const charset = [' ', '.', ',', ':', ';', '?', '¿', '!', '¡', '(', ')', '"', "'", '-', '_', '/', '#', '%', '\n'];
return charset.includes(char); return charset.includes(char);
} }
yaExiste(campo, m) { yaExiste(campo, m) {
throw new Error(`Ya se encuentra en uso ${m ? 'el' : 'la'} ${campo}.`); throw new BadRequestException(`Ya se encuentra en uso ${m ? 'el' : 'la'} ${campo}.`);
} }
noValido(campo, m, razon) { noValido(campo, m, razon) {
throw new Error(`${m ? 'El' : 'La'} ${campo} no es valid${m ? 'o' : 'a'}, ${razon}.`); throw new BadRequestException(`${m ? 'El' : 'La'} ${campo} no es valid${m ? 'o' : 'a'}, ${razon}.`);
} }
noHay(variable, campo, m) { noHay(variable, campo, m) {
if (!variable) throw new Error(`No se mando ${m ? 'el' : 'la'} ${campo}.`); if (!variable) throw new BadRequestException(`No se mando ${m ? 'el' : 'la'} ${campo}.`);
} }
validacionBasicaStr(texto, campo, m, length) { validacionBasicaStr(texto, campo, m, length) {
this.noHay(texto, campo, m); this.noHay(texto, campo, m);
if (typeof texto !== 'string') this.noValido(campo, m, 'no se mando una string'); if (typeof texto !== 'string') this.noValido(campo, m, 'no se mando una string');
if (length && texto.length > length) this.noValido(campo, m, 'tiene más caracteres de lo permitido'); if (length && texto.length > length) this.noValido(campo, m, 'tiene más caracteres de lo permitido');
return texto; return texto;
} }
validarNumeroEntero(numero, campo, m: boolean = true) { validarNumeroEntero(numero, campo, m: boolean = true) {
this.noHay(numero, campo, m); this.noHay(numero, campo, m);
if (typeof numero === 'number') numero = numero.toString(); if (typeof numero === 'number') numero = numero.toString();
if (!validator.isNumeric(numero, { no_symbols: true })) this.noValido(campo, m, 'no es un número entero válido'); if (!validator.isNumeric(numero, { no_symbols: true })) this.noValido(campo, m, 'no es un número entero válido');
return Number(numero); return Number(numero);
} }
validarCorreo(correo: any, length?: any, campo: string = 'correo', m: boolean = true) { validarCorreo(correo: any, length?: any, campo: string = 'correo', m: boolean = true) {
this.validacionBasicaStr(correo, campo, m, length); this.validacionBasicaStr(correo, campo, m, length);
if (!validator.isEmail(correo)) this.noValido(campo, m, 'no es un correo válido'); if (!validator.isEmail(correo)) this.noValido(campo, m, 'no es un correo válido');
return correo; return correo;
} }
validarTexto(texto, campo, m, length) { validarTexto(texto, campo, m, length) {
this.validacionBasicaStr(texto, campo, m, length); this.validacionBasicaStr(texto, campo, m, length);
for (const char of texto) { for (const char of texto) {
if (!validator.isAlpha(char, 'es-ES') && char !== ' ' && char !== '.') { if (!validator.isAlpha(char, 'es-ES') && char !== ' ' && char !== '.') {
@@ -60,7 +60,7 @@ export class ValidacionService {
return texto; return texto;
} }
validarAlfanumerico(texto, campo, m, length) { validarAlfanumerico(texto, campo, m, length) {
this.validacionBasicaStr(texto, campo, m, length); this.validacionBasicaStr(texto, campo, m, length);
for (const char of texto) { for (const char of texto) {
if (!this.caracterEspecial(char) && !validator.isAlphanumeric(char, 'es-ES')) { if (!this.caracterEspecial(char) && !validator.isAlphanumeric(char, 'es-ES')) {
@@ -70,7 +70,7 @@ export class ValidacionService {
return texto; return texto;
} }
validarNumeroCuenta(numeroCuenta, campo = 'numero de cuenta', m = true) { validarNumeroCuenta(numeroCuenta, campo = 'numero de cuenta', m = true) {
this.validacionBasicaStr(numeroCuenta, campo, m, 9); this.validacionBasicaStr(numeroCuenta, campo, m, 9);
if (!validator.isNumeric(numeroCuenta, { no_symbols: true })) { if (!validator.isNumeric(numeroCuenta, { no_symbols: true })) {
this.noValido(campo, m, 'tiene caracteres que no son números'); this.noValido(campo, m, 'tiene caracteres que no son números');
@@ -78,14 +78,14 @@ export class ValidacionService {
return numeroCuenta; return numeroCuenta;
} }
validarFecha(fecha, campo, m) { validarFecha(fecha, campo, m) {
const fechaMoment = moment(fecha); const fechaMoment = moment(fecha);
this.noHay(fecha, campo, m); this.noHay(fecha, campo, m);
if (!fechaMoment.isValid()) this.noValido(campo, m, 'no es una fecha válida'); if (!fechaMoment.isValid()) this.noValido(campo, m, 'no es una fecha válida');
return fechaMoment; return fechaMoment;
} }
validarNumero(numero, campo, m, length, no_symbols = true, makeNumber = false) { validarNumero(numero, campo, m, length, no_symbols = true, makeNumber = false) {
this.validacionBasicaStr(numero, campo, m, length); this.validacionBasicaStr(numero, campo, m, length);
if (!validator.isNumeric(numero, { no_symbols })) { if (!validator.isNumeric(numero, { no_symbols })) {
this.noValido(campo, m, 'tiene caracteres que no son números'); this.noValido(campo, m, 'tiene caracteres que no son números');
@@ -93,32 +93,53 @@ export class ValidacionService {
return makeNumber ? Number(numero) : numero; return makeNumber ? Number(numero) : numero;
} }
validarObjetoVacio(obj) { validarObjetoVacio(obj) {
return Object.keys(obj).length === 0; return Object.keys(obj).length === 0;
} }
async validarPreTermino(idServicio) { async validarPreTermino(idServicio: number) {
const res = await this.servicioRepository.findOne({ where: { idServicio } }); const res = await this.servicioRepository.findOne({
where: { idServicio },
relations: [
'cuestionarioPrograma',
'cuestionarioPrograma2',
'cuestionarioAlumno',
'cuestionarioAlumno2',
],
});
if (!res) return '';
const tieneCuestionarioPrograma =
!!res.cuestionarioPrograma?.idCuestionarioPrograma ||
!!res.cuestionarioPrograma2?.idCuestionarioPrograma2;
const tieneCuestionarioAlumno =
!!res.cuestionarioAlumno?.idCuestionarioAlumno ||
!!res.cuestionarioAlumno2?.idCuestionarioAlumno2;
const tieneDocumentos =
!!res.cartaTermino && !!res.informeGlobal;
if (tieneCuestionarioPrograma && tieneCuestionarioAlumno && tieneDocumentos) {
await this.servicioRepository.update(idServicio, {
status: { idStatus: 5 },
});
if (
(res?.cuestionarioPrograma.idCuestionarioPrograma || res?.cuestionarioPrograma2.idCuestionarioPrograma2) &&
(res.cuestionarioAlumno.idCuestionarioAlumno || res.cuestionarioAlumno2.idCuestionarioAlumno2) &&
res.cartaTermino &&
res.informeGlobal
) {
await this.servicioRepository.update({ idServicio }, { status: {idStatus:5} });
return 'Este Servicio Social pasó a Término, espera a que COESI autorice tu liberación.'; return 'Este Servicio Social pasó a Término, espera a que COESI autorice tu liberación.';
} }
return ''; return '';
} }
validarCuestionarioAlumno2(body) {
validarCuestionarioAlumno2(body) {
const optionalKeys = ['p5', 'p8', 'p16', 'p18']; const optionalKeys = ['p5', 'p8', 'p16', 'p18'];
if (Array.isArray(body.p1)) body.p1 = body.p1.join(', '); if (Array.isArray(body.p1)) body.p1 = body.p1.join(', ');
for (const [key, value] of Object.entries(body)) { for (const [key, value] of Object.entries(body)) {
if (!optionalKeys.includes(key) && (value === null || value === undefined || value === '')) { if (!optionalKeys.includes(key) && (value === null || value === undefined || value === '')) {
throw new Error(`El campo ${key} no puede estar vacío.`); throw new BadRequestException(`El campo ${key} no puede estar vacío.`);
} }
} }
@@ -136,20 +157,20 @@ export class ValidacionService {
for (const key of expectedKeys) { for (const key of expectedKeys) {
if (!(key in body)) { if (!(key in body)) {
throw new Error(`Falta la respuesta para la pregunta ${key}.`); throw new BadRequestException(`Falta la respuesta para la pregunta ${key}.`);
} }
} }
return body; return body;
} }
validarCuestionarioPrograma2(body) { validarCuestionarioPrograma2(body) {
const camposOpcionales = ['p6']; const camposOpcionales = ['p6'];
if (Array.isArray(body.p1)) body.p1 = body.p1.join(', '); if (Array.isArray(body.p1)) body.p1 = body.p1.join(', ');
for (const [key, value] of Object.entries(body)) { for (const [key, value] of Object.entries(body)) {
if (!camposOpcionales.includes(key) && (value === null || value === undefined || value === '')) { if (!camposOpcionales.includes(key) && (value === null || value === undefined || value === '')) {
throw new Error(`El campo ${key} no puede estar vacío.`); throw new BadRequestException(`El campo ${key} no puede estar vacío.`);
} }
} }
@@ -163,10 +184,10 @@ export class ValidacionService {
for (const key of expectedKeys) { for (const key of expectedKeys) {
if (!(key in body)) { if (!(key in body)) {
throw new Error(`Falta la respuesta para la pregunta ${key}.`); throw new BadRequestException(`Falta la respuesta para la pregunta ${key}.`);
} }
} }
return body; return body;
} }
} }
+13 -2
View File
@@ -1,12 +1,23 @@
import { NestFactory } from '@nestjs/core'; import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module'; import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() { async function bootstrap() {
const app = await NestFactory.create(AppModule); const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // elimina propiedades que no existan en el DTO
transform: true, // permite que @Type() funcione
transformOptions: { enableImplicitConversion: true }, // convierte strings → number/date
}),
);
app.enableCors(); app.enableCors();
await app.listen(process.env.PORT ?? 3000); await app.listen(process.env.APP_PORT ?? 3000);
console.log(`Aplicación corriendo en: http://localhost:${process.env.PORT ?? 3000}`); console.log(
`Aplicación corriendo en: http://localhost:${process.env.APP_PORT ?? 3000}`,
);
} }
bootstrap(); bootstrap();
+15 -7
View File
@@ -1,6 +1,13 @@
import { Servicio } from "src/servicio/entities/servicio.entity"; import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Usuario } from "src/usuario/entities/usuario.entity"; import { Usuario } from 'src/usuario/entities/usuario.entity';
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('programa') @Entity('programa')
export class Programa { export class Programa {
@@ -25,9 +32,10 @@ export class Programa {
@Column({ type: 'boolean', default: true }) @Column({ type: 'boolean', default: true })
activo: boolean; activo: boolean;
@ManyToOne(()=>Usuario,(usuario)=>usuario.programa) @ManyToOne(() => Usuario, (usuario) => usuario.programa)
usuario:Usuario; @JoinColumn({ name: 'idUsuario' })
usuario: Usuario;
@OneToMany(()=>Servicio,(servicio)=>servicio.cuestionarioAlumno2) @OneToMany(() => Servicio, (servicio) => servicio.cuestionarioAlumno2)
servicio:Servicio[]; servicio: Servicio[];
} }
+7 -1
View File
@@ -10,6 +10,7 @@ import {
UseInterceptors, UseInterceptors,
BadRequestException, BadRequestException,
Param, Param,
ParseIntPipe,
} from '@nestjs/common'; } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express'; import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer'; import { diskStorage } from 'multer';
@@ -70,13 +71,18 @@ export class ProgramaController {
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
async reasignarProgramas( async reasignarProgramas(
@Body() email: Email, @Body() email: Email,
@Param('idUsuario') idUsuario: number, @Param('idUsuario', ParseIntPipe) idUsuario: number,
) { ) {
console.log('BODY:', email);
console.log('PARAM:', idUsuario);
try { try {
const data = await this.programaService.reasignarProgramas( const data = await this.programaService.reasignarProgramas(
idUsuario, idUsuario,
email.correo, email.correo,
); );
return { return {
statusCode: 200, statusCode: 200,
message: 'Programas reasignados con éxito', message: 'Programas reasignados con éxito',
+19 -1
View File
@@ -1,9 +1,27 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { ProgramaService } from './programa.service'; import { ProgramaService } from './programa.service';
import { ProgramaController } from './programa.controller'; import { ProgramaController } from './programa.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Programa } from './entities/programa.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity';
import { UsuarioModule } from 'src/usuario/usuario.module';
import { ValidacionService } from 'src/helpers.services/validacion.service';
import { AuthService } from 'src/auth/auth.service';
import { gmail } from 'src/helpers.services/gmail.service';
import { AuthModule } from 'src/auth/auth.module';
import { JwtService } from '@nestjs/jwt';
import { Servicio } from 'src/servicio/entities/servicio.entity';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([Programa, Servicio]), UsuarioModule],
controllers: [ProgramaController], controllers: [ProgramaController],
providers: [ProgramaService], providers: [
ProgramaService,
AuthService,
ValidacionService,
gmail,
JwtService,
],
exports: [TypeOrmModule],
}) })
export class ProgramaModule {} export class ProgramaModule {}
+49 -24
View File
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common'; import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm'; import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm'; import { Like, Repository } from 'typeorm';
import * as csv from 'csvtojson'; import * as csv from 'csvtojson';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
@@ -13,6 +13,8 @@ import { ValidacionService } from 'src/helpers.services/validacion.service';
import { AuthService } from 'src/auth/auth.service'; import { AuthService } from 'src/auth/auth.service';
import { gmail } from 'src/helpers.services/gmail.service'; import { gmail } from 'src/helpers.services/gmail.service';
import { SendMailDto } from 'src/helpers.services/dto/send-email.dto'; import { SendMailDto } from 'src/helpers.services/dto/send-email.dto';
const { enviarSec } = require('../helpers.services/msjCorreos');
@Injectable() @Injectable()
export class ProgramaService { export class ProgramaService {
@@ -24,7 +26,7 @@ export class ProgramaService {
private validacionService: ValidacionService, private validacionService: ValidacionService,
private authService: AuthService, private authService: AuthService,
private gmail: gmail, private gmail: gmail,
) {} ) { }
async cargaMasiva(nombreArchivo: string): Promise<{ message: string }> { async cargaMasiva(nombreArchivo: string): Promise<{ message: string }> {
const filePath = path.join('server/uploads', nombreArchivo); const filePath = path.join('server/uploads', nombreArchivo);
@@ -50,12 +52,14 @@ export class ProgramaService {
try { try {
this.validacionService.validarCorreo(prog.correo); this.validacionService.validarCorreo(prog.correo);
} catch { } catch (err) {
console.log(err)
mensaje += 'Correo inválido. Línea omitida.\n\n'; mensaje += 'Correo inválido. Línea omitida.\n\n';
continue; continue;
} }
const acatlan = prog.clave.substr(4, 7) === '-12/20-'; const acatlan = prog.clave.substr(4, 8) === '-12/145-';
console.log("clave:", prog.clave.substr(4, 8))
const activo = Number(prog.clave.substr(0, 4)) === today.year(); const activo = Number(prog.clave.substr(0, 4)) === today.year();
let responsable = await this.usuarioRepo.findOne({ let responsable = await this.usuarioRepo.findOne({
@@ -67,11 +71,18 @@ export class ProgramaService {
}, },
}); });
//Agregamos esta linea para verificar los logs
console.log("prog:", prog);
if (!responsable) { if (!responsable) {
const password = await this.authService.encriptar(
this.authService.generarPassword(), let contraseña = await this.authService.generarPassword();
); const password = await this.authService.encriptar(contraseña);
const correoInfo = enviarSec(password, prog.correo, prog.nombre);
const correoInfo = enviarSec(contraseña, prog.correo, prog.nombre);
responsable = await this.usuarioRepo.create({ responsable = await this.usuarioRepo.create({
usuario: prog.correo, usuario: prog.correo,
@@ -92,10 +103,6 @@ export class ProgramaService {
fecha_recibido: new Date(), fecha_recibido: new Date(),
}; };
if (!process.env.TOKEN_GMAIL) {
throw new Error('No existe el token');
}
try { try {
responsable = await this.usuarioRepo.save(responsable); responsable = await this.usuarioRepo.save(responsable);
@@ -167,6 +174,15 @@ export class ProgramaService {
mensaje += `Error creando programa: ${err.message}\n`; mensaje += `Error creando programa: ${err.message}\n`;
} }
} else { } else {
await this.programaRepo.update(
{ idPrograma: existente.idPrograma },
{ institucion: prog.institucion, programa: prog.programa }
)
await this.usuarioRepo.update(
{ idUsuario: responsable.idUsuario },
{ nombre: prog.nombre }
)
mensaje += `Programa ACATLÁN ya pertenece a ${responsable.usuario}\n`; mensaje += `Programa ACATLÁN ya pertenece a ${responsable.usuario}\n`;
} }
} }
@@ -189,17 +205,22 @@ export class ProgramaService {
const usuario = await this.usuarioRepo.findOne({ const usuario = await this.usuarioRepo.findOne({
where: { idUsuario }, where: { idUsuario },
relations: ['tipoUsuario'],
}); });
if (!usuario) { if (!usuario) {
throw new Error('No existe este usuario.'); throw new BadRequestException('No existe este usuario.');
} }
if (usuario.tipoUsuario.idTipoUsuario !== 2) { if (usuario.tipoUsuario.idTipoUsuario !== 2) {
throw new Error('No es un usuario de tipo responsable'); throw new BadRequestException('No es un usuario de tipo responsable');
} }
return this.programaRepo.find({ const year = new Date().getFullYear().toString();
where: { usuario: { idUsuario } },
return await this.programaRepo.find({
where: {
usuario: { idUsuario }, clavePrograma: Like(`${year}%`),
},
}); });
} }
@@ -211,20 +232,23 @@ export class ProgramaService {
const usuario = await this.usuarioRepo.findOne({ const usuario = await this.usuarioRepo.findOne({
where: { idUsuario }, where: { idUsuario },
relations: ['tipoUsuario'], // Se agrego la relacion con tipo de usuario
}); });
if (!usuario) { if (!usuario) {
throw new Error('No existe este usuario.'); throw new BadRequestException('No existe este usuario.');
} }
if (usuario.tipoUsuario.idTipoUsuario !== 2) { if (usuario.tipoUsuario.idTipoUsuario !== 2) {
throw new Error('No es un usuario de tipo responsable'); throw new BadRequestException('No es un usuario de tipo responsable');
} }
const year = new Date().getFullYear().toString();
return this.programaRepo.find({ return await this.programaRepo.find({
where: { where: {
usuario: { idUsuario }, usuario: { idUsuario },
activo: true, activo: true,
clavePrograma: Like(`${year}%`)
}, },
}); });
} }
@@ -240,19 +264,20 @@ export class ProgramaService {
let usuarioNuevo = await this.usuarioRepo.findOne({ let usuarioNuevo = await this.usuarioRepo.findOne({
where: { idUsuario: idusuario }, where: { idUsuario: idusuario },
relations: ["tipoUsuario"],
}); });
if (!usuarioNuevo) throw new Error('No existe este usuario.'); if (!usuarioNuevo) throw new BadRequestException('No existe este usuario.');
if (usuarioNuevo.tipoUsuario.idTipoUsuario != 2) if (usuarioNuevo.tipoUsuario.idTipoUsuario != 2)
throw new Error('No es un usuario de tipo responsable'); throw new BadRequestException('No es un usuario de tipo responsable');
if (usuarioNuevo.usuario === correoOtroResponsable) if (usuarioNuevo.usuario === correoOtroResponsable)
throw new Error('Son el mismo usuario.'); throw new BadRequestException('Son el mismo usuario.');
let viejoResponsable = await this.usuarioRepo.findOne({ let viejoResponsable = await this.usuarioRepo.findOne({
where: { usuario: otroResponsable }, where: { usuario: otroResponsable },
}); });
if (!viejoResponsable) throw new Error('No existe este usuario.'); if (!viejoResponsable) throw new BadRequestException('No existe este usuario.');
await this.programaRepo.update( await this.programaRepo.update(
{ usuario: viejoResponsable }, { usuario: viejoResponsable },
+4 -4
View File
@@ -26,11 +26,11 @@ export class CrearServicioDto {
@IsEmail() @IsEmail()
correo: string; correo: string;
@IsDate() @IsDateString()
fechaInicio: Date; fechaInicio: string; // Cambiado a string antes tenia Date
@IsDate() @IsDateString()
fechaFin: Date; fechaFin: string; // Cambiado a string antes tenia Date
@IsOptional() @IsOptional()
@IsString() @IsString()
+1 -1
View File
@@ -12,7 +12,7 @@ export class RegistroValidadoDto {
idServicio: number; idServicio: number;
@IsOptional() @IsOptional()
fechaNacimiento?: Date; fechaNacimiento?: string; // Lo pasamos a string antes date
@IsOptional() @IsOptional()
@IsString() @IsString()
+6 -3
View File
@@ -19,15 +19,18 @@ export class UpdateServicioDto {
@IsOptional() @IsOptional()
@IsDateString({}, { message: 'fechaInicio debe ser una fecha válida' }) @IsDateString({}, { message: 'fechaInicio debe ser una fecha válida' })
fechaInicio?: Date; //@Type(() => Date)
fechaInicio?: string; // Lo pasamos a string antes date
@IsOptional() @IsOptional()
@IsDateString({}, { message: 'fechaFin debe ser una fecha válida' }) @IsDateString({}, { message: 'fechaFin debe ser una fecha válida' })
fechaFin?: Date; //@Type(() => Date)
fechaFin?: string; // Lo pasamos a string antes date
@IsOptional() @IsOptional()
@IsDateString({}, { message: 'fechaNacimiento debe ser una fecha válida' }) @IsDateString({}, { message: 'fechaNacimiento debe ser una fecha válida' })
fechaNacimiento?: Date; //@Type(() => Date)
fechaNacimiento?: string; // Lo pasamos a string antes date
@IsOptional() @IsOptional()
@IsString({ message: 'direccion debe ser texto' }) @IsString({ message: 'direccion debe ser texto' })
+106 -74
View File
@@ -1,88 +1,120 @@
import { Carrera } from "src/carrera/entities/carrera.entity"; import { Carrera } from 'src/carrera/entities/carrera.entity';
import { CuestionarioAlumno } from "src/cuestionario-alumno/entities/cuestionario-alumno.entity"; import { CuestionarioAlumno } from 'src/cuestionario-alumno/entities/cuestionario-alumno.entity';
import { CuestionarioAlumno2 } from "src/cuestionario-alumno2/entities/cuestionario-alumno2.entity"; import { CuestionarioAlumno2 } from 'src/cuestionario-alumno2/entities/cuestionario-alumno2.entity';
import { CuestionarioPrograma } from "src/cuestionario-programa/entities/cuestionario-programa.entity"; import { CuestionarioPrograma } from 'src/cuestionario-programa/entities/cuestionario-programa.entity';
import { CuestionarioPrograma2 } from "src/cuestionario-programa2/entities/cuestionario-programa2.entity"; import { CuestionarioPrograma2 } from 'src/cuestionario-programa2/entities/cuestionario-programa2.entity';
import { Programa } from "src/programa/entities/programa.entity"; import { Programa } from 'src/programa/entities/programa.entity';
import { Status } from "src/status/entities/status.entity"; import { Status } from 'src/status/entities/status.entity';
import { Usuario } from "src/usuario/entities/usuario.entity"; import { Usuario } from 'src/usuario/entities/usuario.entity';
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm"; import {
Column,
CreateDateColumn,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity('servicio') @Entity('servicio')
export class Servicio { export class Servicio {
@PrimaryGeneratedColumn()
idServicio: number;
@PrimaryGeneratedColumn() @Column({ type: 'varchar', length: 3 })
idServicio: number; creditos: string;
@Column({ type: 'varchar', length: 3 })
creditos: string;
@Column({ type: 'varchar', length: 60 })
correo: string;
@Column({ type: 'varchar', length: 15, nullable: true, default: null })
telefono?: string;
@Column({ type: 'varchar', length: 200, nullable: true, default: null })
direccion?: string;
@Column({ type: 'date' })
fechaInicio: Date;
@Column({ type: 'date' })
fechaFin: Date;
@Column({ type: 'date', nullable: true, default: null })
fechaLiberacion?: Date;
@Column({ type: 'date', nullable: true, default: null })
fechaNacimiento?: Date;
@Column({ type: 'varchar', length: 60 })
carpeta: string;
@Column({ type: 'varchar', length: 60 })
cartaAceptacion: string;
@Column({ type: 'varchar', length: 60, nullable: true, default: null })
cartaTermino?: string;
@Column({ type: 'varchar', length: 60, nullable: true, default: null })
informeGlobal?: string;
@Column({ type: 'varchar', length: 250, nullable: true, default: null })
programaInterno?: string;
@Column({ type: 'varchar', length: 50, nullable: true, default: null })
profesor?: string;
@Column({ type: 'boolean', nullable: true, default: null })
vistoBuenoAcatlan?: boolean;
@ManyToOne(()=>Usuario,(usuario)=>usuario.servicio) @Column({ type: 'varchar', length: 60 })
usuario:Usuario correo: string;
@ManyToOne(()=>CuestionarioAlumno2,(cuestionarioAlumno2)=>cuestionarioAlumno2.servicio) @Column({ type: 'varchar', length: 15, nullable: true, default: null })
cuestionarioAlumno2:CuestionarioAlumno2; telefono?: string;
@ManyToOne(()=>CuestionarioPrograma2,(cuestionarioPrograma2)=>cuestionarioPrograma2.servicio) @Column({ type: 'varchar', length: 200, nullable: true, default: null })
cuestionarioPrograma2:CuestionarioPrograma2; direccion?: string;
@ManyToOne(()=>CuestionarioPrograma,(cuestionarioPrograma)=>cuestionarioPrograma.servicio) @Column({ type: 'date' })
cuestionarioPrograma:CuestionarioPrograma; fechaInicio: string; // Cambios a tipo string antes tenia date
@ManyToOne(()=>CuestionarioAlumno,(cuestionarioAlumno)=>cuestionarioAlumno.servicio)
cuestionarioAlumno:CuestionarioAlumno;
@ManyToOne(()=>Status,(status)=>status.servicio) @Column({ type: 'date' })
status:Status; fechaFin: string; // Cambios a tipo string antes tenia date
@ManyToOne(()=>Programa,(programa)=>programa.servicio) @Column({ type: 'date', nullable: true, default: null })
programa:Programa; fechaLiberacion?: string; // Cambios a tipo string antes tenia date
@ManyToOne(()=>Carrera,(carrera)=>carrera.servicio) @Column({ type: 'date', nullable: true, default: null })
carrera:Carrera; fechaNacimiento?: string; // Cambios a tipo string antes tenia date
@Column({ type: 'varchar', length: 60 })
carpeta: string;
@Column({ type: 'varchar', length: 60 })
cartaAceptacion: string;
@Column({ type: 'varchar', length: 60, nullable: true, default: null })
cartaTermino?: string;
@Column({ type: 'varchar', length: 60, nullable: true, default: null })
informeGlobal?: string;
@Column({ type: 'varchar', length: 250, nullable: true, default: null })
programaInterno?: string;
@Column({ type: 'varchar', length: 50, nullable: true, default: null })
profesor?: string;
@Column({ type: 'boolean', nullable: true, default: null })
vistoBuenoAcatlan?: boolean;
@CreateDateColumn({ name: 'createdAt', type: 'datetime' })
createdAt: Date;
@UpdateDateColumn({ name: 'updatedAt', type: 'datetime' })
updatedAt: Date;
// faltaba createdAT y updatedAT
@ManyToOne(() => Usuario, (usuario) => usuario.servicio)
@JoinColumn({ name: 'idUsuario' })
usuario: Usuario;
@ManyToOne(
() => CuestionarioAlumno2,
(cuestionarioAlumno2) => cuestionarioAlumno2.servicio,
)
@JoinColumn({ name: 'idCuestionarioAlumno2' })
cuestionarioAlumno2: CuestionarioAlumno2;
@ManyToOne(
() => CuestionarioPrograma2,
(cuestionarioPrograma2) => cuestionarioPrograma2.servicio,
)
@JoinColumn({ name: 'idCuestionarioPrograma2' })
cuestionarioPrograma2: CuestionarioPrograma2;
@ManyToOne(
() => CuestionarioPrograma,
(cuestionarioPrograma) => cuestionarioPrograma.servicio,
)
@JoinColumn({ name: 'idCuestionarioPrograma' })
cuestionarioPrograma: CuestionarioPrograma;
@ManyToOne(
() => CuestionarioAlumno,
(cuestionarioAlumno) => cuestionarioAlumno.servicio,
)
@JoinColumn({ name: 'idCuestionarioAlumno' })
cuestionarioAlumno: CuestionarioAlumno;
@ManyToOne(() => Status, (status) => status.servicio)
@JoinColumn({ name: 'idStatus' })
status: Status;
@ManyToOne(() => Programa, (programa) => programa.servicio)
@JoinColumn({ name: 'idPrograma' })
programa: Programa;
@ManyToOne(() => Carrera, (carrera) => carrera.servicios)
@JoinColumn({ name: 'idCarrera' })
carrera: Carrera;
} }
+13
View File
@@ -0,0 +1,13 @@
import { diskStorage } from 'multer';
import { extname } from 'path';
export const multerConfig = {
storage: diskStorage({
destination: './server/uploads',
filename: (req, file, cb) => {
const uniqueName =
Date.now() + '-' + Math.round(Math.random() * 1e9);
cb(null, uniqueName + extname(file.originalname));
},
}),
};
+115 -12
View File
@@ -11,6 +11,8 @@ import {
UseInterceptors, UseInterceptors,
Res, Res,
ParseIntPipe, ParseIntPipe,
Param,
Req,
} from '@nestjs/common'; } from '@nestjs/common';
import { import {
FileInterceptor, FileInterceptor,
@@ -20,30 +22,85 @@ import { ServicioService } from './servicio.service';
import { AuthGuard } from '@nestjs/passport'; import { AuthGuard } from '@nestjs/passport';
import { CrearServicioDto } from './dto/create-servicio.dto'; import { CrearServicioDto } from './dto/create-servicio.dto';
import { Response } from 'express'; import { Response } from 'express';
import { ServiciosAdminDto } from './dto/servicios-admin.dto';
import { ServiciosResponsableDto } from './dto/servicios-responsable.dto';
import { RegistroValidadoDto } from './dto/registro-validado.dto';
import { multerConfig } from './multer';
import { diskStorage } from 'multer';
@Controller('servicio') @Controller('servicio')
export class ServicioController { export class ServicioController {
constructor(private readonly servicioService: ServicioService) {} constructor(private readonly servicioService: ServicioService) { }
// ------------------ POST ------------------ // ------------------ POST ------------------
// Garegue un nuevo endpoint para hacer pruebas
@Post('test-upload')
@UseInterceptors(FileInterceptor('file'))
uploadTest(
@UploadedFile() file: Express.Multer.File,
@Req() req: any,
) {
console.log('FILE:', file);
console.log('FILES REQ:', req.file);
console.log('FILES BODY:', req.body);
return { ok: true };
}
@Post('registro-validado')
async registroValidado(
@Body() dto: RegistroValidadoDto,
): Promise<{ message: string }> {
console.log('RegistroValidadoDto recibido:', dto);
return this.servicioService.registroValidado(dto);
}
@Post('nuevo') @Post('nuevo')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('cartaAceptacion')) @UseInterceptors(FileInterceptor('cartaAceptacion', {
storage: diskStorage({
destination: './server/uploads',
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
},
}),
}),
)
async crearServicio( async crearServicio(
@UploadedFile() file: Express.Multer.File, @UploadedFile() file: Express.Multer.File,
@Body('alumno') alumnoJson: string, @Body('alumno') alumnoJson: string,
) { ) {
const dto: CrearServicioDto = JSON.parse(alumnoJson); const dto: CrearServicioDto = JSON.parse(alumnoJson);
console.log('Archivo recibido:', file);
return await this.servicioService.registrarNuevoServicio(dto, file); return await this.servicioService.registrarNuevoServicio(dto, file);
} }
// ------------------ GET ------------------ // ------------------ GET ------------------
@Get('todos')
//@UseGuards(AuthGuard('jwt'))
async obtenerTodosLosServicios() {
return this.servicioService.obtenerTodosLosServicios();
}
@Get('admin') @Get('admin')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
async obtenerAdmin(@Query() query: any) { async obtenerServiciosAdmin(@Query() dto: ServiciosAdminDto) {
return this.servicioService.obtenerDetalleServicio(query.idServicio); return await this.servicioService.obtenerServiciosAdmin(dto);
}
@Get('servicios_responsable')
@UseGuards(AuthGuard('jwt'))
async obtenerServiciosResponsable(@Query() dto: ServiciosResponsableDto) {
return await this.servicioService.obtenerServiciosResponsable(dto);
}
@Get('admin/:idServicio')
@UseGuards(AuthGuard('jwt'))
async obtenerAdmin(@Param('idServicio') idServicio: number) {
return this.servicioService.obtenerDetalleServicio(idServicio);
} }
@Get('alumno') @Get('alumno')
@@ -62,10 +119,14 @@ export class ServicioController {
return res.download(path); return res.download(path);
} }
// Corregi el endpoint tenia llamando a otrea funcion
@Get('reporte') @Get('reporte')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
async generarReporte(@Query() query: any, @Res() res: Response) { async generarReporte(
const path = await this.servicioService.generarGustavoBazPrada(query.year); @Query('inicio') inicio: string,
@Query('fin') fin: string,
@Res() res: Response) {
const path = await this.servicioService.generarReporte(inicio, fin);
return res.download(path); return res.download(path);
} }
@@ -78,30 +139,59 @@ export class ServicioController {
} }
@Put('carta_aceptacion') @Put('carta_aceptacion')
@UseGuards(AuthGuard('jwt')) //@UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('cartaAceptacion')) @UseInterceptors(
FileInterceptor('cartaAceptacion', {
storage: diskStorage({
destination: './server/uploads',
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
},
}),
}),
)
async subirCartaAceptacion( async subirCartaAceptacion(
@UploadedFile() file: Express.Multer.File, @UploadedFile() file: Express.Multer.File,
@Body('data') dataJson: string, @Body('data') dataJson: string,
) { ) {
const body = JSON.parse(dataJson); const body = JSON.parse(dataJson);
return this.servicioService.cartaTermino(body.idServicio, file); console.log('file:', file)
return this.servicioService.cartaTermino(body.idServicio, file.filename);
} }
@Put('carta_termino') @Put('carta_termino')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('cartaTermino')) @UseInterceptors(
FileInterceptor('cartaTermino', {
storage: diskStorage({
destination: './server/uploads',
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
},
}),
}),
)
async subirCartaTermino( async subirCartaTermino(
@UploadedFile() file: Express.Multer.File, @UploadedFile() file: Express.Multer.File,
@Body('data') dataJson: string, @Body('data') dataJson: string,
) { ) {
const body = JSON.parse(dataJson); const body = JSON.parse(dataJson);
console.log("file", file);
return this.servicioService.subirCartaTermino(body.idServicio, file); return this.servicioService.subirCartaTermino(body.idServicio, file);
} }
@Put('informe_global') @Put('informe_global')
@UseGuards(AuthGuard('jwt')) @UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('informeGlobal')) @UseInterceptors(
FileInterceptor('informeGlobal', {
storage: diskStorage({
destination: './server/uploads',
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
},
}),
}),
)
async subirInformeGlobal( async subirInformeGlobal(
@UploadedFile() file: Express.Multer.File, @UploadedFile() file: Express.Multer.File,
@Body('data') dataJson: string, @Body('data') dataJson: string,
@@ -146,13 +236,26 @@ export class ServicioController {
{ name: 'cartaAceptacion', maxCount: 1 }, { name: 'cartaAceptacion', maxCount: 1 },
{ name: 'cartaTermino', maxCount: 1 }, { name: 'cartaTermino', maxCount: 1 },
{ name: 'informeGlobal', maxCount: 1 }, { name: 'informeGlobal', maxCount: 1 },
]), ], multerConfig,),
) )
async actualizarServicio( async actualizarServicio(
@UploadedFiles() files: Record<string, Express.Multer.File[]>, @UploadedFiles() files: Record<string, Express.Multer.File[]>,
@Body('data') dataJson: string, @Body('data') dataJson: string,
) { ) {
const data = JSON.parse(dataJson); const data = JSON.parse(dataJson);
return this.servicioService.update(data, files); return this.servicioService.update(data, files);
} }
@Put('registro')
@UseGuards(AuthGuard('jwt'))
async registro(@Body() body: { idServicio: number }) {
try {
return this.servicioService.registro(body.idServicio);
} catch (error) {
throw error;
}
}
} }
+18 -2
View File
@@ -8,9 +8,24 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { Servicio } from './entities/servicio.entity'; import { Servicio } from './entities/servicio.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity'; import { Usuario } from 'src/usuario/entities/usuario.entity';
import { ArchivoService } from 'src/helpers.services/archivo.service'; import { ArchivoService } from 'src/helpers.services/archivo.service';
import { UsuarioModule } from 'src/usuario/usuario.module';
import { ProgramaModule } from 'src/programa/programa.module';
import { Programa } from 'src/programa/entities/programa.entity';
import { Carrera } from 'src/carrera/entities/carrera.entity';
import { Auth } from 'googleapis';
import { AuthService } from 'src/auth/auth.service';
import { AuthModule } from 'src/auth/auth.module';
import { Status } from 'src/status/entities/status.entity';
import { MulterModule } from '@nestjs/platform-express';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([Servicio, Usuario])], imports: [
TypeOrmModule.forFeature([Servicio, Usuario, Programa, Carrera, Status]),
AuthModule,
MulterModule.register({
dest: './uploads',
}),
],
controllers: [ServicioController], controllers: [ServicioController],
providers: [ providers: [
ServicioService, ServicioService,
@@ -19,5 +34,6 @@ import { ArchivoService } from 'src/helpers.services/archivo.service';
gmail, gmail,
ArchivoService, ArchivoService,
], ],
exports: [TypeOrmModule],
}) })
export class ServicioModule {} export class ServicioModule { }
File diff suppressed because it is too large Load Diff
+3
View File
@@ -1,8 +1,11 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { StatusService } from './status.service'; import { StatusService } from './status.service';
import { StatusController } from './status.controller'; import { StatusController } from './status.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Status } from './entities/status.entity';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([Status])],
controllers: [StatusController], controllers: [StatusController],
providers: [StatusService], providers: [StatusService],
}) })
@@ -1,16 +1,14 @@
import { Usuario } from "src/usuario/entities/usuario.entity"; import { Usuario } from 'src/usuario/entities/usuario.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('tipo_usuario') @Entity('tipo_usuario')
export class TipoUsuario { export class TipoUsuario {
@PrimaryGeneratedColumn()
idTipoUsuario: number;
@PrimaryGeneratedColumn() @Column({ type: 'varchar', length: 15 })
idTipoUsuario: number; tipoUsuario: string;
@Column({ type: 'varchar', length: 15 })
tipoUsuario: string;
@OneToMany(()=>Usuario, (usuario)=>usuario.tipoUsuario)
usuario:Usuario;
@OneToMany(() => Usuario, (usuario) => usuario.tipoUsuario)
usuario: Usuario;
} }
@@ -1,34 +0,0 @@
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { TipoUsuarioService } from './tipo-usuario.service';
import { CreateTipoUsuarioDto } from './dto/create-tipo-usuario.dto';
import { UpdateTipoUsuarioDto } from './dto/update-tipo-usuario.dto';
@Controller('tipo-usuario')
export class TipoUsuarioController {
constructor(private readonly tipoUsuarioService: TipoUsuarioService) {}
@Post()
create(@Body() createTipoUsuarioDto: CreateTipoUsuarioDto) {
return this.tipoUsuarioService.create(createTipoUsuarioDto);
}
@Get()
findAll() {
return this.tipoUsuarioService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.tipoUsuarioService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateTipoUsuarioDto: UpdateTipoUsuarioDto) {
return this.tipoUsuarioService.update(+id, updateTipoUsuarioDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.tipoUsuarioService.remove(+id);
}
}
+3
View File
@@ -1,6 +1,9 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TipoUsuario } from './entities/tipo-usuario.entity';
@Module({ @Module({
imports: [TypeOrmModule.forFeature([TipoUsuario])],
controllers: [], controllers: [],
providers: [], providers: [],
}) })
-1
View File
@@ -9,7 +9,6 @@ import {
} from 'class-validator'; } from 'class-validator';
export class CreateUsuarioDto { export class CreateUsuarioDto {
@IsEmail()
@IsString() @IsString()
@MaxLength(100) @MaxLength(100)
usuario: string; usuario: string;
+22 -15
View File
@@ -1,13 +1,20 @@
import { CasoEspecial } from "src/caso-especial/entities/caso-especial.entity"; import { CasoEspecial } from 'src/caso-especial/entities/caso-especial.entity';
import { Programa } from "src/programa/entities/programa.entity"; import { Programa } from 'src/programa/entities/programa.entity';
import { Servicio } from "src/servicio/entities/servicio.entity"; import { Servicio } from 'src/servicio/entities/servicio.entity';
import { TipoUsuario } from "src/tipo-usuario/entities/tipo-usuario.entity"; import { TipoUsuario } from 'src/tipo-usuario/entities/tipo-usuario.entity';
import { Column, Entity, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn, Unique } from "typeorm"; import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
Unique,
} from 'typeorm';
@Entity('usuario') @Entity('usuario')
@Unique(['usuario']) @Unique(['usuario'])
export class Usuario { export class Usuario {
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
idUsuario: number; idUsuario: number;
@@ -23,16 +30,16 @@ export class Usuario {
@Column({ type: 'boolean', default: true }) @Column({ type: 'boolean', default: true })
activo: boolean; activo: boolean;
@ManyToOne(()=>TipoUsuario,(tipoUsuario)=>tipoUsuario.usuario) @ManyToOne(() => TipoUsuario, (tipoUsuario) => tipoUsuario.usuario)
tipoUsuario:TipoUsuario; @JoinColumn({ name: 'idTipoUsuario' }) // Talvez faltaba que se llame idTipoUsuario
tipoUsuario: TipoUsuario;
@OneToMany(()=>CasoEspecial,(casoEspecial)=>casoEspecial.usuario) @OneToMany(() => CasoEspecial, (casoEspecial) => casoEspecial.usuario)
casoEspecial:CasoEspecial[]; casoEspecial: CasoEspecial[];
@OneToMany(()=>Programa,(programa)=>programa.usuario) @OneToMany(() => Programa, (programa) => programa.usuario)
programa:Programa[]; programa: Programa[];
@OneToMany(()=>Servicio,(servicio)=>servicio.usuario)
servicio:Servicio[];
@OneToMany(() => Servicio, (servicio) => servicio.usuario)
servicio: Servicio[];
} }
+34 -3
View File
@@ -8,6 +8,10 @@ import {
Query, Query,
ParseIntPipe, ParseIntPipe,
UseGuards, UseGuards,
UsePipes,
ValidationPipe,
HttpException,
HttpStatus,
} from '@nestjs/common'; } from '@nestjs/common';
import { UsuarioService } from './usuario.service'; import { UsuarioService } from './usuario.service';
import { CreateUsuarioDto } from './dto/create-usuario.dto'; import { CreateUsuarioDto } from './dto/create-usuario.dto';
@@ -16,7 +20,26 @@ import { UpdateUsuarioDto } from './dto/update-usuario.dto';
@Controller('usuario') @Controller('usuario')
export class UsuarioController { export class UsuarioController {
constructor(private readonly usuarioService: UsuarioService) {} constructor(private readonly usuarioService: UsuarioService) { }
/*Get('todos')
//@UseGuards(AuthGuard('jwt'))
async obtenerTodosLosServicios() {
return this.usuarioService.obtenerTodosLosUsuarios();
}*/ //No subir en produccion
@Post('crearUsuario')
@UsePipes(new ValidationPipe({ transform: true }))
async create(@Body() createUsuarioDto: CreateUsuarioDto) {
try {
return await this.usuarioService.create(createUsuarioDto);
} catch (error) {
throw new HttpException(
error.message || 'Error al crear usuario',
HttpStatus.BAD_REQUEST,
);
}
}
@Post('escolares/:numeroDeCuenta') @Post('escolares/:numeroDeCuenta')
async escolares(@Param('numeroDeCuenta') numeroDeCuenta: string) { async escolares(@Param('numeroDeCuenta') numeroDeCuenta: string) {
@@ -24,7 +47,7 @@ export class UsuarioController {
} }
@Post('new-password-alumno/:idServicio') @Post('new-password-alumno/:idServicio')
@UseGuards(AuthGuard('jwt')) //@UseGuards(AuthGuard('jwt'))
async newPasswordAlumno( async newPasswordAlumno(
@Param('idServicio', ParseIntPipe) idServicio: number, @Param('idServicio', ParseIntPipe) idServicio: number,
) { ) {
@@ -56,7 +79,7 @@ export class UsuarioController {
} }
@Patch('responsable/:idUsuario') @Patch('responsable/:idUsuario')
@UseGuards(AuthGuard('jwt')) //seGuards(AuthGuard('jwt'))
async actualizarResponsable( async actualizarResponsable(
@Param('idUsuario', ParseIntPipe) idUsuario: number, @Param('idUsuario', ParseIntPipe) idUsuario: number,
@Body() updateUsuarioDto: UpdateUsuarioDto, @Body() updateUsuarioDto: UpdateUsuarioDto,
@@ -67,4 +90,12 @@ export class UsuarioController {
updateUsuarioDto.nombre, updateUsuarioDto.nombre,
); );
} }
@Get('tipos-usuario')
async findTiposUsuario() {
return await this.usuarioService.findAll();
}
} }
+18 -1
View File
@@ -1,9 +1,26 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { UsuarioService } from './usuario.service'; import { UsuarioService } from './usuario.service';
import { UsuarioController } from './usuario.controller'; import { UsuarioController } from './usuario.controller';
import { Usuario } from './entities/usuario.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CarreraModule } from 'src/carrera/carrera.module';
import { ServicioModule } from 'src/servicio/servicio.module';
import { gmail } from 'src/helpers.services/gmail.service';
import { Auth } from 'googleapis';
import { AuthModule } from 'src/auth/auth.module';
import { TipoUsuario } from 'src/tipo-usuario/entities/tipo-usuario.entity';
import { Carrera } from 'src/carrera/entities/carrera.entity';
import { Servicio } from 'src/servicio/entities/servicio.entity';
@Module({ @Module({
imports: [
TypeOrmModule.forFeature([Usuario, TipoUsuario, Carrera, Servicio]),
CarreraModule,
ServicioModule,
AuthModule,
],
controllers: [UsuarioController], controllers: [UsuarioController],
providers: [UsuarioService], providers: [UsuarioService, gmail],
exports: [TypeOrmModule],
}) })
export class UsuarioModule {} export class UsuarioModule {}
+114 -18
View File
@@ -1,5 +1,7 @@
import { import {
BadRequestException, BadRequestException,
HttpException,
HttpStatus,
Injectable, Injectable,
NotFoundException, NotFoundException,
UnauthorizedException, UnauthorizedException,
@@ -14,6 +16,8 @@ import { Carrera } from 'src/carrera/entities/carrera.entity';
import { Servicio } from 'src/servicio/entities/servicio.entity'; import { Servicio } from 'src/servicio/entities/servicio.entity';
import { gmail } from 'src/helpers.services/gmail.service'; import { gmail } from 'src/helpers.services/gmail.service';
import { AuthService } from 'src/auth/auth.service'; import { AuthService } from 'src/auth/auth.service';
import { TipoUsuario } from 'src/tipo-usuario/entities/tipo-usuario.entity';
import * as bcrypt from 'bcrypt';
@Injectable() @Injectable()
export class UsuarioService { export class UsuarioService {
@@ -52,24 +56,48 @@ export class UsuarioService {
private readonly carreraRepo: Repository<Carrera>, private readonly carreraRepo: Repository<Carrera>,
@InjectRepository(Servicio) @InjectRepository(Servicio)
private readonly servicioRepo: Repository<Servicio>, private readonly servicioRepo: Repository<Servicio>,
@InjectRepository(TipoUsuario)
private readonly tipoUsurioRepo: Repository<TipoUsuario>,
private readonly gmail: gmail, private readonly gmail: gmail,
private readonly authService: AuthService, private readonly authService: AuthService,
) {} ) { }
async escolares(numeroDeCuenta: string) { async escolares(numeroDeCuenta: string) {
let response; let response;
try { try {
response = await axios.post( if (process.env.MODE == 'pruebas') {
`${process.env.ESCOLARES}${numeroDeCuenta}`, /*
{ password: process.env.ESCOLARES_PASS }, Quitamos la promesa ya que muestra un error
response = new Promise((res) => {
res({
data: {
nombre: 'nombre',
carrconst: 'Carrera',
avance: '80',
},
});
});
*/
{ return response = {
headers: { data: {
'Content-Type': 'application/json', // Tipo de contenido nombre: 'nombre',
Authorization: `Bearer ${process.env.API_TOKEN}`, // Header de auth carrconst: 'Carrera',
avance: '80',
}, },
}, };
); } else {
response = await axios.post(
`${process.env.ESCOLARES}${numeroDeCuenta}`,
process.env.ESCOLARES_PASS,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
);
}
} catch (error) { } catch (error) {
throw new UnauthorizedException( throw new UnauthorizedException(
'No se pudo conectar con el servicio de escolares', 'No se pudo conectar con el servicio de escolares',
@@ -81,7 +109,7 @@ export class UsuarioService {
!response.data.carrconst || !response.data.carrconst ||
!response.data.avance !response.data.avance
) { ) {
throw new Error( throw new BadRequestException(
'El alumno no cumple con los requisitos para realizar el Servicio Social. Si cree que esto es erróneo comunícate al Departamento de Servicio Social y Bolsa de Trabajo.', 'El alumno no cumple con los requisitos para realizar el Servicio Social. Si cree que esto es erróneo comunícate al Departamento de Servicio Social y Bolsa de Trabajo.',
); );
} }
@@ -99,7 +127,7 @@ export class UsuarioService {
}; };
if (alumno.creditos < this.carreras[alumno.carrera]) { if (alumno.creditos < this.carreras[alumno.carrera]) {
throw new Error('Este alumno no cuenta con los créditos necesarios.'); throw new BadRequestException('Este alumno no cuenta con los créditos necesarios.');
} }
while (alumno.nombre.search('‘') != -1 && alumno.nombre.search('Ã') != -1) { while (alumno.nombre.search('‘') != -1 && alumno.nombre.search('Ã') != -1) {
alumno.nombre = alumno.nombre.replace('Ã', 'Ñ'); alumno.nombre = alumno.nombre.replace('Ã', 'Ñ');
@@ -129,17 +157,17 @@ export class UsuarioService {
}); });
alum = await this.userRepo.save(nuevoUsuario); alum = await this.userRepo.save(nuevoUsuario);
} }
console.log("el alumno es:", alumno);
return { ...alumno, idUsuario: alum.idUsuario }; return { ...alumno, idUsuario: alum.idUsuario };
} }
async newPasswordAlumno(idServicio: number) { async newPasswordAlumno(idServicio: number) {
const password = this.authService.generarPassword(); const password = await this.authService.generarPassword();
// Buscar el servicio junto con el usuario // Buscar el servicio junto con el usuario
const servicio = await this.servicioRepo.findOne({ const servicio = await this.servicioRepo.findOne({
where: { idServicio }, where: { idServicio },
relations: ['usuario'], // asegura que traiga la relación relations: ['usuario', 'usuario.tipoUsuario'], // asegura que traiga la relación Le agregue la relacion para traer tipoUsuario
}); });
if (!servicio) { if (!servicio) {
@@ -153,6 +181,7 @@ export class UsuarioService {
} }
// Preparar correo // Preparar correo
const { preRegistro } = require('../helpers.services/msjCorreos');
let correo = preRegistro(password, usuario.nombre); let correo = preRegistro(password, usuario.nombre);
// Enviar correo // Enviar correo
@@ -173,10 +202,10 @@ export class UsuarioService {
} }
async newPasswordResponsable(idUsuario: number) { async newPasswordResponsable(idUsuario: number) {
const password = this.authService.generarPassword(); const password = await this.authService.generarPassword();
// Buscar usuario // Buscar usuario
const usuario = await this.userRepo.findOne({ where: { idUsuario } }); const usuario = await this.userRepo.findOne({ where: { idUsuario }, relations: ['tipoUsuario'] });
if (!usuario) { if (!usuario) {
throw new NotFoundException('No existe este Usuario.'); throw new NotFoundException('No existe este Usuario.');
@@ -186,7 +215,10 @@ export class UsuarioService {
throw new BadRequestException('Este usuario no es de tipo responsable.'); throw new BadRequestException('Este usuario no es de tipo responsable.');
} }
// Preparar correo // Preparar correo
const { enviarSec } = require('../helpers.services/msjCorreos');
const correo = enviarSec(password, usuario.usuario, usuario.nombre); const correo = enviarSec(password, usuario.usuario, usuario.nombre);
// Enviar correo // Enviar correo
@@ -233,7 +265,10 @@ export class UsuarioService {
const dataUpdate: Partial<Usuario> = {}; const dataUpdate: Partial<Usuario> = {};
// Buscar responsable // Buscar responsable
const responsable = await this.userRepo.findOne({ where: { idUsuario } }); const responsable = await this.userRepo.findOne({
where: { idUsuario },
relations: ['tipoUsuario'],
});
if (!responsable) { if (!responsable) {
throw new NotFoundException( throw new NotFoundException(
'No existe este responsable en la base de datos.', 'No existe este responsable en la base de datos.',
@@ -249,6 +284,7 @@ export class UsuarioService {
if (correo) { if (correo) {
const yaUsado = await this.userRepo.findOne({ const yaUsado = await this.userRepo.findOne({
where: { usuario: correo, idUsuario: Not(idUsuario) }, where: { usuario: correo, idUsuario: Not(idUsuario) },
relations: ['tipoUsuario'],
}); });
if (yaUsado) { if (yaUsado) {
@@ -278,4 +314,64 @@ export class UsuarioService {
'Se actualizó la información de este responsable de programas correctamente.', 'Se actualizó la información de este responsable de programas correctamente.',
}; };
} }
async create(createUsuarioDto: CreateUsuarioDto) {
// Verificar si el usuario ya existe
const usuarioExistente = await this.userRepo.findOne({
where: { usuario: createUsuarioDto.usuario },
});
if (usuarioExistente) {
throw new HttpException('El usuario ya existe', HttpStatus.BAD_REQUEST);
}
// Verificar tipo de usuario
const tipoUsuario = await this.tipoUsurioRepo.findOne({
where: { idTipoUsuario: createUsuarioDto.tipoUsuario },
});
if (!tipoUsuario) {
throw new HttpException(
'Tipo de usuario no válido',
HttpStatus.BAD_REQUEST,
);
}
// Hash password
let passwordHash = null;
if (createUsuarioDto.password) {
const saltRounds = 10;
passwordHash = await bcrypt.hash(createUsuarioDto.password, saltRounds);
}
// Crear usuario
const usuario = this.userRepo.create({
...createUsuarioDto,
password: passwordHash,
tipoUsuario: tipoUsuario,
});
const usuarioGuardado = await this.userRepo.save(usuario);
// Eliminar password del response
const { password, ...result } = usuarioGuardado;
return {
message: 'Usuario creado exitosamente',
usuario: result,
};
}
async findAll() {
return await this.tipoUsurioRepo.find()
}
/*c obtenerTodosLosUsuarios() {
return await this.userRepo.find({
relations: ['tipoUsuario'],
take: 10,
order: {
idUsuario: 'DESC', // Ordenar por el más reciente primero
},
});
}*/ // no subir a produccion
} }