create-form: создание задачи

This commit is contained in:
tsygankoviva 2026-06-08 12:42:36 +03:00
parent e7e8bc0df2
commit 2073569765
5 changed files with 128 additions and 5 deletions

View File

@ -5,13 +5,14 @@ from fastapi import APIRouter, Depends, HTTPException, Response
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from src.services.org_unit_service import OrgUnitService
from src.services.budget_line_service import BudgetLineService from src.services.budget_line_service import BudgetLineService
from src.db.models.form_type import FormTypeEnum from src.db.models.form_type import FormTypeEnum
from src.services.sheet_service import SheetService from src.services.sheet_service import SheetService
from src.domain.schemas import AddLineSchema, BaseListResponse, BaseSingleResponse, BudgetFormResponse, CellPatch, CellsPatch, DirectionSchemaEnum, SheetFormTypeResponse, SheetResponse from src.domain.schemas import AddLineSchema, BaseListResponse, BaseSingleResponse, BudgetFormResponse, CellPatch, CellsPatch, DirectionSchemaEnum, FormCreateSchema, SheetFormTypeResponse, SheetResponse
from src.db.models.app_user import AppUser from src.db.models.app_user import AppUser
from src.services.budget_form_service import BudgetFormService from src.services.budget_form_service import BudgetFormService
from src.api.v1.deps import get_current_active_user_with_set_db from src.api.v1.deps import get_current_active_user_with_set_db, require_admin
from src.db.session import get_db from src.db.session import get_db
router = APIRouter(prefix="/form", tags=["forms"]) router = APIRouter(prefix="/form", tags=["forms"])
@ -353,4 +354,33 @@ async def delete_line(
return BaseListResponse( return BaseListResponse(
count=len(result), count=len(result),
result=_rows_to_sheet_response(result), result=_rows_to_sheet_response(result),
) )
@router.post("/")
async def add_form(
response: Response,
form: FormCreateSchema,
db: AsyncSession = Depends(get_db),
current_user: AppUser = Depends(require_admin),
) -> BaseSingleResponse[BudgetFormResponse]:
t0 = time.perf_counter()
bf_service = BudgetFormService(db)
org_service = OrgUnitService(db)
org_unit = await org_service.get(org_unit_id=form.org_unit_id, user=current_user)
if not org_unit:
raise HTTPException(404, f"ССП {form.org_unit_id} не найден")
new_form = await bf_service.create(
form_type_code=form.form_type_code,
year=form.year,
org_unit_id=org_unit.id,
)
db_ms = (time.perf_counter() - t0) * 1000
response.headers["X-DB-Time-Ms"] = f"{db_ms:.2f}"
return BaseSingleResponse(
result = BudgetFormResponse.model_validate(new_form),
)

View File

@ -220,6 +220,12 @@ class FormTypeSchemaEnum(str, enum.Enum):
FORM_4 = "FORM_4" FORM_4 = "FORM_4"
class FormCreateSchema(BaseModel):
year: int
form_type_code: FormTypeSchemaEnum
org_unit_id: int
class OrgUnitBaseSchema(BaseModel): class OrgUnitBaseSchema(BaseModel):
title: str title: str
is_ssp: bool is_ssp: bool

View File

@ -1,4 +1,4 @@
from sqlalchemy import func, select from sqlalchemy import func, select, text
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from src.db.models.budget_form import BudgetForm from src.db.models.budget_form import BudgetForm
@ -82,4 +82,34 @@ class BudgetFormRepository:
joinedload(BudgetForm.form_type) joinedload(BudgetForm.form_type)
) )
return (await self.db.execute(query)).scalars().first() return (await self.db.execute(query)).scalars().first()
async def create(
self,
form_type_code: str,
year: int,
org_unit_id: int,
) -> BudgetForm | None:
form_id = (
await self.db.execute(
text(
"""
SELECT *
FROM v3.add_budget_form(
:form_type,
:year,
:org_unit_id
)
"""
),
{
"form_type": form_type_code,
"year": year,
"org_unit_id": org_unit_id,
}
)
).scalar_one_or_none()
if form_id is None:
return None
return await self.get(
budget_form_id=form_id,
)

View File

@ -65,3 +65,15 @@ class BudgetFormService:
load_form_type=load_form_type, load_form_type=load_form_type,
load_org=load_org, load_org=load_org,
) )
async def create(
self,
form_type_code: str,
year: int,
org_unit_id: int,
) -> BudgetForm | None:
return await self.bf_repo.create(
form_type_code=form_type_code,
year=year,
org_unit_id=org_unit_id,
)

View File

@ -135,3 +135,48 @@ def test_forms_delete_line_not_found_error_shape(client, admin_tokens, auth_head
payload = response.json() payload = response.json()
assert isinstance(payload, dict) assert isinstance(payload, dict)
assert "message" in payload assert "message" in payload
def test_forms_add_form_success(client, admin_tokens, auth_headers):
response = client.post(
"/api/v1/form/",
json={"year": 2025, "form_type_code": "FORM_1", "org_unit_id": 1},
headers=auth_headers(admin_tokens),
)
assert response.status_code == 200
payload = response.json()
assert "result" in payload
assert isinstance(payload["result"], dict)
assert payload["result"]["year"] == 2025
assert "id" in payload["result"]
form_id = payload["result"]["id"]
response = client.get(
f"/api/v1/form/{form_id}",
headers=auth_headers(admin_tokens),
)
assert response.status_code == 200
def test_forms_add_form_org_unit_not_found(client, admin_tokens, auth_headers):
response = client.post(
"/api/v1/form/",
json={"year": 2025, "form_type_code": "FORM_1", "org_unit_id": 999},
headers=auth_headers(admin_tokens),
)
assert response.status_code == 404
payload = response.json()
assert isinstance(payload, dict)
assert "message" in payload
def test_forms_add_form_forbidden_not_admin(client, isp_tokens, auth_headers):
response = client.post(
"/api/v1/form/",
json={"year": 2025, "form_type_code": "FORM_1", "org_unit_id": 1},
headers=auth_headers(isp_tokens),
)
assert response.status_code == 403
payload = response.json()
assert isinstance(payload, dict)
assert "message" in payload