Re_Backend/DYNAMIC_WORKING_HOURS.md

14 KiB

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

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

// In admin.controller.ts
clearConfigCache();              // Clear general config cache
clearWorkingHoursCache();        // Clear TAT working hours cache

4. Next TAT Calculation Uses New Values

// 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

// 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)

// Re_Backend/src/utils/tatTimeUtils.ts

let workingHoursCache: WorkingHoursConfig | null = null;
let workingHoursCacheExpiry: Date | null = null;

async function loadWorkingHoursCache(): Promise<void> {
  // 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)

// Re_Backend/src/controllers/admin.controller.ts

export const updateConfiguration = async (req: Request, res: Response): Promise<void> => {
  // ... 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

# 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

# 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

# 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:

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:

-- 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):

// ❌ 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):

// ✅ 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! 🎉