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 { LeaveRequestController } from "./controllers/leave-requests.controller";
import { LeaveRequestsService } from "./services/leave-requests.service"; import { LeaveRequestsService } from "./services/leave-requests.service";
import { Module } from "@nestjs/common"; import { Module } from "@nestjs/common";
import { HolidayService } from "src/business-logic/holiday.service";
@Module({ @Module({
controllers: [LeaveRequestController], controllers: [LeaveRequestController],
providers: [ LeaveRequestsService, PrismaService], providers: [
LeaveRequestsService,
PrismaService,
HolidayService,
],
exports: [ LeaveRequestsService], exports: [ LeaveRequestsService],
}) })

View File

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