Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
86 lines
3.7 KiB
Python
86 lines
3.7 KiB
Python
"""Конвейер форм книги: кандидаты адаптера → пакет → документы и предупреждения файла контракта 2.0."""
|
||
|
||
import pytest
|
||
from gridbuilder import at, sheet_from_rows
|
||
from result_contract import assert_valid_result
|
||
|
||
from arm_statements import Rules as StatementRules
|
||
from arm_statements import default_rules
|
||
from src.contract import FileWarning, Metrics, ResultStatus
|
||
from src.parser import ParseOutcome
|
||
from src.parser.deadline import Deadline
|
||
from src.parser.detect import SheetKind
|
||
from src.parser.grid import Sheet
|
||
from src.parser.statements.pipeline import StatementPipeline
|
||
|
||
NO_DEADLINE = Deadline(10_000.0)
|
||
BALANCE_LINES = (
|
||
("Основные средства", 1150, 100),
|
||
("Итого по разделу I", 1100, 100),
|
||
("Запасы", 1210, 50),
|
||
("Итого по разделу II", 1200, 50),
|
||
("БАЛАНС", 1600, 150),
|
||
("Нераспределенная прибыль", 1370, 150),
|
||
("Итого по разделу III", 1300, 150),
|
||
("БАЛАНС", 1700, 150),
|
||
)
|
||
|
||
|
||
@pytest.fixture(scope="module")
|
||
def statement_rules() -> StatementRules:
|
||
return default_rules()
|
||
|
||
|
||
def _balance(name: str = "Баланс") -> Sheet:
|
||
"""Шапка из восьми строк, подпись столбцов в 9-й, первая строка с кодом — 10-я (код в столбце C)."""
|
||
rows = [
|
||
at({1: "Бухгалтерский баланс"}),
|
||
at({1: "на 31 марта 2025 г."}),
|
||
at({1: "Организация", 2: "ООО Ромашка"}),
|
||
at({1: "ИНН", 2: "7700000000"}),
|
||
at({1: "Единица измерения", 2: "тыс. руб."}),
|
||
[],
|
||
[],
|
||
[],
|
||
at({1: "Наименование показателя", 3: "Код", 4: "На 31 марта 2025 г."}),
|
||
*[at({1: title, 3: code, 4: amount}) for title, code, amount in BALANCE_LINES],
|
||
]
|
||
return sheet_from_rows(rows, name=name)
|
||
|
||
|
||
def test_document_converts_to_contract(statement_rules: StatementRules) -> None:
|
||
outcome = StatementPipeline(statement_rules).parse([(_balance(), SheetKind.STATEMENT)], NO_DEADLINE, xlsb=False)
|
||
|
||
assert outcome.file_warnings == ()
|
||
document = outcome.documents[0]
|
||
assert document.header.form_name == "Бухгалтерский баланс"
|
||
assert (document.header.inn, document.header.inn_text, document.header.inn_confidence) == (
|
||
"7700000000",
|
||
"7700000000",
|
||
100,
|
||
)
|
||
assert document.lines[0].ref == "Баланс!C10"
|
||
assert document.status == "done"
|
||
assert_valid_result(ParseOutcome(status=ResultStatus.DONE, documents=outcome.documents, metrics=Metrics(rows=8)))
|
||
|
||
|
||
def test_file_warnings_are_contract_models(statement_rules: StatementRules) -> None:
|
||
"""Лист без формы (нет заголовка и кодов) → документа нет, `SHEET_OTHER` как `FileWarning` с locator."""
|
||
sheet = sheet_from_rows([at({1: "Примечания"}), at({1: "Текст без кодов"})], name="Примечания")
|
||
|
||
outcome = StatementPipeline(statement_rules).parse(
|
||
[(sheet, SheetKind.STATEMENT_CONTINUATION)], NO_DEADLINE, xlsb=False
|
||
)
|
||
|
||
assert outcome.documents == ()
|
||
assert all(isinstance(w, FileWarning) for w in outcome.file_warnings)
|
||
assert [(w.code, w.severity, w.locator) for w in outcome.file_warnings] == [
|
||
("SHEET_OTHER", "info", "sheet:Примечания")
|
||
]
|
||
|
||
|
||
def test_xlsb_flag_adds_document_warning(statement_rules: StatementRules) -> None:
|
||
outcome = StatementPipeline(statement_rules).parse([(_balance(), SheetKind.STATEMENT)], NO_DEADLINE, xlsb=True)
|
||
|
||
assert "XLSB_NO_FORMATS" in {w.code for w in outcome.documents[0].warnings}
|