Andrey Epifancev ebc1f438da fix(statements): подпись колонки в строке-шапке таблицы не заголовок формы (R-8, #54)
Ячейка в строке с подписью «Код» (словарь пакета) не открывает блок формы:
«Пояснения» на листах-продолжениях НПЗ больше не даёт DISCLOSURE.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BJKMLHdxMwSUeYcdkwqX4W
2026-10-18 20:28:39 +04:00

142 lines
6.0 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.

"""Блоки форм, вид листа и столбцы на словарях `arm_statements` (план 4, задача 4)."""
import pytest
from gridbuilder import at, sheet_from_rows
from arm_statements import DocumentType, continuation_type, default_rules
from src.parser.statements.forms import (
StatementRules,
caption_matches,
classify_statement_sheet,
codes_of,
continuation_marker,
find_blocks,
is_weak_continuation,
matches,
sheet_amount_columns,
sheet_code_column,
)
CODE_COL = 3
@pytest.fixture(scope="module")
def rules() -> StatementRules:
return default_rules()
def test_title_and_okud_without_codes_between_form_one_block(rules: StatementRules) -> None:
sheet = sheet_from_rows(
[
["Бухгалтерская отчетность"],
[],
[None, "Бухгалтерский баланс"],
[],
[*at({3: "Форма по ОКУД", 5: "0710001"})],
[*at({1: "Актив", 3: "Код", 4: "На 30.09.2025"})],
[*at({1: "Основные средства", 3: "1150", 4: 10})],
]
)
[block] = find_blocks(sheet, rules)
assert (block.type_hint, block.title, block.okud, block.first_row, block.last_row) == (
DocumentType.BALANCE,
(3, 2),
(5, 5),
1,
7,
)
def test_second_title_after_codes_opens_new_block(rules: StatementRules) -> None:
balance = [at({1: name, 3: code, 4: 10}) for name, code in (("а", 1110), ("б", 1150), ("в", 1170), ("г", 1600))]
income = [at({1: name, 3: code, 4: 10}) for name, code in (("д", 2110), ("е", 2120), ("ж", 2400))]
sheet = sheet_from_rows(
[["Бухгалтерский баланс"], *balance, ["Отчет о финансовых результатах"], *income],
)
blocks = find_blocks(sheet, rules)
assert [(b.type_hint, b.first_row, b.last_row) for b in blocks] == [
(DocumentType.BALANCE, 1, 5),
(DocumentType.INCOME, 6, 9),
]
def test_okud_wins_over_title(rules: StatementRules) -> None:
blocks = find_blocks(sheet_from_rows([["Бухгалтерский баланс"], ["по ОКУД", "0710002"]]), rules)
assert [block.type_hint for block in blocks] == [DocumentType.INCOME]
def test_report_title_alone_is_not_a_form(rules: StatementRules) -> None:
assert find_blocks(sheet_from_rows([["Бухгалтерская отчетность"]]), rules) == []
def test_continuation_marker_is_not_a_block(rules: StatementRules) -> None:
sheet = sheet_from_rows([["Форма 0710001 с.2"], ["Пассив", "Код", "На 30.09.2025"], ["Капитал", "1310", 10]])
assert find_blocks(sheet, rules) == []
assert classify_statement_sheet(sheet, rules) == "continuation"
def test_code_column_ignores_title_codes_block(rules: StatementRules) -> None:
titles = ["Коды", "0710001", 30, "09", 2025, 12345678, 7700000000, 12267, 16, 384]
codes = [1110, 1150, 1170, 1100, 1210, 1230]
rows = [at({6: value}) for value in titles]
rows += [at({3: code, 4: 10}) for code in codes]
sheet = sheet_from_rows(rows)
assert sheet_code_column(sheet, rules, 1, sheet.n_rows) == CODE_COL
assert codes_of(sheet, rules, CODE_COL, 1, sheet.n_rows) == [str(code) for code in codes]
def test_amount_columns_counts_numeric_columns_right_of_code(rules: StatementRules) -> None:
rows = [at({1: "Актив", 3: "Код", 4: "На 30.09.2025", 7: "Изменение"})]
rows += [at({1: "Статья", 3: code, 4: 1, 5: 2, 6: 3, 7: "рост"}) for code in (1110, 1150, 1170, 1100)]
sheet = sheet_from_rows(rows)
assert sheet_amount_columns(sheet, rules, CODE_COL, 1, sheet.n_rows) == CODE_COL
def test_weak_and_other_sheets(rules: StatementRules) -> None:
weak = sheet_from_rows([["Чистая прибыль", "2400", 10, 8]])
assert classify_statement_sheet(weak, rules) == "other"
assert is_weak_continuation(weak, rules, DocumentType.INCOME)
assert not is_weak_continuation(weak, rules, DocumentType.BALANCE)
notes = sheet_from_rows([["Примечания"], ["Текст пояснения к отчетности"]])
assert classify_statement_sheet(notes, rules) == "other"
assert not is_weak_continuation(notes, rules, DocumentType.INCOME)
def test_marker_with_unknown_okud_is_continuation(rules: StatementRules) -> None:
rows = [["Форма 0710000 с.2"]] + [at({1: "Статья", 3: code, 4: 10}) for code in (1110, 1150, 1170)]
sheet = sheet_from_rows(rows)
assert find_blocks(sheet, rules) == []
assert classify_statement_sheet(sheet, rules) == "continuation"
marker = continuation_marker(sheet, rules)
assert marker is not None
assert marker.text == "Форма 0710000 с.2"
assert continuation_type(marker.text, rules) is None
def test_labels_with_punctuation_match(rules: StatementRules) -> None:
assert matches(rules.headers.labels["address"], "Местонахождение (адрес)")
assert matches(rules.headers.labels["okopf_okfs"], "по ОКОПФ / ОКФС")
assert matches(rules.headers.legal_form, "АО «Пример»")
assert caption_matches([rules.headers.code_caption], "Код")
def test_column_caption_in_table_header_is_not_a_form_title(rules: StatementRules) -> None:
rows = [
["Форма 0710001 с.2"],
at({1: "Наименование", 2: "Пояснения", 3: "Код", 4: "На 30.09.2025"}),
at({1: "Капитал", 3: 1310, 4: 10}),
]
sheet = sheet_from_rows(rows)
assert find_blocks(sheet, rules) == []
assert classify_statement_sheet(sheet, rules) == "continuation"
def test_real_title_above_same_table_header_stays_a_block(rules: StatementRules) -> None:
rows = [
["Бухгалтерский баланс"],
at({1: "Наименование", 2: "Пояснения", 3: "Код", 4: "На 30.09.2025"}),
at({1: "Капитал", 3: 1310, 4: 10}),
]
[block] = find_blocks(sheet_from_rows(rows), rules)
assert block.type_hint is DocumentType.BALANCE