detect.py: classify_sheet (ОСВ / форма ББ-ОФР / прочее по словарю), find_table_header (строка с ≥ 2 группами сумм, «Дебет/Кредит» под группой в пределах merged-диапазона или до следующей группы, заголовок в 1–4 строки, в т.ч. «Нач. сальдо Дт» в одной ячейке; без «начала»; свёрнутое сальдо в *_debit + collapsed; свёрнутые «Обороты» и половинчатая пара Дт/Кт — InvalidFormatError с названием группы), text_col, inn_col (заголовок «ИНН» или ≥ 80 % значений из 10/12 цифр), right_edge, extra_cols, group_label_rows, first_data_row, is_header_repeat (две группы или копия строки блока). Pattern.search_normalized — ячейка нормализуется один раз (T3.6). osv_groups: известные группировки OTHER перечислены явно, чтобы строка-метка «Основные средства» в неслитом заголовке была частью блока. Хелпер тестов gridbuilder (Sheet из «картинки»). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
57 lines
2.0 KiB
Python
57 lines
2.0 KiB
Python
"""Хелпер тестов `gridbuilder`: «картинка» строк превращается в `Sheet` так же, как его видят детекторы."""
|
||
|
||
from decimal import Decimal
|
||
|
||
import pytest
|
||
from gridbuilder import C, L, M, at, sheet_from_rows
|
||
|
||
from src.parser.grid import MergedRange
|
||
|
||
|
||
def test_rows_and_columns_are_one_based_and_values_typed() -> None:
|
||
sheet = sheet_from_rows([["Счет", 100, Decimal("1.50")], [], [None, "x"]])
|
||
|
||
assert sheet.cell(1, 1).text == "Счет"
|
||
assert sheet.cell(1, 2).value == Decimal(100)
|
||
assert sheet.cell(1, 2).kind == "number"
|
||
assert sheet.cell(1, 3).value == Decimal("1.50")
|
||
assert sheet.cell(3, 2).text == "x"
|
||
assert (sheet.n_rows, sheet.n_cols) == (3, 3)
|
||
|
||
|
||
def test_merged_cell_occupies_its_width_and_covers_rows_below() -> None:
|
||
sheet = sheet_from_rows([[M("Сальдо", 2, 2), "Обороты"], [None, None, "Дебет"]])
|
||
|
||
assert sheet.merged == (MergedRange(1, 1, 2, 2),)
|
||
assert sheet.cell(1, 3).text == "Обороты"
|
||
assert sheet.text_at(2, 2) == "Сальдо"
|
||
assert sheet.cell(2, 3).text == "Дебет"
|
||
|
||
|
||
def test_value_under_merged_range_is_rejected() -> None:
|
||
with pytest.raises(ValueError, match="merged"):
|
||
sheet_from_rows([[M("Сальдо", 1, 2)], ["лишнее"]])
|
||
|
||
|
||
def test_outline_style_and_sparse_rows() -> None:
|
||
sheet = sheet_from_rows([[L(2), C("60.01", bold=True, indent=3)], at({4: 7})])
|
||
|
||
assert [sheet.outline(1)] == [2]
|
||
assert sheet.outline(2) == 0
|
||
assert sheet.cell(1, 1).bold
|
||
assert [sheet.cell(1, 1).indent] == [3]
|
||
assert sheet.cell(2, 4).value == Decimal(7)
|
||
assert [sheet.n_cols] == [4]
|
||
|
||
|
||
def test_whitespace_text_is_kept_as_a_cell_but_row_is_empty_for_detectors() -> None:
|
||
sheet = sheet_from_rows([[" ", "\u00a0"]])
|
||
|
||
assert (1, 1) in sheet.cells
|
||
assert sheet.is_row_empty(1)
|
||
|
||
|
||
def test_float_is_rejected() -> None:
|
||
with pytest.raises(TypeError, match="float"):
|
||
sheet_from_rows([[0.1]])
|