pylib_ai_arm_statements/tests/test_recompute.py

119 lines
5.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from dataclasses import replace
from datetime import date
from decimal import Decimal as D
from arm_statements import StoredColumn, StoredForm, StoredLine, StoredValue, choose_edition, recompute
from arm_statements.loader import Rules
from arm_statements.model import DocStatus, DocumentType, Edition, FormVariant, LineStatus, PeriodKind
from arm_statements.structure import edition_by_date
OK, DETAIL = LineStatus.OK, LineStatus.DETAIL
COL = StoredColumn("c1", 2, date(2025, 9, 30), PeriodKind.ABSOLUTE)
def sl( # noqa: PLR0913 -- конструктор тестовых строк
key: str,
row: int | None,
code: str | None,
amount: str | None,
*,
status: LineStatus = OK,
conf: float = 100.0,
code_fixed: bool = False,
value_conf: float = 100.0,
value_fixed: bool = False,
recognized: bool = True,
) -> StoredLine:
value = StoredValue("c1", None if amount is None else D(amount), amount, value_conf, recognized, value_fixed)
return StoredLine(key, row, status, code, conf, None, code_fixed, None, (value,))
def form(*lines: StoredLine, document_type: DocumentType = DocumentType.BALANCE) -> StoredForm:
return StoredForm(document_type, date(2025, 9, 30), True, True, (), False, (), (COL,), tuple(lines))
def test_recompute_after_edit(rules: Rules) -> None:
result = recompute(
form(
sl("l1210", 0, "1210", "100"),
sl("l1230", 1, "1230", "300", value_conf=60.0, value_fixed=True),
sl("l1250", 2, "1250", "50", conf=40.0, code_fixed=True),
sl("l1260", None, "1260", "50"),
sl("ldet", 3, None, "7", status=DETAIL, value_conf=60.0),
sl("l1200", 4, "1200", "500"),
),
rules,
)
# 1200 = 100 + 300 + 50 + 50 (с 1250 и строкой правки 1260) — сходится; 1600 нет — «пустая цель»
assert [(f.relation, f.column_key, f.column_index) for f in result.checks.relations_failed] == [
("1600 = 1100 + 1200", "c1", 2)
]
assert result.value_needs_review[("l1230", "c1")] is False
assert result.value_needs_review[("ldet", "c1")] is False
assert result.line_needs_review["l1250"] is False
assert result.detail_parents == {"ldet": "l1250"}
assert (result.edition, result.form_variant, result.status) == (
Edition.FSBU4,
FormVariant.FULL,
DocStatus.NEEDS_CHECK,
)
def test_code_change_switches_variant(rules: Rules) -> None:
simplified = form(
sl("a", 0, "1150", "50"), sl("b", 1, "1600", "50"), sl("c", 2, "1300", "50"), sl("d", 3, "1700", "50")
)
assert recompute(simplified, rules).form_variant is FormVariant.SIMPLIFIED
edited = form(
sl("a", 0, "1100", "50", code_fixed=True),
sl("b", 1, "1600", "50"),
sl("c", 2, "1300", "50"),
sl("d", 3, "1700", "50"),
)
result = recompute(edited, rules)
assert result.form_variant is FormVariant.FULL
assert result.status is DocStatus.NEEDS_CHECK
def test_edition_equals_choose_edition(rules: Rules) -> None:
stored = form(sl("a", 0, "1110", "10"), sl("b", 1, "1120", "5", conf=60.0), sl("c", 2, "1130", "5"))
pairs = [("1110", True), ("1120", False), ("1130", True)]
expected = choose_edition(DocumentType.BALANCE, pairs, date(2025, 9, 30), rules)
assert recompute(stored, rules).edition is expected is Edition.RAS_66N
def test_accepted_and_empty_edit_line(rules: Rules) -> None:
broken = form(sl("a", 0, "1600", "100"), sl("b", 1, "1700", "90"), sl("e", None, "1220", None))
assert recompute(broken, rules).status is DocStatus.NEEDS_CHECK
assert recompute(replace(broken, accepted=True), rules).status is DocStatus.DONE
def test_disclosure_has_complete_maps(rules: Rules) -> None:
"""I-3: для расшифровки в словарях ключи всех строк и значений, а не пустые словари."""
stored = form(
sl("a", 0, "1110", "10"),
sl("d", 1, None, "7", status=DETAIL),
sl("e", None, "1120", None),
document_type=DocumentType.DISCLOSURE,
)
result = recompute(stored, rules)
assert result.line_needs_review == {"a": False, "d": False, "e": False}
assert result.value_needs_review == {("a", "c1"): False, ("d", "c1"): False, ("e", "c1"): False}
assert result.detail_parents == {"d": "a"}
assert (result.form_variant, result.checks.relations_failed, result.status) == (None, (), DocStatus.DONE)
assert result.edition is edition_by_date(date(2025, 9, 30))
def test_edit_line_without_amount_is_excluded_from_checks_and_variant(rules: Rules) -> None:
"""T18.6: пустая строка правки не участвует в §7 и не влияет на вариант."""
base = [sl("a", 0, "1150", "50"), sl("b", 1, "1600", "50"), sl("c", 2, "1300", "50"), sl("d", 3, "1700", "50")]
with_edit = recompute(form(*base, sl("e", None, "1100", None)), rules)
without = recompute(form(*base), rules)
assert with_edit.form_variant is without.form_variant is FormVariant.SIMPLIFIED
assert with_edit.checks == without.checks
def test_detail_without_row_index_has_no_parent(rules: Rules) -> None:
stored = form(sl("a", 0, "1210", "10"), sl("d", None, None, "3", status=DETAIL))
assert recompute(stored, rules).detail_parents == {"d": None}