refactor(sick-leave): remake methods to calculate and update sick leave available

This commit is contained in:
Nicolas Drolet 2026-01-06 07:34:26 -05:00
parent 17f06e7867
commit 53ec64ba1e
2 changed files with 118 additions and 2 deletions

View File

@ -55,6 +55,7 @@ model Employees {
user Users @relation("UserEmployee", fields: [user_id], references: [id]) user Users @relation("UserEmployee", fields: [user_id], references: [id])
leave_request LeaveRequests[] @relation("LeaveRequestEmployee") leave_request LeaveRequests[] @relation("LeaveRequestEmployee")
timesheet Timesheets[] @relation("TimesheetEmployee") timesheet Timesheets[] @relation("TimesheetEmployee")
paid_time_off PaidTimeOff? @relation("EmployeePaidTimeOff")
@@map("employees") @@map("employees")
} }
@ -321,6 +322,18 @@ model Preferences {
@@map("preferences") @@map("preferences")
} }
model PaidTimeOff {
id Int @id @default(autoincrement())
employee_id Int @unique
vacation_hours Int @default(0)
sick_hours Int @default(0)
last_updated DateTime @db.Date
employee Employees @relation("EmployeePaidTimeOff", fields: [employee_id], references: [id])
@@map("paid_time_off")
}
view PayPeriods { view PayPeriods {
pay_year Int pay_year Int
pay_period_no Int pay_period_no Int

View File

@ -2,12 +2,115 @@
import { Injectable, Logger } from "@nestjs/common"; import { Injectable, Logger } from "@nestjs/common";
import { PrismaService } from "src/prisma/prisma.service"; import { PrismaService } from "src/prisma/prisma.service";
import { Result } from "src/common/errors/result-error.factory"; import { Result } from "src/common/errors/result-error.factory";
import { Prisma } from "@prisma/client";
@Injectable() @Injectable()
export class SickLeaveService { export class SickLeaveService {
constructor(private readonly prisma: PrismaService) { } constructor(private readonly prisma: PrismaService) { }
//switch employeeId for email async updateSickLeaveHours(employee_id: number): Promise<Result<boolean, string>> {
const THIRTY_DAYS = 1000 * 60 * 60 * 24 * 30;
const FOURTEEN_DAYS = 1000 * 60 * 60 * 24 * 14;
const today = new Date();
// get employee info
const employee = await this.prisma.employees.findUnique({
where: { id: employee_id },
select: {
first_work_day: true,
last_work_day: true,
id: true,
}
})
if (!employee) return { success: false, error: 'EMPLOYEE_NOT_FOUND' };
// check if employee has been inactive for more than 2 weeks
if (employee.last_work_day) {
if (today.getTime() - employee.last_work_day.getTime() >= FOURTEEN_DAYS) {
return { success: false, error: "EMPLOYEE_NOT_ACTIVE" };
}
}
// get employee's PTO info, or create new details if not yet existing
let pto_details: Prisma.Result<typeof this.prisma.paidTimeOff, Prisma.PaidTimeOffDefaultArgs, 'findUnique' | 'create'>;
pto_details = await this.prisma.paidTimeOff.findUnique({
where: { employee_id: employee.id },
})
if (!pto_details) {
const new_pto_entry = await this.createNewPTORow(employee.id, today);
if (!new_pto_entry.success) return { success: false, error: "FAILED_TO_CREATE_EMPLOYEE_PTO" };
pto_details = new_pto_entry.data;
}
// check if employee has gotten his 3 days from completing his 30-day qualifying period from hiring date
if (today.getTime() - employee.first_work_day.getTime() >= THIRTY_DAYS && employee.first_work_day.toISOString() === pto_details?.last_updated?.toISOString()) {
const updated_pto = await this.addHoursToPTO(3 * 8, employee.id, today);
if (!updated_pto.success) return { success: updated_pto.success, error: updated_pto.error }
}
const year_difference = today.getFullYear() - (pto_details!.last_updated.getFullYear() ?? today.getFullYear());
const months_since_last_update = (today.getMonth() + year_difference * 12) - pto_details!.last_updated.getMonth();
if (months_since_last_update > 0) {
const updated_pto = await this.addHoursToPTO(months_since_last_update * 8, employee.id, today);
if (updated_pto.success) {
return {success: true, data: true}
}
}
return { success: false, error: 'UNKNOWN_PTO_UPDATE_ERROR' };
}
// create a new PTO row
async createNewPTORow(employee_id: number, today: Date): Promise<Result<Prisma.Result<typeof this.prisma.paidTimeOff, Prisma.PaidTimeOffDefaultArgs, 'findUnique' | 'create'>, string>> {
try {
const new_pto_entry = await this.prisma.paidTimeOff.create({
data: {
employee_id: employee_id,
last_updated: today,
},
select: {
id: true,
employee_id: true,
sick_hours: true,
vacation_hours: true,
last_updated: true,
}
});
return { success: true, data: new_pto_entry };
} catch (error) {
return { success: false, error };
}
}
// add n number of sick PTO hours to employee PTO
async addHoursToPTO(sick_hours: number, employee_id: number, last_updated: Date) {
try {
const update_pto = await this.prisma.paidTimeOff.update({
where: {
employee_id,
},
data: {
sick_hours,
last_updated,
}
})
return { success: true, data: update_pto };
} catch (error) {
return { success: false, error };
}
};
// switch employeeId for email
async calculateSickLeavePay( async calculateSickLeavePay(
employee_id: number, employee_id: number,
reference_date: Date, reference_date: Date,
@ -69,6 +172,6 @@ export class SickLeaveService {
const payable_days = Math.min(acquired_days, days_requested); const payable_days = Math.min(acquired_days, days_requested);
const raw_hours = payable_days * hours_per_day * modifier; const raw_hours = payable_days * hours_per_day * modifier;
const rounded = roundToQuarterHour(raw_hours); const rounded = roundToQuarterHour(raw_hours);
return {success:true, data: rounded}; return { success: true, data: rounded };
} }
} }