targo-backend/src/time-and-attendance/domains/services/vacation.service.ts

78 lines
3.6 KiB
TypeScript

import { Injectable, Logger, NotFoundException } from "@nestjs/common";
import { PrismaService } from "../../../prisma/prisma.service";
@Injectable()
export class VacationService {
constructor(private readonly prisma: PrismaService) {}
private readonly logger = new Logger(VacationService.name);
//switch employeeId for email
async calculateVacationPay(employee_id: number, start_date: Date, days_requested: number, modifier: number): Promise<number> {
//fetch hiring date
const employee = await this.prisma.employees.findUnique({
where: { id: employee_id },
select: { first_work_day: true },
});
if(!employee) {
throw new NotFoundException(`Employee #${employee_id} not found`);
}
const hire_date = employee.first_work_day;
//sets "year" to may 1st to april 30th
//check if hiring date is in may or later, we use hiring year, otherwise we use the year before
const year_of_request = start_date.getMonth() >= 4
? start_date.getFullYear() : start_date.getFullYear() -1;
const period_start = new Date(year_of_request, 4, 1); //may = 4
const period_end = new Date(year_of_request + 1, 4, 0); //day 0 of may == april 30th
this.logger.debug(`Vacation period for request: ${period_start.toDateString()} -> ${period_end.toDateString()}`);
//steps to reach to get more vacation weeks in years
const checkpoint = [5, 10, 15];
const anniversaries = checkpoint.map(years => {
const anniversary_date = new Date(hire_date);
anniversary_date.setFullYear(anniversary_date.getFullYear() + years);
return anniversary_date;
}).filter(d => d>= period_start && d <= period_end).sort((a,b) => a.getTime() - b.getTime());
this.logger.debug(`anniversatries steps during the period: ${anniversaries.map(date => date.toDateString()).join(',') || 'aucun'}`);
const boundaries = [period_start, ...anniversaries,period_end];
//calculate prorata per segment
let total_vacation_days = 0;
const ms_per_day = 1000 * 60 * 60 * 24;
for (let i = 0; i < boundaries.length -1; i++) {
const segment_start = boundaries[i];
const segment_end = boundaries[i+1];
//number of days in said segment
const days_in_segment = Math.round((segment_end.getTime() - segment_start.getTime())/ ms_per_day);
const years_since_hire = (segment_start.getFullYear() - hire_date.getFullYear()) -
(segment_start < new Date(segment_start.getFullYear(), hire_date.getMonth()) ? 1 : 0);
let alloc_days: number;
if(years_since_hire < 5) alloc_days = 10;
else if(years_since_hire < 10) alloc_days = 15;
else if(years_since_hire < 15) alloc_days = 20;
else alloc_days = 25;
//prorata for said segment
const prorata = (alloc_days / 365) * days_in_segment;
total_vacation_days += prorata;
}
//compares allowed vacation pools with requested days
const payable_days = Math.min(total_vacation_days, days_requested);
const raw_hours = payable_days * 8 * modifier;
const rounded_hours = Math.round(raw_hours * 4) / 4;
this.logger.debug(`Vacation pay: entitledDays=${total_vacation_days.toFixed(2)}, requestedDays=${days_requested},
payableDays=${payable_days.toFixed(2)}, hours=${rounded_hours}`);
return rounded_hours;
}
}