Andrey Epifancev b8bf2f29e4 feat(statements): блоки форм, тип по заголовку и ОКУД, листы-продолжения; classify_sheet без statement_markers
Refs #14
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: cedd7915-2efe-41e5-988c-a9117033598b
2026-10-06 15:36:26 +04:00

138 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.

"""Блоки форм на листе, тип формы, лист-продолжение (design D-2, D-3; spec «Листы форм и их продолжения находятся по
содержимому»)."""
import pytest
from gridbuilder import at, sheet_from_rows
from src.parser.detect import SheetKind, classify_sheet
from src.parser.rules import Rules, load_rules
from src.parser.statements.detect import classify_statement_sheet, continuation_form, find_forms
@pytest.fixture(scope="module")
def rules() -> Rules:
return load_rules()
NPZ_FIRST_SHEET = [
[None, None, "Бухгалтерская отчетность"],
[None, None, "за январь - сентябрь 2025 г."],
[*at({3: "Организация:", 7: 'ООО "НПЗ"', 22: "по ОКПО", 23: "16098811"})],
[
*at(
{
3: "Идентификационный номер налогоплательщика",
22: "ИНН",
23: "2315995868",
}
)
],
[],
[None, None, "Бухгалтерский баланс"],
[None, None, "на 30 сентября 2025 г."],
[*at({22: "Форма по ОКУД", 23: "0710001"})],
[
*at(
{
3: "Пояснения",
5: "Наименование показателя",
13: "Код",
16: "На 30 сентября 2025 г.",
21: "На 31 декабря 2024 г.",
}
)
],
[*at({5: "Гудвил", 13: "1105", 16: 0, 21: 0})],
[*at({5: "Нематериальные активы", 13: "1110", 16: 586260, 21: 905786})],
[*at({5: "Итого по разделу I", 13: "1100", 16: 586260, 21: 905786})],
]
def test_title_after_report_header_gives_one_balance_block(rules: Rules) -> None:
sheet = sheet_from_rows(NPZ_FIRST_SHEET)
blocks, warnings = find_forms(sheet, rules)
assert len(blocks) == 1 and warnings == []
block = blocks[0]
assert block.kind == "BALANCE" and block.form_name == "Бухгалтерский баланс" and block.okud == "0710001"
assert (block.title_row, block.first_row, block.last_row) == (6, 1, sheet.n_rows)
def test_two_forms_on_one_sheet_split_at_second_title(rules: Rules) -> None:
sheet = sheet_from_rows(
[
["Бухгалтерский баланс"],
["Наименование", "Код", "На 31.12.2024"],
["Запасы", "1210", 10],
["БАЛАНС", "1600", 10],
[],
["Отчет о финансовых результатах"],
["Наименование", "Код", "За 2024 г."],
["Выручка", "2110", 100],
]
)
blocks, _ = find_forms(sheet, rules)
assert [(block.kind, block.first_row, block.last_row) for block in blocks] == [("BALANCE", 1, 5), ("INCOME", 6, 8)]
def test_okud_without_title_and_disclosure(rules: Rules) -> None:
sheet = sheet_from_rows([["Форма по ОКУД", "0710002"], ["Код", "За 2024 г."], ["2110", 1]])
assert find_forms(sheet, rules)[0][0].kind == "INCOME"
disclosure = sheet_from_rows([["Пояснения к бухгалтерскому балансу и отчету о финансовых результатах"], ["текст"]])
assert find_forms(disclosure, rules)[0][0].kind == "DISCLOSURE"
odds = sheet_from_rows([["Отчет о движении денежных средств"], ["Код", "За 2024 г."], ["4110", 1]])
assert find_forms(odds, rules)[0][0].kind == "DISCLOSURE"
def test_title_and_okud_conflict_prefers_okud_with_warning(rules: Rules) -> None:
sheet = sheet_from_rows([["Бухгалтерский баланс"], ["Форма по ОКУД", "0710002"]])
blocks, warnings = find_forms(sheet, rules)
assert blocks[0].kind == "INCOME" and warnings and "0710002" in warnings[0]
def test_classify_statement_continuation_and_other(rules: Rules) -> None:
one_c_page_two = sheet_from_rows(
[
[*at({17: "Форма 0710001 с.2"})],
["Пояснения", "Наименование показателя", "Код", "На 31 декабря 2024 г."],
[None, "Уставный капитал", "1310", 250000],
]
)
npz_page = sheet_from_rows(
[
["в том числе:"],
["Биологические активы", "11901", 0, 0],
["Итого по разделу I", "1100", 5, 5],
["Запасы", "1210", 1, 1],
]
)
header_only = sheet_from_rows(
[
["Пояснения", "Наименование показателя", "Код", "На 31 декабря 2024 г."],
["Запасы", None, None, 5],
]
)
other = sheet_from_rows([["Справка"], ["Наименование", "Сумма"], ["Аренда", 100]])
assert classify_statement_sheet(sheet_from_rows(NPZ_FIRST_SHEET), rules) == "statement"
assert classify_statement_sheet(one_c_page_two, rules) == "continuation"
assert classify_statement_sheet(npz_page, rules) == "continuation"
assert classify_statement_sheet(header_only, rules) == "continuation"
assert classify_statement_sheet(other, rules) == "other"
assert continuation_form(one_c_page_two, rules) == ("BALANCE", "Форма 0710001 с.2")
assert continuation_form(npz_page, rules) == (None, None)
def test_sheet_kind_wiring(rules: Rules) -> None:
assert classify_sheet(sheet_from_rows(NPZ_FIRST_SHEET), rules) is SheetKind.STATEMENT
statement_continuation_sheet = sheet_from_rows([["Форма 0710002 с.2"], ["Код", "За 2024 г."], ["2510", 0]])
assert classify_sheet(statement_continuation_sheet, rules) is SheetKind.STATEMENT_CONTINUATION
osv_sheet = sheet_from_rows([["Оборотно-сальдовая ведомость по счету 60"], ["Бухгалтерский баланс"]])
assert classify_sheet(osv_sheet, rules) is SheetKind.OSV