281 lines
9.7 KiB
Python
281 lines
9.7 KiB
Python
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from src.db.models.project import Project
|
||
from src.db.models.app_user import AppUser
|
||
from src.db.models.role import UserRoleEnum
|
||
from src.core.errors import AccessDeniedException, ValidationException
|
||
from src.domain.schemas import placement_type_to_db
|
||
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_list_with_reports(
|
||
self,
|
||
user: AppUser,
|
||
year: int | None = None,
|
||
branch_id: int | None = None,
|
||
offset: int | None = None,
|
||
limit: int | None = None,
|
||
with_count: bool = False,
|
||
) -> list[dict] | tuple[int, list[dict]]:
|
||
org_unit_ids = await self._allowed_org_unit_ids(user)
|
||
return await self.project_repo.get_with_reports(
|
||
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 get_instance(
|
||
self,
|
||
project_id: int,
|
||
user: AppUser | None = None,
|
||
org_unit_ids: list[int] | None = None,
|
||
) -> Project | None:
|
||
if org_unit_ids is None and user is not None:
|
||
org_unit_ids = await self._allowed_org_unit_ids(user)
|
||
return await self.project_repo.get_instance(project_id=project_id, org_unit_ids=org_unit_ids)
|
||
|
||
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 get_project_summary_rows(
|
||
self,
|
||
project_id: int,
|
||
user: AppUser,
|
||
) -> list[tuple]:
|
||
project = await self.get_instance(project_id, user)
|
||
if not project:
|
||
raise ValidationException("Проект не найден")
|
||
return await self.project_repo.get_project_summary_rows(project_id=project_id)
|
||
|
||
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 delete_project(self, project_id: int, user: AppUser):
|
||
project = await self.get(project_id, user)
|
||
if not project:
|
||
raise ValidationException("Проект не найден")
|
||
return await self.project_repo.del_project(project_id)
|
||
|
||
async def upd_project(
|
||
self,
|
||
project_id: int,
|
||
data: dict,
|
||
user: AppUser,
|
||
):
|
||
project = await self.get(project_id, user)
|
||
if not project:
|
||
raise ValidationException("Проект не найден")
|
||
if "placement_type" in data:
|
||
data["placement_type"] = placement_type_to_db(data["placement_type"])
|
||
await self.project_repo.upd_project(project_id, data)
|
||
return await self.get(project_id, user)
|
||
|
||
async def upd_smeta(
|
||
self,
|
||
project_id: int,
|
||
year: int,
|
||
data: dict,
|
||
user: AppUser,
|
||
):
|
||
org_unit_ids = await self._allowed_org_unit_ids(user)
|
||
rf_project_year_id = await self.project_repo.resolve_rf_project_year_id(
|
||
project_id=project_id,
|
||
year=year,
|
||
org_unit_ids=org_unit_ids,
|
||
)
|
||
if rf_project_year_id is None:
|
||
raise ValidationException("Смета не найдена")
|
||
await self.project_repo.upd_smeta(rf_project_year_id, data)
|
||
return await self.project_repo.get_smeta(rf_project_year_id)
|
||
|
||
async def add_project_year(
|
||
self,
|
||
project_id: int,
|
||
year: int,
|
||
user: AppUser,
|
||
) -> dict:
|
||
org_unit_ids = await self._allowed_org_unit_ids(user)
|
||
project = await self.get_instance(
|
||
project_id=project_id,
|
||
org_unit_ids=org_unit_ids,
|
||
)
|
||
if not project:
|
||
raise ValidationException("Проект не найден")
|
||
existing = await self.project_repo.resolve_rf_project_year_id(
|
||
project_id=project_id,
|
||
year=year,
|
||
org_unit_ids=org_unit_ids,
|
||
)
|
||
if existing is not None:
|
||
raise ValidationException(f"Год {year} у проекта уже заведён")
|
||
return await self.project_repo.add_project_year(project_id=project_id, year=year)
|
||
|
||
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_to_db(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]
|