status === 'closed') { throw ValidationException::withMessages([ 'status' => __('Phiếu này đã được đóng.'), ]); } if (! $actor->hasPermissionTo('close-ticket')) { throw new HttpException(403, __('Chỉ lãnh đạo cấp phòng mới có quyền đóng phiếu.')); } if ($actor->hasRole('manager') && ! $actor->hasRole('admin')) { $managedDeptIds = Department::where('manager_id', $actor->id)->pluck('id'); if (! $managedDeptIds->contains($feedback->current_department_id)) { throw new HttpException(403, __('Bạn chỉ có thể đóng phiếu thuộc phòng ban mình quản lý.')); } } $fileService = App::make(FileService::class); if (! $fileService->hasCollection($feedback, 'proof_of_resolution')) { throw ValidationException::withMessages([ 'status' => __('Yêu cầu cung cấp tài liệu bằng chứng để hoàn tất quy trình.'), ]); } $feedback->update(['status' => 'closed']); $this->notifyStakeholders($feedback, $actor); } /** * Resolve a feedback. Any role can resolve. */ public function resolve(Feedback $feedback, User $actor): void { if (in_array($feedback->status, ['closed', 'resolved'])) { throw ValidationException::withMessages([ 'status' => __('Phiếu này đã được xử lý hoặc đóng.'), ]); } $feedback->update(['status' => 'resolved']); } protected function notifyStakeholders(Feedback $feedback, User $actor): void { if ($feedback->assignedTo) { $feedback->assignedTo->notify(new \App\Notifications\TicketClosed($feedback, $actor)); } $department = $feedback->currentDepartment; if ($department && $department->manager && $department->manager->id !== ($feedback->assignedTo?->id ?? null)) { $department->manager->notify(new \App\Notifications\TicketClosed($feedback, $actor)); } } }