Additional Changes
Some checks failed
Tests / PHP 8.3 (push) Has been cancelled
Tests / PHP 8.4 (push) Has been cancelled
Tests / PHP 8.5 (push) Has been cancelled

This commit is contained in:
2026-06-02 22:04:03 +08:00
parent e6a9a80c32
commit 64d4b331a8
151 changed files with 15070 additions and 945 deletions

View File

@@ -19,20 +19,78 @@ class MilestoneController extends Controller
$project = Project::where('ulid', $projectUlid)->firstOrFail();
$project->load([
'tasks' => fn ($q) => $q->select('id', 'project_id')->with('delays'),
'milestones',
'milestones' => fn ($q) => $q->with(['tasks.taskMaterials.material']),
]);
}
return \Inertia\Inertia::render('TimelineScheduling::Timeline/Index', [
'project' => $project,
'projects' => Project::select('id', 'ulid', 'name')->get(),
'milestones' => $project ? $project->milestones->map(fn ($m) => array_merge($m->toArray(), [
'is_completed' => $m->is_completed,
'is_overdue' => $m->is_overdue,
'status' => $m->status,
'status_color' => $m->status_color,
'days_delayed' => $m->days_delayed,
])) : [],
'projects' => Project::with('parentProject:id,ulid,name,code')->get(['id', 'ulid', 'name', 'code', 'project_type', 'parent_project_id']),
'milestones' => $project ? $project->milestones->map(function ($m) {
$tasks = $m->tasks;
// Job done calculation
if ($tasks->isEmpty()) {
$jobDone = $m->actual_date !== null ? 100.0 : 0.0;
} else {
$jobDone = round($tasks->avg('completion_percentage') ?? 0, 2);
}
// Materials Spent and Planned calculations
$totalPlannedCost = 0.0;
$totalActualCost = 0.0;
$materialsMap = [];
foreach ($tasks as $task) {
foreach ($task->taskMaterials as $tm) {
$plannedCost = (float) $tm->planned_qty * (float) $tm->unit_cost;
$actualCost = (float) $tm->actual_qty * (float) $tm->unit_cost;
$totalPlannedCost += $plannedCost;
$totalActualCost += $actualCost;
$matId = $tm->material_id;
$matName = $tm->material->name ?? 'Unknown Material';
$matUnit = $tm->material->unit ?? 'pcs';
if (!isset($materialsMap[$matId])) {
$materialsMap[$matId] = [
'material_id' => $matId,
'name' => $matName,
'unit' => $matUnit,
'planned_qty' => 0.0,
'actual_qty' => 0.0,
'planned_cost' => 0.0,
'actual_cost' => 0.0,
];
}
$materialsMap[$matId]['planned_qty'] += (float) $tm->planned_qty;
$materialsMap[$matId]['actual_qty'] += (float) $tm->actual_qty;
$materialsMap[$matId]['planned_cost'] += $plannedCost;
$materialsMap[$matId]['actual_cost'] += $actualCost;
}
}
$materialsDetail = array_values($materialsMap);
$materialsSpentPercentage = $totalPlannedCost > 0
? round(($totalActualCost / $totalPlannedCost) * 100, 2)
: 0.0;
return array_merge($m->toArray(), [
'is_completed' => $m->is_completed,
'is_overdue' => $m->is_overdue,
'status' => $m->status,
'status_color' => $m->status_color,
'days_delayed' => $m->days_delayed,
'job_done_percentage' => $jobDone,
'total_planned_materials_cost' => $totalPlannedCost,
'total_actual_materials_cost' => $totalActualCost,
'materials_spent_percentage' => $materialsSpentPercentage,
'materials_detail' => $materialsDetail,
'has_tasks_or_materials' => !$tasks->isEmpty() || $totalPlannedCost > 0,
]);
}) : [],
'milestoneStats' => $project ? [
'total' => $project->milestones->count(),
'completed' => $project->milestones->where('actual_date', '!=', null)->count(),