14 KiB
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_HOURandWORK_END_HOUR - Respects
WORK_START_DAYandWORK_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
Re_Backend/src/utils/tatTimeUtils.ts- Dynamic working hours loadingRe_Backend/src/controllers/admin.controller.ts- Cache invalidation on updateRe_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! 🎉