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(