Andrey Epifancev 656486a7d2 OpenSpec change osv-indicator-column и план (#58)
Колонка «Показатели», строки-показатели, «В валюте», BU по умолчанию (AURORA-1613, AURORA-1661 — валютная часть).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:03:13 +04:00

12 KiB
Raw Blame History

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 Синтетика s40–s43 (s33–s39 оставлены треку B) Параллельные треки не должны конфликтовать по именам файлов набора Пропуск номеров в наборе
19.10.2026 Новый модуль indicators.py вместо правки hierarchy.resolve_levels Трек B меняет каскад иерархии параллельно Ещё один модуль конвейера

Решения владельца 19.10.2026 (чат): п. 1 колонка «Показатели» как метки §1.3, НУ без своих проверок; п. 2 «В валюте» → CURRENCY, «Валюта <код>» — строка OTHER в рублях, код валюты не хранится; п. 3 BU по умолчанию с предупреждением — по рекомендации агента, владелец принял без возражений («ошибки давай исправлять сейчас»).