feat: nested department sections, attaching department sections in the user editor
This commit is contained in:
		@@ -1,4 +1,4 @@
 | 
				
			|||||||
from typing import TYPE_CHECKING
 | 
					from typing import TYPE_CHECKING, Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from sqlalchemy import BigInteger, Table, ForeignKey, Column
 | 
					from sqlalchemy import BigInteger, Table, ForeignKey, Column
 | 
				
			||||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
 | 
					from sqlalchemy.orm import Mapped, mapped_column, relationship
 | 
				
			||||||
@@ -31,12 +31,6 @@ user_pay_rate = Table(
 | 
				
			|||||||
    Column('user_id', ForeignKey('users.id'), primary_key=True, unique=True)
 | 
					    Column('user_id', ForeignKey('users.id'), primary_key=True, unique=True)
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
user_department_section = Table(
 | 
					 | 
				
			||||||
    'user_department_section',
 | 
					 | 
				
			||||||
    BaseModel.metadata,
 | 
					 | 
				
			||||||
    Column('department_section_id', ForeignKey('department_sections.id'), primary_key=True),
 | 
					 | 
				
			||||||
    Column('user_id', ForeignKey('users.id'), primary_key=True)
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Permission(BaseModel):
 | 
					class Permission(BaseModel):
 | 
				
			||||||
@@ -60,6 +54,18 @@ class Role(BaseModel):
 | 
				
			|||||||
    # users: Mapped[list["User"]] = relationship("User", back_populates="users")
 | 
					    # users: Mapped[list["User"]] = relationship("User", back_populates="users")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UserDepartmentSection(BaseModel):
 | 
				
			||||||
 | 
					    __tablename__ = 'user_department_section'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    section_id: Mapped[int] = mapped_column(ForeignKey('department_sections.id'), primary_key=True)
 | 
				
			||||||
 | 
					    section: Mapped["DepartmentSection"] = relationship(lazy='selectin', back_populates='users')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    user_id: Mapped[int] = mapped_column(ForeignKey('users.id'), primary_key=True)
 | 
				
			||||||
 | 
					    user: Mapped["User"] = relationship(lazy="selectin", back_populates='department_sections')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    is_chief: Mapped[bool] = mapped_column(nullable=False, default=False, server_default='0')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class User(BaseModel):
 | 
					class User(BaseModel):
 | 
				
			||||||
    __tablename__ = 'users'
 | 
					    __tablename__ = 'users'
 | 
				
			||||||
    id: Mapped[int] = mapped_column(primary_key=True)
 | 
					    id: Mapped[int] = mapped_column(primary_key=True)
 | 
				
			||||||
@@ -126,6 +132,12 @@ class User(BaseModel):
 | 
				
			|||||||
        lazy='selectin'
 | 
					        lazy='selectin'
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    department_sections: Mapped[list[UserDepartmentSection]] = relationship(
 | 
				
			||||||
 | 
					        "UserDepartmentSection",
 | 
				
			||||||
 | 
					        back_populates='user',
 | 
				
			||||||
 | 
					        lazy="noload",
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Position(BaseModel):
 | 
					class Position(BaseModel):
 | 
				
			||||||
    __tablename__ = 'positions'
 | 
					    __tablename__ = 'positions'
 | 
				
			||||||
@@ -166,14 +178,29 @@ class DepartmentSection(BaseModel):
 | 
				
			|||||||
    id: Mapped[int] = mapped_column(primary_key=True)
 | 
					    id: Mapped[int] = mapped_column(primary_key=True)
 | 
				
			||||||
    name: Mapped[str] = mapped_column(index=True)
 | 
					    name: Mapped[str] = mapped_column(index=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    department_id: Mapped[int] = mapped_column(ForeignKey('departments.id'))
 | 
					    department_id: Mapped[Optional[int]] = mapped_column(ForeignKey('departments.id'))
 | 
				
			||||||
    department: Mapped["Department"] = relationship(
 | 
					    department: Mapped["Department"] = relationship(
 | 
				
			||||||
        back_populates='sections',
 | 
					        back_populates='sections',
 | 
				
			||||||
        lazy='selectin',
 | 
					        lazy='selectin',
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    users: Mapped[list[User]] = relationship(
 | 
					    parent_department_section_id: Mapped[Optional[int]] = mapped_column(ForeignKey('department_sections.id'))
 | 
				
			||||||
        'User',
 | 
					    parent_department_section: Mapped["DepartmentSection"] = relationship(
 | 
				
			||||||
        secondary=user_department_section,
 | 
					        "DepartmentSection",
 | 
				
			||||||
        uselist=True,
 | 
					        back_populates="sections",
 | 
				
			||||||
 | 
					        lazy='selectin',
 | 
				
			||||||
 | 
					        remote_side=[id],
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    sections: Mapped[list["DepartmentSection"]] = relationship(
 | 
				
			||||||
 | 
					        "DepartmentSection",
 | 
				
			||||||
 | 
					        back_populates="parent_department_section",
 | 
				
			||||||
 | 
					        uselist=True,
 | 
				
			||||||
 | 
					        cascade='all, delete',
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    users: Mapped[list[UserDepartmentSection]] = relationship(
 | 
				
			||||||
 | 
					        "UserDepartmentSection",
 | 
				
			||||||
 | 
					        lazy='selectin',
 | 
				
			||||||
 | 
					        back_populates='section',
 | 
				
			||||||
 | 
					        cascade='all, delete',
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,6 +59,17 @@ async def delete_department(
 | 
				
			|||||||
    return await DepartmentService(session).delete_department(department_id)
 | 
					    return await DepartmentService(session).delete_department(department_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@department_router.get(
 | 
				
			||||||
 | 
					    "/section",
 | 
				
			||||||
 | 
					    operation_id="get_sections",
 | 
				
			||||||
 | 
					    response_model=GetDepartmentSectionsResponse,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def get_sections(
 | 
				
			||||||
 | 
					        session: SessionDependency,
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    return await DepartmentService(session).get_sections()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@department_router.post(
 | 
					@department_router.post(
 | 
				
			||||||
    "/section",
 | 
					    "/section",
 | 
				
			||||||
    operation_id="create_section",
 | 
					    operation_id="create_section",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,6 +35,19 @@ async def update(
 | 
				
			|||||||
    return await UserService(session).update(request)
 | 
					    return await UserService(session).update(request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@user_router.post(
 | 
				
			||||||
 | 
					    '/update/department-sections/{user_id}',
 | 
				
			||||||
 | 
					    response_model=UpdateUserDepartmentSectionsResponse,
 | 
				
			||||||
 | 
					    operation_id='update_user_department_sections'
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def update_user_department_sections(
 | 
				
			||||||
 | 
					        session: SessionDependency,
 | 
				
			||||||
 | 
					        user_id: int,
 | 
				
			||||||
 | 
					        request: UpdateUserDepartmentSectionsRequest,
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    return await UserService(session).update_department_sections(user_id, request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@user_router.post(
 | 
					@user_router.post(
 | 
				
			||||||
    '/create',
 | 
					    '/create',
 | 
				
			||||||
    response_model=CreateUserResponse,
 | 
					    response_model=CreateUserResponse,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,17 +1,29 @@
 | 
				
			|||||||
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from schemas.base import BaseSchema, OkMessageSchema
 | 
					from schemas.base import BaseSchema, OkMessageSchema
 | 
				
			||||||
from schemas.user import UserSchema
 | 
					from schemas.user import UserSchema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# region Entities
 | 
					# region Entities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UserDepartmentSectionSchema(BaseSchema):
 | 
				
			||||||
 | 
					    user: UserSchema
 | 
				
			||||||
 | 
					    is_chief: bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DepartmentSectionBaseSchema(BaseSchema):
 | 
					class DepartmentSectionBaseSchema(BaseSchema):
 | 
				
			||||||
    name: str
 | 
					    name: str
 | 
				
			||||||
    department_id: int
 | 
					    department_id: Optional[int]
 | 
				
			||||||
 | 
					    parent_department_section_id: Optional[int]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DepartmentSectionSchema(DepartmentSectionBaseSchema):
 | 
					class DepartmentSectionBriefSchema(DepartmentSectionBaseSchema):
 | 
				
			||||||
    id: int
 | 
					    id: int
 | 
				
			||||||
    users: list[UserSchema] = []
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DepartmentSectionSchema(DepartmentSectionBriefSchema):
 | 
				
			||||||
 | 
					    users: list[UserDepartmentSectionSchema] = []
 | 
				
			||||||
 | 
					    sections: list["DepartmentSectionSchema"] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DepartmentBaseSchema(BaseSchema):
 | 
					class DepartmentBaseSchema(BaseSchema):
 | 
				
			||||||
@@ -46,6 +58,7 @@ class UpdateDepartmentSectionRequest(BaseSchema):
 | 
				
			|||||||
class AddUserRequest(BaseSchema):
 | 
					class AddUserRequest(BaseSchema):
 | 
				
			||||||
    user_id: int
 | 
					    user_id: int
 | 
				
			||||||
    section_id: int
 | 
					    section_id: int
 | 
				
			||||||
 | 
					    is_chief: bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DeleteUserRequest(BaseSchema):
 | 
					class DeleteUserRequest(BaseSchema):
 | 
				
			||||||
@@ -73,6 +86,10 @@ class DeleteDepartmentResponse(OkMessageSchema):
 | 
				
			|||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class GetDepartmentSectionsResponse(BaseSchema):
 | 
				
			||||||
 | 
					    department_sections: list[DepartmentSectionBriefSchema]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CreateDepartmentSectionResponse(OkMessageSchema):
 | 
					class CreateDepartmentSectionResponse(OkMessageSchema):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,12 +16,17 @@ class PassportImageSchema(BaseSchema):
 | 
				
			|||||||
    image_url: str
 | 
					    image_url: str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UserDepartmentSectionsSchema(BaseSchema):
 | 
				
			||||||
 | 
					    section_id: int
 | 
				
			||||||
 | 
					    is_chief: bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class BasicUser(BaseSchema):
 | 
					class BasicUser(BaseSchema):
 | 
				
			||||||
    telegram_id: int
 | 
					    telegram_id: int
 | 
				
			||||||
    phone_number: str | None = None
 | 
					    phone_number: str | None = None
 | 
				
			||||||
    first_name: str
 | 
					    first_name: str
 | 
				
			||||||
    second_name: str
 | 
					    second_name: str
 | 
				
			||||||
    patronymic: str
 | 
					    patronymic: str = ""
 | 
				
			||||||
    comment: str
 | 
					    comment: str
 | 
				
			||||||
    passport_data: str | None = None
 | 
					    passport_data: str | None = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -51,6 +56,7 @@ class BaseUser(BasicUser):
 | 
				
			|||||||
class UserSchema(BaseUser):
 | 
					class UserSchema(BaseUser):
 | 
				
			||||||
    role: RoleSchema
 | 
					    role: RoleSchema
 | 
				
			||||||
    position: Optional[PositionSchema] = None
 | 
					    position: Optional[PositionSchema] = None
 | 
				
			||||||
 | 
					    department_sections: list[UserDepartmentSectionsSchema] | None = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class UserUpdate(BaseUser):
 | 
					class UserUpdate(BaseUser):
 | 
				
			||||||
@@ -68,6 +74,10 @@ class UpdateUserRequest(BaseSchema):
 | 
				
			|||||||
    data: UserUpdate
 | 
					    data: UserUpdate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UpdateUserDepartmentSectionsRequest(BaseSchema):
 | 
				
			||||||
 | 
					    department_sections: list[UserDepartmentSectionsSchema]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CreateUserRequest(BaseSchema):
 | 
					class CreateUserRequest(BaseSchema):
 | 
				
			||||||
    data: UserCreate
 | 
					    data: UserCreate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -83,6 +93,10 @@ class UpdateUserResponse(OkMessageSchema):
 | 
				
			|||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class UpdateUserDepartmentSectionsResponse(OkMessageSchema):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CreateUserResponse(OkMessageSchema):
 | 
					class CreateUserResponse(OkMessageSchema):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,19 +1,25 @@
 | 
				
			|||||||
from typing import Optional
 | 
					from sqlalchemy import select, and_, delete, or_
 | 
				
			||||||
 | 
					 | 
				
			||||||
from sqlalchemy import select, and_
 | 
					 | 
				
			||||||
from sqlalchemy.orm import selectinload
 | 
					from sqlalchemy.orm import selectinload
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from models import Department, DepartmentSection, User, user_department_section
 | 
					from models import Department, DepartmentSection, User, UserDepartmentSection
 | 
				
			||||||
from schemas.department import *
 | 
					from schemas.department import *
 | 
				
			||||||
from services.base import BaseService
 | 
					from services.base import BaseService
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DepartmentService(BaseService):
 | 
					class DepartmentService(BaseService):
 | 
				
			||||||
    async def get_departments(self) -> GetDepartmentsResponse:
 | 
					    async def get_departments(self) -> GetDepartmentsResponse:
 | 
				
			||||||
 | 
					        sections_selectinload = selectinload(Department.sections)
 | 
				
			||||||
 | 
					        for _ in range(10):
 | 
				
			||||||
 | 
					            sections_selectinload = sections_selectinload.selectinload(DepartmentSection.sections)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        stmt = (
 | 
					        stmt = (
 | 
				
			||||||
            select(Department)
 | 
					            select(Department)
 | 
				
			||||||
            .options(
 | 
					            .options(
 | 
				
			||||||
                selectinload(Department.sections).selectinload(DepartmentSection.users)
 | 
					                sections_selectinload,
 | 
				
			||||||
 | 
					                selectinload(Department.sections)
 | 
				
			||||||
 | 
					                .selectinload(DepartmentSection.users)
 | 
				
			||||||
 | 
					                .selectinload(UserDepartmentSection.user)
 | 
				
			||||||
 | 
					                .noload(User.department_sections),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .order_by(Department.name)
 | 
					            .order_by(Department.name)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
@@ -67,6 +73,11 @@ class DepartmentService(BaseService):
 | 
				
			|||||||
        await self.session.commit()
 | 
					        await self.session.commit()
 | 
				
			||||||
        return DeleteDepartmentResponse(ok=True, message="Департамент успешно удален")
 | 
					        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]:
 | 
					    async def _get_section(self, filter_condition) -> Optional[DepartmentSection]:
 | 
				
			||||||
        stmt = (
 | 
					        stmt = (
 | 
				
			||||||
            select(DepartmentSection)
 | 
					            select(DepartmentSection)
 | 
				
			||||||
@@ -76,21 +87,40 @@ class DepartmentService(BaseService):
 | 
				
			|||||||
        departments = (await self.session.execute(stmt)).one_or_none()
 | 
					        departments = (await self.session.execute(stmt)).one_or_none()
 | 
				
			||||||
        return departments[0] if departments else None
 | 
					        return departments[0] if departments else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def _get_section_by_name(self, name: str, department_id: int) -> Optional[DepartmentSection]:
 | 
					    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_(
 | 
					        return await self._get_section(and_(
 | 
				
			||||||
            DepartmentSection.name == name,
 | 
					            DepartmentSection.name == name,
 | 
				
			||||||
            DepartmentSection.department_id == department_id,
 | 
					            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]:
 | 
					    async def _get_section_by_id(self, section_id: int) -> Optional[DepartmentSection]:
 | 
				
			||||||
        return await self._get_section(DepartmentSection.id == section_id)
 | 
					        return await self._get_section(DepartmentSection.id == section_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def create_department_section(self, request: CreateDepartmentSectionRequest) -> CreateDepartmentSectionResponse:
 | 
					    async def create_department_section(self, request: CreateDepartmentSectionRequest) -> CreateDepartmentSectionResponse:
 | 
				
			||||||
        section = await self._get_section_by_name(request.section.name, request.section.department_id)
 | 
					        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:
 | 
					        if section:
 | 
				
			||||||
            return CreateDepartmentSectionResponse(ok=False, message="Отдел с таким названием уже существует в департаменте")
 | 
					            return CreateDepartmentSectionResponse(
 | 
				
			||||||
 | 
					                ok=False,
 | 
				
			||||||
 | 
					                message="Отдел с таким названием уже существует в департаменте",
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        section = DepartmentSection(name=request.section.name, department_id=request.section.department_id)
 | 
					        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)
 | 
					        self.session.add(section)
 | 
				
			||||||
        await self.session.commit()
 | 
					        await self.session.commit()
 | 
				
			||||||
        return CreateDepartmentSectionResponse(ok=True, message="Отдел успешно создан")
 | 
					        return CreateDepartmentSectionResponse(ok=True, message="Отдел успешно создан")
 | 
				
			||||||
@@ -99,6 +129,14 @@ class DepartmentService(BaseService):
 | 
				
			|||||||
        section = await self._get_section_by_id(request.section.id)
 | 
					        section = await self._get_section_by_id(request.section.id)
 | 
				
			||||||
        if not section:
 | 
					        if not section:
 | 
				
			||||||
            return UpdateDepartmentSectionResponse(ok=False, message=f"Отдел с ID {request.section.id} не найден")
 | 
					            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
 | 
					        section.name = request.section.name
 | 
				
			||||||
        await self.session.commit()
 | 
					        await self.session.commit()
 | 
				
			||||||
@@ -106,9 +144,9 @@ class DepartmentService(BaseService):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    async def get_available_users_for_section(self, section_id: int) -> GetAvailableUsersForDepartmentSectionResponse:
 | 
					    async def get_available_users_for_section(self, section_id: int) -> GetAvailableUsersForDepartmentSectionResponse:
 | 
				
			||||||
        sub_user_ids_in_section = (
 | 
					        sub_user_ids_in_section = (
 | 
				
			||||||
            select(user_department_section.c.user_id.label("user_id"))
 | 
					            select(UserDepartmentSection.user_id.label("user_id"))
 | 
				
			||||||
            .where(user_department_section.c.department_section_id == section_id)
 | 
					            .where(UserDepartmentSection.section_id == section_id)
 | 
				
			||||||
            .group_by(user_department_section.c.user_id)
 | 
					            .group_by(UserDepartmentSection.user_id)
 | 
				
			||||||
            .subquery()
 | 
					            .subquery()
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        stmt = (
 | 
					        stmt = (
 | 
				
			||||||
@@ -141,10 +179,22 @@ class DepartmentService(BaseService):
 | 
				
			|||||||
            return False, f"Пользователь с ID {request.user_id} не найден"
 | 
					            return False, f"Пользователь с ID {request.user_id} не найден"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if is_adding:
 | 
					        if is_adding:
 | 
				
			||||||
            section.users.append(user)
 | 
					            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"Пользователь успешно добавлен в отдел"
 | 
					            message = f"Пользователь успешно добавлен в отдел"
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            section.users.remove(user)
 | 
					            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"Пользователь успешно удален из отдела"
 | 
					            message = f"Пользователь успешно удален из отдела"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        await self.session.commit()
 | 
					        await self.session.commit()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,9 @@
 | 
				
			|||||||
from sqlalchemy import select, update, delete, insert, and_
 | 
					from sqlalchemy import select, update, delete, insert, and_
 | 
				
			||||||
 | 
					from sqlalchemy.orm import selectinload
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from backend import config
 | 
					from backend import config
 | 
				
			||||||
from external.s3_uploader.uploader import S3Uploader
 | 
					from external.s3_uploader.uploader import S3Uploader
 | 
				
			||||||
from models import User, user_position, user_pay_rate, PassportImage
 | 
					from models import User, user_position, user_pay_rate, PassportImage, DepartmentSection, UserDepartmentSection
 | 
				
			||||||
from services.base import BaseService
 | 
					from services.base import BaseService
 | 
				
			||||||
from schemas.user import *
 | 
					from schemas.user import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -11,6 +12,9 @@ class UserService(BaseService):
 | 
				
			|||||||
    async def get_all(self) -> GetAllUsersResponse:
 | 
					    async def get_all(self) -> GetAllUsersResponse:
 | 
				
			||||||
        stmt = (
 | 
					        stmt = (
 | 
				
			||||||
            select(User)
 | 
					            select(User)
 | 
				
			||||||
 | 
					            .options(
 | 
				
			||||||
 | 
					                selectinload(User.department_sections),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            .order_by(User.id.desc())
 | 
					            .order_by(User.id.desc())
 | 
				
			||||||
            .where(User.is_deleted == False)
 | 
					            .where(User.is_deleted == False)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
@@ -99,6 +103,36 @@ class UserService(BaseService):
 | 
				
			|||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
            return UpdateUserResponse(ok=False, message=str(e))
 | 
					            return UpdateUserResponse(ok=False, message=str(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def update_department_sections(
 | 
				
			||||||
 | 
					            self,
 | 
				
			||||||
 | 
					            user_id: int,
 | 
				
			||||||
 | 
					            request: UpdateUserDepartmentSectionsRequest
 | 
				
			||||||
 | 
					    ) -> UpdateUserDepartmentSectionsResponse:
 | 
				
			||||||
 | 
					        user = await self.get_by_id(user_id)
 | 
				
			||||||
 | 
					        if not user:
 | 
				
			||||||
 | 
					            return UpdateUserDepartmentSectionsResponse(ok=False, message=f"Пользователь с ID: {user_id} не найден")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        stmt_delete = delete(UserDepartmentSection).where(UserDepartmentSection.user_id == user_id)
 | 
				
			||||||
 | 
					        await self.session.execute(stmt_delete)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for section_schema in request.department_sections:
 | 
				
			||||||
 | 
					            section = await self.session.get(DepartmentSection, section_schema.section_id)
 | 
				
			||||||
 | 
					            if not section:
 | 
				
			||||||
 | 
					                await self.session.rollback()
 | 
				
			||||||
 | 
					                return UpdateUserDepartmentSectionsResponse(
 | 
				
			||||||
 | 
					                    ok=False, message=f"Отдел с ID: {section_schema.section_id} не найден"
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            user_section = UserDepartmentSection(
 | 
				
			||||||
 | 
					                user_id=user_id,
 | 
				
			||||||
 | 
					                is_chief=section_schema.is_chief,
 | 
				
			||||||
 | 
					                section_id=section_schema.section_id
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					            user.department_sections.append(user_section)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await self.session.commit()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return UpdateUserDepartmentSectionsResponse(ok=True, message="Отделы пользователя успешно обновлены")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def upload_passport_image(self, user_id: int, file_bytes: bytes) -> UploadPassportImageResponse:
 | 
					    async def upload_passport_image(self, user_id: int, file_bytes: bytes) -> UploadPassportImageResponse:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            user: Optional[User] = await self.session.get(User, user_id)
 | 
					            user: Optional[User] = await self.session.get(User, user_id)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user