"""Конвейер форм книги: кандидаты адаптера → пакет → документы и предупреждения файла контракта 2.0.""" import pytest from arm_statements import Rules as StatementRules from arm_statements import default_rules from gridbuilder import at, sheet_from_rows from result_contract import assert_valid_result 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}