Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
81 lines
3.3 KiB
Python
81 lines
3.3 KiB
Python
"""Предупреждения ОСВ с кодами `OSV_*` (P4-12): коды по источнику, вывод 1.x прежний."""
|
||
|
||
import pytest
|
||
from gridbuilder import L, M, sheet_from_rows
|
||
from test_checks import row, total, tree
|
||
|
||
from src.parser.checks import run_checks
|
||
from src.parser.deadline import Deadline
|
||
from src.parser.grid import Sheet
|
||
from src.parser.osv import OsvParser
|
||
from src.parser.osv_warnings import OsvNote, hierarchy_note, legacy_line, render_legacy
|
||
from src.parser.rules import Rules, load_rules
|
||
|
||
SUMS = (None, None, 100, 100, None, None)
|
||
BROKEN = (None, None, 100, 100, None, 5)
|
||
|
||
|
||
@pytest.fixture(scope="module")
|
||
def rules() -> Rules:
|
||
return load_rules()
|
||
|
||
|
||
def _sheet(*rows: list[object]) -> Sheet:
|
||
head: list[list[object]] = [
|
||
["ООО Зерновая"],
|
||
["Оборотно-сальдовая ведомость по счету 60"],
|
||
["Период: 9 месяцев 2025 г."],
|
||
["Детализация по субсчетам, субконто: Контрагенты"],
|
||
["Выводимые данные: сумма"],
|
||
[],
|
||
["Субконто", M("Сальдо на начало периода", 2), M("Оборот за период", 2), M("Сальдо на конец периода", 2)],
|
||
[None, "Дебет", "Кредит", "Дебет", "Кредит", "Дебет", "Кредит"],
|
||
]
|
||
return sheet_from_rows([*head, *rows])
|
||
|
||
|
||
def test_check_notes_have_codes() -> None:
|
||
rows = tree()
|
||
rows[2] = row(9, ("40", None, "30", "20", "51", None), level=1, parent=7)
|
||
rows[-1] = total(10, ("100", None, "50", "30", "121", None))
|
||
|
||
_, notes = run_checks(rows)
|
||
|
||
assert [note.code for note in notes] == ["OSV_BALANCE_IDENTITY", "OSV_PARENT_SUM", "OSV_TOTAL"]
|
||
|
||
|
||
def test_hierarchy_note_code_by_strategy() -> None:
|
||
assert hierarchy_note("sums") == OsvNote("OSV_HIERARCHY_BY_SUMS", "hierarchy=sums")
|
||
assert hierarchy_note("account_pattern").code == "OSV_HIERARCHY_BY_ACCOUNT_PATTERN"
|
||
|
||
|
||
def test_legacy_line_and_render() -> None:
|
||
note = hierarchy_note("outline")
|
||
assert legacy_line("Лист1", note) == "sheet=Лист1 hierarchy=outline"
|
||
other = OsvNote("OSV_ROWS", "текст")
|
||
assert render_legacy("Лист1", [note, other, other]) == ["sheet=Лист1 hierarchy=outline", "текст"]
|
||
|
||
|
||
def test_parse_sheet_notes_start_with_hierarchy(rules: Rules) -> None:
|
||
sheet = _sheet([L(0), "60.01", *BROKEN], [L(1), "ООО Альфа", *SUMS], [L(0), "Итого", *BROKEN])
|
||
|
||
result = OsvParser(rules).parse_sheet(sheet, Deadline(60))
|
||
|
||
assert result.notes[0].code.startswith("OSV_HIERARCHY_BY_")
|
||
assert all(note.code.startswith("OSV_") for note in result.notes)
|
||
assert not result.ok
|
||
|
||
|
||
def test_legacy_lines_are_unchanged(rules: Rules) -> None:
|
||
sheet = _sheet([L(0), "60.01", *BROKEN], [L(1), "ООО Альфа", *SUMS], [L(0), "Итого", *BROKEN])
|
||
|
||
result = OsvParser(rules).parse_sheet(sheet, Deadline(60))
|
||
|
||
lines = render_legacy(sheet.name, result.notes)
|
||
assert lines == [
|
||
"sheet=Лист_1 hierarchy=outline",
|
||
"валюта в шапке не найдена",
|
||
"§1.4: сальдо на начало + обороты ≠ сальдо на конец (±0.01): строки 9, 11",
|
||
"§1.4: суммы родителя ≠ сумме детей (±0.01): строки 9",
|
||
]
|