diff --git a/src/entities/pendaftaranTesis.entity.ts b/src/entities/pendaftaranTesis.entity.ts index 637a05e71e7be84615568bcaef45f0adc2d14e2c..e9a8fa31c2f525bcc45c5d87cf250d018901ceda 100644 --- a/src/entities/pendaftaranTesis.entity.ts +++ b/src/entities/pendaftaranTesis.entity.ts @@ -1,6 +1,7 @@ import { Column, Entity, + JoinColumn, ManyToOne, OneToMany, PrimaryGeneratedColumn, @@ -56,13 +57,17 @@ export class PendaftaranTesis { status: RegStatus; @ApiProperty({ type: Topik }) - @ManyToOne(() => Topik, (topik) => topik.id) + @ManyToOne(() => Topik, (topik) => topik.id, { cascade: true }) topik: Topik; @ApiProperty() @ManyToOne(() => Pengguna, (pengguna) => pengguna.id) + @JoinColumn({ name: "mahasiswaId" }) mahasiswa: Pengguna; + @Column() + mahasiswaId: string; + @ApiProperty() @ManyToOne(() => Pengguna, (pengguna) => pengguna.id) penerima: Pengguna; diff --git a/src/registrasi-tesis/registrasi-tesis.controller.ts b/src/registrasi-tesis/registrasi-tesis.controller.ts index e41920cdafcd7e91a4378bd0224bfdbcb9605ec7..9802752f5de61d55f75b2b89791038abc827c2e2 100644 --- a/src/registrasi-tesis/registrasi-tesis.controller.ts +++ b/src/registrasi-tesis/registrasi-tesis.controller.ts @@ -12,8 +12,11 @@ import { UseGuards, } from "@nestjs/common"; import { + ApiBadRequestResponse, ApiBearerAuth, ApiCookieAuth, + ApiCreatedResponse, + ApiNotFoundResponse, ApiOkResponse, ApiOperation, ApiTags, @@ -52,21 +55,35 @@ export class RegistrasiTesisController { ) {} @ApiOperation({ - summary: - "Create new registration. Roles: S2_MAHASISWA, ADMIN, S2_TIM_TESIS", + summary: "Create new registration. Roles: S2_MAHASISWA, ADMIN", + }) + @ApiCreatedResponse({ type: IdDto }) + @ApiNotFoundResponse({ description: "Penerima atau topik tidak ditemukan" }) + @ApiBadRequestResponse({ + description: + "Mahasiswa sedang memiliki pendaftaran aktif atau judul dan deskripsi topik baru tidak ada", }) @UseGuards(CustomAuthGuard, RolesGuard) - @Roles(RoleEnum.S2_MAHASISWA, RoleEnum.ADMIN, RoleEnum.S2_TIM_TESIS) + @Roles(RoleEnum.S2_MAHASISWA, RoleEnum.ADMIN) @Post() async createTopicRegistration( @Body() topicRegistrationDto: RegDto, @Req() req: Request, - ) { + ): Promise<IdDto> { const { id } = req.user as AuthDto; + const periode = await this.konfService.getKonfigurasiByKey( + process.env.KONF_PERIODE_KEY, + ); + + if (!periode) { + throw new BadRequestException("Periode belum dikonfigurasi."); + } + return this.registrasiTesisService.createTopicRegistration( id, topicRegistrationDto, + periode, ); } diff --git a/src/registrasi-tesis/registrasi-tesis.dto.ts b/src/registrasi-tesis/registrasi-tesis.dto.ts index 330f31d2bf8ea6118fa74403d210c1cf87e13921..cc9541b2956aedc63449aa5144a9b87c34f6e0ba 100644 --- a/src/registrasi-tesis/registrasi-tesis.dto.ts +++ b/src/registrasi-tesis/registrasi-tesis.dto.ts @@ -16,25 +16,28 @@ import { import { Pengguna, RoleEnum } from "src/entities/pengguna.entity"; export class RegDto { - @IsUUID() - @ApiProperty() - idMahasiswa: string; - @IsUUID() @ApiProperty() idPenerima: string; - @IsString() - @ApiProperty() - judulTopik: string; - - @IsString() - @ApiProperty() - deskripsi: string; + @IsUUID() + @IsOptional() + @ApiPropertyOptional() + idTopik?: string; @IsEnum(JalurEnum) @ApiProperty({ enum: JalurEnum }) jalurPilihan: JalurEnum; + + @IsString() + @IsOptional() + @ApiPropertyOptional() + judulTopik?: string; + + @IsString() + @IsOptional() + @ApiPropertyOptional() + deskripsiTopik?: string; } export class RegByMhsParamDto { diff --git a/src/registrasi-tesis/registrasi-tesis.service.ts b/src/registrasi-tesis/registrasi-tesis.service.ts index 74afb3e99311335adae3e191090ce7b4902964cb..e19127b3878b6411462d75d1a7126a6d565ae723 100644 --- a/src/registrasi-tesis/registrasi-tesis.service.ts +++ b/src/registrasi-tesis/registrasi-tesis.service.ts @@ -14,7 +14,6 @@ import { import { Pengguna, RoleEnum } from "src/entities/pengguna.entity"; import { Topik } from "src/entities/topik.entity"; import { generateQueryBuilderOrderByObj } from "src/helper/sorting"; -import { validateId } from "src/helper/validation"; import { ArrayContains, Brackets, DataSource, In, Repository } from "typeorm"; import { FindAllNewestRegRespDto, @@ -44,47 +43,84 @@ export class RegistrasiTesisService { async createTopicRegistration( userId: string, topicRegistrationDto: RegDto, - ): Promise<PendaftaranTesis> { - // TODO: Proper validations - - // Validate id - validateId([ - { id: userId, object: "Pengguna" }, - { id: topicRegistrationDto.idPenerima, object: "Pembimbing" }, - ]); - - // Validate user id, supervisor id - const [user, supervisor, topic] = await Promise.all([ - this.penggunaRepository.findOne({ - where: { id: userId }, - }), + periode: string, + ): Promise<IdDto> { + const queries: ( + | Promise<void | PendaftaranTesis> + | Promise<Pengguna> + | Promise<Topik> + )[] = [ + this.getNewestRegByMhsOrFail(userId, periode).catch( + (ex: BadRequestException) => { + if (ex.message === "No mahasiswa user with given id exists") { + throw ex; + } + // else: mahasiswa does not have pending registration -> allowed + }, + ), this.penggunaRepository.findOne({ where: { id: topicRegistrationDto.idPenerima }, }), - this.topicRepostitory.findOne({ - where: { judul: topicRegistrationDto.judulTopik }, - }), - ]); + ]; + + if (topicRegistrationDto.idTopik) { + queries.push( + this.topicRepostitory.findOne({ + where: { id: topicRegistrationDto.idTopik }, + }), + ); + } + + const queryResult = await Promise.all(queries); + const lastPendaftaran = queryResult[0] as PendaftaranTesis; + const penerima = queryResult[1] as Pengguna; + let topik = topicRegistrationDto.idTopik ? (queryResult[2] as Topik) : null; - if (!user) { - throw new NotFoundException("User not found."); - } else if (!supervisor) { - throw new NotFoundException("Supervisor not found."); - } else if (!topic) { + if (!penerima) { + throw new NotFoundException("Penerima not found."); + } + + if (topicRegistrationDto.idTopik && !topik) { throw new NotFoundException("Topic not found."); } + if (lastPendaftaran && lastPendaftaran.status !== RegStatus.REJECTED) { + throw new BadRequestException( + "Mahasiswa already has pending registration in this period", + ); + } + + if (!topik) { + if ( + !topicRegistrationDto.judulTopik || + !topicRegistrationDto.deskripsiTopik + ) { + throw new BadRequestException( + "Judul dan deskripsi topik tidak boleh kosong.", + ); + } + + topik = this.topicRepostitory.create({ + judul: topicRegistrationDto.judulTopik, + deskripsi: topicRegistrationDto.deskripsiTopik, + idPengaju: userId, + periode, + }); + } + // Create new registration const createdRegistration = this.pendaftaranTesisRepository.create({ ...topicRegistrationDto, - mahasiswa: user, - penerima: supervisor, - topik: topic, + mahasiswaId: userId, + penerima, + topik, }); await this.pendaftaranTesisRepository.save(createdRegistration); - return createdRegistration; + return { + id: createdRegistration.id, + }; } async findByUserId( @@ -324,7 +360,7 @@ export class RegistrasiTesisService { return resData; } - private async getNewestRegByMhs(mahasiswaId: string, periode: string) { + private async getNewestRegByMhsOrFail(mahasiswaId: string, periode: string) { const mahasiswa = await this.penggunaRepository.findOne({ select: { id: true, @@ -393,7 +429,7 @@ export class RegistrasiTesisService { ); } - const newestReg = await this.getNewestRegByMhs(mahasiswaId, periode); + const newestReg = await this.getNewestRegByMhsOrFail(mahasiswaId, periode); if (newestReg && idPenerima && newestReg.penerima.id !== idPenerima) { throw new ForbiddenException(); @@ -427,7 +463,7 @@ export class RegistrasiTesisService { dto: UpdateStatusBodyDto, idPenerima?: string, ) { - const newestReg = await this.getNewestRegByMhs(mahasiswaId, periode); + const newestReg = await this.getNewestRegByMhsOrFail(mahasiswaId, periode); if (newestReg && idPenerima && newestReg.penerima.id !== idPenerima) { throw new ForbiddenException(); @@ -473,7 +509,7 @@ export class RegistrasiTesisService { periode: string, { pembimbing_ids: dosen_ids }: UpdatePembimbingBodyDto, ) { - const newestReg = await this.getNewestRegByMhs(mahasiswaId, periode); + const newestReg = await this.getNewestRegByMhsOrFail(mahasiswaId, periode); if (newestReg.status !== RegStatus.APPROVED) throw new BadRequestException(