penisularhr / src / modules / monthly-record / monthly-record.service.ts
monthly-record.service.ts
Raw
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import moment from 'moment';
import { type FindOptionsWhere, Repository } from 'typeorm';

import { type PageDto } from '../../common/dto/page.dto';
import { Order } from '../../constants';
import { type EmployeeEntity } from '../employee/employee.entity';
import { type GetByEmployeeDto } from '../report/dtos/get-report-by-employee.dto';
import { type CreateMonthlyRecordDto } from './dtos/create-monthly-record.dto';
import { type MonthlyRecordPageOptionsDto } from './dtos/get-monthly-record-page.dto';
import { type MonthlyRecordDto } from './dtos/monthly-record.dto';
import { type UpdateMonthlyRecordDto } from './dtos/update-monthly-record.dto';
import { MonthlyRecordEntity } from './monthly-record.entity';

@Injectable()
export class MonthlyRecordService {
  constructor(
    @InjectRepository(MonthlyRecordEntity)
    private monthlyRecordReposiory: Repository<MonthlyRecordEntity>,
  ) {}

  async findOne(
    findData: FindOptionsWhere<MonthlyRecordEntity>,
  ): Promise<MonthlyRecordEntity | null> {
    const queryBuilder =
      this.monthlyRecordReposiory.createQueryBuilder('monthlyRecord');

    queryBuilder.where(findData);

    queryBuilder
      .leftJoin('monthlyRecord.employee', 'employee')
      .addSelect(['employee.id', 'employee.name']);

    return queryBuilder.getOne();
  }

  async findMany(
    pageOptionsDto: MonthlyRecordPageOptionsDto,
  ): Promise<PageDto<MonthlyRecordDto>> {
    const { order, date, employeeName } = pageOptionsDto;

    const queryBuilder =
      this.monthlyRecordReposiory.createQueryBuilder('monthlyRecord');

    queryBuilder
      .leftJoin('monthlyRecord.employee', 'employee')
      .addSelect(['employee.name']);

    if (date) {
      queryBuilder.andWhere('monthlyRecord.date = :date', { date });
    }

    if (employeeName) {
      queryBuilder.searchByString(employeeName, ['employee.name']);
    }

    queryBuilder.orderBy('monthlyRecord.date', order);
    queryBuilder.addOrderBy('employee.name', Order.ASC);

    const [items, pageMetaDto] = await queryBuilder.paginate(pageOptionsDto);

    return items.toPageDto(pageMetaDto);
  }

  async createMonthlyRecordByFunction(
    employee: EmployeeEntity,
    dto: CreateMonthlyRecordDto,
  ) {
    const { date } = dto;

    const recordDate = moment.utc(date).startOf('month');

    // 1. Check if employee is active
    if (!employee.isActive) {
      return null;
    }

    // 2. Check if current record exist
    const existingRecord = await this.findOne({
      date: recordDate.toDate(),
      employee: { id: employee.id },
    });

    if (existingRecord) {
      return null;
    }

    // 3. Create Record
    const monthlyRecordEntity = this.monthlyRecordReposiory.create({
      employee,
      monthlyAllowance: employee.monthlyAllowanceAmount,
      ...dto,
    });

    await this.monthlyRecordReposiory.save(monthlyRecordEntity);

    return monthlyRecordEntity;
  }

  async updateMonthlyRecord(
    monthlyRecordEntity: MonthlyRecordEntity,
    dto: UpdateMonthlyRecordDto,
  ): Promise<MonthlyRecordEntity> {
    const {
      addition,
      advance,
      cleaningFee,
      deduct,
      restDayWages,
      eis,
      epf,
      levy,
      socso,
      remark,
    } = dto;

    if (addition !== undefined) {
      monthlyRecordEntity.addition = addition;
    }

    if (advance !== undefined) {
      monthlyRecordEntity.advance = advance;
    }

    if (cleaningFee !== undefined) {
      monthlyRecordEntity.cleaningFee = cleaningFee;
    }

    if (deduct !== undefined) {
      monthlyRecordEntity.deduct = deduct;
    }

    if (restDayWages !== undefined) {
      monthlyRecordEntity.restDayWages = restDayWages;
    }

    if (eis !== undefined) {
      monthlyRecordEntity.eis = eis;
    }

    if (epf !== undefined) {
      monthlyRecordEntity.epf = epf;
    }

    if (socso !== undefined) {
      monthlyRecordEntity.socso = socso;
    }

    if (levy !== undefined) {
      monthlyRecordEntity.levy = levy;
    }

    if (remark !== undefined) {
      monthlyRecordEntity.remark = remark;
    }

    await this.monthlyRecordReposiory.save(monthlyRecordEntity);

    return monthlyRecordEntity;
  }

  async getSummarMonthlyByEmployee(
    dto: GetByEmployeeDto,
    employee: EmployeeEntity,
  ) {
    const { monthFrom, monthTo, year } = dto;

    const currentYear = year ?? moment.utc().year();

    const queryBuilder = this.monthlyRecordReposiory
      .createQueryBuilder('monthlyRecord')
      .select('SUM(monthlyRecord.restDayWages)', 'restDayWages')
      .addSelect('SUM(monthlyRecord.monthlyAllowance)', 'monthlyAllowance')
      .addSelect('SUM(monthlyRecord.advance)', 'advance')
      .addSelect('SUM(monthlyRecord.cleaningFee)', 'cleaningFee')
      .addSelect('SUM(monthlyRecord.deduct)', 'deduct')
      .addSelect('SUM(monthlyRecord.addition)', 'addition')
      .addSelect('SUM(monthlyRecord.levy)', 'levy')
      .addSelect('SUM(monthlyRecord.epf)', 'epf')
      .addSelect('SUM(monthlyRecord.eis)', 'eis')
      .addSelect('SUM(monthlyRecord.socso)', 'socso')
      .leftJoin('monthlyRecord.employee', 'employee')
      .addSelect(['employee.id', 'employee.name'])
      .where('employee.id = :id', { id: employee.id });

    queryBuilder.groupBy('employee.id');

    if (monthFrom) {
      const fromDate = moment
        .utc({
          year: currentYear,
          month: monthFrom - 1,
          date: 1,
        })
        .toDate();

      queryBuilder.andWhere('monthlyRecord.date >= :fromDate', { fromDate });
    }

    if (monthTo) {
      const toDate = moment
        .utc({ year: currentYear, month: monthTo - 1, date: 1 })
        .add(1, 'months')
        .toDate();

      queryBuilder.andWhere('monthlyRecord.date < :toDate', { toDate });
    }

    // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
    const returnData:
      | {
          restDayWages: string;
          advance: string;
          monthlyAllowance: string;
          cleaningFee: string;
          deduct: string;
          addition: string;
          levy: string;
          epf: string;
          eis: string;
          socso: string;
        }
      | undefined = await queryBuilder.getRawOne();

    return returnData
      ? {
          restDayWages: Number(returnData.restDayWages),
          advance: Number(returnData.advance),
          allowance: Number(returnData.monthlyAllowance),
          cleaningFee: Number(returnData.cleaningFee),
          deduct: Number(returnData.deduct),
          addition: Number(returnData.addition),
          levy: Number(returnData.levy),
          epf: Number(returnData.epf),
          eis: Number(returnData.eis),
          socso: Number(returnData.levy),
          employeeName: employee.name,
        }
      : {
          restDayWages: 0,
          advance: 0,
          allowance: 0,
          cleaningFee: 0,
          deduct: 0,
          addition: 0,
          levy: 0,
          epf: 0,
          eis: 0,
          socso: 0,
          employeeName: employee.name,
        };
  }
}