176 lines
6.4 KiB
TypeScript
176 lines
6.4 KiB
TypeScript
import { Injectable } from "@nestjs/common";
|
|
import { Prisma } from "prisma/postgres/generated/prisma/client/postgres/client";
|
|
import { PrismaPostgresService } from "prisma/postgres/prisma-postgres.service";
|
|
import { Result } from "src/common/errors/result-error.factory";
|
|
|
|
|
|
@Injectable()
|
|
export class SickLeaveService {
|
|
constructor(private readonly prisma: PrismaPostgresService) { }
|
|
|
|
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() ?? 0);
|
|
|
|
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,
|
|
banked_hours: true,
|
|
last_updated: true,
|
|
}
|
|
});
|
|
|
|
return { success: true, data: new_pto_entry };
|
|
} catch (error) {
|
|
return { success: false, error: '' + 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) {
|
|
console.error(error);
|
|
return { success: false, error: ''};
|
|
}
|
|
};
|
|
|
|
takeSickLeaveHours = async (
|
|
employee_id: number,
|
|
asked_hours: number
|
|
): Promise<Result<number, string>> => {
|
|
if (asked_hours <= 0) return { success: false, error: 'INVALID_BANKING_HOURS' };
|
|
|
|
try {
|
|
const result = await this.prisma.$transaction(async (tx) => {
|
|
const employee = await this.prisma.employees.findUnique({
|
|
where: { id: employee_id },
|
|
select: {
|
|
id: true,
|
|
paid_time_off: {
|
|
select: {
|
|
sick_hours: true
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
if (!employee) {
|
|
return { success: false, error: 'EMPLOYEE_NOT_FOUND' } as Result<number, string>;
|
|
}
|
|
if (!employee.paid_time_off) {
|
|
return { success: false, error: 'SICK_HOURS_BANK_NOT_FOUND' } as Result<number, string>;
|
|
}
|
|
const sick_bank = (employee.paid_time_off.sick_hours).toNumber();
|
|
if (sick_bank <= 0) return { success: false, error: 'EMPTY_SICK_HOURS_BANK' } as Result<number, string>;
|
|
|
|
if (asked_hours > sick_bank) {
|
|
return { success: true, data: sick_bank } as Result<number, string>;
|
|
} else {
|
|
await tx.paidTimeOff.update({
|
|
where: { employee_id: employee.id },
|
|
data: {
|
|
sick_hours: { decrement: asked_hours },
|
|
last_updated: new Date(),
|
|
},
|
|
});
|
|
|
|
return { success: true, data: asked_hours } as Result<number, string>;
|
|
}
|
|
});
|
|
return result;
|
|
|
|
} catch (error) {
|
|
console.error(error);
|
|
return { success: false, error: 'INVALID_BANKING_SHIFT' };
|
|
}
|
|
|
|
}
|
|
}
|