.
This commit is contained in:
parent
5935eceadf
commit
07056e6266
@ -62,6 +62,7 @@ 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)
|
||||
@ -76,6 +77,11 @@ 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(
|
||||
|
||||
@ -10,6 +10,7 @@ from .column_constants import (
|
||||
CASHLESS_CLEARED_COLUMNS,
|
||||
CASHLESS_TRANSFER_TYPES,
|
||||
COL_ADDRESS_ONE_LINE,
|
||||
COL_ADDRESS_STRUCTURED,
|
||||
COL_AMOUNT_CURRENCY,
|
||||
COL_AMOUNT_RUB,
|
||||
COL_BIRTH_REG_DATE,
|
||||
@ -56,6 +57,7 @@ from .column_constants import (
|
||||
COL_RESIDENT_SIGN,
|
||||
COL_SALE_AMOUNT,
|
||||
COL_SALE_CURRENCY,
|
||||
COL_STAY_DOCUMENT_TYPE,
|
||||
COL_SUSPENSION_BASIS,
|
||||
COL_SUSPICIOUS_ID,
|
||||
COL_TERRITORY_CODE,
|
||||
@ -66,6 +68,7 @@ from .column_constants import (
|
||||
EMPLOYEE_ABSENT_CODE,
|
||||
EXPORT_SUBSIDIARY_COMMENT_CODES,
|
||||
OKATO_COUNTRY_PAIRS,
|
||||
PARTICIPANT_ADDRESS_COMPONENT_COLUMNS,
|
||||
PARTICIPANT_TYPE_FL,
|
||||
PARTICIPANT_TYPE_FLCHP,
|
||||
PARTICIPANT_TYPE_IP,
|
||||
@ -87,6 +90,7 @@ class _CompiledCandidate:
|
||||
raw: str
|
||||
normalized: str
|
||||
short: str
|
||||
strict_scoped: bool
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@ -134,6 +138,11 @@ _EIO_BLOCK_NAMES = (
|
||||
"БенефициарФЛИП",
|
||||
"БенефициарИНБОЮЛ",
|
||||
)
|
||||
_PARTICIPANT_ADDRESS_BASE_PATHS = (
|
||||
"УчастникЮЛ.СведЮЛ.АдрРегЮЛ",
|
||||
"УчастникФЛИП.СведФЛИП.АдрРег",
|
||||
"УчастникИНБОЮЛ.СведИНБОЮЛ.МестоДеятельностьИНБОЮЛ",
|
||||
)
|
||||
|
||||
|
||||
def _bank_bik_candidate_keys(bank_block: str) -> tuple[str, ...]:
|
||||
@ -153,13 +162,21 @@ def _bank_bik_candidate_keys(bank_block: str) -> tuple[str, ...]:
|
||||
|
||||
|
||||
def _compile_candidates(candidates: tuple[str, ...]) -> tuple[_CompiledCandidate, ...]:
|
||||
normalized_candidates = tuple(
|
||||
_INDEXED_PATH_SEGMENT.sub("", candidate) for candidate in candidates
|
||||
)
|
||||
return tuple(
|
||||
_CompiledCandidate(
|
||||
raw=candidate,
|
||||
normalized=_INDEXED_PATH_SEGMENT.sub("", candidate),
|
||||
normalized=normalized,
|
||||
short=candidate.rsplit(".", maxsplit=1)[-1],
|
||||
strict_scoped="." in normalized
|
||||
and not any(
|
||||
other != normalized and other.endswith(f".{normalized}")
|
||||
for other in normalized_candidates
|
||||
),
|
||||
)
|
||||
for candidate in candidates
|
||||
for candidate, normalized in zip(candidates, normalized_candidates)
|
||||
)
|
||||
|
||||
|
||||
@ -207,6 +224,21 @@ def _build_rules(mapping_data: tuple[ColumnMeta, ...]) -> tuple[MappingRule, ...
|
||||
for candidate in [*path_candidates, *candidates]
|
||||
if candidate.endswith(("ДатаРегЮЛ", "ДатаРождения"))
|
||||
]
|
||||
elif index == COL_STAY_DOCUMENT_TYPE:
|
||||
candidates = [
|
||||
candidate
|
||||
for candidate in path_candidates
|
||||
if candidate.endswith("УчастникФЛИП.СведФЛИП.СведДокПреб.ВидДокКод")
|
||||
]
|
||||
elif (
|
||||
index == COL_ADDRESS_STRUCTURED
|
||||
or index in PARTICIPANT_ADDRESS_COMPONENT_COLUMNS
|
||||
):
|
||||
candidates = [
|
||||
f"{base_path}.{address_tag}"
|
||||
for base_path in _PARTICIPANT_ADDRESS_BASE_PATHS
|
||||
for address_tag in _extract_tag_candidate_keys(tag)
|
||||
]
|
||||
elif index == COL_REG_PLACE_ONE_LINE:
|
||||
candidates = [
|
||||
"УчастникЮЛ.СведЮЛ.АдрРегЮЛ.АдресСтрока",
|
||||
@ -402,18 +434,19 @@ def build_fixed_row_by_index(
|
||||
source_lookup = operation_lookup
|
||||
else:
|
||||
source_lookup = merged_lookup
|
||||
allow_short_lookup = rule.allow_short_lookup or rule.column_index in BLOCK_EIO
|
||||
if rule.structured_value:
|
||||
row[rule.column_index] = _pick_structured_value(
|
||||
lookup=source_lookup,
|
||||
candidates=rule.compiled_candidates,
|
||||
join_with_space="фио" in rule.column_name.lower(),
|
||||
allow_short_lookup=rule.allow_short_lookup,
|
||||
allow_short_lookup=allow_short_lookup,
|
||||
)
|
||||
else:
|
||||
row[rule.column_index] = _pick_value(
|
||||
source_lookup,
|
||||
rule.compiled_candidates,
|
||||
allow_short_lookup=rule.allow_short_lookup,
|
||||
allow_short_lookup=allow_short_lookup,
|
||||
)
|
||||
if not is_continuation:
|
||||
_apply_participant_identity_rules(row, participant_lookup)
|
||||
@ -633,11 +666,20 @@ def _pick_value(
|
||||
allow_short_lookup: bool = True,
|
||||
) -> str:
|
||||
for candidate in candidates:
|
||||
if not allow_short_lookup and not candidate.strict_scoped:
|
||||
continue
|
||||
if value := lookup.values.get(candidate.raw):
|
||||
return value
|
||||
if value := lookup.normalized_suffix_first.get(candidate.normalized):
|
||||
return value
|
||||
if not allow_short_lookup:
|
||||
for entry in lookup.entries:
|
||||
if (
|
||||
entry.value
|
||||
and "." in entry.normalized_path
|
||||
and candidate.normalized.endswith(f".{entry.normalized_path}")
|
||||
):
|
||||
return entry.value
|
||||
continue
|
||||
if value := lookup.values.get(candidate.short):
|
||||
return value
|
||||
@ -877,6 +919,11 @@ def _rule_correspondent_accounts(
|
||||
row: dict[int, str], operator_type: str, transfer_type: str
|
||||
) -> None:
|
||||
"""Правила 44-45: корсчета зависят от оператора и вида перевода."""
|
||||
if not operator_type and not transfer_type:
|
||||
_set(row, COL_PAYER_BANK_ACCOUNT, "")
|
||||
_set(row, COL_RECIPIENT_BANK_ACCOUNT, "")
|
||||
return
|
||||
|
||||
if operator_type in {"3", "5"} or transfer_type in {"12", "13"}:
|
||||
_set(row, COL_PAYER_BANK_ACCOUNT, "")
|
||||
elif not _get(row, COL_PAYER_BANK_ACCOUNT):
|
||||
|
||||
@ -2706,9 +2706,9 @@
|
||||
"block": "participant_documents",
|
||||
"report_group": "participant_identity",
|
||||
"xml_tag": "ВидДокКод",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФЛИП/СведФЛИП/СведДокУдЛичн/ВидДокКод",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФЛИП/СведФЛИП/СведДокПреб/ВидДокКод",
|
||||
"source_scope": "participant",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": false,
|
||||
"structured_group": "",
|
||||
@ -2828,7 +2828,7 @@
|
||||
"xml_tag": "Индекс|КодОКСМ|КодСубъектаПоОКАТО|Район|Пункт|Улица|Дом|Корп|Оф",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЮЛ/АдрРегЮЛ/ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФлип/СведФЛИП/АдрРег/ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/МестоДеятельностьИНБОЮЛ/",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": true,
|
||||
"structured_group": "legacy_142",
|
||||
@ -2848,7 +2848,7 @@
|
||||
"xml_tag": "Индекс",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЮЛ/АдрРегЮЛ/Индекс | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФлип/СведФЛИП/АдрРег/Индекс | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/МестоДеятельностьИНБОЮЛ/Индекс",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": false,
|
||||
"structured_group": "legacy_142",
|
||||
@ -2868,7 +2868,7 @@
|
||||
"xml_tag": "КодОКСМ",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЮЛ/АдрРегЮЛ/КодОКСМ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФлип/СведФЛИП/АдрРег/КодОКСМ | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/МестоДеятельностьИНБОЮЛ/КодОКСМ",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": false,
|
||||
"structured_group": "legacy_142",
|
||||
@ -2888,7 +2888,7 @@
|
||||
"xml_tag": "КодСубъектаПоОКАТО",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЮЛ/АдрРегЮЛ/КодСубъектаПоОКАТО | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФлип/СведФЛИП/АдрРег/КодСубъектаПоОКАТО | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/МестоДеятельностьИНБОЮЛ/КодСубъектаПоОКАТО",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": false,
|
||||
"structured_group": "legacy_142",
|
||||
@ -2908,7 +2908,7 @@
|
||||
"xml_tag": "Район",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЮЛ/АдрРегЮЛ/Район | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФлип/СведФЛИП/АдрРег/Район | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/МестоДеятельностьИНБОЮЛ/Район",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": false,
|
||||
"structured_group": "legacy_142",
|
||||
@ -2928,7 +2928,7 @@
|
||||
"xml_tag": "Пункт",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЮЛ/АдрРегЮЛ/Пункт | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФлип/СведФЛИП/АдрРег/Пункт | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/МестоДеятельностьИНБОЮЛ/Пункт",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": false,
|
||||
"structured_group": "legacy_142",
|
||||
@ -2948,7 +2948,7 @@
|
||||
"xml_tag": "Улица",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЮЛ/АдрРегЮЛ/Улица | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФлип/СведФЛИП/АдрРег/Улица | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/МестоДеятельностьИНБОЮЛ/Улица",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": false,
|
||||
"structured_group": "legacy_142",
|
||||
@ -2968,7 +2968,7 @@
|
||||
"xml_tag": "Дом",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЮЛ/АдрРегЮЛ/Дом | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФлип/СведФЛИП/АдрРег/Дом | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/МестоДеятельностьИНБОЮЛ/Дом",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": false,
|
||||
"structured_group": "legacy_142",
|
||||
@ -2988,7 +2988,7 @@
|
||||
"xml_tag": "Корп",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЮЛ/АдрРегЮЛ/Корп | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФлип/СведФЛИП/АдрРег/Корп | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/МестоДеятельностьИНБОЮЛ/Корп",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": false,
|
||||
"structured_group": "legacy_142",
|
||||
@ -3008,7 +3008,7 @@
|
||||
"xml_tag": "Оф",
|
||||
"xml_path": "/СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникЮЛ/СведЮЛ/АдрРегЮЛ/Оф | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникФлип/СведФЛИП/АдрРег/Оф | /СообщОперКО/ИнформЧасть/СведКО/Операция/УчастникОП/УчастникИНБОЮЛ/СведИНБОЮЛ/МестоДеятельностьИНБОЮЛ/Оф",
|
||||
"source_scope": "any",
|
||||
"allow_short_lookup": true,
|
||||
"allow_short_lookup": false,
|
||||
"allow_direct_mapping": true,
|
||||
"structured_value": false,
|
||||
"structured_group": "legacy_142",
|
||||
|
||||
@ -82,6 +82,9 @@ class StreamingReportWriter:
|
||||
self._column_formats = [self._resolve_format(column) for column in self.columns]
|
||||
self._build_group_header_row()
|
||||
self._build_header_row()
|
||||
for column_index, column_format in enumerate(self._column_formats):
|
||||
if column_format is self._date_format:
|
||||
self.sheet.set_column(column_index, column_index, 12)
|
||||
# Фиксируем обе строки шапки.
|
||||
self.sheet.freeze_panes(2, 0)
|
||||
self.sheet.autofilter(1, 0, 1, len(self.columns) - 1)
|
||||
|
||||
@ -112,10 +112,10 @@ def test_build_fixed_row_splits_pipe_separated_xml_tag_values() -> None:
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={
|
||||
"Индекс": "674500",
|
||||
operation_fields={},
|
||||
participant_fields={
|
||||
"УчастникФЛИП.СведФЛИП.АдрРег.Индекс": "674500",
|
||||
},
|
||||
participant_fields={},
|
||||
)
|
||||
assert row["Адрес по структуре (целый)"] == "674500"
|
||||
|
||||
@ -194,11 +194,11 @@ def test_structured_address_is_built_from_right_columns() -> None:
|
||||
operation_index=1,
|
||||
operation_fields={},
|
||||
participant_fields={
|
||||
"Индекс": "674500",
|
||||
"КодОКСМ": "643",
|
||||
"Пункт": "Москва",
|
||||
"Улица": "Тверская",
|
||||
"Дом": "1",
|
||||
"УчастникФЛИП.СведФЛИП.АдрРег.Индекс": "674500",
|
||||
"УчастникФЛИП.СведФЛИП.АдрРег.КодОКСМ": "643",
|
||||
"УчастникФЛИП.СведФЛИП.АдрРег.Пункт": "Москва",
|
||||
"УчастникФЛИП.СведФЛИП.АдрРег.Улица": "Тверская",
|
||||
"УчастникФЛИП.СведФЛИП.АдрРег.Дом": "1",
|
||||
},
|
||||
)
|
||||
assert row["Адрес по структуре (целый)"] == "674500, 643, Москва, Тверская, 1"
|
||||
@ -398,6 +398,25 @@ def test_transfer_accounts_get_placeholder_when_required() -> None:
|
||||
assert row["Номер счета получателя"] == "00000000000000000000"
|
||||
|
||||
|
||||
def test_transfer_accounts_stay_empty_without_transfer_block() -> None:
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="SKO115FZ_01_044525111_20260810_000001.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={
|
||||
"НомерОперация": "ЦФТ-БАНК_1361233603059",
|
||||
"ПризнакОперации": "5",
|
||||
"СведенияВнесениеПолучениеНалДС.СуммаНал": "1000.00",
|
||||
},
|
||||
participant_fields={},
|
||||
)
|
||||
|
||||
assert row[38] == ""
|
||||
assert row[44] == ""
|
||||
assert row[45] == ""
|
||||
assert row[46] == ""
|
||||
|
||||
|
||||
def test_operator_type_conditions_clear_irrelevant_bank_fields() -> None:
|
||||
row = build_fixed_row(
|
||||
file_name="f.xml",
|
||||
@ -655,6 +674,49 @@ def test_dul_columns_128_134_keep_expected_positions() -> None:
|
||||
assert row["КП"] == "222-022"
|
||||
|
||||
|
||||
def test_col136_does_not_use_identity_document_type_without_stay_document() -> None:
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={},
|
||||
participant_fields={
|
||||
"УчастникФЛИП.СведФЛИП.СведДокУдЛичн.ВидДокКод": "21",
|
||||
},
|
||||
)
|
||||
|
||||
assert row[136] == ""
|
||||
|
||||
|
||||
def test_col136_uses_stay_document_type_instead_of_identity_document_type() -> None:
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={},
|
||||
participant_fields={
|
||||
"УчастникФЛИП.СведФЛИП.СведДокУдЛичн.ВидДокКод": "21",
|
||||
"УчастникФЛИП.СведФЛИП.СведДокПреб.ВидДокКод": "10",
|
||||
},
|
||||
)
|
||||
|
||||
assert row[136] == "10"
|
||||
|
||||
|
||||
def test_col136_rejects_stay_document_type_from_unrelated_participant_path() -> None:
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={},
|
||||
participant_fields={
|
||||
"ДругойБлок.СведДокПреб.ВидДокКод": "99",
|
||||
},
|
||||
)
|
||||
|
||||
assert row[136] == ""
|
||||
|
||||
|
||||
def test_bank_bik_columns_use_transfer_bank_paths_instead_of_common_bikko() -> None:
|
||||
row = build_fixed_row(
|
||||
file_name="f.xml",
|
||||
@ -672,6 +734,45 @@ def test_bank_bik_columns_use_transfer_bank_paths_instead_of_common_bikko() -> N
|
||||
assert row["БИК банка получателя"] == "022202220"
|
||||
|
||||
|
||||
def test_transfer_bank_fields_do_not_leak_into_unrelated_bank_blocks() -> None:
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={
|
||||
"ТипОператорДС": "2",
|
||||
"ВидПереводДС": "1",
|
||||
"СведенияПереводыДС.СведБанкПлательщик.НаимКО": "ПАО Сбербанк",
|
||||
"СведенияПереводыДС.СведБанкПлательщик.БИККО": "044525225",
|
||||
},
|
||||
participant_fields={},
|
||||
)
|
||||
|
||||
assert row[40] == "ПАО Сбербанк"
|
||||
assert row[41] == "044525225"
|
||||
for column_index in (60, 61, 75, 76, 94, 95, 100, 101):
|
||||
assert row[column_index] == ""
|
||||
|
||||
|
||||
def test_cash_bank_fields_fill_only_their_own_block() -> None:
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={
|
||||
"СведенияВнесениеПолучениеНалДС.СведПриемВыдача.НаимКО": "АО Наличный банк",
|
||||
"СведенияВнесениеПолучениеНалДС.СведПриемВыдача.БИККО": "040407777",
|
||||
"СведенияВнесениеПолучениеНалДС.СведПриемВыдача.КодОКСМ": "643",
|
||||
},
|
||||
participant_fields={},
|
||||
)
|
||||
|
||||
assert row[94] == "АО Наличный банк"
|
||||
assert row[95] == "040407777"
|
||||
for column_index in (40, 41, 42, 43, 60, 61, 75, 76, 98, 100, 101):
|
||||
assert row[column_index] == ""
|
||||
|
||||
|
||||
def test_citizenship_country_code_uses_only_direct_svedflip_value() -> None:
|
||||
row = build_fixed_row(
|
||||
file_name="f.xml",
|
||||
@ -767,6 +868,66 @@ def test_participant_structured_address_stays_in_142_and_components_143_plus() -
|
||||
assert row[144] == "643"
|
||||
|
||||
|
||||
def test_participant_address_does_not_use_citizenship_without_address_block() -> None:
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={},
|
||||
participant_fields={
|
||||
"ТипУчастника": "3",
|
||||
"УчастникФЛИП.СведФЛИП.КодОКСМ": "643",
|
||||
},
|
||||
)
|
||||
|
||||
assert row[126] == "643"
|
||||
assert row[142] == ""
|
||||
assert row[144] == ""
|
||||
|
||||
|
||||
def test_participant_address_country_is_isolated_from_citizenship() -> None:
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={},
|
||||
participant_fields={
|
||||
"ТипУчастника": "2",
|
||||
"УчастникФЛИП.СведФЛИП.КодОКСМ": "762",
|
||||
"УчастникФЛИП.СведФЛИП.АдрРег.Индекс": "123456",
|
||||
"УчастникФЛИП.СведФЛИП.АдрРег.КодОКСМ": "643",
|
||||
"УчастникФЛИП.СведФЛИП.АдрРег.Пункт": "Москва",
|
||||
},
|
||||
)
|
||||
|
||||
assert row[126] == "762"
|
||||
assert row[142] == "123456, 643, Москва"
|
||||
assert row[144] == "643"
|
||||
|
||||
|
||||
def test_participant_address_components_reject_unrelated_address_fields() -> None:
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
record_id="R1",
|
||||
operation_index=1,
|
||||
operation_fields={
|
||||
"ДругойАдрес.Индекс": "999999",
|
||||
"ДругойАдрес.Район": "Чужой район",
|
||||
"ДругойАдрес.Дом": "9",
|
||||
},
|
||||
participant_fields={
|
||||
"ТипУчастника": "3",
|
||||
"УчастникФЛИП.СведФЛИП.КодОКСМ": "643",
|
||||
},
|
||||
)
|
||||
|
||||
assert row[142] == ""
|
||||
assert row[143] == ""
|
||||
assert row[144] == ""
|
||||
assert row[146] == ""
|
||||
assert row[149] == ""
|
||||
|
||||
|
||||
def test_col153_uses_only_legal_entity_registration_address_components() -> None:
|
||||
row = build_fixed_row_by_index(
|
||||
file_name="f.xml",
|
||||
|
||||
@ -5,6 +5,7 @@ from pathlib import Path
|
||||
from unittest.mock import patch
|
||||
|
||||
from openpyxl import load_workbook
|
||||
from openpyxl.utils import get_column_letter
|
||||
|
||||
from app.pipeline.column_registry import COLUMNS
|
||||
from app.pipeline.mapping import FIXED_REPORT_COLUMNS
|
||||
@ -44,6 +45,20 @@ def test_write_report_streaming_preserves_header_and_formats(tmp_path: Path) ->
|
||||
assert sheet.cell(row=3, column=sum_col_idx).number_format == "#,##0.00"
|
||||
|
||||
|
||||
def test_write_report_expands_only_date_columns(tmp_path: Path) -> None:
|
||||
destination = tmp_path / "report.xlsx"
|
||||
write_report([], destination)
|
||||
|
||||
workbook = load_workbook(destination)
|
||||
sheet = workbook["Отчет"]
|
||||
date_col_idx = FIXED_REPORT_COLUMNS.index("Дата сообщения") + 1
|
||||
date_column_letter = get_column_letter(date_col_idx)
|
||||
|
||||
assert date_column_letter in sheet.column_dimensions
|
||||
assert sheet.column_dimensions[date_column_letter].width >= 12
|
||||
assert "A" not in sheet.column_dimensions
|
||||
|
||||
|
||||
def test_write_report_contains_top_group_header_row(tmp_path: Path) -> None:
|
||||
destination = tmp_path / "report.xlsx"
|
||||
write_report([], destination)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user