orderBy('sort_order')->get(); $departmentIds = $departments->pluck('id'); $tree = $departments ->filter(fn ($department) => $department->parent_id === null || ! $departmentIds->contains($department->parent_id)) ->values() ->map(fn ($d) => $this->formatNode($d, $departments)); return response()->json([ 'success' => true, 'data' => $tree, 'flat' => $departments->values()->map(fn ($d) => [ 'id' => $d->id, 'name' => $d->name, 'type' => $d->type ?? 'department', 'parent_id' => $d->parent_id, 'parent_name' => $departments->firstWhere('id', $d->parent_id)?->name, 'children_count' => $departments->where('parent_id', $d->id)->count(), ]), 'message' => 'ساختار سازمانی', ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'خطا در دریافت ساختار سازمانی', ], 500); } } private function formatNode($dept, $all): array { $children = $all->where('parent_id', $dept->id)->values(); return [ 'id' => $dept->id, 'name' => $dept->name, 'description' => $dept->description, 'type' => $dept->type ?? 'department', 'parent_id' => $dept->parent_id, 'manager' => $dept->manager ? ['id' => $dept->manager->id, 'name' => $dept->manager->name] : null, 'manager_id' => $dept->manager_id, 'manager_changed_at' => $dept->manager_changed_at, 'is_active' => $dept->is_active, 'sort_order' => $dept->sort_order, 'users_count' => $dept->members()->count() ?: $dept->users()->count(), 'children' => $children->map(fn ($c) => $this->formatNode($c, $all)), ]; } public function store(Request $request): JsonResponse { try { $request->validate([ 'name' => 'required|string|max:255', 'description' => 'nullable|string', 'type' => 'nullable|string|in:organization,department,unit,team', 'parent_id' => 'nullable|exists:departments,id', 'manager_id' => 'nullable|exists:users,id', 'is_active' => 'nullable|boolean', 'sort_order' => 'nullable|integer', ]); $data = $request->only(['name', 'description', 'type', 'parent_id', 'manager_id', 'is_active', 'sort_order']); $data['type'] = $data['type'] ?? 'department'; $data['is_active'] = $data['is_active'] ?? true; if (! empty($data['manager_id'])) { $data['manager_changed_at'] = now(); } $dept = Department::create($data); $this->activityLogService->log( $request->user()?->id, 'create_department', "بخش {$dept->name} ایجاد شد", 'department', $dept->id, null, null, ['new' => $dept->only(['name', 'type', 'parent_id', 'manager_id', 'is_active'])] ); return response()->json([ 'success' => true, 'data' => $dept->load('manager'), 'message' => 'دپارتمان با موفقیت ایجاد شد', ], 201); } catch (ValidationException $e) { throw $e; } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'خطا در ایجاد دپارتمان', ], 500); } } public function show(Department $department): JsonResponse { try { $department->load(['manager', 'parent', 'children']); return response()->json([ 'success' => true, 'data' => $department, 'message' => 'اطلاعات دپارتمان', ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'خطا در دریافت اطلاعات دپارتمان', ], 500); } } public function update(Request $request, Department $department): JsonResponse { try { $request->validate([ 'name' => 'sometimes|required|string|max:255', 'description' => 'nullable|string', 'type' => 'nullable|string|in:organization,department,unit,team', 'parent_id' => 'nullable|exists:departments,id', 'manager_id' => 'nullable|exists:users,id', 'is_active' => 'nullable|boolean', 'sort_order' => 'nullable|integer', ]); $old = $department->only(['name', 'description', 'type', 'parent_id', 'manager_id', 'is_active', 'sort_order']); $payload = $request->only(['name', 'description', 'type', 'parent_id', 'manager_id', 'is_active', 'sort_order']); if (array_key_exists('manager_id', $payload) && (int) $payload['manager_id'] !== (int) $department->manager_id) { $payload['manager_changed_at'] = now(); } $department->update($payload); $changed = collect($payload) ->except('manager_changed_at') ->filter(fn ($value, $key) => array_key_exists($key, $old) && $old[$key] != $department->{$key}) ->map(fn ($value, $key) => ['old' => $old[$key], 'new' => $department->{$key}]) ->all(); if (! empty($changed)) { $this->activityLogService->log( $request->user()?->id, array_key_exists('manager_id', $changed) ? 'change_department_manager' : 'update_department', array_key_exists('manager_id', $changed) ? "مدیر بخش {$department->name} تغییر کرد" : "بخش {$department->name} ویرایش شد", 'department', $department->id, null, null, ['changes' => $changed] ); } return response()->json([ 'success' => true, 'data' => $department->fresh()->load('manager'), 'message' => 'دپارتمان به‌روزرسانی شد', ]); } catch (ValidationException $e) { throw $e; } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'خطا در به‌روزرسانی دپارتمان', ], 500); } } public function destroy(Request $request, Department $department): JsonResponse { try { if ($department->children()->exists()) { return response()->json([ 'success' => false, 'message' => 'این دپارتمان دارای زیرمجموعه است. ابتدا زیرمجموعه‌ها را حذف کنید.', ], 400); } if ($department->members()->exists() || $department->users()->exists()) { return response()->json([ 'success' => false, 'message' => 'این بخش عضو دارد. ابتدا اعضا را منتقل کنید یا بخش را غیرفعال کنید.', ], 400); } $departmentName = $department->name; $departmentId = $department->id; $department->delete(); $this->activityLogService->log( $request->user()?->id, 'delete_department', "بخش {$departmentName} حذف شد", 'department', $departmentId ); return response()->json([ 'success' => true, 'message' => 'دپارتمان حذف شد', ]); } catch (\Exception $e) { return response()->json([ 'success' => false, 'message' => 'خطا در حذف دپارتمان', ], 500); } } public function addMember(Request $request, Department $department): JsonResponse { try { $request->validate([ 'user_id' => 'required|exists:users,id', 'role_in_team' => 'nullable|string|max:255', 'is_primary' => 'nullable|boolean', ]); $user = User::findOrFail($request->user_id); $isPrimary = $request->boolean('is_primary'); $this->syncMember($department, $user, $request->role_in_team, $isPrimary); $this->activityLogService->log( $request->user()?->id, 'add_department_member', "{$user->name} به {$department->name} اضافه شد", 'department', $department->id, null, null, ['user_id' => $user->id, 'role_in_team' => $request->role_in_team, 'is_primary' => $isPrimary] ); return response()->json(['success' => true, 'message' => 'عضو اضافه شد']); } catch (ValidationException $e) { throw $e; } catch (\Exception $e) { return response()->json(['success' => false, 'message' => 'خطا در افزودن عضو'], 500); } } public function updateMember(Request $request, Department $department, User $user): JsonResponse { try { $request->validate([ 'role_in_team' => 'nullable|string|max:255', 'is_primary' => 'nullable|boolean', ]); $isPrimary = $request->boolean('is_primary'); $this->syncMember($department, $user, $request->role_in_team, $isPrimary); $this->activityLogService->log( $request->user()?->id, 'update_department_member', "عضویت {$user->name} در {$department->name} ویرایش شد", 'department', $department->id, null, null, ['user_id' => $user->id, 'role_in_team' => $request->role_in_team, 'is_primary' => $isPrimary] ); return response()->json(['success' => true, 'message' => 'عضویت به‌روزرسانی شد']); } catch (ValidationException $e) { throw $e; } catch (\Exception $e) { return response()->json(['success' => false, 'message' => 'خطا در به‌روزرسانی عضویت'], 500); } } public function removeMember(Request $request, Department $department, User $user): JsonResponse { try { $department->members()->detach($user->id); if ((int) $user->department_id === (int) $department->id) { $primary = $user->departments()->wherePivot('is_primary', true)->first(); $user->update([ 'department_id' => $primary?->id, 'department' => $primary?->name, ]); } $this->activityLogService->log( $request->user()?->id, 'remove_department_member', "{$user->name} از {$department->name} حذف شد", 'department', $department->id, null, null, ['user_id' => $user->id] ); return response()->json(['success' => true, 'message' => 'عضو حذف شد']); } catch (\Exception $e) { return response()->json(['success' => false, 'message' => 'خطا در حذف عضو'], 500); } } public function transferMember(Request $request, Department $department, User $user): JsonResponse { try { $request->validate([ 'target_department_id' => 'required|exists:departments,id', 'role_in_team' => 'nullable|string|max:255', 'is_primary' => 'nullable|boolean', ]); $target = Department::findOrFail($request->target_department_id); $department->members()->detach($user->id); $this->syncMember($target, $user, $request->role_in_team, $request->boolean('is_primary', true)); $this->activityLogService->log( $request->user()?->id, 'transfer_department_member', "{$user->name} از {$department->name} به {$target->name} منتقل شد", 'department', $department->id, null, null, ['user_id' => $user->id, 'from' => $department->id, 'to' => $target->id] ); return response()->json(['success' => true, 'message' => 'عضو منتقل شد']); } catch (ValidationException $e) { throw $e; } catch (\Exception $e) { return response()->json(['success' => false, 'message' => 'خطا در انتقال عضو'], 500); } } private function syncMember(Department $department, User $user, ?string $roleInTeam, bool $isPrimary): void { if ($isPrimary) { DB::table('department_user')->where('user_id', $user->id)->update(['is_primary' => false]); $user->update([ 'department_id' => $department->id, 'department' => $department->name, ]); } $department->members()->syncWithoutDetaching([ $user->id => [ 'role_in_team' => $roleInTeam, 'is_primary' => $isPrimary, 'joined_at' => now(), ], ]); } }