TAT issue in dashboard scren for upcoming deadline

This commit is contained in:
laxmanhalaki 2025-11-07 20:50:20 +05:30
parent c7c9b62358
commit 826c0eedea

View File

@ -10,6 +10,7 @@ import { Op, QueryTypes } from 'sequelize';
import { sequelize } from '@config/database'; import { sequelize } from '@config/database';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import logger from '@utils/logger'; import logger from '@utils/logger';
import { calculateSLAStatus } from '@utils/tatTimeUtils';
interface DateRangeFilter { interface DateRangeFilter {
start: Date; start: Date;
@ -226,7 +227,7 @@ export class DashboardService {
FROM approval_levels al FROM approval_levels al
JOIN workflow_requests wf ON al.request_id = wf.request_id JOIN workflow_requests wf ON al.request_id = wf.request_id
WHERE al.approver_id = :userId WHERE al.approver_id = :userId
AND al.status = 'PENDING' AND al.status = 'IN_PROGRESS'
AND wf.status IN ('PENDING', 'IN_PROGRESS') AND wf.status IN ('PENDING', 'IN_PROGRESS')
AND wf.is_draft = false AND wf.is_draft = false
AND al.level_number = wf.current_level AND al.level_number = wf.current_level
@ -461,7 +462,7 @@ export class DashboardService {
WHERE al.request_id = wf.request_id WHERE al.request_id = wf.request_id
AND al.approver_id = :userId AND al.approver_id = :userId
AND al.level_number = wf.current_level 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 AND ta.is_breached = true
) AS breach_count, ) AS breach_count,
( (
SELECT SUM(al.tat_hours) SELECT 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
FROM approval_levels al FROM approval_levels al
WHERE al.request_id = wf.request_id WHERE al.request_id = wf.request_id
AND al.level_number = wf.current_level AND al.level_number = wf.current_level
LIMIT 1 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 FROM workflow_requests wf
${whereClause} ${whereClause}
AND ( AND (
@ -523,19 +520,40 @@ export class DashboardService {
type: QueryTypes.SELECT type: QueryTypes.SELECT
}); });
return criticalRequests.map((req: any) => ({ // Calculate working hours TAT for each critical request's current level
requestId: req.request_id, const criticalWithSLA = await Promise.all(criticalRequests.map(async (req: any) => {
requestNumber: req.request_number, const priority = (req.priority || 'standard').toLowerCase();
title: req.title, const currentLevelTatHours = parseFloat(req.current_level_tat_hours) || 0;
priority: (req.priority || '').toLowerCase(), const currentLevelStartTime = req.current_level_start_time;
status: (req.status || '').toLowerCase(),
currentLevel: req.current_level, let currentLevelRemainingHours = currentLevelTatHours;
totalLevels: req.total_levels,
submissionDate: req.submission_date, if (currentLevelStartTime && currentLevelTatHours > 0) {
totalTATHours: parseFloat(req.current_level_remaining_hours) || 0, // Use current level remaining try {
breachCount: req.breach_count || 0, // Use working hours calculation for current level
isCritical: req.breach_count > 0 || req.priority === 'EXPRESS' 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 user = await User.findByPk(userId);
const isAdmin = (user as any)?.isAdmin || false; 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 = ` let whereClause = `
WHERE al.status IN ('PENDING', 'IN_PROGRESS') WHERE wf.status IN ('PENDING', 'IN_PROGRESS')
AND wf.is_draft = false AND wf.is_draft = false
${!isAdmin ? `AND ( AND al.status = 'IN_PROGRESS'
al.approver_id = :userId AND al.level_number = wf.current_level
OR wf.initiator_id = :userId ${!isAdmin ? `AND al.approver_id = :userId` : ''}
)` : ''}
`; `;
const deadlines = await sequelize.query(` const deadlines = await sequelize.query(`
@ -568,49 +586,61 @@ export class DashboardService {
wf.request_number, wf.request_number,
wf.title AS request_title, wf.title AS request_title,
wf.priority, wf.priority,
-- Calculate elapsed hours dynamically wf.current_level,
CASE wf.total_levels
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
FROM approval_levels al FROM approval_levels al
JOIN workflow_requests wf ON al.request_id = wf.request_id JOIN workflow_requests wf ON al.request_id = wf.request_id
${whereClause} ${whereClause}
ORDER BY tat_percentage_used DESC, remaining_hours ASC ORDER BY al.level_start_time ASC
LIMIT :limit LIMIT :limit
`, { `, {
replacements: { userId, limit }, replacements: { userId, limit },
type: QueryTypes.SELECT type: QueryTypes.SELECT
}); });
return deadlines.map((d: any) => ({ // Calculate working hours TAT for each deadline
levelId: d.level_id, const deadlinesWithSLA = await Promise.all(deadlines.map(async (d: any) => {
requestId: d.request_id, const priority = (d.priority || 'standard').toLowerCase();
requestNumber: d.request_number, const tatHours = parseFloat(d.tat_hours) || 0;
requestTitle: d.request_title, const levelStartTime = d.level_start_time;
levelNumber: d.level_number,
approverName: d.approver_name, let elapsedHours = 0;
approverEmail: d.approver_email, let remainingHours = tatHours;
tatHours: parseFloat(d.tat_hours) || 0, let tatPercentageUsed = 0;
elapsedHours: parseFloat(d.elapsed_hours) || 0,
remainingHours: parseFloat(d.remaining_hours) || 0, if (levelStartTime && tatHours > 0) {
tatPercentageUsed: parseFloat(d.tat_percentage_used) || 0, try {
levelStartTime: d.level_start_time, // Use working hours calculation (same as RequestDetail screen)
priority: (d.priority || '').toLowerCase() 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);
} }
/** /**