File

apps/recallassess/recallassess-api/src/api/admin/knowledge-review/services/knowledge-review.service.ts

Extends

BNestBaseModuleService

Index

Methods

Constructor

constructor(prisma: BNestPrismaService, systemLogService: SystemLogService)
Parameters :
Name Type Optional
prisma BNestPrismaService No
systemLogService SystemLogService No

Methods

Async add
add(data: any)

Override add method to prevent creation when course has enrollments

Parameters :
Name Type Optional
data any No
Returns : unknown
Private Async checkCourseEnrollments
checkCourseEnrollments(courseId: number | null, knowledgeReviewId?: number)

Check if the associated course has enrollments Also checks if knowledge review is used in course module pages for courses with enrollments

Parameters :
Name Type Optional
courseId number | null No
knowledgeReviewId number Yes
Returns : Promise<void>
Async delete
delete(id: number)

Override delete method to prevent deletion when course has enrollments

Parameters :
Name Type Optional
id number No
Returns : Promise<void>
Async save
save(id: number, data: any)

Override save method to prevent modifications when course has enrollments

Parameters :
Name Type Optional
id number No
data any No
Returns : Promise<SaveResponseDataInterface<any>>
import { BNestBaseModuleService } from "@bish-nest/core/data/module-service/base-module.service";
import { BNestPrismaService } from "@bish-nest/core/services";
import { SystemLogService } from "@api/shared/services";
import { SaveResponseDataInterface } from "@bish-nest/core/interfaces/save-response-data.interface";
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { SystemLogEntityType } from "@prisma/client";
import { plainToInstance } from "class-transformer";

@Injectable()
export class KnowledgeReviewService extends BNestBaseModuleService {
  constructor(
    protected prisma: BNestPrismaService,
    private readonly systemLogService: SystemLogService,
  ) {
    super();
  }
  /**
   * Check if the associated course has enrollments
   * Also checks if knowledge review is used in course module pages for courses with enrollments
   */
  private async checkCourseEnrollments(courseId: number | null, knowledgeReviewId?: number): Promise<void> {
    // If course_id is provided, check that course
    if (courseId) {
      const enrollmentCount = await this.prisma.client.learningGroupParticipant.count({
        where: {
          course_id: courseId,
        },
      });

      if (enrollmentCount > 0) {
        throw new BadRequestException(
          `Cannot modify knowledge review. The associated course has ${enrollmentCount} enrolled participant(s). Knowledge reviews for courses with enrollments cannot be modified or deleted.`,
        );
      }
    }

    // Also check if this knowledge review is used in any course module pages
    if (knowledgeReviewId) {
      const courseModulePages = await this.prisma.client.courseModulePage.findMany({
        where: {
          knowledge_review_id: knowledgeReviewId,
        },
        select: {
          course_id: true,
        },
      });

      for (const page of courseModulePages) {
        const pageEnrollmentCount = await this.prisma.client.learningGroupParticipant.count({
          where: {
            course_id: page.course_id,
          },
        });

        if (pageEnrollmentCount > 0) {
          throw new BadRequestException(
            `Cannot modify knowledge review. This knowledge review is used in course module pages for a course with ${pageEnrollmentCount} enrolled participant(s). Knowledge reviews for courses with enrollments cannot be modified or deleted.`,
          );
        }
      }
    }
  }

  /**
   * Override add method to prevent creation when course has enrollments
   */
  async add(data: any) {
    // Check if course_id is provided in the data
    const courseId = data.course_id || data.course?.id || null;

    if (courseId) {
      await this.checkCourseEnrollments(courseId);
    }

    const repo = this.commonMethods.getRepo(this.gVars.moduleCurrentCfg.repoName);

    let dataAdd = plainToInstance(this.gVars.moduleCurrentCfg.addDto, data);
    // Use scalar audit columns to avoid relation create errors
    dataAdd = await this.dbUtilService.addCreatedById(dataAdd);

    const newRow = await repo.create({ data: dataAdd });
    const addResponse = this.moduleMethods.getReturnDataForAdd(newRow);

    // Log the creation
    await this.systemLogService.logInsert(
      SystemLogEntityType.KNOWLEDGE_REVIEW,
      newRow.id,
      newRow as Record<string, unknown>,
    );

    return addResponse;
  }

  /**
   * Override save method to prevent modifications when course has enrollments
   */
  async save(id: number, data: any): Promise<SaveResponseDataInterface<any>> {
    // Get old data before update
    const oldKnowledgeReview = await this.prisma.client.knowledgeReview.findUnique({
      where: { id },
    });

    if (!oldKnowledgeReview) {
      throw new NotFoundException(`Knowledge review with ID ${id} not found`);
    }

    // Check if the associated course has enrollments and if used in pages
    await this.checkCourseEnrollments(oldKnowledgeReview.course_id, id);

    // Also check if the course_id is being changed in the update data
    const courseId = data.course_id !== undefined ? (data.course_id || data.course?.id || null) : null;
    if (courseId !== null && courseId !== oldKnowledgeReview.course_id) {
      await this.checkCourseEnrollments(courseId, id);
    }

    const repo = this.commonMethods.getRepo(this.gVars.moduleCurrentCfg.repoName);

    let dataSave = plainToInstance(this.gVars.moduleCurrentCfg.saveDto, data);
    // Use scalar audit columns to avoid relation update errors
    dataSave = await this.dbUtilService.addUpdatedById(dataSave);

    await repo.update({ where: { id }, data: dataSave });

    // Get updated data directly from database for logging
    const updatedKnowledgeReview = await this.prisma.client.knowledgeReview.findUnique({
      where: { id },
    });

    if (!updatedKnowledgeReview) {
      throw new NotFoundException(`Knowledge review with ID ${id} not found after update`);
    }

    // Calculate changed fields and log the update
    const changedFields = SystemLogService.calculateChangedFields(
      oldKnowledgeReview as Record<string, unknown>,
      updatedKnowledgeReview as Record<string, unknown>,
    );

    await this.systemLogService.logUpdate(
      SystemLogEntityType.KNOWLEDGE_REVIEW,
      id,
      oldKnowledgeReview as Record<string, unknown>,
      updatedKnowledgeReview as Record<string, unknown>,
      changedFields,
    );

    return await this.getDetail(id);
  }

  /**
   * Override delete method to prevent deletion when course has enrollments
   */
  async delete(id: number): Promise<void> {
    // Get knowledge review data before deletion
    const knowledgeReview = await this.prisma.client.knowledgeReview.findUnique({
      where: { id },
    });

    if (!knowledgeReview) {
      throw new NotFoundException(`Knowledge review with ID ${id} not found`);
    }

    // Check if the associated course has enrollments and if used in pages
    await this.checkCourseEnrollments(knowledgeReview.course_id, id);

    // Call parent delete method
    await super.delete(id);

    // Log the deletion
    await this.systemLogService.logDelete(
      SystemLogEntityType.KNOWLEDGE_REVIEW,
      id,
      knowledgeReview as Record<string, unknown>,
    );
  }
}

results matching ""

    No results matching ""