From c76b799cf743cfac8b50ec7d8f932039da301729 Mon Sep 17 00:00:00 2001 From: laxmanhalaki Date: Fri, 7 Nov 2025 10:11:27 +0530 Subject: [PATCH] TAT calculation enhanced --- src/controllers/admin.controller.ts | 8 +- src/services/configReader.service.ts | 8 +- src/utils/tatTimeUtils.ts | 134 ++++++++++++++++++++++++++- 3 files changed, 137 insertions(+), 13 deletions(-) diff --git a/src/controllers/admin.controller.ts b/src/controllers/admin.controller.ts index 0ecb1db..022363f 100644 --- a/src/controllers/admin.controller.ts +++ b/src/controllers/admin.controller.ts @@ -365,8 +365,8 @@ export const updateConfiguration = async (req: Request, res: Response): Promise< // If working hours config was updated, also clear working hours cache const workingHoursKeys = ['WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY']; if (workingHoursKeys.includes(configKey)) { - clearWorkingHoursCache(); - logger.info(`[Admin] Working hours configuration '${configKey}' updated - cache cleared`); + await clearWorkingHoursCache(); + logger.info(`[Admin] Working hours configuration '${configKey}' updated - cache cleared and reloaded`); } else { logger.info(`[Admin] Configuration '${configKey}' updated and cache cleared`); } @@ -407,8 +407,8 @@ export const resetConfiguration = async (req: Request, res: Response): Promise { }; workingHoursCacheExpiry = dayjs().add(5, 'minute').toDate(); + console.log(`[TAT Utils] ✅ Working hours loaded from admin config: ${hours.startHour}:00 - ${hours.endHour}:00 (Days: ${startDay}-${endDay})`); + } catch (error) { console.error('[TAT] Error loading working hours:', error); // Fallback to default values from TAT_CONFIG @@ -48,7 +50,7 @@ async function loadWorkingHoursCache(): Promise { startDay: TAT_CONFIG.WORK_START_DAY, endDay: TAT_CONFIG.WORK_END_DAY }; - console.log('[TAT Utils] Using fallback working hours from TAT_CONFIG'); + console.log(`[TAT Utils] ⚠️ Using fallback working hours from system config: ${TAT_CONFIG.WORK_START_HOUR}:00 - ${TAT_CONFIG.WORK_END_HOUR}:00`); } } @@ -144,6 +146,37 @@ export async function addWorkingHours(start: Date | string, hoursToAdd: number): await loadWorkingHoursCache(); await loadHolidaysCache(); + const config = workingHoursCache || { + startHour: TAT_CONFIG.WORK_START_HOUR, + endHour: TAT_CONFIG.WORK_END_HOUR, + startDay: TAT_CONFIG.WORK_START_DAY, + endDay: TAT_CONFIG.WORK_END_DAY + }; + + // If start time is before working hours or outside working days/holidays, + // advance to the next working hour start (reset to clean hour) + const originalStart = current.format('YYYY-MM-DD HH:mm:ss'); + const wasOutsideWorkingHours = !isWorkingTime(current); + + while (!isWorkingTime(current)) { + const hour = current.hour(); + const day = current.day(); + + // If before work start hour on a working day, jump to work start hour + if (day >= config.startDay && day <= config.endDay && !isHoliday(current) && hour < config.startHour) { + current = current.hour(config.startHour); + } else { + // After working hours or non-working day - advance to next working period + current = current.add(1, 'hour'); + } + } + + // If start time was outside working hours, reset to clean work start time (no minutes) + if (wasOutsideWorkingHours) { + current = current.minute(0).second(0).millisecond(0); + console.log(`[TAT Utils] Start time ${originalStart} was outside working hours, advanced to ${current.format('YYYY-MM-DD HH:mm:ss')}`); + } + let remaining = hoursToAdd; while (remaining > 0) { @@ -181,6 +214,19 @@ export async function addWorkingHoursExpress(start: Date | string, hoursToAdd: n endDay: TAT_CONFIG.WORK_END_DAY }; + // If start time is outside working hours, advance to work start hour (reset to clean hour) + const originalStart = current.format('YYYY-MM-DD HH:mm:ss'); + const currentHour = current.hour(); + if (currentHour < config.startHour) { + // Before working hours - reset to clean work start + current = current.hour(config.startHour).minute(0).second(0).millisecond(0); + console.log(`[TAT Utils Express] Start time ${originalStart} was before working hours, advanced to ${current.format('YYYY-MM-DD HH:mm:ss')}`); + } else if (currentHour >= config.endHour) { + // After working hours - reset to clean start of next day + current = current.add(1, 'day').hour(config.startHour).minute(0).second(0).millisecond(0); + console.log(`[TAT Utils Express] Start time ${originalStart} was after working hours, advanced to ${current.format('YYYY-MM-DD HH:mm:ss')}`); + } + let remaining = hoursToAdd; while (remaining > 0) { @@ -188,7 +234,7 @@ export async function addWorkingHoursExpress(start: Date | string, hoursToAdd: n const hour = current.hour(); // For express: count ALL days (including weekends/holidays) - // But only during working hours (9 AM - 6 PM) + // But only during working hours (configured start - end hour) if (hour >= config.startHour && hour < config.endHour) { remaining -= 1; } @@ -234,6 +280,32 @@ export function addWorkingHoursSync(start: Date | string, hoursToAdd: number): D endDay: TAT_CONFIG.WORK_END_DAY }; + // If start time is before working hours or outside working days, + // advance to the next working hour start (reset to clean hour) + const originalStart = current.format('YYYY-MM-DD HH:mm:ss'); + let hour = current.hour(); + let day = current.day(); + + // Check if originally outside working hours + const wasOutsideWorkingHours = !(day >= config.startDay && day <= config.endDay && hour >= config.startHour && hour < config.endHour); + + // If before work start hour on a working day, jump to work start hour + if (day >= config.startDay && day <= config.endDay && hour < config.startHour) { + current = current.hour(config.startHour); + } else { + // Advance to next working hour + while (!(day >= config.startDay && day <= config.endDay && hour >= config.startHour && hour < config.endHour)) { + current = current.add(1, 'hour'); + day = current.day(); + hour = current.hour(); + } + } + + // If start time was outside working hours, reset to clean work start time + if (wasOutsideWorkingHours) { + current = current.minute(0).second(0).millisecond(0); + } + let remaining = hoursToAdd; while (remaining > 0) { @@ -260,11 +332,15 @@ export async function initializeHolidaysCache(): Promise { /** * Clear working hours cache (call when admin updates configuration) + * Also immediately reloads the cache with new values */ -export function clearWorkingHoursCache(): void { +export async function clearWorkingHoursCache(): Promise { workingHoursCache = null; workingHoursCacheExpiry = null; - // Cache cleared + console.log('[TAT Utils] Working hours cache cleared - reloading from database...'); + + // Immediately reload the cache with new values + await loadWorkingHoursCache(); } /** @@ -432,7 +508,7 @@ export async function calculateElapsedWorkingHours( await loadWorkingHoursCache(); await loadHolidaysCache(); - const start = dayjs(startDate); + let start = dayjs(startDate); const end = dayjs(endDateParam || new Date()); // In test mode, use raw minutes for 1:1 conversion @@ -447,6 +523,54 @@ export async function calculateElapsedWorkingHours( endDay: TAT_CONFIG.WORK_END_DAY }; + // CRITICAL FIX: If start time is outside working hours, advance to next working period + // This ensures we only count elapsed time when TAT is actually running + const originalStart = start.format('YYYY-MM-DD HH:mm:ss'); + + // For standard priority, check working days and hours + if (priority !== 'express') { + const wasOutsideWorkingHours = !isWorkingTime(start); + + while (!isWorkingTime(start)) { + const hour = start.hour(); + const day = start.day(); + + // If before work start hour on a working day, jump to work start hour + if (day >= config.startDay && day <= config.endDay && !isHoliday(start) && hour < config.startHour) { + start = start.hour(config.startHour); + } else { + // Otherwise, advance by 1 hour and check again + start = start.add(1, 'hour'); + } + } + + // If start time was outside working hours, reset to clean work start time + if (wasOutsideWorkingHours) { + start = start.minute(0).second(0).millisecond(0); + } + } else { + // For express priority, only check working hours (not days) + const hour = start.hour(); + if (hour < config.startHour) { + // Before hours - reset to clean start + start = start.hour(config.startHour).minute(0).second(0).millisecond(0); + } else if (hour >= config.endHour) { + // After hours - reset to clean start of next day + start = start.add(1, 'day').hour(config.startHour).minute(0).second(0).millisecond(0); + } + } + + // Log if we advanced the start time for elapsed calculation + if (start.format('YYYY-MM-DD HH:mm:ss') !== originalStart) { + console.log(`[TAT Utils] Elapsed time calculation: Start ${originalStart} was outside working hours, advanced to ${start.format('YYYY-MM-DD HH:mm:ss')}`); + } + + // If end time is before adjusted start time, return 0 (TAT hasn't started yet) + if (end.isBefore(start)) { + console.log(`[TAT Utils] Current time is before TAT start time - elapsed hours: 0`); + return 0; + } + let totalWorkingMinutes = 0; let currentDate = start.startOf('day'); const endDay = end.startOf('day');