lead_id == $user_id; } public function isProjectMember($user_id, $project_id) { return PpProject::where('id', $project_id) ->whereHas('members', function ($q) use ($user_id) { $q->where('user_id', $user_id); })->exists(); } public function canManageProject($user, $business_id, $project_id) { if ($this->is_admin($user, $business_id)) { return true; } return $this->isProjectLead($user->id, $project_id); } public function canAccessProject($user, $business_id, $project_id) { if ($this->is_admin($user, $business_id)) { return true; } return $this->isProjectLead($user->id, $project_id) || $this->isProjectMember($user->id, $project_id); } public function ensureUncategorisedMilestone($business_id, $project_id, $user_id) { return PpMilestone::firstOrCreate( [ 'business_id' => $business_id, 'project_id' => $project_id, 'milestone_type' => 'uncategorised', ], [ 'title' => __('professionalproject::lang.uncategorised'), 'position' => 0, 'created_by' => $user_id, ] ); } public function ensureTaskStatuses($business_id, $user_id = null) { if (PpTaskStatus::where('business_id', $business_id)->count() === 0) { PpTaskStatus::seedDefaults($business_id, $user_id); } } public function getDefaultTaskStatusId($business_id) { $this->ensureTaskStatuses($business_id); $status = PpTaskStatus::where('business_id', $business_id) ->where('is_completed', false) ->orderBy('position') ->first(); return $status ? $status->id : null; } public function getCompletedStatusIds($business_id) { return PpTaskStatus::where('business_id', $business_id) ->where('is_completed', true) ->pluck('id'); } public function hasActiveCannotCompleteDependencies($task_id) { return PpTaskDependency::where('task_id', $task_id) ->where('dependency_type', 'cannot_complete') ->where('status', 'active') ->exists(); } public function fulfillDependenciesForTask($blocker_task_id) { PpTaskDependency::where('blocker_task_id', $blocker_task_id) ->where('status', 'active') ->update(['status' => 'fulfilled']); } public function getNextKanbanPosition($status_id) { $max = PpTask::where('status_id', $status_id)->max('position'); return ($max ?? 0) + 16384; } public function ganttStartDateForOutput($start_date): string { $start = $start_date ? $start_date->format('Y-m-d') : now()->format('Y-m-d'); $year = (int) substr($start, 0, 4); if ($year >= 1300 && $year <= 1499) { $converted = jalali_to_gregorian($start, 'Y-m-d'); return $converted ?: $start; } return $start; } public function tasksForGantt($project_id) { return PpTask::where('project_id', $project_id) ->orderBy('sortorder') ->get() ->map(function ($task) { $start = $this->ganttStartDateForOutput($task->start_date); $duration = $task->duration ?: 1; return [ 'id' => $task->id, 'text' => $task->title, 'start_date' => $start, 'duration' => $duration, 'progress' => ($task->progress ?? 0) / 100, 'parent' => $task->parent_id ?: 0, 'priority' => $task->priority, 'type' => $task->task_type, 'sortorder' => $task->sortorder, ]; }); } public function notifyUsersAboutAssignedTask(array $user_ids, PpTask $task, $actor_name) { if (empty($user_ids)) { return; } $users = User::whereIn('id', $user_ids)->get(); foreach ($users as $user) { if ((int) $user->id === (int) $task->created_by) { continue; } $user->notify(new NewTaskAssignedNotification($task, $actor_name)); } } }