from __future__ import annotations from app.pipeline.mapping import ( FIXED_REPORT_COLUMNS, _extract_tag_candidate_keys, build_fixed_row, build_fixed_row_by_index, ) def _column_with(prefix: str) -> str: for column in FIXED_REPORT_COLUMNS: if column.startswith(prefix): return column raise AssertionError(f"Колонка с префиксом '{prefix}' не найдена") def test_build_fixed_row_maps_known_fields() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=2, operation_fields={ "ИдФайл": "FILE_01", "НомерОперация": "OP_2", "КодОперации": "5007", "СумОперации": "100.55", }, participant_fields={ "ИННФЛ": "123456789012", "Тип": "01", }, ) assert row["Имя XML файла"] == "f.xml" assert row["Идентификатор записи"] == "R1" assert row["Уникальный номер операции"] == "OP_2" assert row["Код вида операции"] == "5007" assert row["Сумма в валюте проведения"] == "100.55" assert row[_column_with("ИНН")] == "123456789012" assert row["Тип участника"] == "01" def test_build_fixed_row_keeps_missing_values_empty() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={}, ) for column in FIXED_REPORT_COLUMNS: assert column in row assert row["Код вида операции"] == "" assert row[_column_with("ИНН")] == "" def test_build_fixed_row_does_not_take_operation_inn_for_participant() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "ИНН": "0000000000", "Тип": "99", }, participant_fields={ "ИННФЛ": "111111111111", "Тип": "01", }, ) assert row[_column_with("ИНН")] == "111111111111" assert row["Тип участника"] == "01" def test_fixed_report_columns_over_200() -> None: assert len(FIXED_REPORT_COLUMNS) == 204 assert FIXED_REPORT_COLUMNS[126] == "Признак принадлежности к публичным лицам" def test_build_fixed_row_uses_alternative_path_leaf_for_emitent_name() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "СведЦП.Эмитент.НаимЭмитентЦП.ФизЛицо.ФИОСтрока": "Эмитент ФЛ", }, participant_fields={}, ) assert row["Наименование/ ФИО эмитента ЦП"] == "Эмитент ФЛ" def test_build_fixed_row_maps_dotted_participant_keys() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "УчастникФЛИП.СведФЛИП.ИННФЛИП": "123456789012", "УчастникФЛИП.СведФЛИП.СведДокУдЛичн.НомДок": "451099", }, ) assert row[_column_with("ИНН")] == "123456789012" assert row["Номер"] == "451099" def test_build_fixed_row_splits_pipe_separated_xml_tag_values() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "Индекс": "674500", }, participant_fields={}, ) assert row["Адрес по структуре (целый)"] == "674500" def test_extract_tag_candidate_keys_supports_comma_separator() -> None: candidates = _extract_tag_candidate_keys( "КППЮЛ, ИдентификацияФЛ, ПризнакОргФормаИНБОЮЛ" ) assert candidates == ("КППЮЛ", "ИдентификацияФЛ", "ПризнакОргФормаИНБОЮЛ") def test_build_fixed_row_uses_first_suffix_value_from_index() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "УчастникФЛИП.Первый.ИННФЛИП": "111111111111", "УчастникФЛИП.Второй.ИННФЛИП": "222222222222", }, ) assert row[_column_with("ИНН")] == "111111111111" def test_build_fixed_row_skips_empty_suffix_values_in_index() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "УчастникФЛИП.Первый.ИННФЛИП": "", "УчастникФЛИП.Второй.ИННФЛИП": "222222222222", }, ) assert row[_column_with("ИНН")] == "222222222222" def test_structured_address_is_built_from_right_columns() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "Индекс": "674500", "КодОКСМ": "643", "Пункт": "Москва", "Улица": "Тверская", "Дом": "1", }, ) assert row["Адрес по структуре (целый)"] == "674500, 643, Москва, Тверская, 1" def test_one_line_address_clears_structured_and_component_columns() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "АдресСтрока": "г. Москва, Тверская, 1", "Индекс": "674500", "КодОКСМ": "643", "Улица": "Тверская", "Дом": "1", }, ) assert row["Адрес в одной строке"] == "г. Москва, Тверская, 1" assert row["Адрес по структуре (целый)"] == "" assert row["Индекс"] == "" assert row["Код страны"] == "" assert row["Улица"] == "" assert row["Дом"] == "" def test_fio_columns_prefer_participant_fields_over_operation_common_fields() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "Фам": "Кузнецов", "Имя": "Сергей", "Отч": "Владимирович", }, participant_fields={ "УчастникФЛИП.СведФЛИП.ФИОФЛИП.Фам": "Азимов", "УчастникФЛИП.СведФЛИП.ФИОФЛИП.Имя": "Ислом", "УчастникФЛИП.СведФЛИП.ФИОФЛИП.Отч": "Каримович", }, ) assert row["Фамилия"] == "Азимов" assert row["Имя"] == "Ислом" assert row["Отчество"] == "Каримович" assert row["ФИО (по структуре полностью)"] == "Азимов Ислом Каримович" def test_one_line_fio_clears_structured_and_component_fio_columns() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "ФИОСтрока": "Азимов Ислом Каримович", "Фам": "Азимов", "Имя": "Ислом", "Отч": "Каримович", }, ) assert row["ФИО (в одной строке)"] == "Азимов Ислом Каримович" assert row["ФИО (по структуре полностью)"] == "" assert row["Фамилия"] == "" assert row["Имя"] == "" assert row["Отчество"] == "" assert row["Наименование ЮЛ/ФИО ( по структуре полный)"] == "" def test_structured_column_is_not_filled_directly_from_single_line_tag() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "АдресСтрока": "г. Москва, ул. Пример, д. 1", }, ) assert row["Адрес в одной строке"] == "г. Москва, ул. Пример, д. 1" assert ( row[ "Адрес ЮЛ/ Адрес места жительства (места нахождения) " "ЕИО/Бенефициара (одной строкой)" ] == "" ) def test_structured_column_without_component_block_joins_candidate_values() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "УчастникЮЛ.СведЕИО.ЮЛЕИО.АдрРег.Индекс": "121467", "УчастникЮЛ.СведЕИО.ЮЛЕИО.АдрРег.КодОКСМ": "643", "УчастникЮЛ.СведЕИО.ЮЛЕИО.АдрРег.Пункт": "Москва", "УчастникЮЛ.СведЕИО.ЮЛЕИО.АдрРег.Улица": "Молодогвардейская", "УчастникЮЛ.СведЕИО.ЮЛЕИО.АдрРег.Дом": "8", "УчастникЮЛ.СведЕИО.ЮЛЕИО.АдрРег.Корп": "1", "УчастникЮЛ.СведЕИО.ЮЛЕИО.АдрРег.Оф": "28", }, ) assert ( row["Место государственной регистрации ЕИО/Бенефициара (структура полностью)"] == "121467, 643, Москва, Молодогвардейская, 8, 1, 28" ) def test_structured_column_uses_non_one_line_candidate_when_mixed_tags() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "УчастникЮЛ.БенефициарЮЛ.ФЛБенефициар.АдрРег.АдрУчредитель": ( "г. Москва, ул. Пушкина, д. 1" ), }, ) assert row["Адрес в одной строке"] == "" assert row[ "Адрес ЮЛ/ Адрес места жительства (места нахождения) " "ЕИО/Бенефициара (одной строкой)" ] == ("г. Москва, ул. Пушкина, д. 1") assert ( row[ "Адрес ЮЛ/ Адрес места жительства (места нахождения) " "ЕИО/Бенефициара (структура полностью)" ] == "" ) def test_digital_rights_operation_clears_currency_amount_fields() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "КодПризнОперации": "8", "КодВал": "643", "СумОперации": "100.00", "СумРуб": "100.00", }, participant_fields={}, ) assert row["Валюта"] == "" assert row["Сумма в валюте проведения"] == "" assert row["Сумма в рубл. эквиваленте"] == "" def test_abnormal_operation_codes_only_for_6001() -> None: row_without_6001 = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "КодОперации": "5007", "ПризнНеобОперации": "1101", }, participant_fields={}, ) assert row_without_6001["Коды призн. необычной операции"] == "" row_with_6001 = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "КодОперации": "6001", "ПризнНеобОперации": "1101", }, participant_fields={}, ) assert row_with_6001["Коды призн. необычной операции"] == "1101" def test_transfer_accounts_get_placeholder_when_required() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "ВидПереводДС": "1", "ТипОператорДС": "4", }, participant_fields={}, ) assert row["Номер счета плательщика"] == "00000000000000000000" assert row["Номер счета банка плательщика"] == "00000000000000000000" assert row["Номер счета банка получателя"] == "00000000000000000000" assert row["Номер счета получателя"] == "00000000000000000000" def test_operator_type_conditions_clear_irrelevant_bank_fields() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "ТипОператорДС": "3", "НаимКО": "Банк плательщика", "СведБанкПолучатель.НаимКО": "Банк получателя", "СведБанкПолучатель.БИККО": "044525225", "СчетБанкПлательщик": "30101810000000000001", "СчетБанкПолучатель": "30101810000000000002", }, participant_fields={}, ) assert row["Наименование банка плательщика"] == "" assert row["Наименование банка получателя"] == "" assert row["БИК банка получателя"] == "" assert row["Номер счета банка плательщика"] == "" assert row["Номер счета банка получателя"] == "" def test_col96_territory_code_cleared_when_operation_is_not_5016() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "КодОперации": "5007", "КодТерИнГос": "840", }, participant_fields={}, ) assert row["Код территории"] == "" def test_col103_and_105_fill_info_absent_when_no_employee_and_empty() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "ПризнакСотрудник": "0", }, participant_fields={}, ) assert row["Сведения о держателе платежной карты"] == "Информация отсутствует" assert row["Наименование иностранного банка"] == "Информация отсутствует" def test_col103_and_105_keep_original_values_when_present() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "ПризнакСотрудник": "0", "СведДержательКарты": "Иванов И.И.", "НаимИнБанк": "FOREIGN BANK", }, participant_fields={}, ) assert row["Сведения о держателе платежной карты"] == "Иванов И.И." assert row["Наименование иностранного банка"] == "FOREIGN BANK" def test_patronymic_supports_typo_tag_ocht() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "УчастникФЛИП.СведФЛИП.ФИОФЛИП.Фам": "Азимов", "УчастникФЛИП.СведФЛИП.ФИОФЛИП.Имя": "Ислом", "УчастникФЛИП.СведФЛИП.ФИОФЛИП.Очт": "Каримович", }, ) assert row["Отчество"] == "Каримович" def test_col124_cleared_for_physical_person_and_ip() -> None: row_fl = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"ТипФЛЧастнаяПрактика": "1"}, participant_fields={"ТипУчастника": "2"}, ) assert row_fl["Признак филиала: / Тип ФЛЧП"] == "" row_ip = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"ТипФЛЧастнаяПрактика": "1"}, participant_fields={"ТипУчастника": "3"}, ) assert row_ip["Признак филиала: / Тип ФЛЧП"] == "" def test_col125_snils_cleared_for_ip() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={"ТипУчастника": "3", "СНИЛСФЛ": "123-456-789 00"}, ) assert row["ОМС/СНИЛС/Номер телефона"] == "" def test_one_line_address_columns_cleared_when_participant_is_resident_client() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "ПризнУчастника": "1", "ПризнКлиент": "1", "АдресСтрока": "г. Москва, ул. Тестовая, д. 1", }, participant_fields={}, ) assert row["Адрес в одной строке"] == "" assert row["Место государственной регистрации ( в одной строке)"] == "" assert ( row["Место государственной регистрации ЕИО/Бенефициара (одной строкой)"] == "" ) 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: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "КодОКСМ": "840", "КодСубъектаПоОКАТО": "45", }, participant_fields={}, ) assert row["Код ОКАТО"] == "" assert row["ОКАТО"] == "" def test_col163_ogrn_eio_benef_cleared_for_physical_person() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"ОГРНИП": "123456789012345"}, participant_fields={"ТипУчастника": "2"}, ) assert row["ОГРН ЕИО/Бенефициара"] == "" def test_dul_sign_for_legal_entity_does_not_use_operation_doc_fields() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"ДокУдЛичн": "451099", "ТипУчастника": "1"}, participant_fields={}, ) assert row["Признак ДУЛ"] == "" def test_dul_columns_128_134_keep_expected_positions() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "ВидДокКод": "21", "СерияДок": "0104", "НомДок": "476635", "КемВыданДок": "ОВД", "КодПодр": "222-022", "ДатВыдачиДок": "23/08/2005", "ДокУдЛичн": "1", }, ) assert row["ДУЛ (структура полностью)"] == "1, 21, 0104, 476635, ОВД, 222-022" assert row["Признак ДУЛ"] == "1" assert row["Код вида документа/Наименование"] == "21" assert row["Серия"] == "0104" assert row["Номер"] == "476635" assert row["Орган, выдавший документ"] == "ОВД" assert row["КП"] == "222-022" def test_bank_bik_columns_use_transfer_bank_paths_instead_of_common_bikko() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "БИККО": "044525111", "ТипОператорДС": "4", "СвединияПереводыДС.СведБанкПлательщик.БИККО": "011012100", "СвединияПереводыДС.СведБанкПолучатель.БИККО": "022202220", }, participant_fields={}, ) assert row["БИК банка плательщика"] == "011012100" assert row["БИК банка получателя"] == "022202220" def test_citizenship_country_code_uses_only_direct_svedflip_value() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "УчастникФЛИП.СведФЛИП.АдрРег.КодОКСМ": "643", "УчастникФЛИП.СведФЛИП.КодОКСМ": "762", }, ) assert row["Код страны гражданства"] == "762" def test_citizenship_country_code_is_empty_when_only_address_code_exists() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={"УчастникФЛИП.СведФЛИП.АдрРег.КодОКСМ": "643"}, ) assert row["Код страны гражданства"] == "" def test_okato_column_4_uses_branch_okatofl_and_not_bank_okatoko() -> None: row = build_fixed_row( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "ОКАТОКО": "45", "ОКАТОФл": "10", "АдресСтрока": "г. Москва", }, participant_fields={}, ) assert FIXED_REPORT_COLUMNS[3] == "ОКАТО" assert row["ОКАТО"] == "10" def test_col9_uses_operation_number_from_xml() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=7, operation_fields={"НомерОперация": "ЦФТ-БАНК_1177326777530"}, participant_fields={}, ) assert row[9] == "ЦФТ-БАНК_1177326777530" def test_structured_cash_address_does_not_include_bank_fields() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "СведенияПереводыДс.СведПриемНалДС.АдрМестоПриемаВыдача.Индекс": "675520", "СведенияПереводыДс.СведПриемНалДС.АдрМестоПриемаВыдача.КодОКСМ": "643", "СведенияПереводыДс.СведПриемНалДС.АдрМестоПриемаВыдача.КодСубъектаПоОКАТО": "10", "СведенияПереводыДс.СведПриемНалДС.АдрМестоПриемаВыдача.Пункт": "Чигири с", "СведенияПереводыДс.СведПриемНалДС.АдрМестоПриемаВыдача.Улица": "Зеленая ул", "СведенияПереводыДс.СведПриемНалДС.АдрМестоПриемаВыдача.Дом": "1", "СведенияПереводыДс.СведПриемНалДС.БИККО": "044525111", "СведенияПереводыДс.СведПриемНалДС.НаимКО": "АО РСХБ", }, participant_fields={}, ) assert row[50] == "675520, 643, 10, Чигири с, Зеленая ул, 1" assert "044525111" not in row[50] assert "АО РСХБ" not in row[50] def test_participant_structured_address_stays_in_142_and_components_143_plus() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "УчастникФЛИП.СведФЛИП.АдрРег.Индекс": "123456", "УчастникФЛИП.СведФЛИП.АдрРег.КодОКСМ": "643", "УчастникФЛИП.СведФЛИП.АдрРег.КодСубъектаПоОКАТО": "45", "УчастникФЛИП.СведФЛИП.АдрРег.Пункт": "Москва", "УчастникФЛИП.СведФЛИП.АдрРег.Улица": "Тверская", "УчастникФЛИП.СведФЛИП.АдрРег.Дом": "1", }, ) assert row[141] == "" assert row[142] == "123456, 643, 45, Москва, Тверская, 1" assert row[143] == "123456" assert row[144] == "643" def test_col153_uses_only_legal_entity_registration_address_components() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"КодОКСМ": "643", "Индекс": "999999"}, participant_fields={ "УчастникЮЛ.СведЮЛ.АдрРегЮЛ.Индекс": "675520", "УчастникЮЛ.СведЮЛ.АдрРегЮЛ.КодОКСМ": "643", "УчастникЮЛ.СведЮЛ.АдрРегЮЛ.КодСубъектаПоОКАТО": "10", "УчастникЮЛ.СведЮЛ.АдрРегЮЛ.Район": "Благовещенский р-н", "УчастникЮЛ.СведЮЛ.АдрРегЮЛ.Пункт": "Чигири с", "УчастникЮЛ.СведЮЛ.АдрРегЮЛ.Улица": "Зеленая ул", "УчастникЮЛ.СведЮЛ.АдрРегЮЛ.Дом": "1", }, ) assert row[153] == "675520, 643, 10, Благовещенский р-н, Чигири с, Зеленая ул, 1" def test_cash_columns_cleared_for_cashless_transfer_type_1() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "ВидПереводДС": "1", "КодОперации": "4006", "БИККО": "044525111", "СведенияПереводыДс.СведПриемНалДС.БИККО": "044525111", }, participant_fields={}, ) for index in (50, 61, 65, 76, 84, 95, 99, 101): assert row[index] == "" def test_col119_is_empty_for_legal_entity_and_uses_flip_identifier() -> None: row_ul = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={"ТипУчастника": "1", "ИдентификацияФЛ": "1"}, ) assert row_ul[119] == "" row_flip = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "ТипУчастника": "3", "УчастникФЛИП.ИдентификацияФЛ": "1", }, ) assert row_flip[119] == "1" def test_physical_person_type_2_does_not_fill_cols_119_and_122() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "СтатусУчастника": "3", "ТипУчастника": "2", "УчастникФЛИП.ИдентификацияФЛ": "1", "УчастникФЛИП.СведФЛИП.ИННФЛИП": "410116812922", }, ) assert row[119] == "" assert row[120] == "410116812922" assert row[122] == "" def test_col123_uses_registration_date_for_legal_entity() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "ТипУчастника": "1", "УчастникЮЛ.СведЮЛ.ДатаРегЮЛ": "23/08/2005", "УчастникФЛИП.СведФЛИП.ДатаРождения": "01/01/1990", }, ) assert row[123] == "23/08/2005" def test_eio_block_is_empty_when_eio_and_beneficiary_are_absent() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "ТипУчастника": "1", "УчастникЮЛ.СведЮЛ.ИННЮЛ": "2820000210", "УчастникЮЛ.СведЮЛ.ОГРНЮЛ": "1022801198573", "УчастникЮЛ.СведЮЛ.АдрРегЮЛ.КодСубъектаПоОКАТО": "45", }, ) for index in (157, 162, 163, 168, 171): assert row[index] == "" def test_participant_and_beneficiary_inn_are_scoped_regardless_of_field_order() -> None: own_inn = ("УчастникЮЛ.СведЮЛ.ИННЮЛ", "2820000210") beneficiary_fields = ( ("УчастникЮЛ.БенефициарЮЛ.ФЛБенефициар.ФИОФЛИП.Фам", "Иванов"), ("УчастникЮЛ.БенефициарЮЛ.ФЛБенефициар.ФИОФЛИП.Имя", "Иван"), ("УчастникЮЛ.БенефициарЮЛ.ФЛБенефициар.ФИОФЛИП.Отч", "Иванович"), ("УчастникЮЛ.БенефициарЮЛ.ФЛБенефициар.ИННФЛИП", "111111111111"), ) for items in ( (own_inn, *beneficiary_fields), (*reversed(beneficiary_fields), own_inn), ): row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields=dict(items), ) assert row[120] == "2820000210" assert row[157] == "Иванов Иван Иванович" assert row[162] == "111111111111" assert "111111111111" not in row[157] def test_flip_beneficiary_is_scoped_to_eio_columns() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "УчастникФЛИП.СведФЛИП.ИННФЛИП": "222222222222", "УчастникФЛИП.БенефициарФЛИП.ФЛБенефициар.ИННФЛИП": "111111111111", }, ) assert row[120] == "222222222222" assert row[162] == "111111111111" def test_legal_entity_eio_name_is_preserved_in_structured_column() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "УчастникЮЛ.СведЕИО.ЮЛЕИО.НаимЮЛ": "ООО Управляющая компания", "УчастникЮЛ.СведЕИО.ЮЛЕИО.ИННЮЛ": "7707083893", }, ) assert row[157] == "ООО Управляющая компания" assert row[162] == "7707083893" def test_continuation_row_validates_local_codes_without_filling_defaults() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"ПризнФТр": "9"}, participant_fields={}, is_continuation=True, ) assert row[10] == "" assert row[44] == "" assert row[45] == "" def test_cp_block_is_empty_without_sved_cp_and_uses_emitent_inn_when_present() -> None: row_without_cp = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={"ИННЮЛ": "2820000210"}, ) assert row_without_cp[202] == "" row_with_cp = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"СведЦП.Эмитент.ИННЭмитентЦП": "7707083893"}, participant_fields={"ИННЮЛ": "2820000210"}, ) assert row_with_cp[202] == "7707083893" def test_currency_sign_is_preserved_independently_of_operation_code() -> None: non_conversion = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "КодОперации": "6001", "СумКонверсия": "0.00", "ПризнВалОперации": "VO", }, participant_fields={}, ) assert non_conversion[26] == "" assert non_conversion[27] == "VO" conversion = build_fixed_row_by_index( file_name="f.xml", record_id="R2", operation_index=2, operation_fields={ "КодОперации": "6101", "КодВалКонверсия": "978", "СумКонверсия": "100.50", "ПризнВалОперации": "VO", }, participant_fields={}, ) assert conversion[26] == "100.50" assert conversion[27] == "VO" zero_conversion = build_fixed_row_by_index( file_name="f.xml", record_id="R3", operation_index=3, operation_fields={ "КодОперации": "6126", "КодВалКонверсия": "840", "СумКонверсия": "0.00", "ПризнВалОперации": "VO", }, participant_fields={}, ) assert zero_conversion[26] == "" assert zero_conversion[27] == "VO" for operation_code in ("6100", "6127"): boundary_row = build_fixed_row_by_index( file_name="f.xml", record_id=operation_code, operation_index=4, operation_fields={ "КодОперации": operation_code, "ПризнВалОперации": "VO", }, participant_fields={}, ) assert boundary_row[27] == "VO" def test_extra_info_uses_operation_comment() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"КодОперации": "9003", "Коммент": "Комментарий операции"}, participant_fields={}, ) assert row[34] == "Комментарий операции" def test_extra_info_ignores_nested_operation_comment() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "КодОперации": "9003", "СведенияПереводыДС.Коммент": "Комментарий вложенного блока", }, participant_fields={}, ) assert row[34] == "" def test_extra_info_rejects_participant_comment_outside_allowed_codes() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"КодОперации": "9003"}, participant_fields={"КомментУчастник": "Посторонний комментарий"}, ) assert row[34] == "" def test_extra_info_is_not_directly_mapped_on_continuation_row() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"КодОперации": "9003"}, participant_fields={"КомментУчастник": "Посторонний комментарий"}, is_continuation=True, ) assert row[34] == "" def test_extra_info_accepts_participant_comment_for_main_or_extra_code() -> None: for operation_fields in ( {"КодОперации": "6102"}, {"КодОперации": "9003", "ДопКодОперации": "6126"}, ): row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields=operation_fields, participant_fields={"КомментУчастник": "Комментарий участника"}, ) assert row[34] == "Комментарий участника" def test_extra_info_joins_distinct_allowed_comments_without_duplicates() -> None: combined = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={"КодОперации": "6102", "Коммент": "Общий комментарий"}, participant_fields={"КомментУчастник": "Комментарий участника"}, ) duplicate = build_fixed_row_by_index( file_name="f.xml", record_id="R2", operation_index=2, operation_fields={"КодОперации": "6102", "Коммент": "Одинаковый текст"}, participant_fields={"КомментУчастник": "Одинаковый текст"}, ) assert combined[34] == "Общий комментарий; Комментарий участника" assert duplicate[34] == "Одинаковый текст" def test_bank_bik_columns_accept_canonical_transfer_block_spelling() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={ "ТипОператорДС": "4", "СведенияПереводыДС.СведБанкПлательщик.БИККО": "011012100", "СведенияПереводыДС.СведБанкПолучатель.БИККО": "022202220", }, participant_fields={}, ) assert row[41] == "011012100" assert row[43] == "022202220" def test_legal_entity_blocks_do_not_use_physical_person_identification() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "ТипУчастника": "4", "УчастникЮЛ.СведЮЛ.НаимЮЛ": "ООО Тест", "ИдентификацияФЛ": "1", }, ) assert row[119] == "" assert row[122] == "" def test_col124_is_empty_for_private_practice_participant_type_4() -> None: row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields={ "ТипУчастника": "4", "УчастникФЛИП.СведФЛИП.ТипФЛЧастнаяПрактика": "1", }, ) assert row[124] == "" padded_type_row = build_fixed_row_by_index( file_name="f.xml", record_id="R2", operation_index=2, operation_fields={}, participant_fields={ "ТипУчастника": "04", "УчастникФЛИП.СведФЛИП.ТипФЛЧастнаяПрактика": "1", }, ) assert padded_type_row[124] == "" def test_col119_is_not_included_in_structured_fio_or_cleared_by_one_line_fio() -> None: participant_fields = { "ТипУчастника": "2", "УчастникФЛИП.СведФЛИП.ФИОФЛИП.Фам": "Азимов", "УчастникФЛИП.СведФЛИП.ФИОФЛИП.Имя": "Ислом", "УчастникФЛИП.СведФЛИП.ФИОФЛИП.Отч": "Каримович", "УчастникФЛИП.ИдентификацияФЛ": "1", } row = build_fixed_row_by_index( file_name="f.xml", record_id="R1", operation_index=1, operation_fields={}, participant_fields=participant_fields, ) assert row[114] == "Азимов Ислом Каримович" assert row[119] == "" assert row[122] == "" row_with_one_line_fio = build_fixed_row_by_index( file_name="f.xml", record_id="R2", operation_index=2, operation_fields={}, participant_fields={ **participant_fields, "УчастникФЛИП.СведФЛИП.ФИОФЛИП.ФИОСтрока": ("Азимов Ислом Каримович"), }, ) assert row_with_one_line_fio[119] == "" assert row_with_one_line_fio[122] == ""