feat(vsp): выгрузка по статусу и периоду, закрытые ВСП в dropdown по include_inactive (AURORA-1883, #330)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
972dce8994
commit
ca79fdedcf
@ -67,12 +67,15 @@ async def get_vsp(
|
||||
async def get_vsp_dropdown(
|
||||
ssp_id: int | None = None,
|
||||
form_id: int | None = None,
|
||||
include_inactive: bool = False,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: AppUser = Depends(require_executor),
|
||||
):
|
||||
"""Получение записей для выпадающего списка INFO."""
|
||||
service = VSPService(db)
|
||||
result = await service.get_dropdown(user=current_user, ssp_id=ssp_id, form_id=form_id, with_count=True)
|
||||
result = await service.get_dropdown(
|
||||
user=current_user, ssp_id=ssp_id, form_id=form_id, with_count=True, include_inactive=include_inactive,
|
||||
)
|
||||
return BaseListResponse(
|
||||
success=True,
|
||||
message="Список INFO для выпадающего списка",
|
||||
@ -99,6 +102,9 @@ async def export_info(
|
||||
placement_type=body.placement_type,
|
||||
staff_count_min=body.staff_count_min,
|
||||
staff_count_max=body.staff_count_max,
|
||||
status=body.status,
|
||||
close_date_start=body.close_date_start,
|
||||
close_date_end=body.close_date_end,
|
||||
)
|
||||
return StreamingResponse(
|
||||
stream,
|
||||
|
||||
@ -784,6 +784,9 @@ class VSPExportRequest(BaseModel):
|
||||
placement_type: Optional[str] = None
|
||||
staff_count_min: Optional[int] = None
|
||||
staff_count_max: Optional[int] = None
|
||||
status: VSPStatus = "active"
|
||||
close_date_start: Optional[date] = None
|
||||
close_date_end: Optional[date] = None
|
||||
|
||||
|
||||
class ExpenseItemBaseSchema(BaseModel):
|
||||
|
||||
@ -115,6 +115,7 @@ class VSPService:
|
||||
ssp_id: int | None = None,
|
||||
form_id: int | None = None,
|
||||
with_count: bool = False,
|
||||
include_inactive: bool = False,
|
||||
) -> list[Vsp]:
|
||||
ssp_ids = await self._get_scoped_ssp_ids(user=user)
|
||||
|
||||
@ -136,9 +137,10 @@ class VSPService:
|
||||
ssp_ids = list(checking_ssps & set(ssp_ids))
|
||||
|
||||
await self._sync_closed_records()
|
||||
# AURORA-1883: include_inactive — с закрытыми, для подписей ВСП в листах форм
|
||||
return await self.vsp_repo.get_list(
|
||||
ssp_ids=ssp_ids,
|
||||
is_active=True,
|
||||
is_active=None if include_inactive else True,
|
||||
with_count=with_count,
|
||||
)
|
||||
|
||||
@ -177,6 +179,9 @@ class VSPService:
|
||||
placement_type: str | None = None,
|
||||
staff_count_min: int | None = None,
|
||||
staff_count_max: int | None = None,
|
||||
status: VSPStatus = "active",
|
||||
close_date_start: date | None = None,
|
||||
close_date_end: date | None = None,
|
||||
) -> tuple[io.BytesIO, str]:
|
||||
data = await self.get_list(
|
||||
user=user,
|
||||
@ -188,7 +193,9 @@ class VSPService:
|
||||
placement_type=placement_type,
|
||||
staff_count_min=staff_count_min,
|
||||
staff_count_max=staff_count_max,
|
||||
is_active=True,
|
||||
is_active=vsp_status_to_is_active(status),
|
||||
close_date_start=close_date_start,
|
||||
close_date_end=close_date_end,
|
||||
load_org_unit=True,
|
||||
)
|
||||
|
||||
|
||||
@ -457,3 +457,42 @@ def test_vsp_list_closed_executor_scope(client, admin_tokens, isp_tokens, auth_h
|
||||
regs = _list_reg_numbers(client, isp_tokens, auth_headers, f"?status={status}")
|
||||
assert own in regs
|
||||
assert foreign not in regs
|
||||
|
||||
|
||||
def test_vsp_dropdown_include_inactive(client, admin_tokens, auth_headers):
|
||||
closed = _create_named_vsp(client, admin_tokens, auth_headers, ssp_id=2, close_date="2021-03-15")
|
||||
|
||||
with_closed = client.get(
|
||||
"/api/v1/dict/info/dropdown?form_id=2&include_inactive=true", headers=auth_headers(admin_tokens)
|
||||
)
|
||||
assert with_closed.status_code == 200
|
||||
by_reg = {item["registration_number"]: item for item in with_closed.json()["result"]}
|
||||
assert closed in by_reg
|
||||
assert by_reg[closed]["is_active"] is False
|
||||
|
||||
default = client.get("/api/v1/dict/info/dropdown?form_id=2", headers=auth_headers(admin_tokens))
|
||||
assert closed not in [item["registration_number"] for item in default.json()["result"]]
|
||||
|
||||
|
||||
def _export_values(client, tokens, auth_headers, body: dict) -> set:
|
||||
import io
|
||||
|
||||
from openpyxl import load_workbook
|
||||
|
||||
response = client.post("/api/v1/dict/info/export", json=body, headers=auth_headers(tokens))
|
||||
assert response.status_code == 200, response.text
|
||||
sheet = load_workbook(io.BytesIO(response.content)).active
|
||||
return {cell for row in sheet.iter_rows(values_only=True) for cell in row}
|
||||
|
||||
|
||||
def test_vsp_export_status_closed(client, admin_tokens, auth_headers):
|
||||
active = _create_named_vsp(client, admin_tokens, auth_headers)
|
||||
closed = _create_named_vsp(client, admin_tokens, auth_headers, close_date="2021-03-15")
|
||||
|
||||
closed_values = _export_values(client, admin_tokens, auth_headers, {"ssp_ids": [1], "status": "closed"})
|
||||
assert closed in closed_values
|
||||
assert active not in closed_values
|
||||
|
||||
default_values = _export_values(client, admin_tokens, auth_headers, {"ssp_ids": [1]})
|
||||
assert active in default_values
|
||||
assert closed not in default_values
|
||||
|
||||
@ -6,3 +6,53 @@ from src.services.vsp_service import vsp_status_to_is_active
|
||||
@pytest.mark.parametrize("status, expected", [("active", True), ("closed", False), ("all", None)])
|
||||
def test_vsp_status_to_is_active(status, expected):
|
||||
assert vsp_status_to_is_active(status) is expected
|
||||
|
||||
|
||||
# --- выгрузка и dropdown (AURORA-1883, задача 2) ---
|
||||
from datetime import date
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
from src.db.models import UserRoleEnum
|
||||
from src.services.vsp_service import VSPService
|
||||
|
||||
|
||||
def _admin():
|
||||
user = MagicMock()
|
||||
user.id = 1
|
||||
user.role_id = UserRoleEnum.ADMIN
|
||||
return user
|
||||
|
||||
|
||||
def _dropdown_service() -> VSPService:
|
||||
service = VSPService.__new__(VSPService)
|
||||
service.vsp_repo = MagicMock()
|
||||
service.vsp_repo.get_list = AsyncMock(return_value=(0, []))
|
||||
service.form_repo = MagicMock()
|
||||
service.form_repo.get = AsyncMock(return_value=None)
|
||||
service._get_scoped_ssp_ids = AsyncMock(return_value=None)
|
||||
service._sync_closed_records = AsyncMock()
|
||||
return service
|
||||
|
||||
|
||||
async def test_get_dropdown_include_inactive_passes_none():
|
||||
service = _dropdown_service()
|
||||
await service.get_dropdown(user=_admin(), ssp_id=2, include_inactive=True, with_count=True)
|
||||
assert service.vsp_repo.get_list.await_args.kwargs["is_active"] is None
|
||||
|
||||
|
||||
async def test_get_dropdown_default_active_only():
|
||||
service = _dropdown_service()
|
||||
await service.get_dropdown(user=_admin(), ssp_id=2, with_count=True)
|
||||
assert service.vsp_repo.get_list.await_args.kwargs["is_active"] is True
|
||||
|
||||
|
||||
async def test_export_xlsx_status_closed():
|
||||
service = VSPService.__new__(VSPService)
|
||||
service.get_list = AsyncMock(return_value=[])
|
||||
|
||||
await service.export_xlsx(user=_admin(), status="closed", close_date_start=date(2021, 1, 1))
|
||||
|
||||
kwargs = service.get_list.await_args.kwargs
|
||||
assert kwargs["is_active"] is False
|
||||
assert kwargs["close_date_start"] == date(2021, 1, 1)
|
||||
assert kwargs["close_date_end"] is None
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user