feat(timesheet-approval): add overtime calculation for overview and timesheet details

This commit is contained in:
Nicolas Drolet 2026-01-13 16:27:27 -05:00
parent 8f93c2b0f7
commit fa64b7d919
3 changed files with 74 additions and 29 deletions

View File

@ -3,7 +3,6 @@ import { UserMessageDto } from 'src/chatbot/dtos/user-message.dto';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
import { PageContextDto } from 'src/chatbot/dtos/page-context.dto';
import { UserDto } from 'src/identity-and-account/users-management/user.dto';
import { Message } from 'src/chatbot/dtos/dialog-message.dto';
@Injectable()
@ -19,8 +18,6 @@ export class ChatbotService {
),
);
console.log('chatbot response: ', response);
const cleanText =
Array.isArray(response.data) && response.data[0]?.output
? response.data[0].output

View File

@ -1,6 +1,7 @@
import { Injectable } from "@nestjs/common";
import { Prisma } from "@prisma/client";
import { Result } from "src/common/errors/result-error.factory";
import { computeHours, computePeriod, toDateFromString } from "src/common/utils/date-utils";
import { computeHours, computePeriod, sevenDaysFrom, toDateFromString, toStringFromDate } from "src/common/utils/date-utils";
import { PrismaService } from "src/prisma/prisma.service";
import { EmployeePeriodOverviewDto, Overview, PayPeriodOverviewDto } from "src/time-and-attendance/pay-period/dtos/overview-pay-period.dto";
@ -62,12 +63,15 @@ export class GetOverviewService {
select: {
id: true,
is_approved: true,
start_date: true,
shift: {
select: {
start_time: true,
end_time: true,
date: true,
bank_code: { select: { type: true } },
},
orderBy: { date: 'asc' },
},
expense: {
select: {
@ -117,37 +121,60 @@ export class GetOverviewService {
for (const timesheet of employee.timesheet) {
let total_weekly_hours: number = 0;
let daily_hours: number = 0;
let previous_shift_date: string = '';
//totals by types for shifts
for (const shift of timesheet.shift) {
const hours = computeHours(shift.start_time, shift.end_time);
const type = (shift.bank_code?.type ?? '').toUpperCase();
if (previous_shift_date !== toStringFromDate(shift.date)) {
previous_shift_date = toStringFromDate(shift.date);
daily_hours = 0;
}
switch (type) {
case "EVENING": record.other_hours.evening_hours += hours;
case "EVENING":
if (total_weekly_hours + hours <= 40) {
record.other_hours.evening_hours += Math.min(hours, 8 - daily_hours);
record.other_hours.overtime_hours += Math.max(daily_hours + hours - 8, 0);
} else {
record.other_hours.evening_hours += Math.max(40 - total_weekly_hours, 0);
record.other_hours.overtime_hours += Math.min(total_weekly_hours + hours - 40, hours);
}
total_weekly_hours += hours;
record.total_hours += hours;
break;
case "EMERGENCY":
record.other_hours.emergency_hours += hours;
record.total_hours += hours;
total_weekly_hours += hours;
break;
case "EMERGENCY": record.other_hours.emergency_hours += hours;
record.total_hours += hours;
total_weekly_hours += hours;
case "SICK":
record.other_hours.sick_hours += hours;
break;
case "OVERTIME": record.other_hours.overtime_hours += hours;
record.total_hours += hours;
total_weekly_hours += hours;
break;
case "SICK": record.other_hours.sick_hours += hours;
break;
case "HOLIDAY": record.other_hours.holiday_hours += hours;
case "HOLIDAY":
record.other_hours.holiday_hours += hours;
record.total_hours += hours;
total_weekly_hours += hours;
break;
case "VACATION": record.other_hours.vacation_hours += hours;
break;
case "REGULAR": record.regular_hours += hours;
record.total_hours += hours;
case "REGULAR":
if (total_weekly_hours + hours <= 40) {
record.regular_hours += Math.min(hours, 8 - daily_hours);
record.other_hours.overtime_hours += Math.max(daily_hours + hours - 8, 0);
} else {
record.regular_hours += Math.max(40 - total_weekly_hours, 0);
record.other_hours.overtime_hours += Math.min(total_weekly_hours + hours - 40, hours);
}
total_weekly_hours += hours;
record.total_hours += hours;
break;
}
daily_hours += hours;
}
//totals by type for expenses
for (const expense of timesheet.expense) {

View File

@ -74,9 +74,27 @@ export const mapOneTimesheet = async (timesheet: Prisma.TimesheetsGetPayload<{
for (const shift of shifts_source) {
const hours = diffOfHours(shift.start_time, shift.end_time);
const subgroup = hoursSubGroupFromBankCode(shift.bank_code);
const worked_weekly_hours = weekly_hours.regular + weekly_hours.emergency + weekly_hours.banking + weekly_hours.evening + weekly_hours.overtime + weekly_hours.holiday;
if ((worked_weekly_hours + hours <= 40) && (subgroup === 'regular' || subgroup === 'evening')) {
daily_hours['overtime'] += Math.max(daily_hours[subgroup] + hours - 8, 0);
weekly_hours['overtime'] += Math.max(daily_hours[subgroup] + hours - 8, 0);
weekly_hours[subgroup] += Math.min(hours, 8 - daily_hours[subgroup]);
daily_hours[subgroup] += Math.min(hours, 8 - daily_hours[subgroup]);
} else if (subgroup === 'regular' || subgroup === 'evening') {
daily_hours[subgroup] += Math.max((40 - worked_weekly_hours), 0);
daily_hours['overtime'] += Math.min((worked_weekly_hours + hours) - 40, hours);
weekly_hours[subgroup] += Math.max((40 - worked_weekly_hours), 0);
weekly_hours['overtime'] += Math.min((worked_weekly_hours + hours) - 40, hours);
} else {
daily_hours[subgroup] += hours;
weekly_hours[subgroup] += hours;
}
}
//totals by expense types
for (const expense of expenses_source) {
@ -108,6 +126,9 @@ export const mapOneTimesheet = async (timesheet: Prisma.TimesheetsGetPayload<{
};
});
weekly_hours['overtime'] = Math.max(weekly_hours['regular'] - 40, 0);
weekly_hours['regular'] = Math.min(weekly_hours['regular'], 40);
return {
timesheet_id: timesheet.id,
is_approved: timesheet.is_approved ?? false,