From 45d9049d27056cf055bcedca466eb438a497a6ca Mon Sep 17 00:00:00 2001 From: chamikaJ Date: Mon, 2 Jun 2025 17:10:37 +0530 Subject: [PATCH] feat(reporting-allocation): enhance working hours calculation and localization updates - Improved the logic for calculating total working hours, introducing a minimal baseline for non-working days to ensure accurate utilization metrics. - Updated individual member calculations to reflect over-utilization during non-working periods. - Added new localization keys for "Overtime Work" and "Review work-life balance policies" in English, Spanish, and Portuguese time report JSON files to enhance user experience. --- .../reporting-allocation-controller.ts | 28 ++++++++++++++++--- .../public/locales/en/time-report.json | 4 ++- .../public/locales/es/time-report.json | 4 ++- .../public/locales/pt/time-report.json | 4 ++- 4 files changed, 33 insertions(+), 7 deletions(-) diff --git a/worklenz-backend/src/controllers/reporting/reporting-allocation-controller.ts b/worklenz-backend/src/controllers/reporting/reporting-allocation-controller.ts index 0173966f..2127d00a 100644 --- a/worklenz-backend/src/controllers/reporting/reporting-allocation-controller.ts +++ b/worklenz-backend/src/controllers/reporting/reporting-allocation-controller.ts @@ -497,7 +497,19 @@ export default class ReportingAllocationController extends ReportingControllerBa const orgWorkingHoursQuery = `SELECT working_hours FROM organizations WHERE id = (SELECT t.organization_id FROM teams t WHERE t.id IN (${teamIds}) LIMIT 1)`; const orgWorkingHoursResult = await db.query(orgWorkingHoursQuery, []); const orgWorkingHours = orgWorkingHoursResult.rows[0]?.working_hours || 8; + + // Calculate total working hours with minimum baseline for non-working day scenarios let totalWorkingHours = workingDays * orgWorkingHours; + let isNonWorkingPeriod = false; + + // If no working days but there might be logged time, set minimum baseline + // This ensures that time logged on non-working days is treated as over-utilization + // Business Logic: If someone works on weekends/holidays when workingDays = 0, + // we use a minimal baseline (1 hour) so any logged time results in >100% utilization + if (totalWorkingHours === 0) { + totalWorkingHours = 1; // Minimal baseline to ensure over-utilization + isNonWorkingPeriod = true; + } const archivedClause = archived ? "" @@ -586,14 +598,22 @@ export default class ReportingAllocationController extends ReportingControllerBa const member = result.rows[i]; const loggedSeconds = member.logged_time ? parseFloat(member.logged_time) : 0; const utilizedHours = loggedSeconds / 3600; - const utilizationPercent = totalWorkingSeconds > 0 && loggedSeconds - ? ((loggedSeconds / totalWorkingSeconds) * 100) + + // For individual members, use the same logic as total calculation + let memberWorkingHours = totalWorkingHours; + if (isNonWorkingPeriod && loggedSeconds > 0) { + // Any time logged during non-working period should be treated as over-utilization + memberWorkingHours = Math.min(utilizedHours, 1); // Use actual time or 1 hour, whichever is smaller + } + + const utilizationPercent = memberWorkingHours > 0 && loggedSeconds + ? ((loggedSeconds / (memberWorkingHours * 3600)) * 100) : 0; - const overUnder = utilizedHours - totalWorkingHours; + const overUnder = utilizedHours - memberWorkingHours; member.value = utilizedHours ? parseFloat(utilizedHours.toFixed(2)) : 0; member.color_code = getColor(member.name); - member.total_working_hours = totalWorkingHours; + member.total_working_hours = memberWorkingHours; member.utilization_percent = utilizationPercent.toFixed(2); member.utilized_hours = utilizedHours.toFixed(2); member.over_under_utilized_hours = overUnder.toFixed(2); diff --git a/worklenz-frontend/public/locales/en/time-report.json b/worklenz-frontend/public/locales/en/time-report.json index fd5ee164..5d73c247 100644 --- a/worklenz-frontend/public/locales/en/time-report.json +++ b/worklenz-frontend/public/locales/en/time-report.json @@ -58,5 +58,7 @@ "underCapacity": "Under capacity", "considerWorkloadRedistribution": "Consider workload redistribution", "capacityAvailableForNewProjects": "Capacity available for new projects", - "targetRange": "Target: 90-110%" + "targetRange": "Target: 90-110%", + "overtimeWork": "Overtime Work", + "reviewWorkLifeBalance": "Review work-life balance policies" } diff --git a/worklenz-frontend/public/locales/es/time-report.json b/worklenz-frontend/public/locales/es/time-report.json index eb523cf3..03fdd744 100644 --- a/worklenz-frontend/public/locales/es/time-report.json +++ b/worklenz-frontend/public/locales/es/time-report.json @@ -58,5 +58,7 @@ "underCapacity": "Bajo capacidad", "considerWorkloadRedistribution": "Considerar redistribución de carga de trabajo", "capacityAvailableForNewProjects": "Capacidad disponible para nuevos proyectos", - "targetRange": "Objetivo: 90-110%" + "targetRange": "Objetivo: 90-110%", + "overtimeWork": "Trabajo de Horas Extras", + "reviewWorkLifeBalance": "Revisar políticas de equilibrio trabajo-vida" } diff --git a/worklenz-frontend/public/locales/pt/time-report.json b/worklenz-frontend/public/locales/pt/time-report.json index f6adb098..97679df8 100644 --- a/worklenz-frontend/public/locales/pt/time-report.json +++ b/worklenz-frontend/public/locales/pt/time-report.json @@ -58,5 +58,7 @@ "underCapacity": "Abaixo da capacidade", "considerWorkloadRedistribution": "Considerar redistribuição da carga de trabalho", "capacityAvailableForNewProjects": "Capacidade disponível para novos projetos", - "targetRange": "Meta: 90-110%" + "targetRange": "Meta: 90-110%", + "overtimeWork": "Trabalho de Horas Extras", + "reviewWorkLifeBalance": "Revisar políticas de equilíbrio trabalho-vida" }