feat(reporting-allocation): enhance query logic and filters for reporting allocations
- Added logging for SQL queries and results to aid in debugging. - Introduced additional filters for categories and projects in the reporting allocation query. - Modified the duration clause to handle cases with no project IDs. - Improved total calculations for time logs and estimated hours based on filtered results. - Refactored SQL query to optimize performance and clarity in data retrieval.
This commit is contained in:
@@ -373,7 +373,11 @@ export default class ReportingAllocationController extends ReportingControllerBa
|
|||||||
WHERE p.id IN (${projectIds}) ${durationClause} ${archivedClause}
|
WHERE p.id IN (${projectIds}) ${durationClause} ${archivedClause}
|
||||||
GROUP BY p.id, p.name
|
GROUP BY p.id, p.name
|
||||||
ORDER BY logged_time DESC;`;
|
ORDER BY logged_time DESC;`;
|
||||||
|
console.log('Query:', q);
|
||||||
const result = await db.query(q, []);
|
const result = await db.query(q, []);
|
||||||
|
console.log('Query result count:', result.rows.length);
|
||||||
|
console.log('Query results:', result.rows);
|
||||||
|
const utilization = (req.body.utilization || []) as string[];
|
||||||
|
|
||||||
const data = [];
|
const data = [];
|
||||||
|
|
||||||
@@ -401,10 +405,11 @@ export default class ReportingAllocationController extends ReportingControllerBa
|
|||||||
const projects = (req.body.projects || []) as string[];
|
const projects = (req.body.projects || []) as string[];
|
||||||
const projectIds = projects.map(p => `'${p}'`).join(",");
|
const projectIds = projects.map(p => `'${p}'`).join(",");
|
||||||
|
|
||||||
|
const categories = (req.body.categories || []) as string[];
|
||||||
const billable = req.body.billable;
|
const billable = req.body.billable;
|
||||||
|
|
||||||
if (!teamIds || !projectIds.length)
|
if (!teamIds)
|
||||||
return res.status(200).send(new ServerResponse(true, { users: [], projects: [] }));
|
return res.status(200).send(new ServerResponse(true, { filteredRows: [], totals: { total_time_logs: "0", total_estimated_hours: "0", total_utilization: "0" } }));
|
||||||
|
|
||||||
const { duration, date_range } = req.body;
|
const { duration, date_range } = req.body;
|
||||||
|
|
||||||
@@ -416,7 +421,9 @@ export default class ReportingAllocationController extends ReportingControllerBa
|
|||||||
endDate = moment(date_range[1]);
|
endDate = moment(date_range[1]);
|
||||||
} else if (duration === DATE_RANGES.ALL_TIME) {
|
} else if (duration === DATE_RANGES.ALL_TIME) {
|
||||||
// Fetch the earliest start_date (or created_at if null) from selected projects
|
// Fetch the earliest start_date (or created_at if null) from selected projects
|
||||||
const minDateQuery = `SELECT MIN(COALESCE(start_date, created_at)) as min_date FROM projects WHERE id IN (${projectIds})`;
|
const minDateQuery = projectIds.length > 0
|
||||||
|
? `SELECT MIN(COALESCE(start_date, created_at)) as min_date FROM projects WHERE id IN (${projectIds})`
|
||||||
|
: `SELECT MIN(COALESCE(start_date, created_at)) as min_date FROM projects WHERE team_id IN (${teamIds})`;
|
||||||
const minDateResult = await db.query(minDateQuery, []);
|
const minDateResult = await db.query(minDateQuery, []);
|
||||||
const minDate = minDateResult.rows[0]?.min_date;
|
const minDate = minDateResult.rows[0]?.min_date;
|
||||||
startDate = minDate ? moment(minDate) : moment('2000-01-01');
|
startDate = minDate ? moment(minDate) : moment('2000-01-01');
|
||||||
@@ -492,30 +499,81 @@ export default class ReportingAllocationController extends ReportingControllerBa
|
|||||||
const orgWorkingHours = orgWorkingHoursResult.rows[0]?.working_hours || 8;
|
const orgWorkingHours = orgWorkingHoursResult.rows[0]?.working_hours || 8;
|
||||||
let totalWorkingHours = workingDays * orgWorkingHours;
|
let totalWorkingHours = workingDays * orgWorkingHours;
|
||||||
|
|
||||||
const durationClause = this.getDateRangeClause(duration || DATE_RANGES.LAST_WEEK, date_range);
|
|
||||||
const archivedClause = archived
|
const archivedClause = archived
|
||||||
? ""
|
? ""
|
||||||
: `AND p.id NOT IN (SELECT project_id FROM archived_projects WHERE project_id = p.id AND user_id = '${req.user?.id}') `;
|
: `AND p.id NOT IN (SELECT project_id FROM archived_projects WHERE project_id = p.id AND user_id = '${req.user?.id}') `;
|
||||||
|
|
||||||
const billableQuery = this.buildBillableQuery(billable);
|
const billableQuery = this.buildBillableQuery(billable);
|
||||||
const members = (req.body.members || []) as string[];
|
const members = (req.body.members || []) as string[];
|
||||||
|
|
||||||
// Prepare members filter
|
// Prepare members filter
|
||||||
let membersFilter = "";
|
let membersFilter = "";
|
||||||
if (members.length > 0) {
|
if (members.length > 0) {
|
||||||
const memberIds = members.map(id => `'${id}'`).join(",");
|
const memberIds = members.map(id => `'${id}'`).join(",");
|
||||||
membersFilter = `AND tmiv.team_member_id IN (${memberIds})`;
|
membersFilter = `AND tmiv.team_member_id IN (${memberIds})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare projects filter
|
||||||
|
let projectsFilter = "";
|
||||||
|
if (projectIds.length > 0) {
|
||||||
|
projectsFilter = `AND p.id IN (${projectIds})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare categories filter
|
||||||
|
let categoriesFilter = "";
|
||||||
|
if (categories.length > 0) {
|
||||||
|
const categoryIds = categories.map(id => `'${id}'`).join(",");
|
||||||
|
categoriesFilter = `AND p.category_id IN (${categoryIds})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create custom duration clause for twl table alias
|
||||||
|
let customDurationClause = "";
|
||||||
|
if (date_range && date_range.length === 2) {
|
||||||
|
const start = moment(date_range[0]).format("YYYY-MM-DD");
|
||||||
|
const end = moment(date_range[1]).format("YYYY-MM-DD");
|
||||||
|
if (start === end) {
|
||||||
|
customDurationClause = `AND twl.created_at::DATE = '${start}'::DATE`;
|
||||||
|
} else {
|
||||||
|
customDurationClause = `AND twl.created_at::DATE >= '${start}'::DATE AND twl.created_at < '${end}'::DATE + INTERVAL '1 day'`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const key = duration || DATE_RANGES.LAST_WEEK;
|
||||||
|
if (key === DATE_RANGES.YESTERDAY)
|
||||||
|
customDurationClause = "AND twl.created_at >= (CURRENT_DATE - INTERVAL '1 day')::DATE AND twl.created_at < CURRENT_DATE::DATE";
|
||||||
|
else if (key === DATE_RANGES.LAST_WEEK)
|
||||||
|
customDurationClause = "AND twl.created_at >= (CURRENT_DATE - INTERVAL '1 week')::DATE AND twl.created_at < CURRENT_DATE::DATE + INTERVAL '1 day'";
|
||||||
|
else if (key === DATE_RANGES.LAST_MONTH)
|
||||||
|
customDurationClause = "AND twl.created_at >= (CURRENT_DATE - INTERVAL '1 month')::DATE AND twl.created_at < CURRENT_DATE::DATE + INTERVAL '1 day'";
|
||||||
|
else if (key === DATE_RANGES.LAST_QUARTER)
|
||||||
|
customDurationClause = "AND twl.created_at >= (CURRENT_DATE - INTERVAL '3 months')::DATE AND twl.created_at < CURRENT_DATE::DATE + INTERVAL '1 day'";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modified query to start from team members and calculate filtered time logs
|
||||||
const q = `
|
const q = `
|
||||||
SELECT tmiv.team_member_id, tmiv.email, tmiv.name, SUM(time_spent) AS logged_time
|
SELECT
|
||||||
|
tmiv.team_member_id,
|
||||||
|
tmiv.email,
|
||||||
|
tmiv.name,
|
||||||
|
COALESCE(
|
||||||
|
(SELECT SUM(twl.time_spent)
|
||||||
|
FROM task_work_log twl
|
||||||
|
LEFT JOIN tasks t ON t.id = twl.task_id ${billableQuery}
|
||||||
|
LEFT JOIN projects p ON p.id = t.project_id
|
||||||
|
WHERE twl.user_id = tmiv.user_id
|
||||||
|
${customDurationClause}
|
||||||
|
${projectsFilter}
|
||||||
|
${categoriesFilter}
|
||||||
|
${archivedClause}
|
||||||
|
AND p.team_id = tmiv.team_id
|
||||||
|
), 0
|
||||||
|
) AS logged_time
|
||||||
FROM team_member_info_view tmiv
|
FROM team_member_info_view tmiv
|
||||||
LEFT JOIN task_work_log ON task_work_log.user_id = tmiv.user_id
|
WHERE tmiv.team_id IN (${teamIds})
|
||||||
LEFT JOIN tasks ON tasks.id = task_work_log.task_id ${billableQuery}
|
AND tmiv.active = TRUE
|
||||||
LEFT JOIN projects p ON p.id = tasks.project_id AND p.team_id = tmiv.team_id
|
|
||||||
WHERE p.id IN (${projectIds})
|
|
||||||
${durationClause} ${archivedClause}
|
|
||||||
${membersFilter}
|
${membersFilter}
|
||||||
GROUP BY tmiv.email, tmiv.name, tmiv.team_member_id
|
GROUP BY tmiv.email, tmiv.name, tmiv.team_member_id, tmiv.user_id, tmiv.team_id
|
||||||
ORDER BY logged_time DESC;`;
|
ORDER BY logged_time DESC;`;
|
||||||
|
|
||||||
const result = await db.query(q, []);
|
const result = await db.query(q, []);
|
||||||
const utilization = (req.body.utilization || []) as string[];
|
const utilization = (req.body.utilization || []) as string[];
|
||||||
|
|
||||||
@@ -555,17 +613,17 @@ export default class ReportingAllocationController extends ReportingControllerBa
|
|||||||
|
|
||||||
// Calculate totals
|
// Calculate totals
|
||||||
const total_time_logs = filteredRows.reduce((sum, member) => sum + parseFloat(member.logged_time || '0'), 0);
|
const total_time_logs = filteredRows.reduce((sum, member) => sum + parseFloat(member.logged_time || '0'), 0);
|
||||||
const total_estimated_hours = totalWorkingHours;
|
const total_estimated_hours = totalWorkingHours * filteredRows.length; // Total for all members
|
||||||
const total_utilization = total_time_logs > 0 && totalWorkingSeconds > 0
|
const total_utilization = total_time_logs > 0 && total_estimated_hours > 0
|
||||||
? ((total_time_logs / totalWorkingSeconds) * 100).toFixed(1)
|
? ((total_time_logs / (total_estimated_hours * 3600)) * 100).toFixed(1)
|
||||||
: '0';
|
: '0';
|
||||||
|
|
||||||
return res.status(200).send(new ServerResponse(true, {
|
return res.status(200).send(new ServerResponse(true, {
|
||||||
filteredRows,
|
filteredRows,
|
||||||
totals: {
|
totals: {
|
||||||
total_time_logs: ((total_time_logs / 3600).toFixed(2)).toString(),
|
total_time_logs: ((total_time_logs / 3600).toFixed(2)).toString(),
|
||||||
total_estimated_hours: total_estimated_hours.toString(),
|
total_estimated_hours: total_estimated_hours.toString(),
|
||||||
total_utilization: total_utilization.toString(),
|
total_utilization: total_utilization.toString(),
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user