fix: search parser records by registry organization
All checks were successful
CI/CD Pipeline / Quality Gate (push) Successful in 32s
CI/CD Pipeline / Build and Push Images (push) Successful in 19s
CI/CD Pipeline / Internal Notify (push) Successful in 1s
CI/CD Pipeline / Deploy Dev in Dokploy (push) Successful in 2s

This commit is contained in:
2026-06-01 11:42:35 +02:00
parent e81bf65289
commit 2368bb7fb0
2 changed files with 259 additions and 31 deletions

View File

@@ -187,6 +187,16 @@ CORE_PROFILE_INDUSTRIAL_SOURCES = {
ParserLoadLog.Source.INDUSTRIAL_PRODUCTS,
ParserLoadLog.Source.MANUFACTURES,
}
REGISTRY_ORGANIZATION_SEARCH_FIELDS = [
"registry_organization__pn_name",
"registry_organization_inn_text",
"registry_organization_ogrn_text",
"registry_organization__mn_okpo",
]
REGISTRY_ORGANIZATION_SEARCH_DESCRIPTION = (
"Поиск также включает название, ИНН, ОГРН и ОКПО связанной организации "
"из реестров."
)
EXISTING_TASK_PARAMS = {
"industrial": {"proxies", "requested_by_id"},
"manufactures": {"proxies", "requested_by_id"},
@@ -205,6 +215,30 @@ EXISTING_TASK_PARAMS = {
},
"fns_financial": {"requested_by_id"},
}
def _with_registry_organization_search_annotations(queryset):
return queryset.annotate(
registry_organization_inn_text=Cast(
"registry_organization__mn_inn",
output_field=CharField(),
),
registry_organization_ogrn_text=Cast(
"registry_organization__mn_ogrn",
output_field=CharField(),
),
)
def _registry_organization_search_q(search: str) -> Q:
return (
Q(registry_organization__pn_name__icontains=search)
| Q(registry_organization_inn_text__icontains=search)
| Q(registry_organization_ogrn_text__icontains=search)
| Q(registry_organization__mn_okpo__icontains=search)
)
TRUDVSEM_PARAMS = {
"limit",
"offset",
@@ -580,7 +614,9 @@ class IndustrialCertificateViewSet(ReadOnlyModelViewSet):
Только чтение - добавление через парсер/админку.
"""
queryset = IndustrialCertificateRecord.objects.all().order_by("-created_at")
queryset = _with_registry_organization_search_annotations(
IndustrialCertificateRecord.objects.all()
).order_by("-created_at")
serializer_class = IndustrialCertificateSerializer
permission_classes = [IsAuthenticated]
filterset_fields = [
@@ -590,7 +626,13 @@ class IndustrialCertificateViewSet(ReadOnlyModelViewSet):
"load_batch",
"registry_organization",
]
search_fields = ["organisation_name", "certificate_number", "inn", "ogrn"]
search_fields = [
"organisation_name",
"certificate_number",
"inn",
"ogrn",
*REGISTRY_ORGANIZATION_SEARCH_FIELDS,
]
@swagger_auto_schema(
tags=[MINPROMTORG_TAG],
@@ -598,7 +640,8 @@ class IndustrialCertificateViewSet(ReadOnlyModelViewSet):
operation_description=(
"Возвращает список сертификатов промышленного производства.\n"
"Поддерживает фильтрацию по: inn, ogrn, certificate_number, load_batch.\n"
"Поддерживает поиск по: organisation_name, certificate_number, inn, ogrn."
"Поддерживает поиск по: organisation_name, certificate_number, inn, ogrn.\n"
f"{REGISTRY_ORGANIZATION_SEARCH_DESCRIPTION}"
),
responses={
200: IndustrialCertificateSerializer(many=True),
@@ -634,11 +677,19 @@ class ManufacturerViewSet(ReadOnlyModelViewSet):
Только чтение - добавление через парсер/админку.
"""
queryset = ManufacturerRecord.objects.all().order_by("-created_at")
queryset = _with_registry_organization_search_annotations(
ManufacturerRecord.objects.all()
).order_by("-created_at")
serializer_class = ManufacturerSerializer
permission_classes = [IsAuthenticated]
filterset_fields = ["inn", "ogrn", "load_batch", "registry_organization"]
search_fields = ["full_legal_name", "inn", "ogrn", "address"]
search_fields = [
"full_legal_name",
"inn",
"ogrn",
"address",
*REGISTRY_ORGANIZATION_SEARCH_FIELDS,
]
@swagger_auto_schema(
tags=[MINPROMTORG_TAG],
@@ -646,7 +697,8 @@ class ManufacturerViewSet(ReadOnlyModelViewSet):
operation_description=(
"Возвращает список производителей из реестра Минпромторга.\n"
"Поддерживает фильтрацию по: inn, ogrn, load_batch.\n"
"Поддерживает поиск по: full_legal_name, inn, ogrn, address."
"Поддерживает поиск по: full_legal_name, inn, ogrn, address.\n"
f"{REGISTRY_ORGANIZATION_SEARCH_DESCRIPTION}"
),
responses={
200: ManufacturerSerializer(many=True),
@@ -677,7 +729,9 @@ class IndustrialProductViewSet(ReadOnlyModelViewSet):
Только чтение - добавление через парсер/админку.
"""
queryset = IndustrialProductRecord.objects.all().order_by("-created_at")
queryset = _with_registry_organization_search_annotations(
IndustrialProductRecord.objects.all()
).order_by("-created_at")
serializer_class = IndustrialProductSerializer
permission_classes = [IsAuthenticated]
filterset_fields = [
@@ -694,6 +748,7 @@ class IndustrialProductViewSet(ReadOnlyModelViewSet):
"registry_number",
"inn",
"ogrn",
*REGISTRY_ORGANIZATION_SEARCH_FIELDS,
]
@swagger_auto_schema(
@@ -704,7 +759,8 @@ class IndustrialProductViewSet(ReadOnlyModelViewSet):
"Минпромторга.\n"
"Поддерживает фильтрацию по: inn, ogrn, registry_number, load_batch.\n"
"Поддерживает поиск по: full_organisation_name, product_name, "
"product_model, registry_number, inn, ogrn."
"product_model, registry_number, inn, ogrn.\n"
f"{REGISTRY_ORGANIZATION_SEARCH_DESCRIPTION}"
),
responses={
200: IndustrialProductSerializer(many=True),
@@ -743,7 +799,9 @@ class InspectionViewSet(ReadOnlyModelViewSet):
Только чтение - добавление через парсер/админку.
"""
queryset = InspectionRecord.objects.all().order_by("-created_at")
queryset = _with_registry_organization_search_annotations(
InspectionRecord.objects.all()
).order_by("-created_at")
serializer_class = InspectionSerializer
permission_classes = [IsAuthenticated]
filterset_fields = [
@@ -762,6 +820,7 @@ class InspectionViewSet(ReadOnlyModelViewSet):
"inn",
"ogrn",
"control_authority",
*REGISTRY_ORGANIZATION_SEARCH_FIELDS,
]
@swagger_auto_schema(
@@ -772,7 +831,8 @@ class InspectionViewSet(ReadOnlyModelViewSet):
"Поддерживает фильтрацию по: inn, ogrn, registration_number, "
"is_federal_law_248, data_year, data_month, load_batch.\n"
"Поддерживает поиск по: organisation_name, registration_number, "
"inn, ogrn, control_authority."
"inn, ogrn, control_authority.\n"
f"{REGISTRY_ORGANIZATION_SEARCH_DESCRIPTION}"
),
responses={
200: InspectionSerializer(many=True),
@@ -809,7 +869,9 @@ class ProcurementViewSet(ReadOnlyModelViewSet):
Только чтение - добавление через парсер/админку.
"""
queryset = ProcurementRecord.objects.all().order_by("-created_at")
queryset = _with_registry_organization_search_annotations(
ProcurementRecord.objects.all()
).order_by("-created_at")
serializer_class = ProcurementSerializer
permission_classes = [IsAuthenticated]
filterset_fields = [
@@ -830,6 +892,7 @@ class ProcurementViewSet(ReadOnlyModelViewSet):
"customer_name",
"customer_inn",
"customer_ogrn",
*REGISTRY_ORGANIZATION_SEARCH_FIELDS,
]
@swagger_auto_schema(
@@ -841,7 +904,8 @@ class ProcurementViewSet(ReadOnlyModelViewSet):
"purchase_number, law_type, status, region_code, "
"data_year, data_month, load_batch.\n"
"Поддерживает поиск по: purchase_name, purchase_number, "
"customer_name, customer_inn, customer_ogrn."
"customer_name, customer_inn, customer_ogrn.\n"
f"{REGISTRY_ORGANIZATION_SEARCH_DESCRIPTION}"
),
responses={
200: ProcurementSerializer(many=True),
@@ -877,9 +941,9 @@ class FinancialReportViewSet(ReadOnlyModelViewSet):
Только чтение - добавление через загрузку файлов.
"""
queryset = FinancialReport.objects.annotate(lines_count=Count("lines")).order_by(
"-created_at"
)
queryset = _with_registry_organization_search_annotations(
FinancialReport.objects.annotate(lines_count=Count("lines"))
).order_by("-created_at")
permission_classes = [IsAuthenticated]
filterset_fields = [
"ogrn",
@@ -888,7 +952,12 @@ class FinancialReportViewSet(ReadOnlyModelViewSet):
"load_batch",
"registry_organization",
]
search_fields = ["ogrn", "external_id", "file_name"]
search_fields = [
"ogrn",
"external_id",
"file_name",
*REGISTRY_ORGANIZATION_SEARCH_FIELDS,
]
def get_queryset(self):
queryset = super().get_queryset()
@@ -915,7 +984,8 @@ class FinancialReportViewSet(ReadOnlyModelViewSet):
"Возвращает список финансовых отчетов ФНС.\n"
"Поддерживает фильтрацию по: ogrn, external_id, status, "
"source, load_batch.\n"
"Поддерживает поиск по: ogrn, external_id, file_name."
"Поддерживает поиск по: ogrn, external_id, file_name.\n"
f"{REGISTRY_ORGANIZATION_SEARCH_DESCRIPTION}"
),
responses={
200: FinancialReportSerializer(many=True),
@@ -2206,17 +2276,8 @@ def _generic_search_q(search: str) -> Q:
def _apply_native_search(queryset, source: str, search: str):
if source != ParserLoadLog.Source.FNS_REPORTS:
return queryset.filter(_native_search_q(source, search))
return queryset.annotate(
registry_organization_inn_text=Cast(
"registry_organization__mn_inn",
output_field=CharField(),
),
).filter(
_native_search_q(source, search)
| Q(registry_organization_inn_text__icontains=search)
return _with_registry_organization_search_annotations(queryset).filter(
_native_search_q(source, search) | _registry_organization_search_q(search)
)
@@ -2249,7 +2310,9 @@ def _result_sources_for_request(descriptor, params: dict) -> set[str]:
def _filter_native_result_queryset(source: str, params: dict, sources: set[str]):
queryset = NATIVE_RECORD_MODELS[source].objects.all()
queryset = _with_registry_organization_search_annotations(
NATIVE_RECORD_MODELS[source].objects.all()
)
if source == ParserLoadLog.Source.FNS_REPORTS:
queryset = queryset.select_related("registry_organization")
if not sources:
@@ -2278,7 +2341,9 @@ def _filter_generic_result_queryset(
*,
route_source: str,
):
queryset = GenericParserRecord.objects.filter(source__in=sources)
queryset = _with_registry_organization_search_annotations(
GenericParserRecord.objects.filter(source__in=sources)
)
if not sources:
queryset = queryset.none()
requested_source = params.get("source")
@@ -2300,7 +2365,10 @@ def _filter_generic_result_queryset(
if params.get("record_date"):
queryset = queryset.filter(record_date=params["record_date"])
if params.get("search"):
queryset = queryset.filter(_generic_search_q(params["search"]))
search = params["search"]
queryset = queryset.filter(
_generic_search_q(search) | _registry_organization_search_q(search)
)
field_map = {
"id": "id",
"load_batch": "load_batch",