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, }; } }