SFM/app/pipeline/column_constants.py
Raykov-MS d07eedfafb Fix column 99: assemble foreign card address from structured components
The АдрМестаПриемаВыдача block under СведенияКартаИнБанк (code 1009) has no
single АдресСтрока tag, unlike the same-named block under
СведенияВнесениеПолучениеНалДС — column 99 was mapped directly onto that
nonexistent tag and stayed empty. It now assembles the value from the
individual address components (Индекс, КодОКСМ, КодСубъектаПоОКАТО, Район,
Пункт, Улица, Дом, Корп, Оф), the same way column 153 already does for
registration place. Also disabled allow_short_lookup for this column, since
it was previously able to pick up a same-named tag (e.g. Улица) from an
unrelated address elsewhere in the file.

Added regression tests at both the mapping level and a full parse_xml_content
-> build_fixed_row_by_index pipeline level with a synthetic code-1009 XML.
2026-09-18 11:09:31 +03:00

126 lines
3.7 KiB
Python

from __future__ import annotations
from .column_registry import COLUMNS
def _index(legacy_position: int) -> int:
return COLUMNS.index_of(f"legacy_{legacy_position:03d}")
# Служебные поля и операция.
COL_FILE_NAME = _index(1)
COL_RECORD_ID = _index(7)
COL_FTR_SIGN = _index(10)
COL_SUSPENSION_BASIS = _index(11)
COL_OPERATION_SIGN = _index(14)
COL_ESP_SIGN = _index(15)
COL_OPERATION_CODE = _index(19)
COL_EXTRA_CODES = _index(20)
COL_UNUSUAL_CODES = _index(21)
COL_CURRENCY = _index(22)
COL_AMOUNT_CURRENCY = _index(23)
COL_AMOUNT_RUB = _index(24)
COL_SALE_CURRENCY = _index(25)
COL_SALE_AMOUNT = _index(26)
COL_CURRENCY_SIGN = _index(27)
COL_SUSPICIOUS_ID = _index(28)
COL_METAL_CODE = _index(29)
COL_METAL_NAME = _index(30)
COL_ITEM_TYPE = _index(31)
COL_EXTRA_INFO = _index(34)
# Перевод.
COL_TRANSFER_TYPE = _index(35)
COL_TERRITORY_CODE = _index(36)
COL_OPERATOR_TYPE = _index(37)
COL_PAYER_ACCOUNT = _index(38)
COL_PAYER_ESP = _index(39)
COL_PAYER_BANK_NAME = _index(40)
COL_PAYER_BANK_BIK = _index(41)
COL_RECIPIENT_BANK_NAME = _index(42)
COL_RECIPIENT_BANK_BIK = _index(43)
COL_PAYER_BANK_ACCOUNT = _index(44)
COL_RECIPIENT_BANK_ACCOUNT = _index(45)
COL_RECIPIENT_ACCOUNT = _index(46)
COL_RECIPIENT_ESP = _index(47)
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)
COL_OPERATION_DOCUMENTS = _index(107)
# Участник.
COL_PARTICIPANT_TYPE = _index(109)
COL_RESIDENT_SIGN = _index(110)
COL_CLIENT_SIGN = _index(111)
COL_IDENT_FL = _index(119)
COL_INN = _index(120)
COL_KPP = _index(122)
COL_BIRTH_REG_DATE = _index(123)
COL_BRANCH_SIGN = _index(124)
COL_INSURANCE_NUMBER = _index(125)
COL_COUNTRY_CODE = _index(126)
COL_STAY_DOCUMENT_TYPE = _index(136)
COL_ADDRESS_ONE_LINE = _index(141)
COL_ADDRESS_STRUCTURED = _index(142)
COL_REG_PLACE_ONE_LINE = _index(152)
COL_REG_PLACE_STRUCTURED = _index(153)
COL_EIO_ADDRESS_ONE_LINE = _index(167)
# ЕИО и ценные бумаги.
COL_EIO_INN = _index(163)
COL_EIO_OTHER_INFO = _index(164)
COL_EIO_REG_PLACE_ONE_LINE = _index(178)
COL_EIO_REG_PLACE_STRUCTURED = _index(179)
COL_CP_ISSUER_INN = _index(202)
# Блоки схемы.
BLOCK_EIO = COLUMNS.block("eio")
BLOCK_CP = COLUMNS.block("cp")
PARTICIPANT_ADDRESS_COMPONENT_COLUMNS = tuple(
column.index
for column in COLUMNS.ordered_columns()
if column.structured_group == "legacy_142" and column.structured_role == "component"
)
# Точные поля наличных, которые исторически очищаются для безналичных переводов.
CASHLESS_CLEARED_COLUMNS = tuple(
_index(position) for position in (50, 61, 65, 76, 84, 95, 99, 101)
)
OKATO_COUNTRY_PAIRS: tuple[tuple[int, int], ...] = tuple(
(_index(country), _index(okato))
for country, okato in (
(52, 53),
(67, 68),
(86, 87),
(144, 145),
(170, 171),
)
)
# Бизнес-константы.
ACCOUNT_PLACEHOLDER = "00000000000000000000"
EXPORT_SUBSIDIARY_COMMENT_CODES = frozenset(str(code) for code in range(6102, 6127))
CASHLESS_TRANSFER_TYPES = frozenset(
{"1", "2", "3", "4", "7", "10", "11", "12", "13", "14"}
)
VALID_FTR_SIGNS = frozenset({"0", "1", "2", "3", "4", "5"})
VALID_SUSPENSION_BASES = frozenset({"1", "2", "3"})
VALID_ESP_SIGNS = frozenset({"1", "2", "3", "4"})
PRECIOUS_ITEM_CODES = frozenset({"5020", "5021", "5022", "5023"})
SUSPICIOUS_CODE = "6001"
TERRITORY_CODE = "5016"
PRECIOUS_METAL_CODE = "C99"
EMPLOYEE_ABSENT_CODE = "0"
RUSSIA_COUNTRY_CODE = "643"
PARTICIPANT_TYPE_UL = "1"
PARTICIPANT_TYPE_FL = "2"
PARTICIPANT_TYPE_IP = "3"
PARTICIPANT_TYPE_FLCHP = "4"