diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 0e68333..b810226 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -55,6 +55,7 @@ model Employees { user Users @relation("UserEmployee", fields: [user_id], references: [id]) leave_request LeaveRequests[] @relation("LeaveRequestEmployee") timesheet Timesheets[] @relation("TimesheetEmployee") + paid_time_off PaidTimeOff? @relation("EmployeePaidTimeOff") @@map("employees") } @@ -321,6 +322,18 @@ model 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 { pay_year Int pay_period_no Int diff --git a/src/time-and-attendance/domains/services/sick-leave.service.ts b/src/time-and-attendance/domains/services/sick-leave.service.ts index 4122262..f78cc31 100644 --- a/src/time-and-attendance/domains/services/sick-leave.service.ts +++ b/src/time-and-attendance/domains/services/sick-leave.service.ts @@ -2,12 +2,115 @@ import { Injectable, Logger } from "@nestjs/common"; import { PrismaService } from "src/prisma/prisma.service"; import { Result } from "src/common/errors/result-error.factory"; +import { Prisma } from "@prisma/client"; @Injectable() export class SickLeaveService { constructor(private readonly prisma: PrismaService) { } - //switch employeeId for email + async updateSickLeaveHours(employee_id: number): Promise> { + 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; + + 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, 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( employee_id: number, reference_date: Date, @@ -69,6 +172,6 @@ export class SickLeaveService { const payable_days = Math.min(acquired_days, days_requested); const raw_hours = payable_days * hours_per_day * modifier; const rounded = roundToQuarterHour(raw_hours); - return {success:true, data: rounded}; + return { success: true, data: rounded }; } }