diff --git a/api/src/parser/statements/checks.py b/api/src/parser/statements/checks.py index c7ce031..4d52634 100644 --- a/api/src/parser/statements/checks.py +++ b/api/src/parser/statements/checks.py @@ -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 diff --git a/api/tests/unit/parser/statements/test_checks.py b/api/tests/unit/parser/statements/test_checks.py index 8083ba0..28d9d78 100644 --- a/api/tests/unit/parser/statements/test_checks.py +++ b/api/tests/unit/parser/statements/test_checks.py @@ -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"] == []