feat(business-logic): holiday implementation for leave Requests

This commit is contained in:
Matthieu Haineault 2025-07-31 13:09:19 -04:00
parent c6ff3139f2
commit 2e6bafeb18
3 changed files with 108 additions and 62 deletions

View File

@ -0,0 +1,56 @@
import { Injectable, Logger } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service";
@Injectable()
export class HolidayService {
private readonly logger = new Logger(HolidayService.name);
constructor(private readonly prisma: PrismaService) {}
//return the sunday of the current week that includes the holiday
private getWeekStart(date: Date): Date {
const day = new Date(date);
const offset = day.getDay();
day.setDate(day.getDate() - offset);
day.setHours(0,0,0,0);
return day;
}
//rounds minutes to 5s
private computeHours(start: Date, end: Date): number {
const durationMS = end.getTime() - start.getTime();
const totalMinutes = durationMS / 60000;
const rounded = Math.round(totalMinutes / 5) * 5;
return rounded / 60;
}
private async computeHoursPrevious4Weeks(employeeId: number, holidayDate: Date): Promise<number> {
//sets the end of the window to 1ms before the week with the holiday
const holidayWeekStart = this.getWeekStart(holidayDate);
const windowEnd = new Date(holidayWeekStart.getTime() - 1);
//sets the start of the window to 28 days ( 4 completed weeks ) before the week with the holiday
const windowStart = new Date(windowEnd.getTime() - 28 * 24 * 60 * 60000 + 1 )
const validCodes = ['G1', 'G45', 'G56', 'G104', 'G105', 'G700'];
//fetches all shift of the employee in said window ( 4 completed weeks )
const shifts = await this.prisma.shifts.findMany({
where: { timesheet: { employee_id: employeeId } ,
date: { gte: windowStart, lte: windowEnd },
bank_code: { bank_code: { in: validCodes } },
},
select: { date: true, start_time: true, end_time: true },
});
const totalHours = shifts.map(s => this.computeHours(s.start_time, s.end_time)).reduce((sum, h)=> sum + h, 0);
const dailyHours = totalHours / 20;
return dailyHours;
}
async calculateHolidayPay( employeeId: number, holidayDate: Date, modifier: number): Promise<number> {
const hours = await this. computeHoursPrevious4Weeks(employeeId, holidayDate);
const dailyRate = Math.min(hours, 8);
this.logger.debug(`Holiday pay calculation: hours=${hours.toFixed(2)}`);
return dailyRate * modifier;
}
}

View File

@ -2,10 +2,15 @@ import { PrismaService } from "src/prisma/prisma.service";
import { LeaveRequestController } from "./controllers/leave-requests.controller";
import { LeaveRequestsService } from "./services/leave-requests.service";
import { Module } from "@nestjs/common";
import { HolidayService } from "src/business-logic/holiday.service";
@Module({
controllers: [LeaveRequestController],
providers: [ LeaveRequestsService, PrismaService],
providers: [
LeaveRequestsService,
PrismaService,
HolidayService,
],
exports: [ LeaveRequestsService],
})

View File

@ -3,87 +3,78 @@ import { PrismaService } from "src/prisma/prisma.service";
import { CreateLeaveRequestsDto } from "../dtos/create-leave-requests.dto";
import { LeaveRequests, LeaveRequestsArchive } from "@prisma/client";
import { UpdateLeaveRequestsDto } from "../dtos/update-leave-requests.dto";
import { HolidayService } from "src/business-logic/holiday.service";
@Injectable()
export class LeaveRequestsService {
constructor(private readonly prisma: PrismaService) {}
constructor(
private readonly prisma: PrismaService,
private readonly holidayService: HolidayService,
) {}
async create(dto: CreateLeaveRequestsDto): Promise<LeaveRequests> {
const {
employee_id,
bank_code_id,
leave_type,
start_date_time,
end_date_time,
comment,
approval_status,
} = dto;
const { employee_id, bank_code_id, leave_type, start_date_time,
end_date_time, comment, approval_status } = dto;
return this.prisma.leaveRequests.create({
data: {
employee_id,
bank_code_id,
leave_type,
start_date_time,
end_date_time,
comment,
approval_status: approval_status ?? undefined,
},
include: {
employee: {
include: {
user: true
},
},
data: { employee_id, bank_code_id, leave_type, start_date_time,
end_date_time, comment, approval_status: approval_status ?? undefined
},
include: { employee: { include: { user: true } } },
});
}
findAll(): Promise<LeaveRequests[]> {
return this.prisma.leaveRequests.findMany({
include: {
employee: {
include: {
user: true
},
},
bank_code: true,
async findAll(): Promise<any[]> {
const list = await this.prisma.leaveRequests.findMany({
include: { employee: { include: { user: true } },
bank_code: true,
},
});
return Promise.all(
list.map(async request => {
if(request.bank_code?.type === 'holiday') {
const cost = await this.holidayService.calculateHolidayPay(
request.employee_id,
request.start_date_time,
request.bank_code.modifier
);
return { ...request, cost };
}
return request;
}),
);
}
async findOne(id:number): Promise<LeaveRequests> {
const req = await this.prisma.leaveRequests.findUnique({
async findOne(id:number): Promise<any> {
const request = await this.prisma.leaveRequests.findUnique({
where: { id },
include: {
employee: {
include: {
user: true
},
},
bank_code: true,
include: { employee: { include: { user: true } },
bank_code: true,
},
});
if(!req) {
if(!request) {
throw new NotFoundException(`LeaveRequest #${id} not found`);
}
return req;
}
//search for leave type. if holiday
if (request.bank_code?.type === 'holiday') {
const cost = await this.holidayService.calculateHolidayPay(
request.employee_id,
request.start_date_time,
request.bank_code.modifier,
);
return { ...request, cost };
}
return request;
}
async update(
id: number,
dto: UpdateLeaveRequestsDto,
): Promise<LeaveRequests> {
await this.findOne(id);
const {
employee_id,
leave_type,
start_date_time,
end_date_time,
comment,
approval_status,
} = dto;
const { employee_id, leave_type, start_date_time, end_date_time, comment, approval_status } = dto;
return this.prisma.leaveRequests.update({
where: { id },
data: {
@ -94,13 +85,7 @@ export class LeaveRequestsService {
...(comment !== undefined && { comment }),
...(approval_status == undefined && { approval_status }),
},
include: {
employee: {
include: {
user:true
},
},
},
include: { employee: { include: { user:true } } },
});
}