refactor(project-finance): streamline task cost calculations and prevent double counting

- Removed fixed cost from budget calculations, as actual costs are now aggregated from logs and backend data.
- Updated recursive functions in the FinanceTable and project finance slice to ensure accurate totals without double counting.
- Enhanced comments for clarity on the calculation logic for parent and leaf tasks, improving maintainability.
This commit is contained in:
chamikaJ
2025-06-11 16:24:54 +05:30
parent c5bac36c53
commit c84034b436
3 changed files with 26 additions and 24 deletions

View File

@@ -23,9 +23,9 @@ const secondsToHours = (seconds: number) => seconds / 3600;
const calculateTaskCosts = (task: IProjectFinanceTask) => {
const hours = secondsToHours(task.estimated_seconds || 0);
const timeLoggedHours = secondsToHours(task.total_time_logged_seconds || 0);
const fixedCost = task.fixed_cost || 0;
const totalBudget = (task.estimated_cost || 0) + fixedCost;
const totalBudget = task.estimated_cost || 0;
// task.total_actual already includes actual_cost_from_logs + fixed_cost from backend
const totalActual = task.total_actual || 0;
const variance = totalActual - totalBudget;
@@ -114,8 +114,8 @@ const recalculateTaskHierarchy = (tasks: IProjectFinanceTask[]): IProjectFinance
total_actual: totalActual,
estimated_seconds: subtaskTotals.estimated_seconds,
total_time_logged_seconds: subtaskTotals.total_time_logged_seconds,
total_budget: totalEstimatedCost + totalFixedCost,
variance: totalActual - (totalEstimatedCost + totalFixedCost)
total_budget: totalEstimatedCost,
variance: totalActual - totalEstimatedCost
};
return updatedTask;

View File

@@ -67,29 +67,31 @@ const FinanceTableWrapper: React.FC<FinanceTableWrapperProps> = ({ activeTablesL
// Recursive function to calculate totals from task hierarchy without double counting
const calculateTaskTotalsRecursively = (tasks: IProjectFinanceTask[]): any => {
return tasks.reduce((acc, task) => {
// For parent tasks with subtasks, only count the aggregated values (no double counting)
// For leaf tasks, count their individual values
// For parent tasks with subtasks, aggregate values from subtasks only
// For leaf tasks, use their individual values
if (task.sub_tasks && task.sub_tasks.length > 0) {
// Parent task - use its aggregated values which already include subtask totals
// Parent task - only use aggregated values from subtasks (no parent's own values)
const subtaskTotals = calculateTaskTotalsRecursively(task.sub_tasks);
return {
hours: acc.hours + (task.estimated_seconds || 0),
cost: acc.cost + (task.actual_cost_from_logs || 0),
fixedCost: acc.fixedCost + (task.fixed_cost || 0),
totalBudget: acc.totalBudget + (task.total_budget || 0),
totalActual: acc.totalActual + (task.total_actual || 0),
variance: acc.variance + (task.variance || 0),
total_time_logged: acc.total_time_logged + (task.total_time_logged_seconds || 0),
estimated_cost: acc.estimated_cost + (task.estimated_cost || 0)
hours: acc.hours + subtaskTotals.hours,
cost: acc.cost + subtaskTotals.cost,
fixedCost: acc.fixedCost + subtaskTotals.fixedCost,
totalBudget: acc.totalBudget + subtaskTotals.totalBudget,
totalActual: acc.totalActual + subtaskTotals.totalActual,
variance: acc.variance + subtaskTotals.variance,
total_time_logged: acc.total_time_logged + subtaskTotals.total_time_logged,
estimated_cost: acc.estimated_cost + subtaskTotals.estimated_cost
};
} else {
// Leaf task - use its individual values
// Leaf task - calculate values from individual task properties
const leafTotalActual = (task.actual_cost_from_logs || 0) + (task.fixed_cost || 0);
return {
hours: acc.hours + (task.estimated_seconds || 0),
cost: acc.cost + (task.actual_cost_from_logs || 0),
fixedCost: acc.fixedCost + (task.fixed_cost || 0),
totalBudget: acc.totalBudget + (task.total_budget || 0),
totalActual: acc.totalActual + (task.total_actual || 0),
variance: acc.variance + (task.variance || 0),
totalBudget: acc.totalBudget + (task.estimated_cost || 0),
totalActual: acc.totalActual + leafTotalActual,
variance: acc.variance + (leafTotalActual - (task.estimated_cost || 0)),
total_time_logged: acc.total_time_logged + (task.total_time_logged_seconds || 0),
estimated_cost: acc.estimated_cost + (task.estimated_cost || 0)
};

View File

@@ -539,8 +539,7 @@ const FinanceTable = ({
for (const task of taskList) {
if (task.sub_tasks && task.sub_tasks.length > 0) {
// Parent task with loaded subtasks - only count the subtasks recursively
// This completely avoids the parent's aggregated values to prevent double counting
// Parent task with loaded subtasks - only use subtasks values (no parent's own values)
const subtaskTotals = calculateTaskTotalsRecursive(task.sub_tasks);
totals.hours += subtaskTotals.hours;
totals.total_time_logged += subtaskTotals.total_time_logged;
@@ -552,14 +551,15 @@ const FinanceTable = ({
totals.variance += subtaskTotals.variance;
} else {
// Leaf task or parent task without loaded subtasks - use its values directly
const leafTotalActual = (task.actual_cost_from_logs || 0) + (task.fixed_cost || 0);
totals.hours += task.estimated_seconds || 0;
totals.total_time_logged += task.total_time_logged_seconds || 0;
totals.estimated_cost += task.estimated_cost || 0;
totals.actual_cost_from_logs += task.actual_cost_from_logs || 0;
totals.fixed_cost += task.fixed_cost || 0;
totals.total_budget += task.total_budget || 0;
totals.total_actual += task.total_actual || 0;
totals.variance += task.variance || 0;
totals.total_budget += task.estimated_cost || 0;
totals.total_actual += leafTotalActual;
totals.variance += leafTotalActual - (task.estimated_cost || 0);
}
}