Andrey Epifancev b6ead52d03 docs(openspec): change osv-flat-form и план — плоская форма ОСВ 1С (#59)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 11:00:06 +04:00

104 lines
13 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.

## Context
Плоская форма ОСВ 1С по одному счёту (образцы — выгрузка контура `awp-ca/result_dir.zip` и файл письма
`awp-ca/ОСВ сч. 60 за 2 кв. 2026.xlsx`; в git не кладутся, только локальная сверка):
```
r2 Оборотно-сальдовая ведомость по счету: 76.Н.1
r3 Контрагенты ← вид субконто (у письма — «Организации», у O-3 — «Материалы»)
r4 за 2025 г. ← у письма: «за 2 Квартал 2026 г. План счетов ООО Хреновская Нива»
r5 ООО "Товарное хозяйство" ← организация (у письма — хвост «План счетов …» строки периода)
r7 Единица измерения: рубль (код по ОКЕИ 383)
r9 Субконто | Сальдо на начало периода | Обороты за период | Сальдо на конец периода
r10 | Дебет | Кредит | Дебет | Кредит | Дебет | Кредит
r11 Август - Агро ООО | … суммы … ← сразу контрагенты: outline 0, отступ 0, строки счёта нет
…
Итого развернутое | … ← у файлов ТХО и письма — ПЕРЕД «Итого»
Итого | …
```
Вариант со строками субсчетов (ТХО 76, `270b41da`): строки «76.2», «76.5», …, «76.Н.1» на уровне 0 без строки «76»,
«Итого развернутое» перед «Итого». Вариант «Субконто» с отступами (ОСВОКТ-24, `8c7e95d9`, лист «60 сч.»): «60.01», под
ним контрагенты с отступом 1, «Итого», затем «Итого развернутое» — контрагенты сейчас `OTHER` без имени.
Текущее поведение (main `aef2d35`): письмо и `26e4c90d` — `INVALID_FORMAT` на шаге «иерархия строк»; `270b41da` —
`account_code_raw = "76.2"` из первой строки, «Итого развернутое» (r27) — `OTHER` ребёнок «76.Н.1», нарушения
`parent_sum [13, 26]`, `balance_identity [27]` (13 — валюта, трек A).
## Goals / Non-Goals
**Goals:** разбор плоской формы без `INVALID_FORMAT`; счёт и организация из шапки; «Итого развернутое» — итог в любом
порядке; контрагенты расчётных счетов видны ядру как `COUNTERPARTY`.
**Non-Goals:** см. proposal (валюта и `data_type` — трек A; «Учредители»; «Организации» по имени).
## Decisions
Решения владельца — текст ожидаемого результата AURORA-1661 (заведён по команде владельца 16.10.2026) и запуск трека
19.10.2026 («трек B можешь запускать»):
1. Счёт — из шапки «по счету[:] X»; строки таблицы — его аналитика. Фолбэк OQ-134 (код первой строки-счёта) — только
если в шапке счёта нет.
2. «Итого развернутое» — итоговая строка в любом порядке относительно «Итого».
3. Если вид субконто не назван: на расчётных счетах (58, 60, 62, 66, 67, 71, 73, 75, 76, 80) нижний уровень —
контрагенты (`COUNTERPARTY`) с предупреждением.
Реализация:
- **Код счёта с двоеточием** — `header._account_code`: хвост после метки `account` очищается от ведущих «:» и пробелов
перед `_ACCOUNT_CODE`. Отдельный синоним не нужен: «по счету:» уже матчит `\bпо\s+счету\b`.
- **Строка вида субконто** — ячейки шапки строго между строкой названия (ячейка с меткой `account`) и строкой периода
(`_find_period`), если название выше периода. Они не кандидаты в организацию. Их текст (через запятую) — виды
субконто: элемент, который `rules.groups` узнаёт **явной** записью `mapping` (не `default`), добавляется в схему
после ACCOUNT, если схема варианта 1 состоит из одной ACCOUNT («Субконто»). Неузнанный элемент («Организации»,
«Материалы») в схему не добавляется.
- **Организация** — порядок: хвост метки «План счетов» (`header_markers.chart_of_accounts`, новый ключ) → первая
не-метка шапки без строки вида субконто → хвост названия ведомости (как сейчас).
- **Итоговый блок** — `collect_body_rows` возвращает номером «Итого» **первую** строку итогового блока: вторые итоговые
строки, стоящие непосредственно перед основной «Итого» (`total_row_secondary`), входят в блок. Всё, что использует
`row >= total_row` (каскад, классификация, `inn_rows`), работает без изменений. Сверка «Итого = корень»
(`checks._total`) берёт первую строку TOTAL, текст которой не матчит `total_row_secondary`; если такой нет — первую
TOTAL (как сейчас).
- **Стратегия `header_account`** — `hierarchy.strategy_header_account`: все строки уровня 0. `resolve_levels` получает
`header_account: bool` (в шапке есть код счёта) и пробует стратегию после `account_pattern` и перед `sums`, только
если `header_account` и ни одна строка тела не `is_account`. Код предупреждения `OSV_HIERARCHY_BY_HEADER_ACCOUNT`,
текст «иерархия строк — плоский список под счётом шапки (header_account)».
- **Счёт шапки у корня ветки** — `classify_rows` получает `header_account: str | None`; корневая ветка
`_Branch(header_account, 0)` вместо `_Branch(None, 0)`.
- **«Итого» без корня ACCOUNT** — `checks.total_matches_root`: нет корневой ACCOUNT, но есть строки уровня 0 (кроме
показателей) — сверка «Итого» с их суммой (допуски R-21, как у суммы уровня 0); строк уровня 0 нет — прежнее правило
R-13 (пустая/нулевая «Итого»).
- **Контрагенты по плану счетов** — `osv_groups.yaml: counterparty_accounts` (коды верхнего уровня; запись `76`
покрывает `76`, `76.2`, `76.Н.1`, но не `760` — как `accounts_without_analytic_balance`). В `_classify_row`: позиция
`1` (первая не-счётная) за пределами схемы и счёт ветки из списка → `COUNTERPARTY` без имени группировки; ИНН — по
общим правилам (колонка, строка ИНН, хвост названия). Глубже — `OTHER`, как сейчас. Предупреждение один раз на
документ (`OSV_ROWS`, `osv.py`): «вид субконто в шапке не назван: строки аналитики счёта <код> приняты за контрагентов
по плану счетов — проверьте».
## Decisions made without discussion
| № | Маркер | Дата | Решение | Обоснование | Цена ошибки |
|---|---|---|---|---|---|
| 1 | R-1 | 19.10.2026 | Строка между названием и периодом — вид субконто, не организация | 6 из 98 xlsx контура (все — «Контрагенты»), в `fixtures` таких строк нет — регрессии golden нет | Организация, напечатанная между названием и периодом, не найдётся: предупреждение «наименование организации не найдено», ядро сверяет по ИНН |
| 2 | R-2 | 19.10.2026 | Известный вид субконто из этой строки — группировка схемы; неизвестный — не добавляется | «Контрагенты» даёт COUNTERPARTY с именем без угадывания по счёту; «Организации» у письма (60) — неизвестно, что это за вид | Неизвестный вид на нерасчётном счёте остаётся OTHER без имени (как сейчас) |
| 3 | R-3 | 19.10.2026 | `header_account` — перед `sums`, только без строк-счетов и при счёте в шапке | Плоский список с равными суммами соседей `sums` вложил бы ложно; условие — определение плоской формы 1С | Файл без оформления и счетов, но с реальной вложенностью по суммам станет плоским → «Итого» не сойдётся → `needs_check` (видно) |
| 4 | R-4 | 19.10.2026 | Корень ветки наследует счёт шапки | Ядро группирует свод по `account_code`; без этого у строк плоского файла счёт `null` | Строки до первой строки-счёта в обычных файлах получат счёт шапки вместо `null` (golden покажет) |
| 5 | R-5 | 19.10.2026 | «Итого» без корня ACCOUNT сверяется с суммой уровня 0 | Плоский список — несколько корней без счёта; прежнее R-13 требовало нулевой «Итого» | — (пустая ОСВ — прежнее правило) |
| 6 | R-6 | 19.10.2026 | Правило расчётных счетов — только позиция 1, только без имени в схеме | 1С кладёт контрагентов первым видом на этих счетах; именованные группировки («Работники организаций», «Учредители») не трогаем | Физлица и подотчётные на 71/73/76 без названия вида → COUNTERPARTY: попадут в «Осталось N» сопоставления (OQ-158) |
| 7 | R-7 | 19.10.2026 | Синтетика `s40`–`s44` | Номера `s33`–`s39` оставлены параллельному треку A, чтобы реестр генератора не конфликтовал | Перенумерация при слиянии |
## Risks / Trade-offs
- [OQ-158] Контрагенты по плану счетов на 71/73/76 — в том числе физлица и подотчётные: «Осталось N» в сопоставлении
вырастет. Смягчение: предупреждение `OSV_ROWS` на документе; вопрос владельцу — сузить список (без 71/73).
- [Трек A] До слияния трека A строки без «Выводимые данные» — `data_type = null`; ядро их в свод не берёт.
- [Golden] Изменения затронут снимки, где схема — одна ACCOUNT («Субконто») на расчётном счёте и есть не-счётные строки
(пакет ДКБ, `osv-dkb/**`): OTHER → COUNTERPARTY. Каждое изменение снимка — с просмотром diff.
## Open Questions
- «Учредители» на 80 (именованная OTHER, O-29, ОСВ 80 «Тестовой ГК 1» `02c5df51`): считать COUNTERPARTY? Нужен для
гудвилла (§4 п. 2).
- Сузить список расчётных счетов (71, 73 — подотчётные и персонал)?
- «Организации» в строке вида субконто у письма (60): что это за вид в 1С и стоит ли считать его контрагентами по имени.