'date', 'target_date' => 'date', 'completed_at' => 'datetime', 'progress_percent' => 'integer', ]; protected static function boot() { parent::boot(); static::creating(function ($project) { if (empty($project->slug)) { $project->slug = Str::slug($project->title) . '-' . Str::random(5); } if (empty($project->client_access_code)) { $project->client_access_code = strtoupper(Str::random(6)); } }); } public function proposal() { return $this->belongsTo(Proposal::class); } public function modules() { return $this->hasMany(ProjectModule::class)->orderBy('order', 'asc'); } public function tasks() { return $this->hasMany(ProjectTask::class)->orderBy('order_index', 'asc'); } public function updates() { return $this->hasMany(ProjectUpdate::class)->latest(); } /** * Recalculate progress percentage based on completed modules weight */ public function recalculateProgress() { $totalWeight = $this->modules()->sum('weight_percent'); if ($totalWeight > 0) { $completedWeight = $this->modules()->where('status', 'completed')->sum('weight_percent'); $progress = (int) round(($completedWeight / $totalWeight) * 100); } else { $totalTasks = $this->tasks()->count(); if ($totalTasks > 0) { $completedTasks = $this->tasks()->where('status', 'done')->count(); $progress = (int) round(($completedTasks / $totalTasks) * 100); } else { $progress = $this->progress_percent; } } $this->update(['progress_percent' => min(100, max(0, $progress))]); return $this->progress_percent; } }