47 lines
1.5 KiB
PHP
47 lines
1.5 KiB
PHP
<?php
|
|
|
|
namespace Modules\ProjectProgress\Services;
|
|
|
|
use Modules\ProjectManagement\Models\Project;
|
|
use Modules\TaskManagement\Models\Task;
|
|
|
|
class ProjectProgressService
|
|
{
|
|
/**
|
|
* Calculate project progress dynamically using actual material consumed vs BOQ planned quantity.
|
|
* Formula: sum((Actual Material Consumed / Planned BOQ) * Task Weight)
|
|
*/
|
|
public function calculateProjectProgress(Project $project): float
|
|
{
|
|
$tasks = Task::where('project_id', $project->id)
|
|
->with(['materials'])
|
|
->get();
|
|
|
|
if ($tasks->isEmpty()) {
|
|
return 0.0;
|
|
}
|
|
|
|
$totalWeightedProgress = 0.0;
|
|
$totalWeight = 0.0;
|
|
|
|
foreach ($tasks as $task) {
|
|
$taskWeight = $task->weight ?? 1.0;
|
|
$totalWeight += $taskWeight;
|
|
|
|
$plannedBoq = $task->materials->sum('planned_quantity');
|
|
$consumedQty = $task->materials->sum('consumed_quantity');
|
|
|
|
if ($plannedBoq > 0) {
|
|
$materialProgress = min(1.0, $consumedQty / $plannedBoq);
|
|
$totalWeightedProgress += ($materialProgress * 100) * $taskWeight;
|
|
} else {
|
|
// Fallback to task completion percentage if no BOQ material planned
|
|
$taskProgress = $task->status === 'completed' ? 100 : ($task->progress_percentage ?? 0);
|
|
$totalWeightedProgress += $taskProgress * $taskWeight;
|
|
}
|
|
}
|
|
|
|
return $totalWeight > 0 ? round($totalWeightedProgress / $totalWeight, 2) : 0.0;
|
|
}
|
|
}
|