from sqlalchemy import select, and_, delete, or_ from sqlalchemy.orm import selectinload from models import Department, DepartmentSection, User, UserDepartmentSection from schemas.department import * from services.base import BaseService class DepartmentService(BaseService): async def get_departments(self) -> GetDepartmentsResponse: sections_selectinload = selectinload(Department.sections) for _ in range(10): sections_selectinload = sections_selectinload.selectinload(DepartmentSection.sections) stmt = ( select(Department) .options( sections_selectinload, selectinload(Department.sections) .selectinload(DepartmentSection.users) .selectinload(UserDepartmentSection.user) .noload(User.department_sections), ) .order_by(Department.name) ) departments = (await self.session.execute(stmt)).scalars().all() return GetDepartmentsResponse(departments=departments) async def _get_department(self, filter_condition) -> Optional[Department]: stmt = ( select(Department) .where(filter_condition) .options(selectinload(Department.sections)) ) departments = (await self.session.execute(stmt)).one_or_none() return departments[0] if departments else None async def _get_department_by_name(self, name: str) -> Optional[Department]: return await self._get_department(Department.name == name) async def _get_department_by_id(self, department_id: int) -> Optional[Department]: return await self._get_department(Department.id == department_id) async def create_department(self, request: CreateDepartmentRequest) -> CreateDepartmentResponse: department = await self._get_department_by_name(request.department.name) if department: return CreateDepartmentResponse(ok=False, message="Департамент с таким названием уже существует") department = Department(name=request.department.name) self.session.add(department) await self.session.commit() return CreateDepartmentResponse(ok=True, message="Департамент успешно создан") async def update_department(self, request: UpdateDepartmentRequest) -> UpdateDepartmentResponse: department_same_name = await self._get_department_by_name(request.department.name) if department_same_name and department_same_name.id != request.department.id: return UpdateDepartmentResponse(ok=False, message="Департамент с данным именем уже существует") department = await self._get_department_by_id(request.department.id) if not department: return UpdateDepartmentResponse(ok=False, message=f"Департамент с ID {request.department.id} не найден") department.name = request.department.name await self.session.commit() return UpdateDepartmentResponse(ok=True, message="Департамент успешно изменен") async def delete_department(self, department_id: int) -> DeleteDepartmentResponse: department = await self.session.get(Department, department_id) if not department: return CreateDepartmentResponse(ok=False, message=f"Департамент с ID {department_id} не найден") await self.session.delete(department) await self.session.commit() return DeleteDepartmentResponse(ok=True, message="Департамент успешно удален") async def get_sections(self) -> GetDepartmentSectionsResponse: stmt = select(DepartmentSection) sections = (await self.session.execute(stmt)).scalars().all() return GetDepartmentSectionsResponse(department_sections=sections) async def _get_section(self, filter_condition) -> Optional[DepartmentSection]: stmt = ( select(DepartmentSection) .where(filter_condition) .options(selectinload(DepartmentSection.users)) ) departments = (await self.session.execute(stmt)).one_or_none() return departments[0] if departments else None async def _get_section_by_name( self, name: str, department_id: int = None, parent_section_id: int = None, ) -> Optional[DepartmentSection]: return await self._get_section(and_( DepartmentSection.name == name, and_( or_(department_id is None, DepartmentSection.department_id == department_id), or_(parent_section_id is None, DepartmentSection.parent_department_section_id == parent_section_id), ) )) async def _get_section_by_id(self, section_id: int) -> Optional[DepartmentSection]: return await self._get_section(DepartmentSection.id == section_id) async def create_department_section(self, request: CreateDepartmentSectionRequest) -> CreateDepartmentSectionResponse: section = await self._get_section_by_name( request.section.name, department_id=request.section.department_id, parent_section_id=request.section.parent_department_section_id, ) if section: return CreateDepartmentSectionResponse( ok=False, message="Отдел с таким названием уже существует в департаменте", ) section = DepartmentSection( name=request.section.name, department_id=request.section.department_id, parent_department_section_id=request.section.parent_department_section_id, ) self.session.add(section) await self.session.commit() return CreateDepartmentSectionResponse(ok=True, message="Отдел успешно создан") async def update_department_section(self, request: UpdateDepartmentSectionRequest) -> UpdateDepartmentSectionResponse: section = await self._get_section_by_id(request.section.id) if not section: return UpdateDepartmentSectionResponse(ok=False, message=f"Отдел с ID {request.section.id} не найден") if section.name != request.section.name: section_same_name = await self._get_section_by_name( request.section.name, department_id=request.section.department_id, parent_section_id=request.section.parent_department_section_id, ) if section_same_name: return UpdateDepartmentSectionResponse(ok=False, message="Отдел с данным именем уже существует") section.name = request.section.name await self.session.commit() return UpdateDepartmentSectionResponse(ok=True, message="Отдел успешно изменен") async def get_available_users_for_section(self, section_id: int) -> GetAvailableUsersForDepartmentSectionResponse: sub_user_ids_in_section = ( select(UserDepartmentSection.user_id.label("user_id")) .where(UserDepartmentSection.section_id == section_id) .group_by(UserDepartmentSection.user_id) .subquery() ) stmt = ( select(User) .join(sub_user_ids_in_section, sub_user_ids_in_section.c.user_id == User.id, isouter=True) .where(and_( sub_user_ids_in_section.c.user_id == None, User.is_deleted == False, )) ) users = (await self.session.execute(stmt)).scalars().all() return GetAvailableUsersForDepartmentSectionResponse(users=users) async def delete_department_section(self, section_id: int) -> DeleteDepartmentSectionResponse: section = await self._get_section_by_id(section_id) if not section: return DeleteDepartmentSectionResponse(ok=False, message=f"Отдел с ID {section_id} не найден") await self.session.delete(section) await self.session.commit() return DeleteDepartmentSectionResponse(ok=True, message="Отдел успешно удален") async def _modify_user(self, request: AddUserRequest | DeleteUserRequest, is_adding: bool) -> tuple[bool, str]: section = await self._get_section_by_id(request.section_id) if not section: return False, f"Отдел с ID {request.section_id} не найден" user = await self.session.get(User, request.user_id) if not user: return False, f"Пользователь с ID {request.user_id} не найден" if is_adding: user_department_section = UserDepartmentSection( user_id=request.user_id, section_id=section.id, is_chief=request.is_chief, ) section.users.append(user_department_section) message = f"Пользователь успешно добавлен в отдел" else: delete_stmt = ( delete(UserDepartmentSection) .where(and_( UserDepartmentSection.user_id == request.user_id, UserDepartmentSection.section_id == request.section_id, )) ) await self.session.execute(delete_stmt) message = f"Пользователь успешно удален из отдела" await self.session.commit() return True, message async def add_user(self, request: AddUserRequest) -> AddUserResponse: ok, message = await self._modify_user(request, True) return AddUserResponse(ok=ok, message=message) async def delete_user(self, request: DeleteUserRequest) -> DeleteUserResponse: ok, message = await self._modify_user(request, False) return DeleteUserResponse(ok=ok, message=message)