AURORA-1984: проекты ПД без программы (+1979, 1980, 1989) #306
@ -482,7 +482,9 @@ class FormEventProcess:
|
||||
case "project_added":
|
||||
return await self.__add_project(
|
||||
event_data=event_data["data"],
|
||||
form_id=form_id,
|
||||
user=curr_user,
|
||||
sheet=sheet,
|
||||
)
|
||||
case "program_updated":
|
||||
return await self.__update_form4_program(
|
||||
@ -610,17 +612,21 @@ class FormEventProcess:
|
||||
async def __add_project(
|
||||
self,
|
||||
event_data: dict,
|
||||
form_id: int,
|
||||
sheet: str,
|
||||
user: AppUser,
|
||||
) -> list[tuple]:
|
||||
"""
|
||||
data = {
|
||||
name: str,
|
||||
program_id: int
|
||||
program_id?: int
|
||||
}
|
||||
"""
|
||||
return await self.sheet_service.add_project(
|
||||
name=event_data["name"],
|
||||
program_id=event_data["program_id"],
|
||||
program_id=event_data.get("program_id"),
|
||||
form_id=form_id,
|
||||
sheet=sheet,
|
||||
user=user,
|
||||
)
|
||||
|
||||
|
||||
@ -208,7 +208,9 @@ class SheetRepository:
|
||||
async def add_project(
|
||||
self,
|
||||
name: str,
|
||||
program_id: int,
|
||||
program_id: int | None = None,
|
||||
form_id: int | None = None,
|
||||
section_code: str | None = None,
|
||||
) -> list[tuple]:
|
||||
return [
|
||||
tuple(r) for r in (
|
||||
@ -218,13 +220,17 @@ class SheetRepository:
|
||||
SELECT project_id, row_type, depth, sort_order, data
|
||||
FROM v3.add_form4_project(
|
||||
:p_name,
|
||||
:p_program_id
|
||||
:p_program_id,
|
||||
:p_form_id,
|
||||
:p_section_code
|
||||
)
|
||||
"""
|
||||
),
|
||||
{
|
||||
"p_program_id": program_id,
|
||||
"p_name": name,
|
||||
"p_program_id": program_id,
|
||||
"p_form_id": form_id,
|
||||
"p_section_code": section_code,
|
||||
}
|
||||
)
|
||||
).all()
|
||||
|
||||
@ -158,31 +158,54 @@ class SheetService:
|
||||
async def add_project(
|
||||
self,
|
||||
name: str,
|
||||
program_id: int,
|
||||
program_id: int | None = None,
|
||||
form_id: int | None = None,
|
||||
sheet: str | None = None,
|
||||
user: AppUser | None = None,
|
||||
) -> tuple[int, list[tuple]]:
|
||||
if user.role_id != UserRoleEnum.ADMIN:
|
||||
program = await self.f4_project_repo.get(
|
||||
project_id=program_id,
|
||||
level=Form4ProjectLevelEnum.PROGRAM,
|
||||
) -> tuple[int, list[tuple]] | None:
|
||||
if program_id is None:
|
||||
if user.role_id != UserRoleEnum.ADMIN:
|
||||
user = await self.user_repo.get(
|
||||
user_id=user.id,
|
||||
load_orgs=True,
|
||||
)
|
||||
form = await self.bf_repo.get(
|
||||
budget_form_id=form_id,
|
||||
org_unit=[ou.id for ou in user.org_units],
|
||||
)
|
||||
if not form:
|
||||
raise SheetValidationError("Форма не найдена")
|
||||
result = await self.sheet_repo.add_project(
|
||||
name=name,
|
||||
program_id=None,
|
||||
form_id=form_id,
|
||||
section_code=sheet,
|
||||
)
|
||||
if not program:
|
||||
raise SheetValidationError("Программа не найдена")
|
||||
else:
|
||||
if user.role_id != UserRoleEnum.ADMIN:
|
||||
program = await self.f4_project_repo.get(
|
||||
project_id=program_id,
|
||||
level=Form4ProjectLevelEnum.PROGRAM,
|
||||
)
|
||||
if not program:
|
||||
raise SheetValidationError("Программа не найдена")
|
||||
|
||||
user = await self.user_repo.get(
|
||||
user_id=user.id,
|
||||
load_orgs=True,
|
||||
user = await self.user_repo.get(
|
||||
user_id=user.id,
|
||||
load_orgs=True,
|
||||
)
|
||||
form = await self.bf_repo.get(
|
||||
budget_form_id=program.form_id,
|
||||
org_unit=[ou.id for ou in user.org_units],
|
||||
)
|
||||
if not form:
|
||||
raise SheetValidationError("Программа не найдена")
|
||||
result = await self.sheet_repo.add_project(
|
||||
name=name,
|
||||
program_id=program_id,
|
||||
form_id=None,
|
||||
section_code=None,
|
||||
)
|
||||
form = await self.bf_repo.get(
|
||||
budget_form_id=program.form_id,
|
||||
org_unit=[ou.id for ou in user.org_units],
|
||||
)
|
||||
if not form:
|
||||
raise SheetValidationError("Программа не найдена")
|
||||
result = await self.sheet_repo.add_project(
|
||||
program_id=program_id,
|
||||
name=name,
|
||||
)
|
||||
if result:
|
||||
return (result[0][0], [tuple(r[1:]) for r in result])
|
||||
return None
|
||||
|
||||
@ -6,6 +6,7 @@ from pathlib import Path
|
||||
import pytest
|
||||
|
||||
from sheet_contract import strip_nulls
|
||||
from src.repository.sheet_repository import SheetRepository
|
||||
from test_form4_sheet_sums import F4, SHEET, _loads
|
||||
|
||||
|
||||
@ -169,3 +170,23 @@ def test_delete_standalone_with_line_rejected(f4):
|
||||
solo = standalone(f4); f4.line(solo)
|
||||
with pytest.raises(Exception, match="Нельзя удалить проект, содержащий строки бюджета"):
|
||||
f4.sql("SELECT * FROM v3.del_form4_project(:p)", p=solo)
|
||||
|
||||
|
||||
def test_rename_standalone_with_root_returns_item_and_root(f4):
|
||||
solo = standalone(f4); f4.line(solo)
|
||||
rows = f4.sql("SELECT row_type, data::text FROM v3.upd_form4_project(:p, 'Новое')", p=solo)
|
||||
assert sorted(r[0] for r in rows) == ["ITEM", "ROOT"]
|
||||
|
||||
|
||||
def test_delete_empty_standalone_with_root_returns_root_only(f4):
|
||||
first = standalone(f4, "А"); f4.line(first)
|
||||
second = standalone(f4, "Б")
|
||||
rows = f4.sql("SELECT row_type FROM v3.del_form4_project(:p)", p=second)
|
||||
assert [r[0] for r in rows] == ["ROOT"]
|
||||
|
||||
|
||||
def test_repository_creates_in_section(f4):
|
||||
async def _run():
|
||||
return await SheetRepository(f4.db).add_project(name="Р", form_id=f4.form_id, section_code="CAP")
|
||||
rows = f4.client.portal.call(_run)
|
||||
assert [r[1] for r in rows] == ["ITEM"]
|
||||
|
||||
54
api/tests/unit/test_sheet_service_form4_standalone.py
Normal file
54
api/tests/unit/test_sheet_service_form4_standalone.py
Normal file
@ -0,0 +1,54 @@
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
|
||||
from src.db.models.role import UserRoleEnum
|
||||
from src.repository.sheet_repository import SheetValidationError
|
||||
from src.services.sheet_service import SheetService
|
||||
|
||||
ADMIN = SimpleNamespace(id=1, role_id=UserRoleEnum.ADMIN)
|
||||
EXECUTOR = SimpleNamespace(id=5, role_id=UserRoleEnum.EXECUTOR_RF)
|
||||
|
||||
|
||||
def make_service() -> SheetService:
|
||||
service = SheetService(AsyncMock())
|
||||
service.sheet_repo = AsyncMock()
|
||||
service.bf_repo = AsyncMock()
|
||||
service.user_repo = AsyncMock()
|
||||
service.f4_project_repo = AsyncMock()
|
||||
service.user_repo.get.return_value = SimpleNamespace(org_units=[])
|
||||
return service
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_admin_creates_in_section():
|
||||
service = make_service()
|
||||
service.sheet_repo.add_project.return_value = [(9, "ITEM", 2, 1, {})]
|
||||
result = await service.add_project(name="П", form_id=7, sheet="CAP", user=ADMIN)
|
||||
assert result == (9, [("ITEM", 2, 1, {})])
|
||||
service.sheet_repo.add_project.assert_awaited_once_with(
|
||||
name="П", program_id=None, form_id=7, section_code="CAP"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_foreign_executor_cannot_create_in_section():
|
||||
service = make_service()
|
||||
service.bf_repo.get.return_value = None
|
||||
with pytest.raises(SheetValidationError):
|
||||
await service.add_project(name="П", form_id=7, sheet="CAP", user=EXECUTOR)
|
||||
service.sheet_repo.add_project.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("method", ["update_form4_project", "delete_form4_project"])
|
||||
async def test_foreign_executor_cannot_change_standalone(method):
|
||||
service = make_service()
|
||||
service.f4_project_repo.get.return_value = SimpleNamespace(form_id=7, parent_id=None)
|
||||
service.bf_repo.get.return_value = None
|
||||
kwargs = {"project_id": 3, "user": EXECUTOR}
|
||||
if method == "update_form4_project":
|
||||
kwargs["name"] = "Н"
|
||||
with pytest.raises(SheetValidationError):
|
||||
await getattr(service, method)(**kwargs)
|
||||
Loading…
x
Reference in New Issue
Block a user