## MODIFIED Requirements ### Requirement: Листы ОСВ находятся по содержимому Парсер SHALL считать листом ОСВ любой лист, где ячейка в первых 30 строках листа (`SHEET_MARKER_ROWS`: шапка ведомости всегда над таблицей) матчит признак ОСВ из `rules/osv_headers.yaml` (без учёта регистра, «ё/е», переносов строк и дефисов). Признаки ОСВ — названия ведомости разных учётных систем («Оборотно-сальдовая ведомость», «Оборотная ведомость», «ОСВ по сч.», «Оборотный баланс», «Фин. Обороты», «Отчет по Главной книге»). Лист с признаками формы ББ/ОФР или её продолжения уходит в конвейер форм (спека `statement-parsing`). Лист, не опознанный ни как ОСВ по признаку, ни как форма или её продолжение, SHALL считаться листом ОСВ по запасному признаку, если в первых `SHEET_MARKER_ROWS` строках есть строка групп сумм со всеми тремя группами («начало», «обороты», «конец») и у каждой группы найдены обе стороны — «Дебет» и «Кредит»; свёрнутое сальдо (одна колонка в группе) запасного признака не даёт. Запасной признак MUST проверяться только после классификации форм и MUST NOT отнимать у конвейера форм лист формы, её продолжения или расшифровки. Прочие листы SHALL пропускаться с предупреждением файла `SHEET_OTHER` (`severity = info`, `locator = sheet:<имя>`) в `metrics.warnings`. Книга без единого листа ОСВ и без форм → `status = error`, `error.code = INVALID_DOCUMENT` («в книге нет листов ОСВ или форм»). Несколько листов ОСВ → несколько `documents` с `locator = sheet:<имя>`. Имя листа MUST NOT использоваться для поиска. #### Scenario: Один лист — одна ОСВ (все реальные фикстуры) - **WHEN** разбирается любой файл из `fixtures/osv/**` - **THEN** `documents` содержит ровно один `osvDocument` с `locator = "sheet:Лист_1"`, статус `done` #### Scenario: Два листа ОСВ и посторонний лист - **WHEN** разбирается синтетический файл с листами «ОСВ 60», «Справка», «ОСВ 62» - **THEN** `documents` содержит два документа в порядке листов, в `metrics.warnings` есть `SHEET_OTHER` с `locator = "sheet:Справка"` #### Scenario: Нет ОСВ - **WHEN** разбирается книга с одним листом произвольной таблицы - **THEN** `status = error`, `error.code = INVALID_DOCUMENT`, сообщение называет число просмотренных листов #### Scenario: Название ведомости другой учётной системы - **WHEN** разбирается синтетическая ОСВ, у которой в шапке вместо «Оборотно-сальдовая ведомость» стоит одно из названий «Оборотная ведомость по счету 009», «ОСВ по сч. 41 за 3 кв.», «Оборотный баланс», «Фин. Обороты Листок», «Отчет по Главной книге» - **THEN** лист разобран как ОСВ (`osvDocument`), `SHEET_OTHER` для него нет #### Scenario: Лист без названия ведомости — по заголовку таблицы - **WHEN** разбирается синтетический лист без названия ведомости, таблица начинается в первых строках с групп «НачалоДт | НачалоКт | ОборотДт | ОборотКт | КонецДт | КонецКт» (Navision) - **THEN** лист разобран как ОСВ по запасному признаку #### Scenario: Расшифровка формы не становится ОСВ - **WHEN** разбирается книга с формой ББ и листом расшифровки с колонками «Остаток на начало | Поступило | Выбыло | Остаток на конец» - **THEN** вид листа расшифровки тот же, что без запасного признака (форма или продолжение конвейера форм либо `SHEET_OTHER`), документа ОСВ из него нет #### Scenario: Неполная таблица не даёт запасного признака - **WHEN** лист без названия ведомости содержит группы «обороты» и «конец», но без группы «начало» или со свёрнутым сальдо (одна колонка в группе) - **THEN** лист пропускается с `SHEET_OTHER` ## ADDED Requirements ### Requirement: Написания групп и сторон других учётных систем Словари `groups`, `debit`, `credit` SHALL знать написания групп и сторон выгрузок SAP, Navision и других систем: «Остаток на начало/конец (периода)», «Начальное/Конечное сальдо», «Сальдо начальное/конечное», «Сальдо вх/исх», «НачалоДт/Кт», «КонецДт/Кт». Сторона SHALL находиться и в отдельной ячейке под группой, и в самой ячейке группы после её названия — в том числе в форме «по дебету»/«по кредиту» («Оборот по дебету») и приклеенной к названию без пробела («Сальдо НачальноеДТВВ», «НачалоДт»). Сторона без границы слова SHALL искаться только в остатке ячейки группы после совпадения группы. Итоговая строка «ВСЕГО» SHALL распознаваться как итог листа наравне с «Итого» и «ИТОГ». #### Scenario: Остаток и оборот по дебету - **WHEN** разбирается синтетическая ОСВ с группами «Остаток на начало периода | Оборот по дебету | Оборот по кредиту | Остаток на конец периода» и подзаголовками «Дебет | Кредит» под остатками - **THEN** найдены все шесть колонок сумм; обороты — из ячеек «Оборот по дебету» и «Оборот по кредиту» #### Scenario: Сторона приклеена к названию - **WHEN** разбирается синтетическая ОСВ с заголовком «Сальдо НачальноеДТВВ | Сальдо НачальноеКТВВ | Оборот ДТ | Оборот КТ | Сальдо КонечноеДТВВ | Сальдо КонечноеКТВВ» - **THEN** найдены все шесть колонок сумм с верными сторонами #### Scenario: SAP «Сальдо вх/исх» - **WHEN** разбирается синтетическая ОСВ с группами «Сальдо вх Дт | Сальдо вх Кт | Оборот Дт | Оборот Кт | Сальдо исх Дт | Сальдо исх Кт» - **THEN** найдены все шесть колонок сумм #### Scenario: Итог «ВСЕГО» - **WHEN** разбирается синтетическая ОСВ, итоговая строка которой подписана «ВСЕГО» - **THEN** строка — `TOTAL`, предупреждения «строка «Итого» не найдена» нет