feat: export organizations to state corp v3
All checks were successful
CI/CD Pipeline / Quality Gate (push) Successful in 30s
CI/CD Pipeline / Build and Push Images (push) Successful in 41s
CI/CD Pipeline / Internal Notify (push) Successful in 0s
CI/CD Pipeline / Deploy Dev via Compose (push) Successful in 19s

This commit is contained in:
2026-06-13 14:07:40 +02:00
parent 90d6bb90bd
commit f95ba3f56a
11 changed files with 513 additions and 218 deletions

View File

@@ -34,7 +34,7 @@ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from django.conf import settings
from django.db.models import Q
from django.utils import timezone
from registers.models import Organization, RegistryMembershipPeriod
from organizations.models import Organization
class StateCorpExchangeError(ValueError):
@@ -60,15 +60,9 @@ class StateCorpExchangeService:
AAD = b"state-corp-exchange-v1"
PAYLOAD_FORMAT = "state-corp-exchange-payload"
BIN_FORMAT = "state-corp-exchange-bin"
SCHEMA_VERSION = 2
ROSATOM_ROSCOSMOS_REGISTRY_NAMES = (
"Реестр госкорпорации Роскосмос",
"Реестр госкорпорации Роскосмос ГОЗ",
"Реестр госкорпорации Роскосмос ОПК",
"Реестр госкорпорации Росатом",
"Реестр госкорпорации Росатом ГОЗ",
"Реестр госкорпорации Росатом ОПК",
)
SCHEMA_VERSION = 3
ROSATOM_ROSCOSMOS_GK_CODE_VALUES = ("rosatom", "roscosmos", "roskosmos")
ROSATOM_ROSCOSMOS_GK_NAME_KEYWORDS = ("Росатом", "Роскосмос")
@classmethod
def build_package(
@@ -86,20 +80,16 @@ class StateCorpExchangeService:
organizations = (
cls._get_organizations(normalized_inns)
if normalized_inns
else cls._get_rosatom_roscosmos_organizations(snapshot_date)
else cls._get_rosatom_roscosmos_organizations()
)
allowed_inns = {str(item.mn_inn) for item in organizations}
allowed_inns = {str(item.inn) for item in organizations if item.inn}
allowed_ogrn_to_inn = {
str(item.mn_ogrn): str(item.mn_inn)
str(item.ogrn): str(item.inn)
for item in organizations
if item.mn_ogrn
if item.ogrn and item.inn
}
data = {
"organizations": cls._serialize_organizations(organizations),
"registry_memberships": cls._serialize_registry_memberships(
organizations=organizations,
actual_date=snapshot_date,
),
"industrial_certificates": cls._serialize_industrial_certificates(
allowed_inns
),
@@ -288,85 +278,92 @@ class StateCorpExchangeService:
@classmethod
def _get_organizations(cls, organization_inns: list[str]) -> list[Organization]:
queryset = Organization.objects.all().order_by("id")
queryset = cls._rosatom_roscosmos_queryset()
if organization_inns:
queryset = queryset.filter(
mn_inn__in=[int(item) for item in organization_inns]
)
queryset = queryset.filter(inn__in=organization_inns)
return list(queryset)
@classmethod
def _get_rosatom_roscosmos_organizations(
cls,
actual_date: date,
) -> list[Organization]:
organization_ids = (
RegistryMembershipPeriod.objects.filter(
registry__name__in=cls.ROSATOM_ROSCOSMOS_REGISTRY_NAMES,
started_at__lte=actual_date,
)
.filter(Q(ended_at__isnull=True) | Q(ended_at__gt=actual_date))
.order_by("organization_id")
.values_list("organization_id", flat=True)
.distinct()
)
return list(Organization.objects.filter(id__in=organization_ids).order_by("id"))
def _get_rosatom_roscosmos_organizations(cls) -> list[Organization]:
return list(cls._rosatom_roscosmos_queryset())
@classmethod
def _rosatom_roscosmos_queryset(cls):
queryset = Organization.objects.exclude(inn="")
name_query = Q()
for keyword in cls.ROSATOM_ROSCOSMOS_GK_NAME_KEYWORDS:
name_query |= Q(gk_name__icontains=keyword)
code_query = Q()
for code in cls.ROSATOM_ROSCOSMOS_GK_CODE_VALUES:
code_query |= Q(gk_code__iexact=code)
return queryset.filter(name_query | code_query).order_by("rn", "uid")
@classmethod
def _serialize_organizations(
cls,
organizations: list[Organization],
) -> list[dict[str, str]]:
) -> list[dict[str, str | int | bool | None]]:
return [
{
"inn": str(item.mn_inn),
"name": item.pn_name,
"ogrn": str(item.mn_ogrn),
"kpp": str(item.in_kpp or ""),
"okpo": item.mn_okpo,
}
for item in organizations
]
@classmethod
def _serialize_registry_memberships(
cls,
*,
organizations: list[Organization],
actual_date: date,
) -> list[dict[str, str | None]]:
organization_ids = [organization.id for organization in organizations]
if not organization_ids:
return []
memberships = (
RegistryMembershipPeriod.objects.select_related("registry", "organization")
.filter(
organization_id__in=organization_ids,
registry__name__in=cls.ROSATOM_ROSCOSMOS_REGISTRY_NAMES,
started_at__lte=actual_date,
)
.filter(Q(ended_at__isnull=True) | Q(ended_at__gt=actual_date))
.order_by(
"registry__name",
"organization__mn_inn",
"started_at",
"id",
)
)
return [
{
"organization_inn": str(membership.organization.mn_inn),
"registry_name": membership.registry.name,
"started_at": membership.started_at.isoformat(),
"ended_at": (
membership.ended_at.isoformat()
if membership.ended_at and membership.ended_at <= actual_date
"mostovik_uid": str(item.uid),
"rn": item.rn,
"name": item.name,
"full_name": item.full_name,
"short_name": item.short_name,
"pn_name": item.pn_name,
"pn_name_en": item.pn_name_en,
"inn": item.inn,
"kpp": item.kpp,
"ogrn": item.ogrn,
"ogrip": item.ogrip,
"okpo": item.okpo,
"identity_status": item.identity_status,
"primary_identity": item.primary_identity,
"gk_code": item.gk_code,
"gk_name": item.gk_name,
"in_korp_code": item.in_korp_code,
"in_korp_name": item.in_korp_name,
"filial": item.filial,
"is_branch": item.is_branch,
"re_za": item.re_za,
"re_zasf": item.re_zasf,
"goz_participation": item.goz_participation,
"opk_registry_membership": item.opk_registry_membership,
"ropk_num": item.ropk_num,
"ropk_razdel_num": item.ropk_razdel_num,
"ropk_razdel_name": item.ropk_razdel_name,
"registration_date": (
item.registration_date.isoformat()
if item.registration_date
else None
),
"create_date": item.create_date,
"organizational_legal_form": item.organizational_legal_form,
"organizational_legal_form1": item.organizational_legal_form1,
"ownership_form": item.ownership_form,
"ownership_form1": item.ownership_form1,
"authorized_capital": cls._serialize_decimal(item.authorized_capital),
"legal_address": item.legal_address,
"business_act_cod": item.business_act_cod,
"business_activity": item.business_activity,
"general_director": item.general_director,
"general_director_tax_id": item.general_director_tax_id,
"uk": item.uk,
"inn_uk": item.inn_uk,
"appointment_date": (
item.appointment_date.isoformat() if item.appointment_date else None
),
"cf_fl_rn": item.cf_fl_rn,
"akc_fs": item.akc_fs,
"akc_sf": item.akc_sf,
"min": item.min,
"dep": item.dep,
"otr": item.otr,
"integrated_structure": item.integrated_structure,
"state_sector_code": item.state_sector_code,
"state_sector_name": item.state_sector_name,
}
for membership in memberships
for item in organizations
]
@classmethod