fix(statements): relations_failed и soft_relations_failed попадают в wire

StatementChecks собирался пустым и дополнялся append, а to_wire сериализует только
заданные поля: нарушения не доходили до JSON. Списки теперь задаются явно, пустой
список у проверенного документа сериализуется (checks.relations_failed = []).

Refs #14

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
This commit is contained in:
Andrey Epifancev 2026-10-06 20:55:43 +04:00
parent 976fc809fe
commit 1b17f2e748
2 changed files with 32 additions and 4 deletions

View File

@ -77,7 +77,8 @@ def run_statement_checks(
relations = list(config.relations_for(form))
subline_rules = _subline_rules(lines, form, rules) if config.subline_check else []
relations.extend(subline_rules)
checks = StatementChecks()
hard: list[FailedRelation] = []
soft: list[FailedRelation] = []
warnings: list[str] = []
for column_index, amounts in enumerate(_amounts(lines, column_count)):
for rule in relations:
@ -91,13 +92,14 @@ def run_statement_checks(
continue
failed = FailedRelation(column_index=column_index, relation=rule.relation, expected=expected, actual=actual)
if rule.strict:
checks.relations_failed.append(failed)
hard.append(failed)
warning_msg = f"соотношение «{rule.relation}» нарушено (колонка {column_index}): {expected} ≠ {actual}"
warnings.append(warning_msg)
else:
checks.soft_relations_failed.append(failed)
soft.append(failed)
warning_msg = (
f"мягкое соотношение «{rule.relation}» нарушено (колонка {column_index}): {expected} ≠ {actual}"
)
warnings.append(warning_msg)
return checks, warnings
# списки задаются явно: `to_wire` сериализует только заданные поля, пустой список = «проверено, сошлось»
return StatementChecks(relations_failed=hard, soft_relations_failed=soft), warnings

View File

@ -187,3 +187,29 @@ def test_target_present_members_absent_as_zero(rules: Rules) -> None:
# Правило 1100 = 1105 + … (strict: true) должно нарушиться
failed_relations = [item.relation for item in checks.relations_failed]
assert any("1100 =" in rel for rel in failed_relations)
def _full_balance(total: str) -> list[StatementLine]:
return [
line("1150", "100"),
line("1100", "100"),
line("1210", "50"),
line("1200", "50"),
line("1600", total),
line("1310", "150"),
line("1300", "150"),
line("1700", "150"),
]
def test_failed_and_empty_relation_lists_reach_the_wire(rules: Rules) -> None:
"""Проверенный документ сериализует `relations_failed`/`soft_relations_failed` всегда: с нарушением и пустыми."""
broken, _ = run_statement_checks(_full_balance("155"), 1, "BALANCE", rules)
clean, _ = run_statement_checks(_full_balance("150"), 1, "BALANCE", rules)
wire = broken.to_wire()
assert [item["relation"] for item in wire["relations_failed"]] == ["1600 = 1100 + 1200", "1600 = 1700"]
assert wire["relations_failed"][0]["expected"] == "155"
assert wire["soft_relations_failed"] == []
assert clean.to_wire()["relations_failed"] == []
assert clean.to_wire()["soft_relations_failed"] == []