563 lines
14 KiB
Markdown
563 lines
14 KiB
Markdown
# 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<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)**
|
|
|
|
```typescript
|
|
// 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**
|
|
|
|
```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! 🎉
|
|
|