from sqlalchemy.ext.asyncio import AsyncSession from src.db.models.app_user import AppUser from src.db.models.role import UserRoleEnum from src.core.errors import AccessDeniedException, ValidationException from src.repository.project_repository import ProjectRepository from src.repository.user_repository import UserRepository class ProjectService: def __init__(self, db: AsyncSession): self.db = db self.project_repo = ProjectRepository(db) self.user_repo = UserRepository(db) async def get_list( self, user: AppUser, year: int | None = None, branch_id: int | None = None, offset: int | None = None, limit: int | None = None, with_count: bool = False, with_org_unit: bool = False, ) -> list[dict] | tuple[int, list[dict]]: org_unit_ids = await self._allowed_org_unit_ids(user) return await self.project_repo.get_list( year=year, branch_id=branch_id, offset=offset, limit=limit, with_count=with_count, org_unit_ids=org_unit_ids, ) async def get(self, project_id: int, user: AppUser) -> dict | None: org_unit_ids = await self._allowed_org_unit_ids(user) project = await self.project_repo.get(project_id=project_id, org_unit_ids=org_unit_ids) if not project: return None project["reports"] = await self.project_repo.get_reports(project_id=project_id) return project async def resolve_report_id( self, project_id: int, year: int, report_type: str, user: AppUser, ) -> int | None: normalized_type = report_type.upper() if normalized_type not in ("LIMIT", "CURRENT_EXPENSES"): raise ValidationException("report_type должен быть LIMIT или CURRENT_EXPENSES") org_unit_ids = await self._allowed_org_unit_ids(user) return await self.project_repo.resolve_report_id( project_id=project_id, year=year, report_type=normalized_type, org_unit_ids=org_unit_ids, ) async def get_report_rows( self, project_id: int, year: int, report_type: str, sections: list[str] | None, user: AppUser, ) -> list[tuple]: report_id = await self.resolve_report_id(project_id, year, report_type, user) if not report_id: raise ValidationException("Отчёт не найден") return await self.project_repo.get_report_rows(report_id=report_id, sections=sections) async def get_rf_rollup_rows( self, branch_id: int, year: int, sections: list[str] | None, user: AppUser, ) -> list[tuple]: org_unit_ids = await self._allowed_org_unit_ids(user) if org_unit_ids is not None and branch_id not in org_unit_ids: raise AccessDeniedException() return await self.project_repo.get_rf_rollup_rows( branch_id=branch_id, year=year, sections=sections, ) async def upd_form3_cell( self, project_id: int, year: int, report_type: str, line_id: int, column: str, value, user: AppUser, ) -> list[tuple]: report_id = await self.resolve_report_id(project_id, year, report_type, user) if not report_id: raise ValidationException("Отчёт не найден") return await self.project_repo.upd_form3_cell(report_id, line_id, column, value) async def upd_form3_cells( self, project_id: int, year: int, report_type: str, changes: list[dict], user: AppUser, ) -> list[tuple]: report_id = await self.resolve_report_id(project_id, year, report_type, user) if not report_id: raise ValidationException("Отчёт не найден") return await self.project_repo.upd_form3_cells(report_id, changes) async def add_form3_line( self, project_id: int, year: int, report_type: str, expense_item_id: int, user: AppUser, ) -> list[tuple]: report_id = await self.resolve_report_id(project_id, year, report_type, user) if not report_id: raise ValidationException("Отчёт не найден") return await self.project_repo.add_form3_line(report_id, expense_item_id) async def del_form3_line( self, project_id: int, year: int, report_type: str, line_id: int, user: AppUser, ) -> list[tuple]: report_id = await self.resolve_report_id(project_id, year, report_type, user) if not report_id: raise ValidationException("Отчёт не найден") return await self.project_repo.del_form3_line(line_id) async def upd_project( self, project_id: int, column: str, value, user: AppUser, ): project = await self.get(project_id, user) if not project: raise ValidationException("Проект не найден") return await self.project_repo.upd_project(project_id, column, value) async def add_project( self, name: str, year: int, org_unit_id: int, level: str = "project", parent_id: int | None = None, project_type: str | None = None, vsp_format: str | None = None, placement_type: str | None = None, object_address: str | None = None, staff_count: int | None = None, total_area: float | None = None, ) -> tuple[int, int, int]: return await self.project_repo.add_project( name=name, year=year, org_unit_id=org_unit_id, level=level, parent_id=parent_id, project_type=project_type, vsp_format=vsp_format, placement_type=placement_type, object_address=object_address, staff_count=staff_count, total_area=total_area, ) async def _allowed_org_unit_ids(self, user: AppUser) -> list[int] | None: if user.role_id == UserRoleEnum.ADMIN: return None db_user = await self.user_repo.get(user_id=user.id, load_orgs=True) if not db_user: return [] return [ou.id for ou in db_user.org_units]