From 0b0aac10e14fb9e36f2aeb53ac107748ab0ef87b Mon Sep 17 00:00:00 2001
From: Chiquita Ahsanunnisa <16521248@mahasiswa.itb.ac.id>
Date: Thu, 2 May 2024 08:52:06 +0700
Subject: [PATCH] refactor: periode validations to active status validation

---
 src/app.module.ts                             |   2 +
 src/bimbingan/bimbingan.module.ts             |   7 +-
 src/bimbingan/bimbingan.service.ts            |  10 +-
 src/dashboard/dashboard.service.ts            |   4 +-
 src/dosen-bimbingan/dosen-bimbingan.module.ts |   7 +-
 src/entities/pengguna.entity.ts               |   4 +
 src/pengguna/pengguna.module.ts               |  11 ++
 src/pengguna/pengguna.service.ts              |  24 ++++
 .../registrasi-tesis.controller.ts            |   4 +-
 .../registrasi-tesis.module.ts                |   5 +-
 .../registrasi-tesis.service.ts               | 110 +++++++-----------
 11 files changed, 108 insertions(+), 80 deletions(-)
 create mode 100644 src/pengguna/pengguna.module.ts
 create mode 100644 src/pengguna/pengguna.service.ts

diff --git a/src/app.module.ts b/src/app.module.ts
index 9367c42..bd865e2 100644
--- a/src/app.module.ts
+++ b/src/app.module.ts
@@ -18,6 +18,7 @@ import { validate } from "./env.validation";
 import { BerkasBimbingan } from "./entities/berkasBimbingan.entity";
 import { PendaftaranSidsem } from "./entities/pendaftaranSidsem";
 import { DosenBimbinganModule } from "./dosen-bimbingan/dosen-bimbingan.module";
+import { PenggunaModule } from "./pengguna/pengguna.module";
 
 @Module({
   imports: [
@@ -48,6 +49,7 @@ import { DosenBimbinganModule } from "./dosen-bimbingan/dosen-bimbingan.module";
     DashboardModule,
     BimbinganModule,
     DosenBimbinganModule,
+    PenggunaModule,
   ],
   controllers: [AppController],
   providers: [AppService],
diff --git a/src/bimbingan/bimbingan.module.ts b/src/bimbingan/bimbingan.module.ts
index e38f360..b77070f 100644
--- a/src/bimbingan/bimbingan.module.ts
+++ b/src/bimbingan/bimbingan.module.ts
@@ -6,6 +6,9 @@ import { Bimbingan } from "src/entities/bimbingan.entity";
 import { PendaftaranTesis } from "src/entities/pendaftaranTesis.entity";
 import { DosenBimbingan } from "src/entities/dosenBimbingan.entity";
 import { BerkasBimbingan } from "src/entities/berkasBimbingan.entity";
+import { PenggunaModule } from "src/pengguna/pengguna.module";
+import { PenggunaService } from "src/pengguna/pengguna.service";
+import { Pengguna } from "src/entities/pengguna.entity";
 
 @Module({
   imports: [
@@ -14,10 +17,12 @@ import { BerkasBimbingan } from "src/entities/berkasBimbingan.entity";
       PendaftaranTesis,
       DosenBimbingan,
       BerkasBimbingan,
+      Pengguna,
     ]),
+    PenggunaModule,
   ],
   controllers: [BimbinganController],
-  providers: [BimbinganService],
+  providers: [BimbinganService, PenggunaService],
   exports: [BimbinganService],
 })
 export class BimbinganModule {}
diff --git a/src/bimbingan/bimbingan.service.ts b/src/bimbingan/bimbingan.service.ts
index 44a2824..825ce91 100644
--- a/src/bimbingan/bimbingan.service.ts
+++ b/src/bimbingan/bimbingan.service.ts
@@ -24,6 +24,7 @@ import {
   UpdateStatusResDto,
 } from "./bimbingan.dto";
 import { BerkasBimbingan } from "src/entities/berkasBimbingan.entity";
+import { PenggunaService } from "src/pengguna/pengguna.service";
 
 @Injectable()
 export class BimbinganService {
@@ -36,12 +37,15 @@ export class BimbinganService {
     private dosenBimbinganRepository: Repository<DosenBimbingan>,
     @InjectRepository(BerkasBimbingan)
     private berkasBimbinganRepository: Repository<BerkasBimbingan>,
+    private penggunaService: PenggunaService,
   ) {}
 
   async getByMahasiswaId(
     mahasiswaId: string,
     user: AuthDto,
   ): Promise<GetByMahasiswaIdResDto> {
+    await this.penggunaService.isMahasiswaAktifOrFail(mahasiswaId);
+
     const pendaftaran = await this.pendaftaranTesisRepository.findOne({
       where: {
         mahasiswa: { id: mahasiswaId },
@@ -159,7 +163,7 @@ export class BimbinganService {
     user: AuthDto,
     dto: UpdateStatusDto,
   ): Promise<UpdateStatusResDto> {
-    const bimbingan = await this.getByBimbinganId(user, dto.bimbinganId);
+    const bimbingan = await this.getByBimbinganId(user, dto.bimbinganId); // already check if mahasiswa is aktif
 
     await this.bimbinganRepository.update(bimbingan.id, {
       disahkan: dto.status,
@@ -189,6 +193,10 @@ export class BimbinganService {
       throw new NotFoundException("Bimbingan tidak ditemukan");
     }
 
+    if (!bimbingan.pendaftaran.mahasiswa.aktif) {
+      throw new BadRequestException("Bimbingan milik mahasiswa tidak aktif");
+    }
+
     if (
       !user.roles.includes(RoleEnum.ADMIN) &&
       !bimbingan.pendaftaran.dosenBimbingan
diff --git a/src/dashboard/dashboard.service.ts b/src/dashboard/dashboard.service.ts
index 7486c33..f168aa3 100644
--- a/src/dashboard/dashboard.service.ts
+++ b/src/dashboard/dashboard.service.ts
@@ -41,6 +41,7 @@ export class DashboardService {
           dosenId,
         },
       )
+      .where("mahasiswa.aktif = true")
       .andWhere("pendaftaranTesis.status = :status", {
         status: RegStatus.APPROVED,
       });
@@ -94,7 +95,7 @@ export class DashboardService {
       .createQueryBuilder("pendaftaranTesis")
       .select("pendaftaranTesis.jalurPilihan", "jalurPilihan")
       .addSelect("COUNT(*)", "count")
-      .leftJoin("pendaftaranTesis.topik", "topik")
+      .leftJoin("pendaftaranTesis.mahasiswa", "mahasiswa")
       .innerJoin(
         "pendaftaranTesis.dosenBimbingan",
         "dosenBimbingan",
@@ -103,6 +104,7 @@ export class DashboardService {
           dosenId,
         },
       )
+      .where("mahasiswa.aktif = true")
       .andWhere("pendaftaranTesis.status = :status", {
         status: RegStatus.APPROVED,
       })
diff --git a/src/dosen-bimbingan/dosen-bimbingan.module.ts b/src/dosen-bimbingan/dosen-bimbingan.module.ts
index 5fef686..1721d9d 100644
--- a/src/dosen-bimbingan/dosen-bimbingan.module.ts
+++ b/src/dosen-bimbingan/dosen-bimbingan.module.ts
@@ -3,16 +3,11 @@ import { DosenBimbinganController } from "./dosen-bimbingan.controller";
 import { DosenBimbinganService } from "./dosen-bimbingan.service";
 import { AuthModule } from "src/auth/auth.module";
 import { TypeOrmModule } from "@nestjs/typeorm";
-import { PendaftaranTesis } from "src/entities/pendaftaranTesis.entity";
-import { DosenBimbingan } from "src/entities/dosenBimbingan.entity";
 import { Pengguna } from "src/entities/pengguna.entity";
 import { CustomStrategy } from "src/middlewares/custom.strategy";
 
 @Module({
-  imports: [
-    TypeOrmModule.forFeature([PendaftaranTesis, DosenBimbingan, Pengguna]),
-    AuthModule,
-  ],
+  imports: [TypeOrmModule.forFeature([Pengguna]), AuthModule],
   controllers: [DosenBimbinganController],
   providers: [DosenBimbinganService, CustomStrategy],
 })
diff --git a/src/entities/pengguna.entity.ts b/src/entities/pengguna.entity.ts
index e1d014a..1b4971a 100644
--- a/src/entities/pengguna.entity.ts
+++ b/src/entities/pengguna.entity.ts
@@ -55,6 +55,10 @@ export class Pengguna {
   @Column({ type: "text", nullable: true })
   kontak: string;
 
+  @ApiHideProperty()
+  @Column({ type: "boolean", default: true })
+  aktif: boolean;
+
   @OneToMany(() => PendaftaranTesis, (pendaftaran) => pendaftaran.mahasiswa)
   pendaftaranTesis: PendaftaranTesis[];
 }
diff --git a/src/pengguna/pengguna.module.ts b/src/pengguna/pengguna.module.ts
new file mode 100644
index 0000000..3e5276c
--- /dev/null
+++ b/src/pengguna/pengguna.module.ts
@@ -0,0 +1,11 @@
+import { Module } from "@nestjs/common";
+import { PenggunaService } from "./pengguna.service";
+import { TypeOrmModule } from "@nestjs/typeorm";
+import { Pengguna } from "src/entities/pengguna.entity";
+
+@Module({
+  imports: [TypeOrmModule.forFeature([Pengguna])],
+  providers: [PenggunaService],
+  exports: [PenggunaService],
+})
+export class PenggunaModule {}
diff --git a/src/pengguna/pengguna.service.ts b/src/pengguna/pengguna.service.ts
new file mode 100644
index 0000000..2828171
--- /dev/null
+++ b/src/pengguna/pengguna.service.ts
@@ -0,0 +1,24 @@
+import { Injectable, NotFoundException } from "@nestjs/common";
+import { InjectRepository } from "@nestjs/typeorm";
+import { Pengguna, RoleEnum } from "src/entities/pengguna.entity";
+import { ArrayContains, Repository } from "typeorm";
+
+@Injectable()
+export class PenggunaService {
+  constructor(
+    @InjectRepository(Pengguna)
+    private penggunaRepo: Repository<Pengguna>,
+  ) {}
+
+  async isMahasiswaAktifOrFail(id: string) {
+    const mhs = await this.penggunaRepo.findOneBy({
+      id,
+      aktif: true,
+      roles: ArrayContains([RoleEnum.S2_MAHASISWA]),
+    });
+
+    if (!mhs) {
+      throw new NotFoundException("Mahasiswa aktif tidak ditemukan");
+    }
+  }
+}
diff --git a/src/registrasi-tesis/registrasi-tesis.controller.ts b/src/registrasi-tesis/registrasi-tesis.controller.ts
index e740095..0ac28ae 100644
--- a/src/registrasi-tesis/registrasi-tesis.controller.ts
+++ b/src/registrasi-tesis/registrasi-tesis.controller.ts
@@ -52,7 +52,7 @@ export class RegistrasiTesisController {
   ) {}
 
   @ApiOperation({
-    summary: "Create new registration. Roles: S2_MAHASISWA, ADMIN",
+    summary: "Create new registration. Roles: S2_MAHASISWA",
   })
   @ApiCreatedResponse({ type: IdDto })
   @ApiNotFoundResponse({ description: "Penerima atau topik tidak ditemukan" })
@@ -61,7 +61,7 @@ export class RegistrasiTesisController {
       "Mahasiswa sedang memiliki pendaftaran aktif atau judul dan deskripsi topik baru tidak ada",
   })
   @UseGuards(CustomAuthGuard, RolesGuard)
-  @Roles(RoleEnum.S2_MAHASISWA, RoleEnum.ADMIN)
+  @Roles(RoleEnum.S2_MAHASISWA)
   @Post()
   async createTopicRegistration(
     @Body() topicRegistrationDto: RegDto,
diff --git a/src/registrasi-tesis/registrasi-tesis.module.ts b/src/registrasi-tesis/registrasi-tesis.module.ts
index 8e177eb..0b64d44 100644
--- a/src/registrasi-tesis/registrasi-tesis.module.ts
+++ b/src/registrasi-tesis/registrasi-tesis.module.ts
@@ -8,6 +8,8 @@ import { RegistrasiTesisService } from "./registrasi-tesis.service";
 import { Topik } from "src/entities/topik.entity";
 import { CustomStrategy } from "src/middlewares/custom.strategy";
 import { AuthModule } from "src/auth/auth.module";
+import { PenggunaModule } from "src/pengguna/pengguna.module";
+import { PenggunaService } from "src/pengguna/pengguna.service";
 
 @Module({
   imports: [
@@ -18,8 +20,9 @@ import { AuthModule } from "src/auth/auth.module";
       Topik,
     ]),
     AuthModule,
+    PenggunaModule,
   ],
   controllers: [RegistrasiTesisController],
-  providers: [RegistrasiTesisService, CustomStrategy],
+  providers: [RegistrasiTesisService, CustomStrategy, PenggunaService],
 })
 export class RegistrasiTesisModule {}
diff --git a/src/registrasi-tesis/registrasi-tesis.service.ts b/src/registrasi-tesis/registrasi-tesis.service.ts
index f3c861c..c2f221a 100644
--- a/src/registrasi-tesis/registrasi-tesis.service.ts
+++ b/src/registrasi-tesis/registrasi-tesis.service.ts
@@ -25,6 +25,7 @@ import {
   UpdatePembimbingBodyDto,
   UpdateStatusBodyDto,
 } from "./registrasi-tesis.dto";
+import { PenggunaService } from "src/pengguna/pengguna.service";
 
 @Injectable()
 export class RegistrasiTesisService {
@@ -38,6 +39,7 @@ export class RegistrasiTesisService {
     @InjectRepository(DosenBimbingan)
     private dosenBimbinganRepository: Repository<DosenBimbingan>,
     private dataSource: DataSource,
+    private penggunaService: PenggunaService,
   ) {}
 
   async createTopicRegistration(
@@ -124,6 +126,8 @@ export class RegistrasiTesisService {
     isNewestOnly: boolean,
     idPenerima?: string,
   ) {
+    await this.penggunaService.isMahasiswaAktifOrFail(mahasiswaId);
+
     const baseQuery = this.pendaftaranTesisRepository
       .createQueryBuilder("pt")
       .select("pt.id")
@@ -208,56 +212,33 @@ export class RegistrasiTesisService {
         "latest",
         "latest.latest_mahasiswaId = pt.mahasiswaId AND pt.waktuPengiriman = latest.latestPengiriman",
       )
-      .innerJoinAndSelect("pt.topik", "topik");
+      .innerJoin("pt.mahasiswa", "mahasiswa")
+      .where("mahasiswa.aktif = true");
 
     if (options.idPenerima) {
-      baseQuery.where("pt.penerimaId = :idPenerima", {
+      baseQuery.andWhere("pt.penerimaId = :idPenerima", {
         idPenerima: options.idPenerima,
       });
 
       totalMahasiswa = baseQuery.getCount();
     }
 
-    let totalDiterima: Promise<number>;
-    let totalProses: Promise<number>;
-    let totalDitolak: Promise<number>;
+    const totalDiterima = baseQuery
+      .clone()
+      .andWhere("pt.status = :status", { status: RegStatus.APPROVED })
+      .getCount();
 
-    if (options.idPenerima) {
-      // where used
-      totalDiterima = baseQuery
-        .clone()
-        .andWhere("pt.status = :status", { status: RegStatus.APPROVED })
-        .getCount();
-
-      totalProses = baseQuery
-        .clone()
-        .andWhere("pt.status IN (:...status)", {
-          status: [RegStatus.NOT_ASSIGNED, RegStatus.INTERVIEW],
-        })
-        .getCount();
-
-      totalDitolak = baseQuery
-        .clone()
-        .andWhere("pt.status = :status", { status: RegStatus.REJECTED })
-        .getCount();
-    } else {
-      totalDiterima = baseQuery
-        .clone()
-        .where("pt.status = :status", { status: RegStatus.APPROVED })
-        .getCount();
-
-      totalProses = baseQuery
-        .clone()
-        .where("pt.status IN (:...status)", {
-          status: [RegStatus.NOT_ASSIGNED, RegStatus.INTERVIEW],
-        })
-        .getCount();
-
-      totalDitolak = baseQuery
-        .clone()
-        .where("pt.status = :status", { status: RegStatus.REJECTED })
-        .getCount();
-    }
+    const totalProses = baseQuery
+      .clone()
+      .andWhere("pt.status IN (:...status)", {
+        status: [RegStatus.NOT_ASSIGNED, RegStatus.INTERVIEW],
+      })
+      .getCount();
+
+    const totalDitolak = baseQuery
+      .clone()
+      .andWhere("pt.status = :status", { status: RegStatus.REJECTED })
+      .getCount();
 
     const [total, diterima, proses, ditolak] = await Promise.all([
       totalMahasiswa,
@@ -311,45 +292,32 @@ export class RegistrasiTesisService {
     );
 
     baseQuery
-      .innerJoinAndSelect("pt.topik", "topik")
       .innerJoinAndSelect("pt.penerima", "penerima")
-      .innerJoinAndSelect("pt.mahasiswa", "mahasiswa");
+      .innerJoinAndSelect("pt.mahasiswa", "mahasiswa")
+      .where("mahasiswa.aktif = true");
 
-    let whereUsed = false;
     if (options.idPenerima) {
-      baseQuery.where("pt.penerimaId = :idPenerima", {
+      baseQuery.andWhere("pt.penerimaId = :idPenerima", {
         idPenerima: options.idPenerima,
       });
-
-      whereUsed = true;
     }
 
     if (options.search) {
-      const bracket = new Brackets((qb) =>
-        qb
-          .where("mahasiswa.nama ILIKE :search", {
-            search: `%${options.search}%`,
-          })
-          .orWhere("mahasiswa.nim ILIKE :search", {
-            search: `%${options.search}%`,
-          }),
+      baseQuery.andWhere(
+        new Brackets((qb) =>
+          qb
+            .where("mahasiswa.nama ILIKE :search", {
+              search: `%${options.search}%`,
+            })
+            .orWhere("mahasiswa.nim ILIKE :search", {
+              search: `%${options.search}%`,
+            }),
+        ),
       );
-
-      if (whereUsed) {
-        baseQuery.andWhere(bracket);
-      } else {
-        baseQuery.where(bracket);
-        whereUsed = true;
-      }
     }
 
     if (options.status) {
-      if (whereUsed) {
-        baseQuery.andWhere("pt.status = :status", { status: options.status });
-      } else {
-        baseQuery.where("pt.status = :status", { status: options.status });
-        whereUsed = true;
-      }
+      baseQuery.andWhere("pt.status = :status", { status: options.status });
     }
 
     if (options.order_by) {
@@ -444,6 +412,8 @@ export class RegistrasiTesisService {
     dto: UpdateInterviewBodyDto,
     idPenerima?: string,
   ) {
+    await this.penggunaService.isMahasiswaAktifOrFail(mahasiswaId);
+
     const minDate = new Date();
     minDate.setDate(minDate.getDate() + 2);
 
@@ -486,6 +456,8 @@ export class RegistrasiTesisService {
     dto: UpdateStatusBodyDto,
     idPenerima?: string,
   ) {
+    await this.penggunaService.isMahasiswaAktifOrFail(mahasiswaId);
+
     const newestReg = await this.getNewestRegByMhsOrFail(mahasiswaId);
 
     if (newestReg && idPenerima && newestReg.penerima.id !== idPenerima) {
@@ -531,6 +503,8 @@ export class RegistrasiTesisService {
     mahasiswaId: string,
     { pembimbing_ids: dosen_ids }: UpdatePembimbingBodyDto,
   ) {
+    await this.penggunaService.isMahasiswaAktifOrFail(mahasiswaId);
+
     const newestReg = await this.getNewestRegByMhsOrFail(mahasiswaId);
 
     if (newestReg.status !== RegStatus.APPROVED)
-- 
GitLab