# Dynamic Working Hours Configuration ## Overview Working hours for TAT (Turn Around Time) calculations are now **dynamically configurable** through the admin settings interface. Admins can change these settings at any time, and the changes will be reflected in all future TAT calculations. --- ## What's Configurable ### **Working Hours Settings:** | Setting | Description | Default | Example | |---------|-------------|---------|---------| | `WORK_START_HOUR` | Working day starts at (hour) | 9 | 8 (8:00 AM) | | `WORK_END_HOUR` | Working day ends at (hour) | 18 | 19 (7:00 PM) | | `WORK_START_DAY` | First working day of week | 1 (Monday) | 1 (Monday) | | `WORK_END_DAY` | Last working day of week | 5 (Friday) | 6 (Saturday) | **Days:** 0 = Sunday, 1 = Monday, 2 = Tuesday, ..., 6 = Saturday --- ## How It Works ### **1. Admin Changes Working Hours** ``` Settings → System Configuration → Working Hours - Work Start Hour: 9:00 → Change to 8:00 - Work End Hour: 18:00 → Change to 20:00 ✅ Save ``` ### **2. Backend Updates Database** ```sql UPDATE admin_configurations SET config_value = '8' WHERE config_key = 'WORK_START_HOUR'; UPDATE admin_configurations SET config_value = '20' WHERE config_key = 'WORK_END_HOUR'; ``` ### **3. Cache is Cleared Automatically** ```typescript // In admin.controller.ts clearConfigCache(); // Clear general config cache clearWorkingHoursCache(); // Clear TAT working hours cache ``` ### **4. Next TAT Calculation Uses New Values** ```typescript // TAT calculation loads fresh values await loadWorkingHoursCache(); // → Reads: startHour=8, endHour=20 from database // Applies new working hours if (hour >= 8 && hour < 20) { // This hour counts as working time ✅ } ``` --- ## Cache Management ### **Working Hours Cache:** **Cache Duration:** 5 minutes (shorter than holidays since it's more critical) **Why Cache?** - Performance: Avoids repeated database queries - Speed: TAT calculations can happen hundreds of times per hour - Efficiency: Reading from memory is ~1000x faster than DB query **Cache Lifecycle:** ``` 1. First TAT Calculation: → loadWorkingHoursCache() called → Database query: SELECT config_value WHERE config_key IN (...) → Store in memory: workingHoursCache = { startHour: 9, endHour: 18, ... } → Set expiry: now + 5 minutes 2. Next 5 Minutes (Cache Valid): → All TAT calculations use cached values → No database queries ✅ FAST 3. After 5 Minutes (Cache Expired): → Next TAT calculation reloads from database → New cache created with 5-minute expiry 4. Admin Updates Config: → clearWorkingHoursCache() called immediately → Cache invalidated → Next calculation loads fresh values ✅ ``` --- ## Example Scenarios ### **Scenario 1: Extend Working Hours** **Before:** ``` Working Hours: 9:00 AM - 6:00 PM (9 hours/day) ``` **Admin Changes To:** ``` Working Hours: 8:00 AM - 8:00 PM (12 hours/day) ``` **Impact on TAT:** ``` Request: STANDARD Priority, 24 working hours Created: Monday 9:00 AM OLD Calculation (9 hours/day): Monday 9 AM - 6 PM = 9 hours (15h remaining) Tuesday 9 AM - 6 PM = 9 hours (6h remaining) Wednesday 9 AM - 3 PM = 6 hours (0h remaining) Deadline: Wednesday 3:00 PM NEW Calculation (12 hours/day): Monday 9 AM - 8 PM = 11 hours (13h remaining) Tuesday 8 AM - 8 PM = 12 hours (1h remaining) Wednesday 8 AM - 9 AM = 1 hour (0h remaining) Deadline: Wednesday 9:00 AM ✅ FASTER! ``` --- ### **Scenario 2: Include Saturday as Working Day** **Before:** ``` Working Days: Monday - Friday (1-5) ``` **Admin Changes To:** ``` Working Days: Monday - Saturday (1-6) ``` **Impact on TAT:** ``` Request: STANDARD Priority, 16 working hours Created: Friday 2:00 PM OLD Calculation (Mon-Fri only): Friday 2 PM - 6 PM = 4 hours (12h remaining) Saturday-Sunday = SKIPPED Monday 9 AM - 6 PM = 9 hours (3h remaining) Tuesday 9 AM - 12 PM = 3 hours (0h remaining) Deadline: Tuesday 12:00 PM NEW Calculation (Mon-Sat): Friday 2 PM - 6 PM = 4 hours (12h remaining) Saturday 9 AM - 6 PM = 9 hours (3h remaining) ✅ Saturday counts! Sunday = SKIPPED Monday 9 AM - 12 PM = 3 hours (0h remaining) Deadline: Monday 12:00 PM ✅ EARLIER! ``` --- ### **Scenario 3: Reduce Working Hours (After-Hours Emergency)** **Before:** ``` Working Hours: 9:00 AM - 6:00 PM ``` **Admin Changes To:** ``` Working Hours: 9:00 AM - 10:00 PM (extended for emergency) ``` **Impact:** ``` Request created at 7:00 PM (after old hours but within new hours) OLD System: 7:00 PM → Not working time First working hour: Tomorrow 9:00 AM TAT starts counting from tomorrow ❌ NEW System: 7:00 PM → Still working time! ✅ TAT starts counting immediately Faster response for urgent requests ✅ ``` --- ## Implementation Details ### **Configuration Reader Service** ```typescript // Re_Backend/src/services/configReader.service.ts export async function getWorkingHours(): Promise<{ startHour: number; endHour: number }> { const startHour = await getConfigNumber('WORK_START_HOUR', 9); const endHour = await getConfigNumber('WORK_END_HOUR', 18); return { startHour, endHour }; } ``` ### **TAT Time Utils (Working Hours Cache)** ```typescript // Re_Backend/src/utils/tatTimeUtils.ts let workingHoursCache: WorkingHoursConfig | null = null; let workingHoursCacheExpiry: Date | null = null; async function loadWorkingHoursCache(): Promise { // Check if cache is still valid if (workingHoursCacheExpiry && new Date() < workingHoursCacheExpiry) { return; // Use cached values } // Load from database const { getWorkingHours, getConfigNumber } = await import('../services/configReader.service'); const hours = await getWorkingHours(); const startDay = await getConfigNumber('WORK_START_DAY', 1); const endDay = await getConfigNumber('WORK_END_DAY', 5); // Store in cache workingHoursCache = { startHour: hours.startHour, endHour: hours.endHour, startDay: startDay, endDay: endDay }; // Set 5-minute expiry workingHoursCacheExpiry = dayjs().add(5, 'minute').toDate(); console.log(`[TAT Utils] Loaded working hours: ${hours.startHour}:00-${hours.endHour}:00`); } function isWorkingTime(date: Dayjs): boolean { // Use cached working hours (with fallback to defaults) const config = workingHoursCache || { startHour: 9, endHour: 18, startDay: 1, endDay: 5 }; const day = date.day(); const hour = date.hour(); // Check based on configured values if (day < config.startDay || day > config.endDay) return false; if (hour < config.startHour || hour >= config.endHour) return false; if (isHoliday(date)) return false; return true; } ``` ### **Admin Controller (Cache Invalidation)** ```typescript // Re_Backend/src/controllers/admin.controller.ts export const updateConfiguration = async (req: Request, res: Response): Promise => { // ... update database ... // Clear config cache clearConfigCache(); // If working hours config was updated, also clear TAT cache const workingHoursKeys = ['WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY']; if (workingHoursKeys.includes(configKey)) { clearWorkingHoursCache(); // ✅ Immediate cache clear logger.info(`Working hours config '${configKey}' updated - cache cleared`); } res.json({ success: true }); }; ``` --- ## Priority Behavior ### **STANDARD Priority** ✅ **Uses configured working hours** - Respects `WORK_START_HOUR` and `WORK_END_HOUR` - Respects `WORK_START_DAY` and `WORK_END_DAY` - Excludes holidays **Example:** ``` Config: 9:00 AM - 6:00 PM, Monday-Friday TAT: 16 working hours → Only hours between 9 AM - 6 PM on Mon-Fri count → Weekends and holidays are skipped ``` ### **EXPRESS Priority** ❌ **Ignores working hours configuration** - Counts ALL 24 hours per day - Counts ALL 7 days per week - Counts holidays **Example:** ``` Config: 9:00 AM - 6:00 PM (ignored) TAT: 16 hours → Simply add 16 hours to start time → No exclusions ``` --- ## Testing Scenarios ### **Test 1: Change Working Hours, Create Request** ```bash # 1. Check current working hours curl http://localhost:5000/api/v1/admin/configurations \ | grep WORK_START_HOUR # → Returns: "configValue": "9" # 2. Update working hours to start at 8:00 AM curl -X PUT http://localhost:5000/api/v1/admin/configurations/WORK_START_HOUR \ -H "Authorization: Bearer TOKEN" \ -d '{"configValue": "8"}' # → Response: "Configuration updated successfully" # 3. Check logs # → Should see: "Working hours configuration 'WORK_START_HOUR' updated - cache cleared" # 4. Create new STANDARD request curl -X POST http://localhost:5000/api/v1/workflows \ -d '{"priority": "STANDARD", "tatHours": 16}' # 5. Check TAT calculation logs # → Should see: "Loaded working hours: 8:00-18:00" ✅ # → Deadline calculation uses new hours ✅ ``` ### **Test 2: Verify Cache Expiry** ```bash # 1. Create request (loads working hours into cache) # → Cache expires in 5 minutes # 2. Wait 6 minutes # 3. Create another request # → Should see log: "Loaded working hours: ..." (cache reloaded) # 4. Create third request immediately # → No log (uses cached values) ``` ### **Test 3: Extend to 6-Day Week** ```bash # 1. Update end day to Saturday curl -X PUT http://localhost:5000/api/v1/admin/configurations/WORK_END_DAY \ -d '{"configValue": "6"}' # 2. Create request on Friday afternoon # → Deadline should include Saturday ✅ # → Sunday still excluded ✅ ``` --- ## Database Configuration ### **Configuration Keys:** ```sql SELECT config_key, config_value, display_name FROM admin_configurations WHERE config_key IN ( 'WORK_START_HOUR', 'WORK_END_HOUR', 'WORK_START_DAY', 'WORK_END_DAY' ); -- Example results: -- WORK_START_HOUR | 9 | Work Start Hour -- WORK_END_HOUR | 18 | Work End Hour -- WORK_START_DAY | 1 | Work Start Day (Monday) -- WORK_END_DAY | 5 | Work End Day (Friday) ``` ### **Update Example:** ```sql -- Change working hours to 8 AM - 8 PM UPDATE admin_configurations SET config_value = '8', updated_at = NOW() WHERE config_key = 'WORK_START_HOUR'; UPDATE admin_configurations SET config_value = '20', updated_at = NOW() WHERE config_key = 'WORK_END_HOUR'; -- Include Saturday as working day UPDATE admin_configurations SET config_value = '6', updated_at = NOW() WHERE config_key = 'WORK_END_DAY'; ``` --- ## Logging Examples ### **Configuration Update:** ``` [Admin] Working hours configuration 'WORK_START_HOUR' updated - cache cleared [ConfigReader] Configuration cache cleared [TAT Utils] Working hours cache cleared ``` ### **TAT Calculation:** ``` [TAT Utils] Loaded working hours: 8:00-20:00, Days: 1-6 [TAT Scheduler] Using STANDARD mode - excludes holidays, weekends, non-working hours [TAT Scheduler] Calculating TAT milestones for request REQ-2025-001 [TAT Scheduler] Priority: STANDARD, TAT Hours: 16 [TAT Scheduler] Start: 2025-11-05 09:00 [TAT Scheduler] Threshold 1 (55%): 2025-11-05 17:48 (using 8-20 working hours) [TAT Scheduler] Threshold 2 (80%): 2025-11-06 10:48 [TAT Scheduler] Breach (100%): 2025-11-06 15:00 ``` --- ## Migration from Hardcoded Values ### **Before (Hardcoded):** ```typescript // ❌ Hardcoded in code const WORK_START_HOUR = 9; const WORK_END_HOUR = 18; const WORK_START_DAY = 1; const WORK_END_DAY = 5; // To change: Need code update + deployment ``` ### **After (Dynamic):** ```typescript // ✅ Read from database const config = await getWorkingHours(); // config = { startHour: 9, endHour: 18 } // To change: Just update in admin UI // No code changes needed ✅ // No deployment needed ✅ ``` --- ## Benefits ### **1. Flexibility** - ✅ Change working hours anytime without code changes - ✅ No deployment needed - ✅ Takes effect within 5 minutes ### **2. Global Organizations** - ✅ Adjust for different time zones - ✅ Support 24/5 or 24/6 operations - ✅ Extended hours for urgent periods ### **3. Seasonal Adjustments** - ✅ Extend hours during busy seasons - ✅ Reduce hours during slow periods - ✅ Special hours for events ### **4. Performance** - ✅ Cache prevents repeated DB queries - ✅ Fast lookups (memory vs database) - ✅ Auto-refresh every 5 minutes ### **5. Consistency** - ✅ All TAT calculations use same values - ✅ Immediate cache invalidation on update - ✅ Fallback to defaults if DB unavailable --- ## Summary | Aspect | Details | |--------|---------| | **Configurable** | ✅ Working hours, working days | | **Admin UI** | ✅ Settings → System Configuration | | **Cache Duration** | 5 minutes | | **Cache Invalidation** | Automatic on config update | | **Applies To** | STANDARD priority only | | **Express Mode** | Ignores working hours (24/7) | | **Performance** | Optimized with caching | | **Fallback** | Uses TAT_CONFIG defaults if DB fails | --- ## Files Modified 1. `Re_Backend/src/utils/tatTimeUtils.ts` - Dynamic working hours loading 2. `Re_Backend/src/controllers/admin.controller.ts` - Cache invalidation on update 3. `Re_Backend/src/services/configReader.service.ts` - `getWorkingHours()` function --- ## Configuration Flow Diagram ``` Admin Updates Working Hours (8:00 AM - 8:00 PM) ↓ Database Updated (admin_configurations table) ↓ clearConfigCache() + clearWorkingHoursCache() ↓ Caches Invalidated (both config and working hours) ↓ Next TAT Calculation ↓ loadWorkingHoursCache() called ↓ Read from Database (startHour=8, endHour=20) ↓ Store in Memory (5-minute cache) ↓ TAT Calculation Uses New Hours ✅ ↓ All Future Requests (for 5 min) Use Cached Values ↓ After 5 Minutes → Reload from Database ``` --- Working hours are now fully dynamic and admin-controlled! 🎉