92 lines
3.8 KiB
TypeScript
92 lines
3.8 KiB
TypeScript
import { Injectable } from "@nestjs/common";
|
|
import { computeHours, getWeekStart } from "src/common/utils/date-utils";
|
|
import { PrismaPostgresService } from "prisma/postgres/prisma-postgres.service";
|
|
import { MS_PER_WEEK } from "src/common/utils/constants.utils";
|
|
import { Result } from "src/common/errors/result-error.factory";
|
|
/*
|
|
le calcul est 1/20 des 4 dernières semaines, précédent la semaine incluant le férier.
|
|
Un maximum de 08h00 est allouable pour le férier
|
|
Un maximum de 40hrs par semaine est retenue pour faire le calcul.
|
|
le bank-code à soumettre à Desjardins doit être le G104
|
|
*/
|
|
|
|
@Injectable()
|
|
export class HolidayService {
|
|
constructor(
|
|
private readonly prisma: PrismaPostgresService,
|
|
) { }
|
|
|
|
/**
|
|
* Calculates the amount of hours to be paid for a given employee on the provided holiday date.
|
|
*
|
|
* @param employee_id id of the employee whom the holiday shift belongs to
|
|
* @param holiday_date date that the holiday was taken on
|
|
*
|
|
* @returns Average daily hours worked in the past four weeks as a `number`, to a maximum of `8`
|
|
*/
|
|
private async computeHoursPrevious4Weeks(
|
|
external_payroll_id: number,
|
|
company_code: number,
|
|
holiday_date: Date
|
|
): Promise<Result<number, string>> {
|
|
try {
|
|
const valid_codes = ['G1', 'G43', 'G48', 'G104', 'G105', 'G109', 'G140', 'G720'];
|
|
const holiday_week_start = getWeekStart(holiday_date);
|
|
const window_start = new Date(holiday_week_start.getTime() - 4 * MS_PER_WEEK);
|
|
const window_end = new Date(holiday_week_start.getTime() - 1);
|
|
|
|
const employee = await this.prisma.employees.findFirst({
|
|
where: { external_payroll_id, company_code }
|
|
});
|
|
|
|
if (!employee) return { success: false, error: 'EMPLOYEE_NOT_FOUND' };
|
|
|
|
const shifts = await this.prisma.shifts.findMany({
|
|
where: {
|
|
timesheet: { employee_id: employee.id },
|
|
date: { gte: window_start, lte: window_end },
|
|
bank_code: { bank_code: { in: valid_codes } },
|
|
},
|
|
select: { date: true, start_time: true, end_time: true },
|
|
});
|
|
|
|
const hours_by_week = new Map<number, number>();
|
|
for (const shift of shifts) {
|
|
const hours = computeHours(shift.start_time, shift.end_time);
|
|
|
|
if (hours <= 0) continue;
|
|
|
|
const shift_week_start = getWeekStart(shift.date);
|
|
const key = shift_week_start.getTime();
|
|
hours_by_week.set(key, (hours_by_week.get(key) ?? 0) + hours);
|
|
}
|
|
|
|
let capped_total = 0;
|
|
for (let offset = 1; offset <= 4; offset++) {
|
|
const week_start = new Date(holiday_week_start.getTime() - offset * MS_PER_WEEK);
|
|
const key = week_start.getTime();
|
|
const weekly_hours = hours_by_week.get(key) ?? 0;
|
|
capped_total += Math.min(weekly_hours, 40);
|
|
}
|
|
|
|
const average_daily_hours = capped_total / 20;
|
|
return { success: true, data: average_daily_hours };
|
|
} catch (error) {
|
|
console.error(error);
|
|
return { success: false, error: `an error occureded during holiday calculation` }
|
|
}
|
|
}
|
|
|
|
async calculateHolidayPay(
|
|
employeePayrollID: number,
|
|
companyCode: number,
|
|
holiday_date: Date
|
|
): Promise<Result<number, string>> {
|
|
const average_daily_hours = await this.computeHoursPrevious4Weeks(employeePayrollID, companyCode, holiday_date);
|
|
|
|
if (!average_daily_hours.success) return { success: false, error: average_daily_hours.error };
|
|
|
|
const daily_rate = (Math.min(average_daily_hours.data, 8));
|
|
return { success: true, data: daily_rate };
|
|
}
|
|
} |