Andrey Epifancev a833945669 feat(parser): детекторы листов ОСВ, заголовка таблицы и колонок по содержимому
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
2026-10-05 12:54:11 +04:00

39 lines
4.5 KiB
Markdown
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.

# rules/ — словари синонимов парсера
Словари `rules/*.yaml`, по которым `tabular-parser` ищет структуру файла по содержимому: заголовки колонок,
группировки, периоды, типы данных. Словари — **данные, а не код**: новый синоним добавляется строкой в YAML без
правки парсера. Парсер не привязан к координатам (буквы колонок, номера строк, имена листов); это проверяет
AST-линтер `tests/unit/parser/test_no_coordinates.py`.
Путь указан относительно `api/` (рабочего каталога команд сервиса): `api/rules/`. Загрузчик — `src/parser/rules.py`
(`load_rules()`), источник содержимого — `docs/context/05-domain-rules.md` §1.0–§1.5.
| Файл | Что описывает |
|---|---|
| `osv_headers.yaml` | маркер листа ОСВ и листов форм ББ/ОФР; группы сумм (начало / обороты / конец); «Дебет»/«Кредит»; служебные колонки (ИНН, КПП, код, количество, валюта); строка «Итого»; метки шапки (счёт, период, выводимые данные, валюта, параметры, субконто) |
| `osv_groups.yaml` | название группировки из шапки -> `ACCOUNT` / `DEPARTMENT` / `COUNTERPARTY` / `CONTRACT` / `OTHER` (`default` — для всего прочего) |
| `periods.yaml` | текст периода -> `period_quarter` и `is_cumulative` (§1.5); `year` — паттерн года |
| `data_types.yaml` | «Выводимые данные» -> `BU` / `NU` / `SUM`; строки «Вал.» / «Кол.»; коды валют ISO 4217; синонимы валюты (`руб.` -> `RUB`) |
## Как писать паттерны
- Паттерн — регулярное выражение Python. Он ищется (`search`) в **нормализованном** тексте ячейки и применяется
без учёта регистра.
- Нормализация текста (`normalize_text`): нижний регистр, «ё» -> «е», дефисы, тире, неразрывные пробелы и переносы
строк -> один пробел, пробелы схлопываются. Пример: `Оборотно-сальдовая\nведомость` -> `оборотно сальдовая ведомость`.
- Поэтому в паттернах **нет «ё», дефисов, тире и диапазонов `[а-я]`**: пишите `оборотно\s*сальдовая`, `д т` (для
«Д-т»), `\w` вместо диапазона. Загрузчик отвергает такой паттерн с указанием файла и ключа.
- Порядок записей в списках `mapping`, `rules` (периоды), `header`, `row` — приоритет: побеждает первое совпадение,
от специфичных правил к общим.
- В `osv_groups.yaml` известные группировки `OTHER` («Основные средства», «Номенклатура», «Статьи затрат», …)
перечислены в `mapping` явно, хотя `default` и так даёт `OTHER`: детектор заголовка таблицы (`src/parser/detect.py`)
считает строку под «Дебет/Кредит» частью блока заголовка, только если её текст совпал с записью `mapping`, с `ИНН`
или с «субконто». Новая группировка в отдельной строке заголовка = новая строка в `mapping`.
- Ошибка в словаре (нет обязательного ключа, неизвестный ключ, неверный регэксп или тип) — `RuntimeError` при
загрузке с именем файла и ключом.
## Что намеренно не в штатном словаре
Синоним «нач. остаток» / «начальный остаток» для группы `opening` не включён: он нужен, чтобы показать, что новый
формат подключается строкой в YAML (тест сценария «Новый синоним»).