# Design: колонка «Показатели» и валютные строки ОСВ Правило — `05-domain-rules.md` §1.3 (абзацы «строки-показатели», «Колонка «Показатели»»), §1.4 (сверка родителя), ветка docs `feat/58-osv-indicators`. ## Как выглядит файл (реальный образец контура, в git не кладётся) `result_dir.zip`, `files/3da66219-….xlsx` — ОСВ по счёту 02, шапка без «Выводимые данные» («Параметры: Валюта БУ, НУ: Руб»): ``` r7 Счет | Показа-\nтели | Сальдо на начало | Обороты за период | Сальдо на конец r8 Основные средства | | Дебет | Кредит | Дебет | Кредит | Дебет | Кредит r10 02 | БУ | | 480896,82 | 45754,41 | 46336,70 | | 481479,11 outline 0 r11 | НУ | | 480896,82 | 45754,41 | 46336,71 | | 481479,12 outline 0 r12 Интерактивная … | БУ | … outline 1 r13 | НУ | … outline 1 r16 Машина … (A16:D17 объединены) | БУ | … outline 1 r17 (продолжение объединения) | НУ | … outline 1 r30 Итого (A30:D31 объединены) | БУ | … outline 0 r31 (продолжение) | НУ | … outline 0 ``` `files/270b41da-….xlsx` (плоская форма, трек B) — пары строк «Валюта CNY» (рубли) / «В валюте» (сумма в валюте) под субсчётом 76.55. Сейчас: колонка «Показа-тели» попадает в `extra_cols` и в `_label_cols` (r11, r13 — пустой текст → текст «НУ» из колонки E, отступ +1000 → ребёнок строки БУ); r17 — текст объединённой ячейки (повтор названия), `data_type` всех строк `null` (шапка молчит); r31 — не «Итого развернутое», `_trailing_totals` обрывается, строка уходит в подвал с предупреждением «ниже таблицы есть числа». ## Решения ### Признак строки-показателя — поле строки, не значение `data_type` Сейчас «показатель» = `data_type ∈ {CURRENCY, QUANTITY}` (`checks._INDICATOR_DATA_TYPES`). NU в этот набор добавить нельзя: документ с «Выводимые данные: НУ» (s12) целиком состоит из строк NU, и все его проверки пропали бы. Поэтому вводится признак `indicator: bool` у `BodyRow` (вычисляется при сборе строк) и у `ClassifiedRow`; `checks._is_indicator` читает признак. Строка-показатель — если: 1. в листе есть колонка «Показатели», ячейка строки в ней непуста и метка ≠ «БУ» (словарь `data_types.indicator_column`); `data_type` — из словаря; «ПР», «ВР» — `null` (словарь `indicator_column_untyped`); метка не из обоих словарей — `null` + предупреждение `OSV_ROWS` «строка N: метка «X» в колонке «Показатели» не распознана — строка не участвует в проверках»; 2. иначе — текст строки матчит `data_types.row` («Вал.», «Кол.», «Кол-во», **«В валюте»**) или равен коду валюты (как сейчас, `row_data_type`). Метка «БУ» в колонке даёт строке `data_type = BU` явно (даже если шапка молчит или называет НУ — колонка точнее шапки). ### Текст строки с меткой колонки — метка У строки-показателя из колонки текст в колонке текста либо пуст, либо повторяет объединённую ячейку строки БУ (r17). `BodyRow.text` = метка («НУ»), `is_account = False`. Так `row_value` не дублирует название объекта, а ИНН-логика и классификатор не принимают строку за счёт или контрагента. Колонка «Показатели» исключается из `_label_cols` (как колонка ИНН) и остаётся в `extra` под своим заголовком. ### Иерархия: показатели вне каскада, родитель — предыдущая строка данных Новый модуль `src/parser/indicators.py` (чтобы не пересекаться с треком B в `hierarchy.py`): `attach_indicators(rows, levels, total_row) -> Levels`. В `OsvParser.parse_sheet` каскад `resolve_levels` получает строки без показателей; затем каждому показателю (до «Итого») назначается родитель — ближайшая строка выше, которая не показатель, — и уровень `level(родителя) + 1`; показатель в начале таблицы без строки данных выше — уровень 0 без родителя. Итоговые строки-показатели — уровень 0 без родителя (как «Итого»). Стратегия иерархии (`Levels.strategy`) — стратегия каскада. Для строк «Вал.»/«Кол.» под контрагентом (s04, s30, реальные файлы) результат совпадает с прежним: 1С печатает показатель сразу под строкой, к которой он относится. Golden-проверка это подтверждает; любое расхождение golden на реальных файлах — стоп и вопрос владельцу (не перегенерировать молча). ### Итоговые строки-показатели `_trailing_totals` продолжает серию итоговых строк и через строки с меткой показателя в колонке «Показатели» (r31 после r30): они остаются в таблице, классифицируются TOTAL с `data_type` метки и `indicator = True`; `checks._total` берёт первую итоговую строку **не показатель**. Трек B меняет `_trailing_totals`/`_total` под «Итого развернутое» перед «Итого» — при слиянии второй трек добавляет своё условие к версии первого. ### Вид данных по умолчанию Если шапка не называет вид данных (`doc.data_type is None`), строки без собственного вида (не показатели и без метки «БУ») получают `BU`; один раз на лист — предупреждение `OSV_HEADER` «вид данных в шапке не указан — строки без метки приняты как БУ». Если все строки данных размечены колонкой «Показатели», предупреждения нет (умолчание не применялось). `header.data_type` остаётся `null`; существующее предупреждение шапки «тип выводимых данных … не найден» сохраняется. ### Что не меняется - `balance_identity` проверяет все строки, включая показатели (как сейчас у «Вал.»/«Кол.»): у НУ/количества тождество «начало + обороты = конец» тоже выполняется. - «Валюта <код>» — обычная строка OTHER (рубли): `row_data_type("Валюта USD")` = `None` уже сейчас; закрепляется тестом. - Контракт, DBML, arm-core. ## Decisions made without discussion | Дата | Решение | Обоснование | Что стоит ошибка | |---|---|---|---| | 19.10.2026 | «ПР», «ВР» и метка не из словаря — `data_type = null`, строка-показатель | В контракте нет значения для разниц; `null` ядро не берёт в свод, проверки строку не видят | Разницы не видны как отдельный вид данных до AURORA-1669; переразметка — правка словаря | | 19.10.2026 | Признак показателя — поле `indicator`, а не набор значений `data_type` | Документ «Выводимые данные: НУ» не должен терять проверки | Лишнее поле в двух dataclass; контракт не меняется | | 19.10.2026 | `row_value` строки с меткой колонки — метка, а не текст колонки текста | Текст там пуст или повтор объединённой ячейки строки БУ | Если 1С когда-то печатает в строке НУ своё название — оно теряется (не встречалось в 31 файле контура) | | 19.10.2026 | Колонка «Показатели» остаётся в `extra` | Первичка не теряется, ядро `extra` хранит как есть | Дублирование метки в `extra` и `data_type` | | 19.10.2026 | Родитель показателя — предыдущая строка данных для всех показателей, включая «Вал.»/«Кол.» в колонке текста | Один механизм для колонки, «В валюте» и старых меток; каскад не видит строк, которые ломают дерево | Если показатель стоит не сразу под своей строкой, родитель будет неверным; ловит golden | | 19.10.2026 | Умолчание BU — только строкам без собственного вида; `header.data_type` остаётся `null` | Шапка действительно молчит; ядру нужен вид строки, не шапки | Предупреждение на листах без «Выводимые данные» (были без него) | | 19.10.2026 | Синтетика s33–s36 (R-1: s40–s44 заняты треком B #59, s37–s39 резерв) | Рулинг владельца R-1: параллельные треки не конфликтуют по именам файлов набора | Пропуск номеров в наборе | | 19.10.2026 | Новый модуль `indicators.py` вместо правки `hierarchy.resolve_levels` | Трек B меняет каскад иерархии параллельно | Ещё один модуль конвейера | Решения владельца 19.10.2026 (чат): п. 1 колонка «Показатели» как метки §1.3, НУ без своих проверок; п. 2 «В валюте» → CURRENCY, «Валюта <код>» — строка OTHER в рублях, код валюты не хранится; п. 3 BU по умолчанию с предупреждением — по рекомендации агента, владелец принял без возражений («ошибки давай исправлять сейчас»).