TAT calculation enhanced

This commit is contained in:
laxmanhalaki 2025-11-07 10:11:27 +05:30
parent 1aa7fb9056
commit c76b799cf7
3 changed files with 137 additions and 13 deletions

View File

@ -365,8 +365,8 @@ export const updateConfiguration = async (req: Request, res: Response): Promise<
// If working hours config was updated, also clear working hours cache // 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']; const workingHoursKeys = ['WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY'];
if (workingHoursKeys.includes(configKey)) { if (workingHoursKeys.includes(configKey)) {
clearWorkingHoursCache(); await clearWorkingHoursCache();
logger.info(`[Admin] Working hours configuration '${configKey}' updated - cache cleared`); logger.info(`[Admin] Working hours configuration '${configKey}' updated - cache cleared and reloaded`);
} else { } else {
logger.info(`[Admin] Configuration '${configKey}' updated and cache cleared`); logger.info(`[Admin] Configuration '${configKey}' updated and cache cleared`);
} }
@ -407,8 +407,8 @@ export const resetConfiguration = async (req: Request, res: Response): Promise<v
// If working hours config was reset, also clear working hours cache // If working hours config was reset, also clear working hours cache
const workingHoursKeys = ['WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY']; const workingHoursKeys = ['WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY'];
if (workingHoursKeys.includes(configKey)) { if (workingHoursKeys.includes(configKey)) {
clearWorkingHoursCache(); await clearWorkingHoursCache();
logger.info(`[Admin] Working hours configuration '${configKey}' reset to default - cache cleared`); logger.info(`[Admin] Working hours configuration '${configKey}' reset to default - cache cleared and reloaded`);
} else { } else {
logger.info(`[Admin] Configuration '${configKey}' reset to default and cache cleared`); logger.info(`[Admin] Configuration '${configKey}' reset to default and cache cleared`);
} }

View File

@ -37,10 +37,10 @@ export async function getConfigValue(configKey: string, defaultValue: string = '
const value = (result[0] as any).config_value; const value = (result[0] as any).config_value;
configCache.set(configKey, value); configCache.set(configKey, value);
// Set cache expiry if not set // Always update cache expiry when loading from database
if (!cacheExpiry) { cacheExpiry = new Date(Date.now() + CACHE_DURATION_MS);
cacheExpiry = new Date(Date.now() + CACHE_DURATION_MS);
} logger.info(`[ConfigReader] Loaded config '${configKey}' = '${value}' from database (cached for 5min)`);
return value; return value;
} }

View File

@ -39,6 +39,8 @@ async function loadWorkingHoursCache(): Promise<void> {
}; };
workingHoursCacheExpiry = dayjs().add(5, 'minute').toDate(); 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) { } catch (error) {
console.error('[TAT] Error loading working hours:', error); console.error('[TAT] Error loading working hours:', error);
// Fallback to default values from TAT_CONFIG // Fallback to default values from TAT_CONFIG
@ -48,7 +50,7 @@ async function loadWorkingHoursCache(): Promise<void> {
startDay: TAT_CONFIG.WORK_START_DAY, startDay: TAT_CONFIG.WORK_START_DAY,
endDay: TAT_CONFIG.WORK_END_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 loadWorkingHoursCache();
await loadHolidaysCache(); 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; let remaining = hoursToAdd;
while (remaining > 0) { while (remaining > 0) {
@ -181,6 +214,19 @@ export async function addWorkingHoursExpress(start: Date | string, hoursToAdd: n
endDay: TAT_CONFIG.WORK_END_DAY 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; let remaining = hoursToAdd;
while (remaining > 0) { while (remaining > 0) {
@ -188,7 +234,7 @@ export async function addWorkingHoursExpress(start: Date | string, hoursToAdd: n
const hour = current.hour(); const hour = current.hour();
// For express: count ALL days (including weekends/holidays) // 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) { if (hour >= config.startHour && hour < config.endHour) {
remaining -= 1; remaining -= 1;
} }
@ -234,6 +280,32 @@ export function addWorkingHoursSync(start: Date | string, hoursToAdd: number): D
endDay: TAT_CONFIG.WORK_END_DAY 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; let remaining = hoursToAdd;
while (remaining > 0) { while (remaining > 0) {
@ -260,11 +332,15 @@ export async function initializeHolidaysCache(): Promise<void> {
/** /**
* Clear working hours cache (call when admin updates configuration) * 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<void> {
workingHoursCache = null; workingHoursCache = null;
workingHoursCacheExpiry = 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 loadWorkingHoursCache();
await loadHolidaysCache(); await loadHolidaysCache();
const start = dayjs(startDate); let start = dayjs(startDate);
const end = dayjs(endDateParam || new Date()); const end = dayjs(endDateParam || new Date());
// In test mode, use raw minutes for 1:1 conversion // In test mode, use raw minutes for 1:1 conversion
@ -447,6 +523,54 @@ export async function calculateElapsedWorkingHours(
endDay: TAT_CONFIG.WORK_END_DAY 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 totalWorkingMinutes = 0;
let currentDate = start.startOf('day'); let currentDate = start.startOf('day');
const endDay = end.startOf('day'); const endDay = end.startOf('day');