fix
This commit is contained in:
parent
9968d84e3b
commit
bef38dedd8
@ -65,6 +65,7 @@ COL_ADDRESS_ONE_LINE = _index(141)
|
|||||||
COL_ADDRESS_STRUCTURED = _index(142)
|
COL_ADDRESS_STRUCTURED = _index(142)
|
||||||
COL_REG_PLACE_ONE_LINE = _index(152)
|
COL_REG_PLACE_ONE_LINE = _index(152)
|
||||||
COL_REG_PLACE_STRUCTURED = _index(153)
|
COL_REG_PLACE_STRUCTURED = _index(153)
|
||||||
|
COL_EIO_ADDRESS_ONE_LINE = _index(167)
|
||||||
|
|
||||||
# ЕИО и ценные бумаги.
|
# ЕИО и ценные бумаги.
|
||||||
COL_EIO_INN = _index(163)
|
COL_EIO_INN = _index(163)
|
||||||
|
|||||||
@ -20,6 +20,7 @@ from .column_constants import (
|
|||||||
COL_CP_ISSUER_INN,
|
COL_CP_ISSUER_INN,
|
||||||
COL_CURRENCY,
|
COL_CURRENCY,
|
||||||
COL_CURRENCY_SIGN,
|
COL_CURRENCY_SIGN,
|
||||||
|
COL_EIO_ADDRESS_ONE_LINE,
|
||||||
COL_EIO_INN,
|
COL_EIO_INN,
|
||||||
COL_EIO_REG_PLACE_ONE_LINE,
|
COL_EIO_REG_PLACE_ONE_LINE,
|
||||||
COL_EMPLOYEE_SIGN,
|
COL_EMPLOYEE_SIGN,
|
||||||
@ -330,6 +331,7 @@ def build_fixed_row_by_index(
|
|||||||
"УчастникЮЛ.БенефициарЮЛ.",
|
"УчастникЮЛ.БенефициарЮЛ.",
|
||||||
"УчастникФЛИП.БенефициарФЛИП.",
|
"УчастникФЛИП.БенефициарФЛИП.",
|
||||||
"УчастникИНБОЮЛ.БенефициарИНБОЮЛ.",
|
"УчастникИНБОЮЛ.БенефициарИНБОЮЛ.",
|
||||||
|
"УчастникИНБОЮЛ.СведИНБОЮЛ.Учредитель.",
|
||||||
"СведЕИО.",
|
"СведЕИО.",
|
||||||
"БенефициарЮЛ.",
|
"БенефициарЮЛ.",
|
||||||
"БенефициарФЛИП.",
|
"БенефициарФЛИП.",
|
||||||
@ -378,6 +380,9 @@ def build_fixed_row_by_index(
|
|||||||
)
|
)
|
||||||
if not is_continuation:
|
if not is_continuation:
|
||||||
_apply_participant_identity_rules(row, participant_fields)
|
_apply_participant_identity_rules(row, participant_fields)
|
||||||
|
# Сначала убираем запрещённые однострочные адреса, чтобы структурная
|
||||||
|
# группа не очистила их компоненты до проверки резидентства.
|
||||||
|
_rule_address_one_line(row)
|
||||||
_apply_structured_group_rules(row)
|
_apply_structured_group_rules(row)
|
||||||
if not is_continuation:
|
if not is_continuation:
|
||||||
_apply_business_rules(
|
_apply_business_rules(
|
||||||
@ -515,7 +520,7 @@ def _normalize_indexed_path(path: str) -> str:
|
|||||||
|
|
||||||
def _is_eio_path(path: str) -> bool:
|
def _is_eio_path(path: str) -> bool:
|
||||||
normalized = _normalize_indexed_path(path)
|
normalized = _normalize_indexed_path(path)
|
||||||
return any(
|
return ".СведИНБОЮЛ.Учредитель." in f".{normalized}." or any(
|
||||||
f".{block_name}." in f".{normalized}." for block_name in _EIO_BLOCK_NAMES
|
f".{block_name}." in f".{normalized}." for block_name in _EIO_BLOCK_NAMES
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -862,10 +867,11 @@ def _allow_one_line_address(row: dict[int, str]) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
def _rule_address_one_line(row: dict[int, str]) -> None:
|
def _rule_address_one_line(row: dict[int, str]) -> None:
|
||||||
"""Правила 141, 152 и 178: однострочные адреса только для нерезидентов."""
|
"""Правила 141, 152, 167 и 178: однострочные адреса только для нерезидентов."""
|
||||||
if not _allow_one_line_address(row):
|
if not _allow_one_line_address(row):
|
||||||
_set(row, COL_ADDRESS_ONE_LINE, "")
|
_set(row, COL_ADDRESS_ONE_LINE, "")
|
||||||
_set(row, COL_REG_PLACE_ONE_LINE, "")
|
_set(row, COL_REG_PLACE_ONE_LINE, "")
|
||||||
|
_set(row, COL_EIO_ADDRESS_ONE_LINE, "")
|
||||||
_set(row, COL_EIO_REG_PLACE_ONE_LINE, "")
|
_set(row, COL_EIO_REG_PLACE_ONE_LINE, "")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -3322,18 +3322,18 @@
|
|||||||
{
|
{
|
||||||
"index": 167,
|
"index": 167,
|
||||||
"column_id": "legacy_167",
|
"column_id": "legacy_167",
|
||||||
"column_name": "Адрес ЮЛ/ Адрес места жительства (места нахождения) ЕИО/Бенефициара (по структуре полный)",
|
"column_name": "Адрес ЮЛ/ Адрес места жительства (места нахождения) ЕИО/Бенефициара (одной строкой)",
|
||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "АдресСтрока|АдрУчредитель",
|
"xml_tag": "АдресСтрока|АдрУчредитель",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/АдресСтрока | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/АдресСтрока | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/АдресСтрока",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/АдресСтрока | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/АдресСтрока | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/АдресСтрока | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/АдресСтрока",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": true,
|
"allow_short_lookup": true,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
"structured_value": true,
|
"structured_value": false,
|
||||||
"structured_group": "",
|
"structured_group": "legacy_168",
|
||||||
"structured_role": "",
|
"structured_role": "one_line",
|
||||||
"structured_order": 0,
|
"structured_order": 1,
|
||||||
"join_with_space": false,
|
"join_with_space": false,
|
||||||
"multiplies": "нет",
|
"multiplies": "нет",
|
||||||
"required": "Обязательно",
|
"required": "Обязательно",
|
||||||
@ -3346,7 +3346,7 @@
|
|||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "Индекс|КодОКСМ|КодСубъектаПоОКАТО|Район|Пункт|Улица|Дом|Корп|Оф|АдрУчредитель",
|
"xml_tag": "Индекс|КодОКСМ|КодСубъектаПоОКАТО|Район|Пункт|Улица|Дом|Корп|Оф|АдрУчредитель",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": true,
|
"allow_short_lookup": true,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
@ -3366,7 +3366,7 @@
|
|||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "Индекс",
|
"xml_tag": "Индекс",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Индекс | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Индекс | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Индекс",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Индекс | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Индекс | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Индекс | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/Индекс",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": false,
|
"allow_short_lookup": false,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
@ -3386,7 +3386,7 @@
|
|||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "КодОКСМ",
|
"xml_tag": "КодОКСМ",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/КодОКСМ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/КодОКСМ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/КодОКСМ",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/КодОКСМ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/КодОКСМ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/КодОКСМ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/КодОКСМ",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": false,
|
"allow_short_lookup": false,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
@ -3406,7 +3406,7 @@
|
|||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "КодСубъектаПоОКАТО",
|
"xml_tag": "КодСубъектаПоОКАТО",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/КодСубъектаПоОКАТО | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/КодСубъектаПоОКАТО | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/КодСубъектаПоОКАТО",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/КодСубъектаПоОКАТО | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/КодСубъектаПоОКАТО | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/КодСубъектаПоОКАТО | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/КодСубъектаПоОКАТО",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": false,
|
"allow_short_lookup": false,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
@ -3426,7 +3426,7 @@
|
|||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "Район",
|
"xml_tag": "Район",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Район | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Район | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Район",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Район | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Район | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Район | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/Район",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": false,
|
"allow_short_lookup": false,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
@ -3446,7 +3446,7 @@
|
|||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "Пункт",
|
"xml_tag": "Пункт",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Пункт | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Пункт | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Пункт",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Пункт | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Пункт | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Пункт | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/Пункт",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": false,
|
"allow_short_lookup": false,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
@ -3466,7 +3466,7 @@
|
|||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "Улица",
|
"xml_tag": "Улица",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Улица | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Улица | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Улица",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Улица | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Улица | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Улица | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/Улица",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": false,
|
"allow_short_lookup": false,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
@ -3486,7 +3486,7 @@
|
|||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "Дом",
|
"xml_tag": "Дом",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Дом | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Дом | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Дом",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Дом | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Дом | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Дом | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/Дом",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": false,
|
"allow_short_lookup": false,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
@ -3506,7 +3506,7 @@
|
|||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "Корп",
|
"xml_tag": "Корп",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Корп | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Корп | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Корп",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Корп | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Корп | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Корп | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/Корп",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": false,
|
"allow_short_lookup": false,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
@ -3526,7 +3526,7 @@
|
|||||||
"block": "eio",
|
"block": "eio",
|
||||||
"report_group": "eio",
|
"report_group": "eio",
|
||||||
"xml_tag": "Оф",
|
"xml_tag": "Оф",
|
||||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Оф | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Оф | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Оф",
|
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ЮЛЕИО/АдрЮЛ/Оф | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЕИО/ФЛЕИО/АдрРег/Оф | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/БенефициарЮЛ/ФЛБенефициар/АдрРег/Оф | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/Учредитель/АдрУчредитель/Оф",
|
||||||
"source_scope": "participant",
|
"source_scope": "participant",
|
||||||
"allow_short_lookup": false,
|
"allow_short_lookup": false,
|
||||||
"allow_direct_mapping": true,
|
"allow_direct_mapping": true,
|
||||||
|
|||||||
@ -79,8 +79,9 @@ def parse_xml_content(file_name: str, xml_content: bytes | str) -> FileParseResu
|
|||||||
operation, excluded_tags={"УчастникОп"}
|
operation, excluded_tags={"УчастникОп"}
|
||||||
)
|
)
|
||||||
operation_fields = {**common_fields, **operation_fields}
|
operation_fields = {**common_fields, **operation_fields}
|
||||||
|
operation_fields = _collapse_indexed_fields(operation_fields)
|
||||||
record_id = _extract_record_id(operation_fields, index)
|
record_id = _extract_record_id(operation_fields, index)
|
||||||
operation_rows = _expand_indexed_fields(operation_fields)
|
operation_rows = [operation_fields]
|
||||||
participants = _find_children_by_name(operation, "УчастникОп")
|
participants = _find_children_by_name(operation, "УчастникОп")
|
||||||
|
|
||||||
if participants:
|
if participants:
|
||||||
@ -92,7 +93,7 @@ def parse_xml_content(file_name: str, xml_content: bytes | str) -> FileParseResu
|
|||||||
file_name=file_name,
|
file_name=file_name,
|
||||||
operation_index=index,
|
operation_index=index,
|
||||||
record_id=record_id,
|
record_id=record_id,
|
||||||
operation_rows=operation_rows,
|
operation_rows=operation_rows[:1],
|
||||||
participant_rows=participant_rows,
|
participant_rows=participant_rows,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@ -141,13 +142,14 @@ def _extract_direct_fields(
|
|||||||
fields: dict[str, str] = {}
|
fields: dict[str, str] = {}
|
||||||
children = [child for child in element if _normalize_tag(child.tag) not in excluded]
|
children = [child for child in element if _normalize_tag(child.tag) not in excluded]
|
||||||
counts = Counter(_normalize_tag(child.tag) for child in children)
|
counts = Counter(_normalize_tag(child.tag) for child in children)
|
||||||
|
structural_repeats = _find_structural_repeat_tags(children, counts)
|
||||||
positions: Counter[str] = Counter()
|
positions: Counter[str] = Counter()
|
||||||
|
|
||||||
for child in children:
|
for child in children:
|
||||||
tag = _normalize_tag(child.tag)
|
tag = _normalize_tag(child.tag)
|
||||||
position = positions[tag]
|
position = positions[tag]
|
||||||
positions[tag] += 1
|
positions[tag] += 1
|
||||||
path = f"{tag}[{position}]" if counts[tag] > 1 else tag
|
path = f"{tag}[{position}]" if tag in structural_repeats else tag
|
||||||
_collect_leaf_fields(child, path, fields)
|
_collect_leaf_fields(child, path, fields)
|
||||||
|
|
||||||
return fields
|
return fields
|
||||||
@ -196,6 +198,15 @@ def _build_expanded_rows(
|
|||||||
return rows
|
return rows
|
||||||
|
|
||||||
|
|
||||||
|
def _collapse_indexed_fields(fields: dict[str, str]) -> dict[str, str]:
|
||||||
|
"""Сохраняет все операционные повторы в одной строке без потери значений."""
|
||||||
|
collapsed: dict[str, str] = {}
|
||||||
|
for key, value in fields.items():
|
||||||
|
deindexed_key = re.sub(r"\[\d+\]", "", key)
|
||||||
|
_append_value(collapsed, deindexed_key, value)
|
||||||
|
return collapsed
|
||||||
|
|
||||||
|
|
||||||
def _expand_indexed_fields(
|
def _expand_indexed_fields(
|
||||||
fields: dict[str, str],
|
fields: dict[str, str],
|
||||||
*,
|
*,
|
||||||
@ -250,6 +261,21 @@ def _expand_indexed_fields(
|
|||||||
return [primary, *continuations]
|
return [primary, *continuations]
|
||||||
|
|
||||||
|
|
||||||
|
def _find_structural_repeat_tags(
|
||||||
|
children: list[ET.Element],
|
||||||
|
counts: Counter[str],
|
||||||
|
) -> set[str]:
|
||||||
|
"""Возвращает повторяемые теги структурных, а не скалярных XML-узлов."""
|
||||||
|
first_by_tag: dict[str, ET.Element] = {}
|
||||||
|
for child in children:
|
||||||
|
first_by_tag.setdefault(_normalize_tag(child.tag), child)
|
||||||
|
return {
|
||||||
|
tag
|
||||||
|
for tag, first_child in first_by_tag.items()
|
||||||
|
if counts[tag] > 1 and (len(first_child) > 0 or bool(first_child.attrib))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _extract_record_id(operation_fields: dict[str, str], operation_index: int) -> str:
|
def _extract_record_id(operation_fields: dict[str, str], operation_index: int) -> str:
|
||||||
possible_keys = (
|
possible_keys = (
|
||||||
"ИдентификаторЗаписи",
|
"ИдентификаторЗаписи",
|
||||||
@ -317,17 +343,22 @@ def _collect_leaf_fields(
|
|||||||
_collect_attributes(element, current_path, fields)
|
_collect_attributes(element, current_path, fields)
|
||||||
children = list(element)
|
children = list(element)
|
||||||
if not children:
|
if not children:
|
||||||
_append_value(fields, current_path, (element.text or "").strip())
|
text = (element.text or "").strip()
|
||||||
|
if text or not element.attrib:
|
||||||
|
_append_value(fields, current_path, text)
|
||||||
return
|
return
|
||||||
|
|
||||||
counts = Counter(_normalize_tag(child.tag) for child in children)
|
counts = Counter(_normalize_tag(child.tag) for child in children)
|
||||||
|
structural_repeats = _find_structural_repeat_tags(children, counts)
|
||||||
positions: Counter[str] = Counter()
|
positions: Counter[str] = Counter()
|
||||||
for child in children:
|
for child in children:
|
||||||
child_name = _normalize_tag(child.tag)
|
child_name = _normalize_tag(child.tag)
|
||||||
position = positions[child_name]
|
position = positions[child_name]
|
||||||
positions[child_name] += 1
|
positions[child_name] += 1
|
||||||
child_path = (
|
child_path = (
|
||||||
f"{child_name}[{position}]" if counts[child_name] > 1 else child_name
|
f"{child_name}[{position}]"
|
||||||
|
if child_name in structural_repeats
|
||||||
|
else child_name
|
||||||
)
|
)
|
||||||
_collect_leaf_fields(child, f"{current_path}.{child_path}", fields)
|
_collect_leaf_fields(child, f"{current_path}.{child_path}", fields)
|
||||||
|
|
||||||
|
|||||||
@ -250,7 +250,7 @@ def test_structured_column_is_not_filled_directly_from_single_line_tag() -> None
|
|||||||
assert (
|
assert (
|
||||||
row[
|
row[
|
||||||
"Адрес ЮЛ/ Адрес места жительства (места нахождения) "
|
"Адрес ЮЛ/ Адрес места жительства (места нахождения) "
|
||||||
"ЕИО/Бенефициара (по структуре полный)"
|
"ЕИО/Бенефициара (одной строкой)"
|
||||||
]
|
]
|
||||||
== ""
|
== ""
|
||||||
)
|
)
|
||||||
@ -291,12 +291,16 @@ def test_structured_column_uses_non_one_line_candidate_when_mixed_tags() -> None
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
assert row["Адрес в одной строке"] == ""
|
assert row["Адрес в одной строке"] == ""
|
||||||
|
assert row[
|
||||||
|
"Адрес ЮЛ/ Адрес места жительства (места нахождения) "
|
||||||
|
"ЕИО/Бенефициара (одной строкой)"
|
||||||
|
] == ("г. Москва, ул. Пушкина, д. 1")
|
||||||
assert (
|
assert (
|
||||||
row[
|
row[
|
||||||
"Адрес ЮЛ/ Адрес места жительства (места нахождения) "
|
"Адрес ЮЛ/ Адрес места жительства (места нахождения) "
|
||||||
"ЕИО/Бенефициара (по структуре полный)"
|
"ЕИО/Бенефициара (структура полностью)"
|
||||||
]
|
]
|
||||||
== "г. Москва, ул. Пушкина, д. 1"
|
== ""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -492,6 +496,70 @@ def test_one_line_address_columns_cleared_when_participant_is_resident_client()
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_eio_one_line_address_obeys_participant_residency_rule() -> None:
|
||||||
|
participant_fields = {
|
||||||
|
"УчастникЮЛ.СведЕИО.ФЛЕИО.АдрРег.АдресСтрока": "г. Москва, ул. Тестовая",
|
||||||
|
"УчастникЮЛ.СведЕИО.ФЛЕИО.АдрРег.Индекс": "101000",
|
||||||
|
"УчастникЮЛ.СведЕИО.ФЛЕИО.АдрРег.КодОКСМ": "643",
|
||||||
|
}
|
||||||
|
resident_client = build_fixed_row_by_index(
|
||||||
|
file_name="f.xml",
|
||||||
|
record_id="R1",
|
||||||
|
operation_index=1,
|
||||||
|
operation_fields={"ПризнУчастника": "1", "ПризнКлиент": "1"},
|
||||||
|
participant_fields=participant_fields,
|
||||||
|
)
|
||||||
|
nonresident = build_fixed_row_by_index(
|
||||||
|
file_name="f.xml",
|
||||||
|
record_id="R2",
|
||||||
|
operation_index=2,
|
||||||
|
operation_fields={"ПризнУчастника": "0", "ПризнКлиент": "1"},
|
||||||
|
participant_fields=participant_fields,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert resident_client[167] == ""
|
||||||
|
assert resident_client[169] == "101000"
|
||||||
|
assert resident_client[170] == "643"
|
||||||
|
assert nonresident[167] == "г. Москва, ул. Тестовая"
|
||||||
|
|
||||||
|
|
||||||
|
def test_foreign_structure_founder_address_populates_eio_address_block() -> None:
|
||||||
|
row = build_fixed_row_by_index(
|
||||||
|
file_name="f.xml",
|
||||||
|
record_id="R1",
|
||||||
|
operation_index=1,
|
||||||
|
operation_fields={},
|
||||||
|
participant_fields={
|
||||||
|
"УчастникИНБОЮЛ.СведИНБОЮЛ.Учредитель.АдрУчредитель.Индекс": "101000",
|
||||||
|
"УчастникИНБОЮЛ.СведИНБОЮЛ.Учредитель.АдрУчредитель.КодОКСМ": "643",
|
||||||
|
"УчастникИНБОЮЛ.СведИНБОЮЛ.Учредитель.АдрУчредитель.Пункт": "Москва",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert row[167] == ""
|
||||||
|
assert row[168] == "101000, 643, Москва"
|
||||||
|
assert row[169] == "101000"
|
||||||
|
assert row[170] == "643"
|
||||||
|
assert row[173] == "Москва"
|
||||||
|
|
||||||
|
|
||||||
|
def test_foreign_structure_founder_one_line_address_activates_eio_block() -> None:
|
||||||
|
row = build_fixed_row_by_index(
|
||||||
|
file_name="f.xml",
|
||||||
|
record_id="R1",
|
||||||
|
operation_index=1,
|
||||||
|
operation_fields={"ПризнУчастника": "0"},
|
||||||
|
participant_fields={
|
||||||
|
"УчастникИНБОЮЛ.СведИНБОЮЛ.Учредитель.АдрУчредитель.АдресСтрока": (
|
||||||
|
"г. Москва, ул. Пушкина, д. 1"
|
||||||
|
),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
assert row[167] == "г. Москва, ул. Пушкина, д. 1"
|
||||||
|
assert row[168] == ""
|
||||||
|
|
||||||
|
|
||||||
def test_okato_cleared_when_country_code_is_not_russia() -> None:
|
def test_okato_cleared_when_country_code_is_not_russia() -> None:
|
||||||
row = build_fixed_row(
|
row = build_fixed_row(
|
||||||
file_name="f.xml",
|
file_name="f.xml",
|
||||||
|
|||||||
@ -157,6 +157,27 @@ def test_extract_direct_fields_keeps_single_sibling_path_unindexed() -> None:
|
|||||||
assert fields == {"Документ.НомДок": "DOC-1"}
|
assert fields == {"Документ.НомДок": "DOC-1"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_xml_separates_repeated_attribute_only_structures() -> None:
|
||||||
|
xml = _base_xml(
|
||||||
|
"""
|
||||||
|
<Операция>
|
||||||
|
<ИдентификаторЗаписи>OP-ATTR</ИдентификаторЗаписи>
|
||||||
|
<УчастникОп>
|
||||||
|
<ТипУчастника>1</ТипУчастника>
|
||||||
|
<Документ НомДок="DOC-1"/>
|
||||||
|
<Документ НомДок="DOC-2"/>
|
||||||
|
</УчастникОп>
|
||||||
|
</Операция>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
result = parse_xml_content("SKO115FZ_01_123456789_20260616_X00001.xml", xml)
|
||||||
|
|
||||||
|
assert len(result.rows) == 2
|
||||||
|
assert result.rows[0].participant_fields["Документ.НомДок"] == "DOC-1"
|
||||||
|
assert result.rows[1].participant_fields == {"Документ.НомДок": "DOC-2"}
|
||||||
|
|
||||||
|
|
||||||
def test_parse_xml_expands_documents_and_eio_into_additive_rows() -> None:
|
def test_parse_xml_expands_documents_and_eio_into_additive_rows() -> None:
|
||||||
xml = _base_xml(
|
xml = _base_xml(
|
||||||
"""
|
"""
|
||||||
@ -224,3 +245,90 @@ def test_parse_xml_separates_single_eio_and_single_beneficiary() -> None:
|
|||||||
assert result.rows[1].participant_fields == {
|
assert result.rows[1].participant_fields == {
|
||||||
"УчастникЮЛ.БенефициарЮЛ.ФЛБенефициар.ИННФЛИП": "111111111111"
|
"УчастникЮЛ.БенефициарЮЛ.ФЛБенефициар.ИННФЛИП": "111111111111"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_operation_repeats_are_collapsed_once_for_two_participants() -> None:
|
||||||
|
xml = _base_xml(
|
||||||
|
"""
|
||||||
|
<Операция>
|
||||||
|
<ИдентификаторЗаписи>OP8</ИдентификаторЗаписи>
|
||||||
|
<ОснованиеОп><НомДок>BASE-1</НомДок></ОснованиеОп>
|
||||||
|
<ОснованиеОп><НомДок>BASE-2</НомДок></ОснованиеОп>
|
||||||
|
<УчастникОп><ТипУчастника>1</ТипУчастника></УчастникОп>
|
||||||
|
<УчастникОп><ТипУчастника>2</ТипУчастника></УчастникОп>
|
||||||
|
</Операция>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
result = parse_xml_content("SKO115FZ_01_123456789_20260616_X00001.xml", xml)
|
||||||
|
|
||||||
|
assert len(result.rows) == 2
|
||||||
|
assert [row.participant_fields.get("ТипУчастника") for row in result.rows] == [
|
||||||
|
"1",
|
||||||
|
"2",
|
||||||
|
]
|
||||||
|
for row in result.rows:
|
||||||
|
assert row.operation_fields["ОснованиеОп.НомДок"] == "BASE-1; BASE-2"
|
||||||
|
|
||||||
|
|
||||||
|
def test_operation_repeats_are_collapsed_for_single_participant() -> None:
|
||||||
|
xml = _base_xml(
|
||||||
|
"""
|
||||||
|
<Операция>
|
||||||
|
<ИдентификаторЗаписи>OP9</ИдентификаторЗаписи>
|
||||||
|
<ОснованиеОп><НомДок>BASE-1</НомДок></ОснованиеОп>
|
||||||
|
<ОснованиеОп><НомДок>BASE-2</НомДок></ОснованиеОп>
|
||||||
|
<УчастникОп><ТипУчастника>1</ТипУчастника></УчастникОп>
|
||||||
|
</Операция>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
result = parse_xml_content("SKO115FZ_01_123456789_20260616_X00001.xml", xml)
|
||||||
|
|
||||||
|
assert len(result.rows) == 1
|
||||||
|
assert result.rows[0].operation_fields["ОснованиеОп.НомДок"] == "BASE-1; BASE-2"
|
||||||
|
|
||||||
|
|
||||||
|
def test_mixed_operation_and_eio_repeats_are_additive_not_cartesian() -> None:
|
||||||
|
xml = _base_xml(
|
||||||
|
"""
|
||||||
|
<Операция>
|
||||||
|
<ИдентификаторЗаписи>OP10</ИдентификаторЗаписи>
|
||||||
|
<ОснованиеОп><НомДок>BASE-1</НомДок></ОснованиеОп>
|
||||||
|
<ОснованиеОп><НомДок>BASE-2</НомДок></ОснованиеОп>
|
||||||
|
<УчастникОп>
|
||||||
|
<ТипУчастника>1</ТипУчастника>
|
||||||
|
<УчастникЮЛ>
|
||||||
|
<СведЕИО><ФЛЕИО><ИННФЛИП>111111111111</ИННФЛИП></ФЛЕИО></СведЕИО>
|
||||||
|
<СведЕИО><ФЛЕИО><ИННФЛИП>222222222222</ИННФЛИП></ФЛЕИО></СведЕИО>
|
||||||
|
</УчастникЮЛ>
|
||||||
|
</УчастникОп>
|
||||||
|
<УчастникОп><ТипУчастника>2</ТипУчастника></УчастникОп>
|
||||||
|
</Операция>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
result = parse_xml_content("SKO115FZ_01_123456789_20260616_X00001.xml", xml)
|
||||||
|
|
||||||
|
assert len(result.rows) == 3
|
||||||
|
assert result.rows[0].participant_fields["ТипУчастника"] == "1"
|
||||||
|
assert result.rows[1].participant_fields == {
|
||||||
|
"УчастникЮЛ.СведЕИО.ФЛЕИО.ИННФЛИП": "222222222222"
|
||||||
|
}
|
||||||
|
assert result.rows[2].participant_fields["ТипУчастника"] == "2"
|
||||||
|
assert result.rows[0].operation_fields["ОснованиеОп.НомДок"] == "BASE-1; BASE-2"
|
||||||
|
|
||||||
|
|
||||||
|
def test_repeated_scalar_leaves_are_joined_without_continuation_rows() -> None:
|
||||||
|
operation = ET.fromstring(
|
||||||
|
"""
|
||||||
|
<Операция>
|
||||||
|
<ПризнНеобОперации>1106</ПризнНеобОперации>
|
||||||
|
<ПризнНеобОперации>1499</ПризнНеобОперации>
|
||||||
|
</Операция>
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
fields = _extract_direct_fields(operation)
|
||||||
|
|
||||||
|
assert fields == {"ПризнНеобОперации": "1106; 1499"}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user