TAT issue in dashboard scren for upcoming deadline
This commit is contained in:
parent
c7c9b62358
commit
826c0eedea
@ -10,6 +10,7 @@ import { Op, QueryTypes } from 'sequelize';
|
||||
import { sequelize } from '@config/database';
|
||||
import dayjs from 'dayjs';
|
||||
import logger from '@utils/logger';
|
||||
import { calculateSLAStatus } from '@utils/tatTimeUtils';
|
||||
|
||||
interface DateRangeFilter {
|
||||
start: Date;
|
||||
@ -226,7 +227,7 @@ export class DashboardService {
|
||||
FROM approval_levels al
|
||||
JOIN workflow_requests wf ON al.request_id = wf.request_id
|
||||
WHERE al.approver_id = :userId
|
||||
AND al.status = 'PENDING'
|
||||
AND al.status = 'IN_PROGRESS'
|
||||
AND wf.status IN ('PENDING', 'IN_PROGRESS')
|
||||
AND wf.is_draft = false
|
||||
AND al.level_number = wf.current_level
|
||||
@ -461,7 +462,7 @@ export class DashboardService {
|
||||
WHERE al.request_id = wf.request_id
|
||||
AND al.approver_id = :userId
|
||||
AND al.level_number = wf.current_level
|
||||
AND al.status = 'PENDING'
|
||||
AND al.status = 'IN_PROGRESS'
|
||||
)
|
||||
)` : ''}
|
||||
`;
|
||||
@ -484,23 +485,19 @@ export class DashboardService {
|
||||
AND ta.is_breached = true
|
||||
) AS breach_count,
|
||||
(
|
||||
SELECT SUM(al.tat_hours)
|
||||
FROM approval_levels al
|
||||
WHERE al.request_id = wf.request_id
|
||||
) AS total_allocated_tat,
|
||||
-- Calculate current level's remaining TAT dynamically
|
||||
(
|
||||
SELECT
|
||||
CASE
|
||||
WHEN al.level_start_time IS NOT NULL AND al.tat_hours IS NOT NULL THEN
|
||||
GREATEST(0, al.tat_hours - (EXTRACT(EPOCH FROM (NOW() - al.level_start_time)) / 3600.0))
|
||||
ELSE al.tat_hours
|
||||
END
|
||||
SELECT al.tat_hours
|
||||
FROM approval_levels al
|
||||
WHERE al.request_id = wf.request_id
|
||||
AND al.level_number = wf.current_level
|
||||
LIMIT 1
|
||||
) AS current_level_remaining_hours
|
||||
) AS current_level_tat_hours,
|
||||
(
|
||||
SELECT al.level_start_time
|
||||
FROM approval_levels al
|
||||
WHERE al.request_id = wf.request_id
|
||||
AND al.level_number = wf.current_level
|
||||
LIMIT 1
|
||||
) AS current_level_start_time
|
||||
FROM workflow_requests wf
|
||||
${whereClause}
|
||||
AND (
|
||||
@ -523,19 +520,40 @@ export class DashboardService {
|
||||
type: QueryTypes.SELECT
|
||||
});
|
||||
|
||||
return criticalRequests.map((req: any) => ({
|
||||
requestId: req.request_id,
|
||||
requestNumber: req.request_number,
|
||||
title: req.title,
|
||||
priority: (req.priority || '').toLowerCase(),
|
||||
status: (req.status || '').toLowerCase(),
|
||||
currentLevel: req.current_level,
|
||||
totalLevels: req.total_levels,
|
||||
submissionDate: req.submission_date,
|
||||
totalTATHours: parseFloat(req.current_level_remaining_hours) || 0, // Use current level remaining
|
||||
breachCount: req.breach_count || 0,
|
||||
isCritical: req.breach_count > 0 || req.priority === 'EXPRESS'
|
||||
// Calculate working hours TAT for each critical request's current level
|
||||
const criticalWithSLA = await Promise.all(criticalRequests.map(async (req: any) => {
|
||||
const priority = (req.priority || 'standard').toLowerCase();
|
||||
const currentLevelTatHours = parseFloat(req.current_level_tat_hours) || 0;
|
||||
const currentLevelStartTime = req.current_level_start_time;
|
||||
|
||||
let currentLevelRemainingHours = currentLevelTatHours;
|
||||
|
||||
if (currentLevelStartTime && currentLevelTatHours > 0) {
|
||||
try {
|
||||
// Use working hours calculation for current level
|
||||
const slaData = await calculateSLAStatus(currentLevelStartTime, currentLevelTatHours, priority);
|
||||
currentLevelRemainingHours = slaData.remainingHours;
|
||||
} catch (error) {
|
||||
logger.error(`[Dashboard] Error calculating SLA for critical request ${req.request_id}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
requestId: req.request_id,
|
||||
requestNumber: req.request_number,
|
||||
title: req.title,
|
||||
priority,
|
||||
status: (req.status || '').toLowerCase(),
|
||||
currentLevel: req.current_level,
|
||||
totalLevels: req.total_levels,
|
||||
submissionDate: req.submission_date,
|
||||
totalTATHours: currentLevelRemainingHours, // Current level remaining hours
|
||||
breachCount: req.breach_count || 0,
|
||||
isCritical: req.breach_count > 0 || req.priority === 'EXPRESS'
|
||||
};
|
||||
}));
|
||||
|
||||
return criticalWithSLA;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -546,14 +564,14 @@ export class DashboardService {
|
||||
const user = await User.findByPk(userId);
|
||||
const isAdmin = (user as any)?.isAdmin || false;
|
||||
|
||||
// For regular users: only show levels where they are approver OR requests they initiated
|
||||
// For regular users: only show CURRENT LEVEL where they are the approver
|
||||
// For admins: show all current active levels
|
||||
let whereClause = `
|
||||
WHERE al.status IN ('PENDING', 'IN_PROGRESS')
|
||||
WHERE wf.status IN ('PENDING', 'IN_PROGRESS')
|
||||
AND wf.is_draft = false
|
||||
${!isAdmin ? `AND (
|
||||
al.approver_id = :userId
|
||||
OR wf.initiator_id = :userId
|
||||
)` : ''}
|
||||
AND al.status = 'IN_PROGRESS'
|
||||
AND al.level_number = wf.current_level
|
||||
${!isAdmin ? `AND al.approver_id = :userId` : ''}
|
||||
`;
|
||||
|
||||
const deadlines = await sequelize.query(`
|
||||
@ -568,49 +586,61 @@ export class DashboardService {
|
||||
wf.request_number,
|
||||
wf.title AS request_title,
|
||||
wf.priority,
|
||||
-- Calculate elapsed hours dynamically
|
||||
CASE
|
||||
WHEN al.level_start_time IS NOT NULL THEN
|
||||
EXTRACT(EPOCH FROM (NOW() - al.level_start_time)) / 3600.0
|
||||
ELSE 0
|
||||
END AS elapsed_hours,
|
||||
-- Calculate remaining hours dynamically
|
||||
CASE
|
||||
WHEN al.level_start_time IS NOT NULL AND al.tat_hours IS NOT NULL THEN
|
||||
GREATEST(0, al.tat_hours - (EXTRACT(EPOCH FROM (NOW() - al.level_start_time)) / 3600.0))
|
||||
ELSE al.tat_hours
|
||||
END AS remaining_hours,
|
||||
-- Calculate percentage used dynamically
|
||||
CASE
|
||||
WHEN al.level_start_time IS NOT NULL AND al.tat_hours IS NOT NULL AND al.tat_hours > 0 THEN
|
||||
LEAST(100, ((EXTRACT(EPOCH FROM (NOW() - al.level_start_time)) / 3600.0) / al.tat_hours) * 100)
|
||||
ELSE 0
|
||||
END AS tat_percentage_used
|
||||
wf.current_level,
|
||||
wf.total_levels
|
||||
FROM approval_levels al
|
||||
JOIN workflow_requests wf ON al.request_id = wf.request_id
|
||||
${whereClause}
|
||||
ORDER BY tat_percentage_used DESC, remaining_hours ASC
|
||||
ORDER BY al.level_start_time ASC
|
||||
LIMIT :limit
|
||||
`, {
|
||||
replacements: { userId, limit },
|
||||
type: QueryTypes.SELECT
|
||||
});
|
||||
|
||||
return deadlines.map((d: any) => ({
|
||||
levelId: d.level_id,
|
||||
requestId: d.request_id,
|
||||
requestNumber: d.request_number,
|
||||
requestTitle: d.request_title,
|
||||
levelNumber: d.level_number,
|
||||
approverName: d.approver_name,
|
||||
approverEmail: d.approver_email,
|
||||
tatHours: parseFloat(d.tat_hours) || 0,
|
||||
elapsedHours: parseFloat(d.elapsed_hours) || 0,
|
||||
remainingHours: parseFloat(d.remaining_hours) || 0,
|
||||
tatPercentageUsed: parseFloat(d.tat_percentage_used) || 0,
|
||||
levelStartTime: d.level_start_time,
|
||||
priority: (d.priority || '').toLowerCase()
|
||||
// Calculate working hours TAT for each deadline
|
||||
const deadlinesWithSLA = await Promise.all(deadlines.map(async (d: any) => {
|
||||
const priority = (d.priority || 'standard').toLowerCase();
|
||||
const tatHours = parseFloat(d.tat_hours) || 0;
|
||||
const levelStartTime = d.level_start_time;
|
||||
|
||||
let elapsedHours = 0;
|
||||
let remainingHours = tatHours;
|
||||
let tatPercentageUsed = 0;
|
||||
|
||||
if (levelStartTime && tatHours > 0) {
|
||||
try {
|
||||
// Use working hours calculation (same as RequestDetail screen)
|
||||
const slaData = await calculateSLAStatus(levelStartTime, tatHours, priority);
|
||||
elapsedHours = slaData.elapsedHours;
|
||||
remainingHours = slaData.remainingHours;
|
||||
tatPercentageUsed = slaData.percentageUsed;
|
||||
} catch (error) {
|
||||
logger.error(`[Dashboard] Error calculating SLA for level ${d.level_id}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
levelId: d.level_id,
|
||||
requestId: d.request_id,
|
||||
requestNumber: d.request_number,
|
||||
requestTitle: d.request_title,
|
||||
levelNumber: d.level_number,
|
||||
currentLevel: d.current_level,
|
||||
totalLevels: d.total_levels,
|
||||
approverName: d.approver_name,
|
||||
approverEmail: d.approver_email,
|
||||
tatHours,
|
||||
elapsedHours,
|
||||
remainingHours,
|
||||
tatPercentageUsed,
|
||||
levelStartTime,
|
||||
priority
|
||||
};
|
||||
}));
|
||||
|
||||
// Sort by TAT percentage used (descending) and return
|
||||
return deadlinesWithSLA.sort((a, b) => b.tatPercentageUsed - a.tatPercentageUsed);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user