withCount(['documents', 'commitments', 'risicos']) ->get(); } /** * Get a single project with full details. */ public function getWithDetails(int $id): Project { return Project::with([ 'eigenaar', 'speerpunt.thema', 'fases', 'risicos', 'commitments.acties', 'commitments.eigenaar', 'documents', 'besluiten', 'teamleden', 'afhankelijkheden.afhankelijkVan', 'overdrachtsplannen.criteria', ])->findOrFail($id); } /** * Create a new project with initial phase. */ public function create(array $data): Project { return DB::transaction(function () use ($data) { $project = Project::create([ 'naam' => $data['naam'], 'beschrijving' => $data['beschrijving'] ?? '', 'eigenaar_id' => $data['eigenaar_id'] ?? Auth::id(), 'speerpunt_id' => $data['speerpunt_id'] ?? null, 'status' => ProjectStatus::Signaal, 'prioriteit' => $data['prioriteit'] ?? \App\Enums\Prioriteit::Midden, 'startdatum' => $data['startdatum'] ?? now(), 'streef_einddatum' => $data['streef_einddatum'] ?? null, ]); // Create initial "signaal" phase $project->fases()->create([ 'type' => FaseType::Signaal, 'status' => FaseStatus::Actief, 'startdatum' => now(), ]); // Assign creator as project owner $project->teamleden()->attach(Auth::id(), ['rol' => \App\Enums\ProjectRol::Eigenaar]); $this->audit('created', $project); return $project; }); } /** * Update project details. */ public function update(Project $project, array $data): Project { $project->update(array_filter([ 'naam' => $data['naam'] ?? null, 'beschrijving' => $data['beschrijving'] ?? null, 'prioriteit' => $data['prioriteit'] ?? null, 'speerpunt_id' => $data['speerpunt_id'] ?? null, 'streef_einddatum' => $data['streef_einddatum'] ?? null, ], fn ($v) => $v !== null)); $this->audit('updated', $project); return $project->fresh(); } /** * Transition a project to the next phase. */ public function transitionPhase(Project $project, ProjectStatus $newStatus): Project { return DB::transaction(function () use ($project, $newStatus) { $oldStatus = $project->status; // Close current active phase $project->fases() ->where('status', FaseStatus::Actief) ->update([ 'status' => FaseStatus::Afgerond->value, 'einddatum' => now(), ]); // Create new phase (if it maps to a FaseType) $faseType = FaseType::tryFrom($newStatus->value); if ($faseType) { $project->fases()->create([ 'type' => $faseType, 'status' => FaseStatus::Actief, 'startdatum' => now(), ]); } // Update project status $project->update(['status' => $newStatus]); $this->audit('phase_transition', $project, [ 'from' => $oldStatus->value, 'to' => $newStatus->value, ]); return $project->fresh(['fases']); }); } /** * Park a project (temporarily halt). */ public function park(Project $project, string $reason = ''): Project { return $this->transitionToSpecialStatus($project, ProjectStatus::Geparkeerd, $reason); } /** * Stop a project permanently. */ public function stop(Project $project, string $reason = ''): Project { return $this->transitionToSpecialStatus($project, ProjectStatus::Gestopt, $reason); } private function transitionToSpecialStatus(Project $project, ProjectStatus $status, string $reason): Project { return DB::transaction(function () use ($project, $status, $reason) { $oldStatus = $project->status; $project->fases() ->where('status', FaseStatus::Actief) ->update([ 'status' => FaseStatus::Afgerond->value, 'einddatum' => now(), 'opmerkingen' => $reason, ]); $project->update(['status' => $status]); $this->audit('status_change', $project, [ 'from' => $oldStatus->value, 'to' => $status->value, 'reason' => $reason, ]); return $project->fresh(); }); } private function audit(string $action, Project $project, ?array $extra = null): void { AuditLog::create([ 'user_id' => Auth::id(), 'action' => "project.{$action}", 'entity_type' => 'project', 'entity_id' => $project->id, 'payload' => $extra, ]); } }