Add organizations v2 API and registry enrichment
This commit is contained in:
921
tests/apps/organizations/test_api_v2.py
Normal file
921
tests/apps/organizations/test_api_v2.py
Normal file
@@ -0,0 +1,921 @@
|
||||
"""Tests for organizations API v2."""
|
||||
|
||||
from apps.parsers.models import (
|
||||
FinancialReport,
|
||||
FinancialReportLine,
|
||||
GenericParserRecord,
|
||||
ParserLoadLog,
|
||||
)
|
||||
from django.core.cache import cache
|
||||
from django.db import connection
|
||||
from django.test import override_settings
|
||||
from django.test.utils import CaptureQueriesContext
|
||||
from django.urls import reverse
|
||||
from organizations.models import Organization
|
||||
from organizations.services import OrganizationDataSnapshotRefreshService
|
||||
from rest_framework import status
|
||||
from rest_framework.test import APITestCase
|
||||
|
||||
from tests.apps.parsers.factories import (
|
||||
IndustrialCertificateRecordFactory,
|
||||
IndustrialProductRecordFactory,
|
||||
InspectionRecordFactory,
|
||||
ManufacturerRecordFactory,
|
||||
ProcurementRecordFactory,
|
||||
)
|
||||
from tests.apps.registers.factories import (
|
||||
OrganizationFactory as RegistryOrganizationFactory,
|
||||
)
|
||||
from tests.apps.registers.factories import (
|
||||
RegisterFactory,
|
||||
RegistryMembershipPeriodFactory,
|
||||
)
|
||||
from tests.apps.user.factories import UserFactory
|
||||
|
||||
|
||||
class OrganizationsApiV2Test(APITestCase):
|
||||
"""Checks v2 canonical organizations endpoints."""
|
||||
|
||||
def setUp(self):
|
||||
cache.clear()
|
||||
self.user = UserFactory.create_user()
|
||||
self.client.force_authenticate(self.user)
|
||||
|
||||
def test_list_is_paginated_and_available_only_under_v2(self):
|
||||
Organization.objects.create(
|
||||
name='ООО "Альфа"',
|
||||
inn="7707083893",
|
||||
kpp="770701001",
|
||||
ogrn="1027700132195",
|
||||
)
|
||||
Organization.objects.create(
|
||||
name="ИП Иванов Иван Иванович",
|
||||
inn="500100732259",
|
||||
ogrip="304500116000157",
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"page_size": 1, "has_registry": "false"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["success"], True)
|
||||
self.assertEqual(len(response.data["data"]), 1)
|
||||
self.assertEqual(response.data["meta"]["pagination"]["total_count"], 2)
|
||||
self.assertEqual(response.data["meta"]["pagination"]["page_size"], 1)
|
||||
|
||||
v1_response = self.client.get("/api/v1/organizations/")
|
||||
self.assertEqual(v1_response.status_code, status.HTTP_404_NOT_FOUND)
|
||||
|
||||
def test_openapi_documents_v2_organizations_endpoints(self):
|
||||
response = self.client.get(
|
||||
reverse("schema-swagger-ui"),
|
||||
{"format": "openapi"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
paths = response.data["paths"]
|
||||
list_operation = paths["/api/v2/organizations/"]["get"]
|
||||
detail_operation = paths["/api/v2/organizations/{uid}/"]["get"]
|
||||
|
||||
self.assertEqual(list_operation["tags"], ["Организации"])
|
||||
self.assertEqual(detail_operation["tags"], ["Организации"])
|
||||
self.assertEqual(
|
||||
list_operation["operationId"],
|
||||
"v2_organizations_list",
|
||||
)
|
||||
self.assertEqual(
|
||||
detail_operation["operationId"],
|
||||
"v2_organizations_retrieve",
|
||||
)
|
||||
list_parameters = {
|
||||
parameter["name"]: parameter for parameter in list_operation["parameters"]
|
||||
}
|
||||
for expected_name in (
|
||||
"page",
|
||||
"page_size",
|
||||
"search",
|
||||
"ordering",
|
||||
"registry",
|
||||
"has_registry",
|
||||
"has_fns_reports",
|
||||
"data",
|
||||
"exclude_data",
|
||||
):
|
||||
self.assertIn(expected_name, list_parameters)
|
||||
self.assertIn("по умолчанию true", list_parameters["has_registry"]["description"])
|
||||
self.assertIn(
|
||||
"industrial_products",
|
||||
list_parameters["data"]["description"],
|
||||
)
|
||||
detail_parameters = {
|
||||
parameter["name"]: parameter
|
||||
for parameter in detail_operation["parameters"]
|
||||
}
|
||||
self.assertEqual(detail_parameters["uid"]["type"], "string")
|
||||
self.assertEqual(detail_parameters["uid"]["format"], "uuid")
|
||||
self.assertIn("data", detail_parameters)
|
||||
self.assertIn("exclude_data", detail_parameters)
|
||||
self.assertIn("Пагинированный", list_operation["responses"]["200"]["description"])
|
||||
self.assertIn(
|
||||
"Карточка организации",
|
||||
detail_operation["responses"]["200"]["description"],
|
||||
)
|
||||
|
||||
def test_retrieve_returns_item_by_uid(self):
|
||||
organization = Organization.objects.create(
|
||||
name='ООО "Ромашка"',
|
||||
inn="7712345678",
|
||||
kpp="771201001",
|
||||
ogrn="1027700132196",
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-detail", args=[organization.uid])
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["uid"], str(organization.uid))
|
||||
self.assertEqual(response.data["name"], 'ООО "Ромашка"')
|
||||
|
||||
def test_response_includes_normalized_name(self):
|
||||
organization = Organization.objects.create(
|
||||
name='АКЦИОНЕРНОЕ ОБЩЕСТВО "СЕВЕРНЫЙ МОСТ"',
|
||||
inn="7712345679",
|
||||
kpp="771201002",
|
||||
ogrn="1027700132197",
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-detail", args=[organization.uid])
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["name"], 'АКЦИОНЕРНОЕ ОБЩЕСТВО "СЕВЕРНЫЙ МОСТ"')
|
||||
self.assertEqual(response.data["normalized_name"], 'АО "Северный Мост"')
|
||||
|
||||
def test_list_keeps_data_source_summary_when_data_payload_is_excluded(self):
|
||||
organization = Organization.objects.create(
|
||||
name='ООО "Сводка данных"',
|
||||
inn="7712345681",
|
||||
kpp="771201004",
|
||||
ogrn="1027700132199",
|
||||
)
|
||||
IndustrialCertificateRecordFactory(
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
)
|
||||
OrganizationDataSnapshotRefreshService.refresh(
|
||||
organization_uids=[str(organization.uid)]
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{
|
||||
"inn": organization.inn,
|
||||
"has_registry": "false",
|
||||
"exclude_data": (
|
||||
"industrial,industrial_products,manufactures,inspections,"
|
||||
"procurements,procurements_44fz,procurements_223fz,contracts,"
|
||||
"unfair_suppliers,fas_goz,arbitration,fedresurs_bankruptcy,"
|
||||
"fstec,vacancies,fns_reports"
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
item = response.data["data"][0]
|
||||
self.assertEqual(item["data"], {})
|
||||
self.assertEqual(
|
||||
item["data_sources"],
|
||||
[{"source": "industrial", "count": 1}],
|
||||
)
|
||||
|
||||
def test_normalized_name_compacts_scientific_production_forms(self):
|
||||
organization = Organization.objects.create(
|
||||
name='Научно-Производственное Предприятие "ИМПУЛЬС"',
|
||||
inn="7712345680",
|
||||
kpp="771201003",
|
||||
ogrn="1027700132198",
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-detail", args=[organization.uid])
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["normalized_name"], 'НПП "Импульс"')
|
||||
|
||||
def test_list_filters_by_identifiers_and_searches_by_name(self):
|
||||
target = Organization.objects.create(
|
||||
name='ООО "Северный мост"',
|
||||
inn="7711111111",
|
||||
kpp="771101001",
|
||||
ogrn="1027700132111",
|
||||
)
|
||||
Organization.objects.create(
|
||||
name='АО "Южный путь"',
|
||||
inn="7722222222",
|
||||
kpp="772201001",
|
||||
ogrn="1027700132222",
|
||||
)
|
||||
|
||||
by_inn = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"inn": target.inn, "has_registry": "false"},
|
||||
)
|
||||
by_search = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"search": "мост", "has_registry": "false"},
|
||||
)
|
||||
|
||||
self.assertEqual(by_inn.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(by_inn.data["meta"]["pagination"]["total_count"], 1)
|
||||
self.assertEqual(by_inn.data["data"][0]["uid"], str(target.uid))
|
||||
|
||||
self.assertEqual(by_search.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(by_search.data["meta"]["pagination"]["total_count"], 1)
|
||||
self.assertEqual(by_search.data["data"][0]["uid"], str(target.uid))
|
||||
|
||||
def test_list_searches_by_identifier_fragments_for_dashboard(self):
|
||||
target = Organization.objects.create(
|
||||
name='ООО "Поиск по реквизитам"',
|
||||
inn="7711111122",
|
||||
kpp="771102222",
|
||||
ogrn="1027700132122",
|
||||
)
|
||||
Organization.objects.create(
|
||||
name='ООО "Другая организация"',
|
||||
inn="7722222233",
|
||||
kpp="772203333",
|
||||
ogrn="1027700132233",
|
||||
)
|
||||
url = reverse("api_v2:organizations:organizations-list")
|
||||
|
||||
for search_value in ("111122", "1027700132122", "102222"):
|
||||
with self.subTest(search_value=search_value):
|
||||
response = self.client.get(
|
||||
url,
|
||||
{"search": search_value, "has_registry": "false"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["meta"]["pagination"]["total_count"], 1)
|
||||
self.assertEqual(response.data["data"][0]["uid"], str(target.uid))
|
||||
|
||||
def test_list_response_is_cached_by_query_string(self):
|
||||
Organization.objects.create(
|
||||
name='ООО "Кеш"',
|
||||
inn="7733333333",
|
||||
kpp="773301001",
|
||||
ogrn="1027700132333",
|
||||
)
|
||||
url = reverse("api_v2:organizations:organizations-list")
|
||||
|
||||
first_response = self.client.get(
|
||||
url,
|
||||
{"inn": "7733333333", "has_registry": "false"},
|
||||
)
|
||||
Organization.objects.create(
|
||||
name='ООО "После кеша"',
|
||||
inn="7744444444",
|
||||
kpp="774401001",
|
||||
ogrn="1027700132444",
|
||||
)
|
||||
second_response = self.client.get(
|
||||
url,
|
||||
{"inn": "7733333333", "has_registry": "false"},
|
||||
)
|
||||
different_query_response = self.client.get(
|
||||
url,
|
||||
{"inn": "7744444444", "has_registry": "false"},
|
||||
)
|
||||
|
||||
self.assertEqual(first_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(first_response["X-Cache"], "MISS")
|
||||
self.assertEqual(second_response["X-Cache"], "HIT")
|
||||
self.assertEqual(second_response.data, first_response.data)
|
||||
self.assertEqual(different_query_response["X-Cache"], "MISS")
|
||||
self.assertEqual(different_query_response.data["data"][0]["inn"], "7744444444")
|
||||
|
||||
def test_retrieve_response_is_cached(self):
|
||||
organization = Organization.objects.create(
|
||||
name='ООО "Деталь"',
|
||||
inn="7755555555",
|
||||
kpp="775501001",
|
||||
ogrn="1027700132555",
|
||||
)
|
||||
url = reverse("api_v2:organizations:organizations-detail", args=[organization.uid])
|
||||
|
||||
first_response = self.client.get(url)
|
||||
organization.name = 'ООО "Изменено"'
|
||||
organization.save(update_fields=["name"])
|
||||
second_response = self.client.get(url)
|
||||
|
||||
self.assertEqual(first_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(first_response["X-Cache"], "MISS")
|
||||
self.assertEqual(second_response["X-Cache"], "HIT")
|
||||
self.assertEqual(second_response.data["name"], 'ООО "Деталь"')
|
||||
|
||||
def test_list_includes_parser_presence_and_active_registries(self):
|
||||
organization = Organization.objects.create(
|
||||
name='ООО "Данные"',
|
||||
inn="7777777777",
|
||||
kpp="777701001",
|
||||
ogrn="1027700132777",
|
||||
)
|
||||
registry = RegisterFactory(name="Росатом ГОЗ")
|
||||
registry_organization = RegistryOrganizationFactory(
|
||||
mn_inn=int(organization.inn),
|
||||
mn_ogrn=int(organization.ogrn),
|
||||
)
|
||||
RegistryMembershipPeriodFactory(
|
||||
registry=registry,
|
||||
organization=registry_organization,
|
||||
)
|
||||
certificate = IndustrialCertificateRecordFactory(
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
certificate_number="CERT-ORG-V2-1",
|
||||
)
|
||||
product = IndustrialProductRecordFactory(
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
registry_number="PRODUCT-ORG-V2-1",
|
||||
)
|
||||
manufacturer = ManufacturerRecordFactory(
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
full_legal_name='ООО "Данные"',
|
||||
)
|
||||
inspection = InspectionRecordFactory(
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
registration_number="INSPECTION-ORG-V2-1",
|
||||
)
|
||||
procurement = ProcurementRecordFactory(
|
||||
customer_inn=organization.inn,
|
||||
customer_ogrn=organization.ogrn,
|
||||
purchase_number="PROCUREMENT-ORG-V2-1",
|
||||
)
|
||||
generic_record = GenericParserRecord.objects.create(
|
||||
load_batch=1,
|
||||
source=ParserLoadLog.Source.FSTEC,
|
||||
external_id="fstec-1",
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
organisation_name=organization.name,
|
||||
title="FSTEC record",
|
||||
payload={"source": "fstec"},
|
||||
)
|
||||
GenericParserRecord.objects.create(
|
||||
load_batch=1,
|
||||
source=ParserLoadLog.Source.PROCUREMENTS_44FZ,
|
||||
external_id="procurements-44fz-1",
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
organisation_name=organization.name,
|
||||
title="44-FZ record",
|
||||
)
|
||||
financial_report = FinancialReport.objects.create(
|
||||
external_id="fin-1",
|
||||
ogrn=organization.ogrn,
|
||||
file_name="fin.xlsx",
|
||||
file_hash="a" * 64,
|
||||
load_batch=1,
|
||||
status=FinancialReport.Status.SUCCESS,
|
||||
source=FinancialReport.SourceType.API,
|
||||
)
|
||||
FinancialReportLine.objects.create(
|
||||
report=financial_report,
|
||||
form_code="1",
|
||||
line_code="1100",
|
||||
line_name="Внеоборотные активы",
|
||||
year=2021,
|
||||
period_start=100,
|
||||
period_end=200,
|
||||
)
|
||||
FinancialReportLine.objects.create(
|
||||
report=financial_report,
|
||||
form_code="1",
|
||||
line_code="1300",
|
||||
line_name="Капитал и резервы",
|
||||
year=2021,
|
||||
period_start=300,
|
||||
period_end=400,
|
||||
)
|
||||
FinancialReportLine.objects.create(
|
||||
report=financial_report,
|
||||
form_code="2",
|
||||
line_code="2110",
|
||||
line_name="Выручка",
|
||||
year=2022,
|
||||
period_start=None,
|
||||
period_end=500,
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"inn": organization.inn},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
item = response.data["data"][0]
|
||||
self.assertEqual(
|
||||
item["registries"],
|
||||
[{"id": str(registry.id), "name": "Росатом ГОЗ"}],
|
||||
)
|
||||
self.assertNotIn("data_presence", item)
|
||||
self.assertIn("data", item)
|
||||
self.assertEqual(
|
||||
item["data"]["industrial"],
|
||||
[
|
||||
{
|
||||
"id": certificate.id,
|
||||
"load_batch": certificate.load_batch,
|
||||
"issue_date": certificate.issue_date,
|
||||
"issue_date_normalized": None,
|
||||
"certificate_number": "CERT-ORG-V2-1",
|
||||
"expiry_date": certificate.expiry_date,
|
||||
"expiry_date_normalized": None,
|
||||
"certificate_file_url": certificate.certificate_file_url,
|
||||
"organisation_name": certificate.organisation_name,
|
||||
"inn": organization.inn,
|
||||
"ogrn": organization.ogrn,
|
||||
"registry_organization": None,
|
||||
"created_at": certificate.created_at.isoformat().replace(
|
||||
"+00:00", "Z"
|
||||
),
|
||||
"updated_at": certificate.updated_at.isoformat().replace(
|
||||
"+00:00", "Z"
|
||||
),
|
||||
}
|
||||
],
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["industrial_products"][0]["registry_number"],
|
||||
"PRODUCT-ORG-V2-1",
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["industrial_products"][0]["id"],
|
||||
product.id,
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["manufactures"][0]["id"],
|
||||
manufacturer.id,
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["inspections"][0]["registration_number"],
|
||||
"INSPECTION-ORG-V2-1",
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["inspections"][0]["id"],
|
||||
inspection.id,
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["procurements"][0]["purchase_number"],
|
||||
"PROCUREMENT-ORG-V2-1",
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["procurements"][0]["id"],
|
||||
procurement.id,
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["procurements_44fz"][0]["external_id"],
|
||||
"procurements-44fz-1",
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["fstec"][0]["id"],
|
||||
generic_record.id,
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["fns_reports"][0]["external_id"],
|
||||
"fin-1",
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["fns_reports"][0]["id"],
|
||||
financial_report.id,
|
||||
)
|
||||
self.assertEqual(item["data"]["fns_reports"][0]["lines_count"], 3)
|
||||
self.assertEqual(
|
||||
item["data"]["fns_reports"][0]["lines"]["2021"]["active"]["1100"],
|
||||
{
|
||||
"form_code": "1",
|
||||
"name": "Внеоборотные активы",
|
||||
"period_start": 100,
|
||||
"period_end": 200,
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["fns_reports"][0]["lines"]["2021"]["passive"]["1300"],
|
||||
{
|
||||
"form_code": "1",
|
||||
"name": "Капитал и резервы",
|
||||
"period_start": 300,
|
||||
"period_end": 400,
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
item["data"]["fns_reports"][0]["lines"]["2022"]["form_2"]["2110"],
|
||||
{
|
||||
"form_code": "2",
|
||||
"name": "Выручка",
|
||||
"period_start": None,
|
||||
"period_end": 500,
|
||||
},
|
||||
)
|
||||
self.assertTrue(
|
||||
all(isinstance(value, list) for value in item["data"].values())
|
||||
)
|
||||
|
||||
def test_filters_by_registry_and_has_registry(self):
|
||||
with_registry = Organization.objects.create(
|
||||
name='ООО "В реестре"',
|
||||
inn="7788888888",
|
||||
kpp="778801001",
|
||||
ogrn="1027700132888",
|
||||
)
|
||||
without_registry = Organization.objects.create(
|
||||
name='ООО "Без реестра"',
|
||||
inn="7799999999",
|
||||
kpp="779901001",
|
||||
ogrn="1027700132999",
|
||||
)
|
||||
registry = RegisterFactory(name="Роскосмос ОПК")
|
||||
registry_organization = RegistryOrganizationFactory(
|
||||
mn_inn=int(with_registry.inn),
|
||||
mn_ogrn=int(with_registry.ogrn),
|
||||
)
|
||||
RegistryMembershipPeriodFactory(
|
||||
registry=registry,
|
||||
organization=registry_organization,
|
||||
)
|
||||
|
||||
by_registry = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"registry": str(registry.id)},
|
||||
)
|
||||
has_registry = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"has_registry": "true"},
|
||||
)
|
||||
no_registry = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"has_registry": "false", "ordering": "inn"},
|
||||
)
|
||||
|
||||
self.assertEqual(by_registry.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(by_registry.data["meta"]["pagination"]["total_count"], 1)
|
||||
self.assertEqual(by_registry.data["data"][0]["uid"], str(with_registry.uid))
|
||||
|
||||
self.assertEqual(has_registry.data["meta"]["pagination"]["total_count"], 1)
|
||||
self.assertEqual(has_registry.data["data"][0]["uid"], str(with_registry.uid))
|
||||
|
||||
self.assertEqual(no_registry.data["meta"]["pagination"]["total_count"], 1)
|
||||
self.assertEqual(no_registry.data["data"][0]["uid"], str(without_registry.uid))
|
||||
|
||||
def test_list_defaults_to_has_registry_true(self):
|
||||
with_registry = Organization.objects.create(
|
||||
name='ООО "Дефолтный реестр"',
|
||||
inn="7800000101",
|
||||
kpp="780001101",
|
||||
ogrn="1027700133101",
|
||||
)
|
||||
Organization.objects.create(
|
||||
name='ООО "Скрыто по дефолту"',
|
||||
inn="7800000102",
|
||||
kpp="780001102",
|
||||
ogrn="1027700133102",
|
||||
)
|
||||
registry = RegisterFactory(name="Дефолтный реестр")
|
||||
registry_organization = RegistryOrganizationFactory(
|
||||
mn_inn=int(with_registry.inn),
|
||||
mn_ogrn=int(with_registry.ogrn),
|
||||
)
|
||||
RegistryMembershipPeriodFactory(
|
||||
registry=registry,
|
||||
organization=registry_organization,
|
||||
)
|
||||
|
||||
default_response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list")
|
||||
)
|
||||
explicit_false_response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"has_registry": "false"},
|
||||
)
|
||||
|
||||
self.assertEqual(default_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(default_response.data["meta"]["pagination"]["total_count"], 1)
|
||||
self.assertEqual(default_response.data["data"][0]["uid"], str(with_registry.uid))
|
||||
|
||||
self.assertEqual(
|
||||
explicit_false_response.data["meta"]["pagination"]["total_count"],
|
||||
1,
|
||||
)
|
||||
self.assertEqual(
|
||||
explicit_false_response.data["data"][0]["inn"],
|
||||
"7800000102",
|
||||
)
|
||||
|
||||
def test_filters_by_data_presence(self):
|
||||
with_industrial = Organization.objects.create(
|
||||
name='ООО "С промышленностью"',
|
||||
inn="7800000001",
|
||||
kpp="780001001",
|
||||
ogrn="1027700133001",
|
||||
)
|
||||
with_fns = Organization.objects.create(
|
||||
name='ООО "С отчетностью"',
|
||||
inn="7800000002",
|
||||
kpp="780001002",
|
||||
ogrn="1027700133002",
|
||||
)
|
||||
without_data = Organization.objects.create(
|
||||
name='ООО "Без данных"',
|
||||
inn="7800000003",
|
||||
kpp="780001003",
|
||||
ogrn="1027700133003",
|
||||
)
|
||||
IndustrialCertificateRecordFactory(
|
||||
inn=with_industrial.inn,
|
||||
ogrn=with_industrial.ogrn,
|
||||
)
|
||||
FinancialReport.objects.create(
|
||||
external_id="fin-presence-1",
|
||||
ogrn=with_fns.ogrn,
|
||||
file_name="fin_presence.xlsx",
|
||||
file_hash="b" * 64,
|
||||
load_batch=1,
|
||||
status=FinancialReport.Status.SUCCESS,
|
||||
source=FinancialReport.SourceType.API,
|
||||
)
|
||||
|
||||
has_industrial = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"has_industrial": "true", "has_registry": "false"},
|
||||
)
|
||||
no_industrial = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{
|
||||
"has_industrial": "false",
|
||||
"has_registry": "false",
|
||||
"ordering": "inn",
|
||||
},
|
||||
)
|
||||
has_fns = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"has_fns_reports": "true", "has_registry": "false"},
|
||||
)
|
||||
|
||||
self.assertEqual(has_industrial.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(has_industrial.data["meta"]["pagination"]["total_count"], 1)
|
||||
self.assertEqual(
|
||||
has_industrial.data["data"][0]["uid"],
|
||||
str(with_industrial.uid),
|
||||
)
|
||||
|
||||
self.assertEqual(no_industrial.data["meta"]["pagination"]["total_count"], 2)
|
||||
self.assertEqual(
|
||||
[item["uid"] for item in no_industrial.data["data"]],
|
||||
[str(with_fns.uid), str(without_data.uid)],
|
||||
)
|
||||
|
||||
self.assertEqual(has_fns.data["meta"]["pagination"]["total_count"], 1)
|
||||
self.assertEqual(has_fns.data["data"][0]["uid"], str(with_fns.uid))
|
||||
|
||||
def test_limits_response_data_sources(self):
|
||||
organization = Organization.objects.create(
|
||||
name='ООО "Источник"',
|
||||
inn="7800000201",
|
||||
kpp="780002101",
|
||||
ogrn="1027700133201",
|
||||
)
|
||||
IndustrialCertificateRecordFactory(
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
)
|
||||
GenericParserRecord.objects.create(
|
||||
load_batch=1,
|
||||
source=ParserLoadLog.Source.FSTEC,
|
||||
external_id="fstec-source-limit",
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
organisation_name=organization.name,
|
||||
)
|
||||
FinancialReport.objects.create(
|
||||
external_id="fin-source-limit",
|
||||
ogrn=organization.ogrn,
|
||||
file_name="fin_source_limit.xlsx",
|
||||
file_hash="c" * 64,
|
||||
load_batch=1,
|
||||
status=FinancialReport.Status.SUCCESS,
|
||||
source=FinancialReport.SourceType.API,
|
||||
)
|
||||
|
||||
detail_url = reverse(
|
||||
"api_v2:organizations:organizations-detail",
|
||||
args=[organization.uid],
|
||||
)
|
||||
only_response = self.client.get(
|
||||
detail_url,
|
||||
{"data": "industrial,fns_reports"},
|
||||
)
|
||||
excluded_response = self.client.get(
|
||||
detail_url,
|
||||
{"exclude_data": "fns_reports"},
|
||||
)
|
||||
repeated_response = self.client.get(
|
||||
detail_url,
|
||||
[("data", "industrial"), ("data", "fstec")],
|
||||
)
|
||||
empty_response = self.client.get(
|
||||
detail_url,
|
||||
{"data": "industrial", "exclude_data": "industrial"},
|
||||
)
|
||||
|
||||
self.assertEqual(only_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
set(only_response.data["data"]),
|
||||
{"industrial", "fns_reports"},
|
||||
)
|
||||
self.assertEqual(len(only_response.data["data"]["industrial"]), 1)
|
||||
self.assertEqual(len(only_response.data["data"]["fns_reports"]), 1)
|
||||
|
||||
self.assertEqual(excluded_response.status_code, status.HTTP_200_OK)
|
||||
self.assertNotIn("fns_reports", excluded_response.data["data"])
|
||||
self.assertIn("industrial", excluded_response.data["data"])
|
||||
|
||||
self.assertEqual(repeated_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
set(repeated_response.data["data"]),
|
||||
{"industrial", "fstec"},
|
||||
)
|
||||
self.assertEqual(empty_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(empty_response.data["data"], {})
|
||||
|
||||
def test_rejects_unknown_response_data_source(self):
|
||||
organization = Organization.objects.create(
|
||||
name='ООО "Неверный источник"',
|
||||
inn="7800000202",
|
||||
kpp="780002102",
|
||||
ogrn="1027700133202",
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-detail", args=[organization.uid]),
|
||||
{"data": "unknown"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||
self.assertIn("data", response.data)
|
||||
|
||||
def test_selected_data_sources_keep_constant_query_count(self):
|
||||
organization = Organization.objects.create(
|
||||
name='ООО "Без N+1"',
|
||||
inn="7800000203",
|
||||
kpp="780002103",
|
||||
ogrn="1027700133203",
|
||||
)
|
||||
IndustrialCertificateRecordFactory(
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
)
|
||||
for index, year in enumerate((2021, 2022, 2023, 2024), start=1):
|
||||
report = FinancialReport.objects.create(
|
||||
external_id=f"fin-query-count-{year}",
|
||||
ogrn=organization.ogrn,
|
||||
file_name=f"fin_query_count_{year}.xlsx",
|
||||
file_hash=f"{index}" * 64,
|
||||
load_batch=1,
|
||||
status=FinancialReport.Status.SUCCESS,
|
||||
source=FinancialReport.SourceType.API,
|
||||
)
|
||||
FinancialReportLine.objects.bulk_create(
|
||||
[
|
||||
FinancialReportLine(
|
||||
report=report,
|
||||
form_code="1",
|
||||
line_code=str(1100 + line_index),
|
||||
line_name=f"Line {line_index}",
|
||||
year=year,
|
||||
period_start=line_index,
|
||||
period_end=line_index * 2,
|
||||
)
|
||||
for line_index in range(1, 101)
|
||||
]
|
||||
)
|
||||
for source in (
|
||||
ParserLoadLog.Source.FSTEC,
|
||||
ParserLoadLog.Source.CONTRACTS,
|
||||
ParserLoadLog.Source.TRUDVSEM,
|
||||
):
|
||||
GenericParserRecord.objects.create(
|
||||
load_batch=1,
|
||||
source=source,
|
||||
external_id=f"{source}-query-count",
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
organisation_name=organization.name,
|
||||
)
|
||||
|
||||
OrganizationDataSnapshotRefreshService.refresh(
|
||||
organization_uids=[str(organization.uid)],
|
||||
)
|
||||
url = reverse("api_v2:organizations:organizations-detail", args=[organization.uid])
|
||||
with CaptureQueriesContext(connection) as captured:
|
||||
response = self.client.get(
|
||||
url,
|
||||
{"data": "industrial,fns_reports,fstec,contracts,vacancies"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
set(response.data["data"]),
|
||||
{"industrial", "fns_reports", "fstec", "contracts", "vacancies"},
|
||||
)
|
||||
self.assertNotIn("trudvsem", response.data["data"])
|
||||
self.assertEqual(len(response.data["data"]["fns_reports"]), 4)
|
||||
self.assertLessEqual(len(captured), 1)
|
||||
|
||||
def test_v2_renames_trudvsem_data_source_to_vacancies(self):
|
||||
organization = Organization.objects.create(
|
||||
name='ООО "Вакансии"',
|
||||
inn="7800000204",
|
||||
kpp="780002104",
|
||||
ogrn="1027700133204",
|
||||
)
|
||||
GenericParserRecord.objects.create(
|
||||
load_batch=1,
|
||||
source=ParserLoadLog.Source.TRUDVSEM,
|
||||
external_id="vacancy-1",
|
||||
inn=organization.inn,
|
||||
ogrn=organization.ogrn,
|
||||
organisation_name=organization.name,
|
||||
title="Инженер",
|
||||
)
|
||||
OrganizationDataSnapshotRefreshService.refresh(
|
||||
organization_uids=[str(organization.uid)],
|
||||
)
|
||||
|
||||
detail_url = reverse(
|
||||
"api_v2:organizations:organizations-detail",
|
||||
args=[organization.uid],
|
||||
)
|
||||
vacancies_response = self.client.get(detail_url, {"data": "vacancies"})
|
||||
internal_source_response = self.client.get(detail_url, {"data": "trudvsem"})
|
||||
has_vacancies_response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{
|
||||
"has_vacancies": "true",
|
||||
"has_registry": "false",
|
||||
"data": "vacancies",
|
||||
},
|
||||
)
|
||||
|
||||
self.assertEqual(vacancies_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(set(vacancies_response.data["data"]), {"vacancies"})
|
||||
self.assertEqual(
|
||||
vacancies_response.data["data"]["vacancies"][0]["external_id"],
|
||||
"vacancy-1",
|
||||
)
|
||||
self.assertNotIn("trudvsem", vacancies_response.data["data"])
|
||||
|
||||
self.assertEqual(
|
||||
internal_source_response.status_code,
|
||||
status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
self.assertIn("trudvsem", str(internal_source_response.data))
|
||||
|
||||
self.assertEqual(has_vacancies_response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(
|
||||
has_vacancies_response.data["data"][0]["uid"],
|
||||
str(organization.uid),
|
||||
)
|
||||
|
||||
@override_settings(ORGANIZATIONS_V2_ALLOW_ANONYMOUS=True)
|
||||
def test_dev_flag_allows_anonymous_access(self):
|
||||
self.client.force_authenticate(user=None)
|
||||
Organization.objects.create(
|
||||
name='ООО "Без токена"',
|
||||
inn="7766666666",
|
||||
kpp="776601001",
|
||||
ogrn="1027700132666",
|
||||
)
|
||||
|
||||
response = self.client.get(
|
||||
reverse("api_v2:organizations:organizations-list"),
|
||||
{"has_registry": "false"},
|
||||
)
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(response.data["meta"]["pagination"]["total_count"], 1)
|
||||
|
||||
@override_settings(ORGANIZATIONS_V2_ALLOW_ANONYMOUS=False)
|
||||
def test_without_dev_flag_requires_authentication(self):
|
||||
self.client.force_authenticate(user=None)
|
||||
|
||||
response = self.client.get(reverse("api_v2:organizations:organizations-list"))
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
|
||||
Reference in New Issue
Block a user