From 5f4fb3594a22641b9fb254bc1baff99f35869d1e Mon Sep 17 00:00:00 2001 From: Nic D Date: Fri, 13 Mar 2026 15:21:04 -0400 Subject: [PATCH] fix(pay-period, timesheet): fix issue where overtime wasn't calculated properly. Also fix issue where employee overviews would sometimes falsely return as approved if employee had no timesheets created yet. This is due to Array.prototype.every returning true on an empty array. --- .../pay-periods-build-overview.service.ts | 4 +- .../timesheets/timesheet.mapper.ts | 57 +++++++------------ 2 files changed, 23 insertions(+), 38 deletions(-) diff --git a/src/time-and-attendance/pay-period/services/pay-periods-build-overview.service.ts b/src/time-and-attendance/pay-period/services/pay-periods-build-overview.service.ts index 1989943..7c7eca4 100644 --- a/src/time-and-attendance/pay-period/services/pay-periods-build-overview.service.ts +++ b/src/time-and-attendance/pay-period/services/pay-periods-build-overview.service.ts @@ -211,7 +211,9 @@ export class GetOverviewService { cutoff_date.setDate(cutoff_date.getDate() + 14); const is_active = employee.last_work_day ? employee.last_work_day.getTime() >= cutoff_date.getTime() : true; - record.is_approved = timesheets.every(timesheet => timesheet.is_approved === true); + if (timesheets.length > 0) + record.is_approved = timesheets.every(timesheet => timesheet.is_approved); + record.is_active = is_active; } diff --git a/src/time-and-attendance/timesheets/timesheet.mapper.ts b/src/time-and-attendance/timesheets/timesheet.mapper.ts index ab91fee..3f1ef56 100644 --- a/src/time-and-attendance/timesheets/timesheet.mapper.ts +++ b/src/time-and-attendance/timesheets/timesheet.mapper.ts @@ -73,53 +73,31 @@ export const mapOneTimesheet = ( const daily_hours = emptyHours(); const daily_expenses = emptyExpenses(); - //totals by shift types + //daily totals by shift types 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; - } + daily_hours[subgroup] += hours; + weekly_hours[subgroup] += hours; } - + // const dailyOvertimeOwed = Math.max(daily_hours.regular - timesheet.employee.daily_expected_hours, 0) + // daily_hours.overtime = dailyOvertimeOwed; + // daily_hours.regular -= dailyOvertimeOwed; //totals by expense types for (const expense of expenses_source) { const subgroup = expenseSubgroupFromBankCode(expense.bank_code); if (subgroup === 'mileage') { - const mileage = num(expense.mileage); - daily_expenses.mileage += mileage; - weekly_expenses.mileage += mileage; - } else if (subgroup === 'per_diem') { - const amount = num(expense.amount); - daily_expenses.per_diem += amount; - weekly_expenses.per_diem += amount; - } else if (subgroup === 'on_call') { - const amount = num(expense.amount); - daily_expenses.on_call += amount; - weekly_expenses.on_call += amount; + daily_expenses.mileage += Number(expense.mileage); + weekly_expenses.mileage += Number(expense.mileage); } else { - const amount = num(expense.amount); - daily_expenses.expenses += amount; - weekly_expenses.expenses += amount; + daily_expenses.expenses[subgroup] += Number(expense.amount); + weekly_expenses.expenses[subgroup] += Number(expense.amount); } - } + } + return { date: date_iso, shifts, @@ -129,6 +107,14 @@ export const mapOneTimesheet = ( }; }); + const totalWeekHoursWorked = weekly_hours.regular + + weekly_hours.evening + + weekly_hours.emergency + + weekly_hours.holiday + + weekly_hours.vacation + + weekly_hours.overtime = Math.max(totalWeekHoursWorked - 40, 0); + return { timesheet_id: timesheet.id, is_approved: timesheet.is_approved ?? false, @@ -149,9 +135,6 @@ const diffOfHours = (a: Date, b: Date): number => { return Math.max(0, Math.round((ms / 36e5) * 1000) / 1000); } -//validate numeric values -const num = (value: any): number => { return value ? Number(value) : 0 }; - // shift's subgroup types const hoursSubGroupFromBankCode = (bank_code: BankCodeDto): keyof TotalHours => { const type = bank_code.type;