Compare commits
2 Commits
1d46f8cc6d
...
bf3d3f12e9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf3d3f12e9 | ||
|
|
6cc68862c1 |
@ -47,6 +47,7 @@ COL_TRANSFER_STATUS = _index(62)
|
||||
|
||||
# Карты и место операции.
|
||||
COL_TERRITORY_CODE_CARD = _index(96)
|
||||
COL_FOREIGN_CARD_ADDRESS_STRUCTURED = _index(99)
|
||||
COL_CARD_HOLDER_INFO = _index(103)
|
||||
COL_EMPLOYEE_SIGN = _index(104)
|
||||
COL_FOREIGN_BANK_NAME = _index(105)
|
||||
|
||||
@ -31,6 +31,7 @@ from .column_constants import (
|
||||
COL_EXTRA_INFO,
|
||||
COL_FILE_NAME,
|
||||
COL_FOREIGN_BANK_NAME,
|
||||
COL_FOREIGN_CARD_ADDRESS_STRUCTURED,
|
||||
COL_FTR_SIGN,
|
||||
COL_IDENT_FL,
|
||||
COL_INN,
|
||||
@ -290,6 +291,32 @@ def _build_rules(mapping_data: tuple[ColumnMeta, ...]) -> tuple[MappingRule, ...
|
||||
candidates = [
|
||||
f"{base_path}.{tag}" for base_path in base_paths for tag in address_tags
|
||||
]
|
||||
elif index == COL_FOREIGN_CARD_ADDRESS_STRUCTURED:
|
||||
# legacy_099: адрес места приёма/выдачи по карте иностранного
|
||||
# банка (код 1009) приходит в XML разложенным по тем же тегам
|
||||
# АдрМестаПриемаВыдача, что и другие адресные блоки (см.
|
||||
# COL_REG_PLACE_STRUCTURED выше) — АдресСтрока как единого поля
|
||||
# для этого блока в схеме нет, поэтому колонку нужно собирать
|
||||
# из компонентов, а не мапить напрямую на несуществующий тег.
|
||||
base_paths = (
|
||||
"СведенияКартаИнБанк.СведМестоОперация.АдрМестаПриемаВыдача",
|
||||
"СообщОперКО.ИнформЧасть.СведКО.Операция.СведенияКартаИнБанк."
|
||||
"СведМестоОперация.АдрМестаПриемаВыдача",
|
||||
)
|
||||
address_tags = (
|
||||
"Индекс",
|
||||
"КодОКСМ",
|
||||
"КодСубъектаПоОКАТО",
|
||||
"Район",
|
||||
"Пункт",
|
||||
"Улица",
|
||||
"Дом",
|
||||
"Корп",
|
||||
"Оф",
|
||||
)
|
||||
candidates = [
|
||||
f"{base_path}.{tag}" for base_path in base_paths for tag in address_tags
|
||||
]
|
||||
elif index == COL_CP_ISSUER_INN:
|
||||
candidates = [
|
||||
candidate
|
||||
|
||||
@ -1965,10 +1965,10 @@
|
||||
"column_name": "Адрес места приема (выдачи) наличных денежных средств (по структуре полный)",
|
||||
"block": "foreign_card",
|
||||
"report_group": "foreign_card_operation",
|
||||
"xml_tag": "АдрМестаПриемаВыдача",
|
||||
"xml_path": "СообщОперКО/ИнформЧасть/СведКО/Операция/СведенияКартаИнБанк/СведМестоОперация/АдрМестаПриемаВыдача/АдресСтрока",
|
||||
"xml_tag": "Индекс|КодОКСМ|КодСубъектаПоОКАТО|Район|Пункт|Улица|Дом|Корп|Оф",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/СведенияКартаИнБанк/СведМестоОперация/АдрМестаПриемаВыдача/",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": true,
|
||||
"structured_group": "",
|
||||
@ -1977,7 +1977,7 @@
|
||||
"join_with_space": false,
|
||||
"multiplies": "нет",
|
||||
"required": "необязательно",
|
||||
"comment": "текстовый показатель, поле может остаться пустым и не передаваться в XML"
|
||||
"comment": "У блока АдрМестаПриемаВыдача в СведенияКартаИнБанк нет единого тега АдресСтрока (в отличие от одноимённого блока в СведенияВнесениеПолучениеНалДС) — значение собирается из компонентов адреса (см. COL_FOREIGN_CARD_ADDRESS_STRUCTURED в mapping.py), поле может остаться пустым"
|
||||
},
|
||||
{
|
||||
"index": 100,
|
||||
|
||||
@ -28,7 +28,7 @@ def test_registry_covers_all_report_columns_without_gaps() -> None:
|
||||
|
||||
|
||||
def test_registry_assigns_card_columns_missing_from_original_plan() -> None:
|
||||
assert COLUMNS.block("foreign_card") == tuple(range(97, 107))
|
||||
assert COLUMNS.block("foreign_card") == tuple(range(96, 107))
|
||||
|
||||
|
||||
def test_registry_explicitly_preserves_structured_lookup_compatibility() -> None:
|
||||
@ -75,14 +75,14 @@ def test_registry_splits_money_and_participant_header_groups() -> None:
|
||||
(
|
||||
"own_account_cash",
|
||||
79,
|
||||
96,
|
||||
95,
|
||||
"Сведения о внесении наличных денежных средств на свой банковский счет "
|
||||
"или о получении наличных денежных средств со своего банковского счета "
|
||||
"у одного оператора по переводу денежных средств",
|
||||
),
|
||||
(
|
||||
"foreign_card_operation",
|
||||
97,
|
||||
96,
|
||||
106,
|
||||
"Сведения об операции с использованием платежной карты иностранного банка",
|
||||
),
|
||||
|
||||
@ -1720,6 +1720,48 @@ def test_col99_foreign_card_address_header_renamed() -> None:
|
||||
)
|
||||
|
||||
|
||||
def test_col99_foreign_card_address_assembled_from_structured_components() -> None:
|
||||
"""Регрессия: у блока АдрМестаПриемаВыдача в СведенияКартаИнБанк (код
|
||||
1009) нет единого тега АдресСтрока, в отличие от одноимённого блока в
|
||||
СведенияВнесениеПолучениеНалДС — колонка 99 раньше мапилась напрямую на
|
||||
несуществующий АдресСтрока и оставалась пустой. Значение должно
|
||||
собираться из компонентов адреса, разложенных в XML по структуре."""
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={
|
||||
"КодОперации": "1009",
|
||||
"СведенияКартаИнБанк.СведМестоОперация.АдрМестаПриемаВыдача.КодОКСМ": "840",
|
||||
"СведенияКартаИнБанк.СведМестоОперация.АдрМестаПриемаВыдача.Пункт": "Нью-Йорк",
|
||||
"СведенияКартаИнБанк.СведМестоОперация.АдрМестаПриемаВыдача.Улица": "Уолл-стрит",
|
||||
"СведенияКартаИнБанк.СведМестоОперация.АдрМестаПриемаВыдача.Дом": "11",
|
||||
},
|
||||
participant_fields={},
|
||||
)
|
||||
|
||||
assert row[99] == "840, Нью-Йорк, Уолл-стрит, 11"
|
||||
|
||||
|
||||
def test_col99_foreign_card_address_not_leaked_from_participant_address() -> None:
|
||||
"""Регрессия: allow_short_lookup для колонки 99 должен быть выключен —
|
||||
иначе адрес участника операции (другой блок, тот же тег 'Улица') может
|
||||
случайно попасть в адрес места приёма/выдачи по карте ин. банка."""
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={
|
||||
"КодОперации": "1009",
|
||||
},
|
||||
participant_fields={
|
||||
"УчастникЮЛ.СведЮЛ.АдрРегЮЛ.Улица": "Совсем другая улица",
|
||||
},
|
||||
)
|
||||
|
||||
assert row[99] == ""
|
||||
|
||||
|
||||
def test_col96_territory_code_card_belongs_to_foreign_card_group() -> None:
|
||||
"""Регрессия: колонка 96 раньше входила в группу заголовков блока
|
||||
'своих' кассовых операций (cash_operation/own_account_cash), из-за чего
|
||||
|
||||
@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
from app.pipeline.mapping import build_fixed_row_by_index
|
||||
from app.pipeline.parser import _extract_direct_fields, parse_xml_content
|
||||
|
||||
|
||||
@ -358,6 +359,65 @@ def test_mixed_operation_and_eio_repeats_are_additive_not_cartesian() -> None:
|
||||
assert result.rows[0].operation_fields["ОснованиеОп.НомДок"] == "BASE-1; BASE-2"
|
||||
|
||||
|
||||
def test_1009_foreign_card_address_survives_full_pipeline() -> None:
|
||||
"""Сквозная проверка на синтетическом файле: реальный XML -> parse_xml_content
|
||||
-> build_fixed_row_by_index. Адрес места приёма/выдачи по карте
|
||||
иностранного банка (код 1009) должен собраться из компонентов
|
||||
АдрМестаПриемаВыдача в колонку 99, а не потеряться и не подхватить
|
||||
чужой адрес участника операции (тот же тег 'Улица' в другом блоке)."""
|
||||
xml = _base_xml(
|
||||
"""
|
||||
<Операция>
|
||||
<ИдентификаторЗаписи>OP1</ИдентификаторЗаписи>
|
||||
<КодОперации>1009</КодОперации>
|
||||
<СведенияКартаИнБанк>
|
||||
<НомерКарта>5536********1234</НомерКарта>
|
||||
<НаимИнБанк>Bank of Testland</НаимИнБанк>
|
||||
<СведМестоОперация>
|
||||
<ИдТерминал>ATM00042</ИдТерминал>
|
||||
<НаимКО>First Test Bank</НаимКО>
|
||||
<АдрМестаПриемаВыдача>
|
||||
<Индекс>10001</Индекс>
|
||||
<КодОКСМ>840</КодОКСМ>
|
||||
<Пункт>Нью-Йорк</Пункт>
|
||||
<Улица>Уолл-стрит</Улица>
|
||||
<Дом>11</Дом>
|
||||
</АдрМестаПриемаВыдача>
|
||||
</СведМестоОперация>
|
||||
</СведенияКартаИнБанк>
|
||||
<УчастникОп>
|
||||
<Тип>01</Тип>
|
||||
<УчастникЮЛ>
|
||||
<СведЮЛ>
|
||||
<АдрРегЮЛ>
|
||||
<Улица>Совсем другая улица участника</Улица>
|
||||
</АдрРегЮЛ>
|
||||
</СведЮЛ>
|
||||
</УчастникЮЛ>
|
||||
</УчастникОп>
|
||||
</Операция>
|
||||
"""
|
||||
)
|
||||
|
||||
result = parse_xml_content("SKO115FZ_01_123456789_20260616_X00001.xml", xml)
|
||||
assert not result.is_fatal
|
||||
assert len(result.rows) == 1
|
||||
|
||||
row = result.rows[0]
|
||||
fixed_row = build_fixed_row_by_index(
|
||||
file_name="SKO115FZ_01_123456789_20260616_X00001.xml",
|
||||
record_id=row.record_id,
|
||||
operation_index=1,
|
||||
operation_fields=row.operation_fields,
|
||||
participant_fields=row.participant_fields,
|
||||
)
|
||||
|
||||
assert fixed_row[97] == "ATM00042"
|
||||
assert fixed_row[98] == "840"
|
||||
assert fixed_row[99] == "10001, 840, Нью-Йорк, Уолл-стрит, 11"
|
||||
assert fixed_row[100] == "First Test Bank"
|
||||
|
||||
|
||||
def test_repeated_scalar_leaves_are_joined_without_continuation_rows() -> None:
|
||||
operation = ET.fromstring(
|
||||
"""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user