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.
This commit is contained in:
chamikaJ
2025-06-02 17:10:37 +05:30
parent 3f7b969e44
commit 45d9049d27
4 changed files with 33 additions and 7 deletions

View File

@@ -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 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 orgWorkingHoursResult = await db.query(orgWorkingHoursQuery, []);
const orgWorkingHours = orgWorkingHoursResult.rows[0]?.working_hours || 8; 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 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 const archivedClause = archived
? "" ? ""
@@ -586,14 +598,22 @@ export default class ReportingAllocationController extends ReportingControllerBa
const member = result.rows[i]; const member = result.rows[i];
const loggedSeconds = member.logged_time ? parseFloat(member.logged_time) : 0; const loggedSeconds = member.logged_time ? parseFloat(member.logged_time) : 0;
const utilizedHours = loggedSeconds / 3600; 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; : 0;
const overUnder = utilizedHours - totalWorkingHours; const overUnder = utilizedHours - memberWorkingHours;
member.value = utilizedHours ? parseFloat(utilizedHours.toFixed(2)) : 0; member.value = utilizedHours ? parseFloat(utilizedHours.toFixed(2)) : 0;
member.color_code = getColor(member.name); member.color_code = getColor(member.name);
member.total_working_hours = totalWorkingHours; member.total_working_hours = memberWorkingHours;
member.utilization_percent = utilizationPercent.toFixed(2); member.utilization_percent = utilizationPercent.toFixed(2);
member.utilized_hours = utilizedHours.toFixed(2); member.utilized_hours = utilizedHours.toFixed(2);
member.over_under_utilized_hours = overUnder.toFixed(2); member.over_under_utilized_hours = overUnder.toFixed(2);

View File

@@ -58,5 +58,7 @@
"underCapacity": "Under capacity", "underCapacity": "Under capacity",
"considerWorkloadRedistribution": "Consider workload redistribution", "considerWorkloadRedistribution": "Consider workload redistribution",
"capacityAvailableForNewProjects": "Capacity available for new projects", "capacityAvailableForNewProjects": "Capacity available for new projects",
"targetRange": "Target: 90-110%" "targetRange": "Target: 90-110%",
"overtimeWork": "Overtime Work",
"reviewWorkLifeBalance": "Review work-life balance policies"
} }

View File

@@ -58,5 +58,7 @@
"underCapacity": "Bajo capacidad", "underCapacity": "Bajo capacidad",
"considerWorkloadRedistribution": "Considerar redistribución de carga de trabajo", "considerWorkloadRedistribution": "Considerar redistribución de carga de trabajo",
"capacityAvailableForNewProjects": "Capacidad disponible para nuevos proyectos", "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"
} }

View File

@@ -58,5 +58,7 @@
"underCapacity": "Abaixo da capacidade", "underCapacity": "Abaixo da capacidade",
"considerWorkloadRedistribution": "Considerar redistribuição da carga de trabalho", "considerWorkloadRedistribution": "Considerar redistribuição da carga de trabalho",
"capacityAvailableForNewProjects": "Capacidade disponível para novos projetos", "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"
} }