tasks-filter: фильтры для задач форм 1 2 4 и обратная сортировка #143

Merged
tsygankoviva merged 1 commits from tasks-filters into test 2026-09-02 12:31:16 +03:00
4 changed files with 79 additions and 5 deletions

View File

@ -1,6 +1,6 @@
import logging
import time
from typing import Optional
from typing import Literal, Optional
from fastapi import APIRouter, Depends, HTTPException, Response
@ -41,6 +41,20 @@ def _parse_sections_csv(sections: Optional[str]) -> Optional[list[str]]:
return parsed or None
def _parse_org_units_csv(org_units: Optional[str]) -> Optional[list[int]]:
if org_units is None:
return None
values = [value.strip() for value in org_units.split(",") if value.strip()]
if not values:
return None
try:
return [int(value) for value in values]
except ValueError as exc:
raise HTTPException(400, "org_units должен содержать ID через запятую") from exc
def _validate_sheet_query_params(
form_type_code: FormTypeEnum,
sheet: str,
@ -63,16 +77,23 @@ def _validate_sheet_query_params(
async def get_forms(
offset: int = 0,
limit: int = 100,
org_units: str | None = None,
form_type: Literal["FORM_1", "FORM_2", "FORM_4"] | None = None,
year: int | None = None,
db: AsyncSession = Depends(get_db),
current_user: AppUser = Depends(get_current_active_user_with_set_db),
) -> BaseListResponse[BudgetFormResponse]:
"""Все строки v3.budget_form для UI-выпадашки."""
org_unit_ids = _parse_org_units_csv(org_units)
bf_service = BudgetFormService(db)
count, forms = await bf_service.get_list(
user=current_user,
with_count=True,
offset=offset,
limit=limit,
org_unit=org_unit_ids,
form_type=form_type,
year=year,
load_org=True,
)
return BaseListResponse(

View File

@ -18,6 +18,8 @@ class BudgetFormRepository:
org_unit: int | list[int] | None = None,
with_count: bool = False,
load_org: bool = False,
form_type: FormTypeEnum | str | None = None,
year: int | None = None,
) -> list[BudgetForm] | tuple[int, list[BudgetForm]]:
if with_count:
query = select(func.count().over().label("total_count"), BudgetForm)
@ -30,13 +32,18 @@ class BudgetFormRepository:
else:
where.append(BudgetForm.org_unit_id.in_(org_unit))
if form_type is not None:
where.append(BudgetForm.form_type_code == form_type)
if year is not None:
where.append(BudgetForm.year == year)
query = query.where(*where)
if load_org:
query = query.options(
joinedload(BudgetForm.org_unit)
)
query = query.order_by(BudgetForm.id)
query = query.order_by(BudgetForm.id.desc())
if offset is not None:
query = query.offset(offset)

View File

@ -1,5 +1,6 @@
import asyncio
from typing import Literal
from sqlalchemy.ext.asyncio import AsyncSession
@ -24,6 +25,9 @@ class BudgetFormService:
limit: int | None = None,
with_count: bool = False,
load_org: bool = False,
org_unit: list[int] | None = None,
form_type: Literal["FORM_1", "FORM_2", "FORM_4"] | None = None,
year: int | None = None,
) -> list[BudgetForm] | tuple[int, list[BudgetForm]]:
if user.role_id == UserRoleEnum.ADMIN:
return await self.bf_repo.get_list(
@ -31,17 +35,29 @@ class BudgetFormService:
limit=limit,
with_count=with_count,
load_org=load_org,
org_unit=org_unit,
form_type=form_type,
year=year,
)
user = await self.user_repo.get(
user_id=user.id,
load_orgs=True,
)
allowed_org_units = [ou.id for ou in user.org_units]
if org_unit is not None:
allowed_org_units = [
org_unit_id
for org_unit_id in org_unit
if org_unit_id in allowed_org_units
]
return await self.bf_repo.get_list(
offset=offset,
limit=limit,
org_unit=[ou.id for ou in user.org_units],
org_unit=allowed_org_units,
with_count=with_count,
load_org=load_org,
form_type=form_type,
year=year,
)
async def get(

View File

@ -10,6 +10,36 @@ def test_forms_list_smoke(client, admin_tokens, auth_headers):
assert isinstance(payload["result"], list)
def test_forms_list_filters_and_orders_by_created_at_desc(
client, admin_tokens, auth_headers
):
response = client.get(
"/api/v1/form/",
params={"org_units": "1,2", "form_type": "FORM_2", "year": 2026},
headers=auth_headers(admin_tokens),
)
assert response.status_code == 200
payload = response.json()
assert payload["count"] == 1
assert [form["id"] for form in payload["result"]] == [4]
response = client.get("/api/v1/form/", headers=auth_headers(admin_tokens))
assert response.status_code == 200
forms = response.json()["result"]
assert [form["id"] for form in forms] == sorted(
(form["id"] for form in forms), reverse=True
)
def test_forms_list_rejects_invalid_org_units(client, admin_tokens, auth_headers):
response = client.get(
"/api/v1/form/",
params={"org_units": "1,invalid"},
headers=auth_headers(admin_tokens),
)
assert response.status_code == 400
def test_form_smoke(client, admin_tokens, auth_headers):
response = client.get("/api/v1/form/1", headers=auth_headers(admin_tokens))
assert response.status_code == 200