From 0d82b5cf5ed7689d7cf0f3253adc2c84b18ace34 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Sun, 18 Oct 2026 22:05:06 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20=D1=87=D0=B5=D1=80=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=B8=D0=BA=20=D0=BF=D1=80=D0=BE=D0=B4=D0=BE=D0=BB=D0=B6=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20=D0=BD=D0=B5=D1=81=D1=91=D1=82=20=D1=87?= =?UTF-8?q?=D0=B8=D1=81=D0=BB=D0=BE=20=D0=BA=D0=BE=D0=BB=D0=BE=D0=BD=D0=BE?= =?UTF-8?q?=D0=BA=20=D1=81=D1=83=D0=BC=D0=BC=20(#54)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M-1: _separate считает form_amount_columns, следующий лист без маркера больше не получает ложный SHEET_NOT_JOINED. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W --- api/src/parser/statements/adapter.py | 15 ++++++++---- .../unit/parser/statements/test_adapter.py | 24 +++++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/api/src/parser/statements/adapter.py b/api/src/parser/statements/adapter.py index ff1a83d..3998273 100644 --- a/api/src/parser/statements/adapter.py +++ b/api/src/parser/statements/adapter.py @@ -129,14 +129,21 @@ def _form_block_drafts(sheet: Sheet, rules: StatementRules) -> list[_Draft]: return drafts -def _separate(sheet: Sheet, type_hint: DocumentType | None) -> _Draft: +def _separate(sheet: Sheet, type_hint: DocumentType | None, rules: StatementRules) -> _Draft: + """Отдельный кандидат из листа-продолжения; число колонок сумм считается, как у блока формы: следующий лист без + маркера сравнивается с ним, а не с нулём.""" formats: set[str] = set() + col = sheet_code_column(sheet, rules, 1, sheet.n_rows) + if col is None and type_hint is not None: + col = sheet_code_column(sheet, rules, 1, sheet.n_rows, relaxed=type_hint) + amounts = 0 if col is None else sheet_amount_columns(sheet, rules, col, 1, sheet.n_rows) return _Draft( type_hint=type_hint, sheet_names=[sheet.name], evidence=Evidence(), header=HeaderCells(), rows=grid_rows(sheet, 1, sheet.n_rows, sheet.n_cols, formats), + form_amount_columns=amounts, formats=formats, ) @@ -160,11 +167,11 @@ def _continuation(sheet: Sheet, drafts: list[_Draft], rules: StatementRules) -> marker = continuation_marker(sheet, rules) marker_type = None if marker is None else continuation_type(marker.text, rules) if open_form is None: - drafts.append(_separate(sheet, marker_type)) + drafts.append(_separate(sheet, marker_type, rules)) return locator = f"sheet:{sheet.name}" if marker_type is not None and marker_type != open_form.type_hint: - separate = _separate(sheet, marker_type) + separate = _separate(sheet, marker_type, rules) message = ( f"лист «{sheet.name}»: тип формы в маркере продолжения ({marker_type}) не совпадает с типом " f"открытой формы ({open_form.type_hint})" @@ -173,7 +180,7 @@ def _continuation(sheet: Sheet, drafts: list[_Draft], rules: StatementRules) -> drafts.append(separate) return if marker is None and not _sheet_joins(sheet, open_form, rules): - separate = _separate(sheet, None) + separate = _separate(sheet, None, rules) message = ( f"лист «{sheet.name}» не присоединён к форме «{open_form.sheet_names[0]}»: коды или число колонок " "сумм не подходят" diff --git a/api/tests/unit/parser/statements/test_adapter.py b/api/tests/unit/parser/statements/test_adapter.py index b0950b2..0f5b82b 100644 --- a/api/tests/unit/parser/statements/test_adapter.py +++ b/api/tests/unit/parser/statements/test_adapter.py @@ -151,3 +151,27 @@ def test_is_osv_sheet_by_marker() -> None: osv = sheet_from_rows([["Оборотно-сальдовая ведомость по счету 60"]]) assert is_osv_sheet(osv, rules) assert not is_osv_sheet(sheet_from_rows([["Бухгалтерский баланс"]]), rules) + + +def test_unmarked_page_joins_separate_draft_without_open_form(rules: StatementRules) -> None: + """M-1: черновик продолжения без открытой формы несёт число колонок сумм — стр. 3 без маркера присоединяется.""" + page2 = _form("", (1210, 1230, 1600), "Баланс (2)", marker="Форма 0710001 с.2") + page3 = _form("", (1250, 1260, 1700), "Баланс (3)") + candidates = build_candidates([(page2, CONTINUATION), (page3, CONTINUATION)], rules, NO_DEADLINE, xlsb=False) + [candidate] = candidates + assert candidate.type_hint is DocumentType.BALANCE + assert candidate.segment_breaks == (page2.n_rows,) + assert candidate.warnings == () + + +def test_unmarked_page_joins_draft_after_type_conflict(rules: StatementRules) -> None: + """M-1: то же после CONTINUATION_TYPE_CONFLICT — следующий лист присоединяется к конфликтному черновику.""" + balance = _form("Бухгалтерский баланс", BALANCE_CODES, "Баланс") + conflict = _form("", INCOME_CODES, "Лист 2", marker="Форма 0710002 с.2") + page3 = _form("", (2200, 2300, 2410), "Лист 3") + first, second = build_candidates( + [(balance, STATEMENT), (conflict, CONTINUATION), (page3, CONTINUATION)], rules, NO_DEADLINE, xlsb=False + ) + assert first.warnings == () + assert [w.code for w in second.warnings] == ["CONTINUATION_TYPE_CONFLICT"] + assert second.segment_breaks == (conflict.n_rows,)