feature/backend-endpoints-contract-implementation-plan #1

Merged
avm merged 19 commits from feature/backend-endpoints-contract-implementation-plan into dev 2026-04-14 12:23:44 +03:00
6 changed files with 129 additions and 6 deletions
Showing only changes of commit 97e269fe1a - Show all commits

View File

@@ -0,0 +1,14 @@
"""URL-маршруты для справочников организации."""
from apps.organization.dictionary_views import CorporationScopeDictionaryView
from django.urls import path
app_name = "organization_dictionaries"
urlpatterns = [
path(
"corporation-scopes/",
CorporationScopeDictionaryView.as_view(),
name="corporation-scopes",
),
]

View File

@@ -0,0 +1,18 @@
"""Dictionary endpoints for organization domain."""
from apps.organization.scope_utils import get_corporation_scope_dictionary
from apps.organization.serializers import CorporationScopeDictionarySerializer
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
class CorporationScopeDictionaryView(APIView):
"""Return static dictionary for organization corporation scopes."""
permission_classes = [IsAuthenticated]
def get(self, request): # noqa: ARG002
items = get_corporation_scope_dictionary()
serializer = CorporationScopeDictionarySerializer(items, many=True)
return Response({"results": serializer.data})

View File

@@ -16,6 +16,21 @@ SCOPE_LABELS: dict[str, str] = {
"rosatom": "Госкорпорация «Росатом»",
"roscosmos": "Госкорпорация «Роскосмос»",
"opk": "Организации ОПК",
"other": "Иная корпорация",
}
SCOPE_SHORT_NAMES: dict[str, str] = {
"rosatom": "Росатом",
"roscosmos": "Роскосмос",
"opk": "ОПК",
"other": "Иная",
}
SCOPE_SORT_ORDER: dict[str, int] = {
"rosatom": 10,
"roscosmos": 20,
"opk": 30,
"other": 90,
}
@@ -38,6 +53,27 @@ def scope_labels(scope_codes: Iterable[str]) -> list[str]:
return [SCOPE_LABELS[code] for code in scope_codes if code in SCOPE_LABELS]
def get_corporation_scope_dictionary() -> list[dict[str, str | int]]:
"""Возвращает справочник корпусов для API-словаря."""
items: list[dict[str, str | int]] = []
for code, sort_order in sorted(
SCOPE_SORT_ORDER.items(), key=lambda item: item[1]
):
label = SCOPE_LABELS.get(code)
short_name = SCOPE_SHORT_NAMES.get(code)
if not label or not short_name:
continue
items.append(
{
"code": code,
"name": label,
"short_name": short_name,
"sort_order": sort_order,
}
)
return items
def build_scope_query(scope_codes: Iterable[str]) -> Q:
query = Q()
for scope_code in scope_codes:

View File

@@ -7,6 +7,7 @@
"""
from apps.organization.models import Organization
from apps.organization.scope_utils import SCOPE_LABELS
from apps.registers.models import Register
from rest_framework import serializers
@@ -108,12 +109,25 @@ class OrganizationCatalogBaseSerializer(serializers.ModelSerializer):
return obj.get_active_registry_names()
@staticmethod
def get_corporation_scope(obj: Organization) -> list[str]:
return obj.get_corporation_scopes()
def _primary_scope_or_default(obj: Organization) -> str:
scopes = obj.get_corporation_scopes()
return scopes[0] if scopes else ""
@staticmethod
def get_corporation_scope_label(obj: Organization) -> list[str]:
return obj.get_corporation_scope_labels()
def get_corporation_scope(obj: Organization) -> str:
return OrganizationCatalogBaseSerializer._primary_scope_or_default(obj)
@staticmethod
def get_corporation_scope_label(obj: Organization) -> str:
scope = OrganizationCatalogBaseSerializer._primary_scope_or_default(obj)
return OrganizationCatalogBaseSerializer._label_or_empty(scope)
@staticmethod
def _label_or_empty(scope_code: str) -> str:
if not scope_code:
return ""
return SCOPE_LABELS.get(scope_code, "")
class OrganizationCatalogListSerializer(OrganizationCatalogBaseSerializer):
@@ -193,3 +207,12 @@ class OrganizationCatalogDetailSerializer(OrganizationCatalogBaseSerializer):
"created_at",
"updated_at",
]
class CorporationScopeDictionarySerializer(serializers.Serializer):
"""Serializer for corporation scope dictionary entries."""
code = serializers.CharField()
name = serializers.CharField()
short_name = serializers.CharField()
sort_order = serializers.IntegerField()

View File

@@ -22,6 +22,7 @@ jobs_urlpatterns = [
urlpatterns = [
path("analytics/", include("apps.organization.analytics_root_urls")),
path("exchange/", include("apps.exchange.urls")),
path("dictionaries/", include("apps.organization.dictionary_urls")),
path("users/", include("apps.user.urls")),
path("jobs/", include((jobs_urlpatterns, "jobs"))),
path("organizations/", include("apps.organization.urls")),

View File

@@ -65,7 +65,11 @@ class OrganizationApiTest(APITestCase):
self.assertEqual(
response.data["results"][0]["active_registry_names"], ["Реестр ОПК"]
)
self.assertEqual(response.data["results"][0]["corporation_scope"], ["opk"])
self.assertEqual(response.data["results"][0]["corporation_scope"], "opk")
self.assertEqual(
response.data["results"][0]["corporation_scope_label"],
"Организации ОПК",
)
self.assertEqual(response.data["results"][0]["short_name"], "АО «Альфа»")
def test_detail_includes_active_registries(self):
@@ -102,7 +106,11 @@ class OrganizationApiTest(APITestCase):
response.data["active_registries"],
[{"id": str(register.id), "name": "Реестр Роскосмос"}],
)
self.assertEqual(response.data["corporation_scope"], ["roscosmos"])
self.assertEqual(response.data["corporation_scope"], "roscosmos")
self.assertEqual(
response.data["corporation_scope_label"],
"Госкорпорация «Роскосмос»",
)
self.assertEqual(
response.data["general_director"]["full_name"],
"Иванов Иван Иванович",
@@ -180,3 +188,26 @@ class OrganizationApiTest(APITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data["results"]), 1)
self.assertEqual(response.data["results"][0]["id"], str(rosatom_org.id))
class OrganizationDictionaryApiTest(APITestCase):
"""Tests for organization dictionaries endpoints."""
def setUp(self):
self.user = UserFactory.create_user()
self.client.force_authenticate(self.user)
def test_corporation_scopes_dictionary_returns_sorted_results(self):
response = self.client.get("/api/v1/dictionaries/corporation-scopes/")
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn("results", response.data)
self.assertGreaterEqual(len(response.data["results"]), 3)
sort_orders = [item["sort_order"] for item in response.data["results"]]
self.assertEqual(sort_orders, sorted(sort_orders))
first_item = response.data["results"][0]
self.assertEqual(first_item["code"], "rosatom")
self.assertIn("short_name", first_item)
self.assertIn("name", first_item)
self.assertIn("sort_order", first_item)