125 lines
23 KiB
Markdown
125 lines
23 KiB
Markdown
## 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; «Учредители»; прочие неизвестные виды субконто; «Организации»
|
||
узнаются только как синоним COUNTERPARTY по множественному числу, R-10, R-17).
|
||
|
||
## Decisions
|
||
|
||
Решения владельца — текст ожидаемого результата AURORA-1661 (заведён по команде владельца 16.10.2026) и запуск трека
|
||
19.10.2026 («трек B можешь запускать»):
|
||
|
||
1. Счёт — из шапки «по счету[:] X»; строки таблицы — его аналитика. Фолбэк OQ-134 (код первой строки-счёта) — только
|
||
если в шапке счёта нет.
|
||
2. «Итого развернутое» — итоговая строка в любом порядке относительно «Итого».
|
||
3. Если вид субконто не назван: на расчётных счетах (58, 60, 62, 66, 67, 75, 76, 80; 71 и 73 исключены — R-9) нижний уровень —
|
||
контрагенты (`COUNTERPARTY`) с предупреждением.
|
||
|
||
Реализация:
|
||
|
||
- **Код счёта с двоеточием** — `header._account_code`: хвост после метки `account` очищается от ведущих «:» и пробелов
|
||
перед `_ACCOUNT_CODE`. Отдельный синоним не нужен: «по счету:» уже матчит `\bпо\s+счету\b`.
|
||
- **Строка вида субконто** — ячейки шапки строго между строкой названия (ячейка с меткой `account`) и строкой периода
|
||
(`_find_period`), если название выше периода. Они не кандидаты в организацию. Их текст (через запятую) — виды
|
||
субконто: элемент, который `rules.groups` узнаёт **явной** записью `mapping` (не `default`), добавляется в схему
|
||
после ACCOUNT, если схема варианта 1 состоит из одной ACCOUNT («Субконто»). Неузнанный элемент («Материалы»,
|
||
единственное «Организация» — R-17) в схему не добавляется; «Организации» узнаётся как COUNTERPARTY (R-10, R-17).
|
||
- **Организация** — порядок: хвост метки «План счетов» (`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`, как сейчас. Строка, которую словарь групп
|
||
явно знает как не-COUNTERPARTY (валюта), остаётся `OTHER` (R-18). Предупреждение по одному на счёт (R-13; `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 с именем без угадывания по счёту; «Материалы» — не контрагенты (вид «Организации» затем узнан R-10, R-17) | Неизвестный вид на нерасчётном счёте остаётся 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С кладёт контрагентов первым видом на этих счетах; именованные группировки («Работники организаций», «Учредители») не трогаем | Физлица на 76 без названия вида → COUNTERPARTY: попадут в «Осталось N» сопоставления (OQ-158); 71/73 исключены R-9 |
|
||
| 7 | R-7 | 19.10.2026 | Синтетика `s40`–`s44` | Номера `s33`–`s39` оставлены параллельному треку A, чтобы реестр генератора не конфликтовал | Перенумерация при слиянии |
|
||
| 8 | R-8 | 19.10.2026 | «Учредители» на счёте 80 не трогаем: именованная группировка остаётся OTHER (R-6 сохраняется) | Принято по рекомендации сессии при одобрении запуска владельцем; именованные группировки правило расчётных счетов не переопределяет | Учредители не станут COUNTERPARTY автоматически — гудвилл (§4 п. 2) потребует ручного сопоставления |
|
||
| 9 | R-9 | 19.10.2026 | Счета 71 и 73 исключены из `counterparty_accounts`; итог: 58, 60, 62, 66, 67, 75, 76, 80 | Подотчётные и персонал — физлица, а не контрагенты (OQ-158); принято по рекомендации сессии | Контрагенты на 71/73 без названия вида останутся OTHER и не попадут в сопоставление |
|
||
| 10 | R-10 (сужен R-17) | 19.10.2026 | «Организации» — синоним COUNTERPARTY в `osv_groups.yaml` (`^организаци`; после R-17 — `^организации\b`) | Вид субконто письма (сч. 60) — организации-контрагенты; принято по рекомендации сессии | Другой смысл вида «Организации» на иных счетах даст COUNTERPARTY; видно в golden/предупреждениях |
|
||
| 11 | R-11 (отменён R-16) | 19.10.2026 | Данные синтетики s40, s41, s42 исправлены: у «Итого развернутое» добавлены обороты, равные суммам строк (s40: 650/750, s41: 5000/4991, s42: 5700/6600) | В плане у этой строки обороты пусты, поэтому тождество строки (начало + обороты = конец) не сходилось, и файлы не могли стать `done` независимо от кода; бриф плана ошибочен | s40–s42 расходятся с исходным текстом плана. **Приёмка Task 6 показала:** в реальных файлах контура у «Итого развернутое» обороты тоже пусты (`45e06f7d`, `638ecb3d`, `7e49f8b5`, `f2e0e04e`, `270b41da`, `738bfcf5`, письмо), тождество на этой строке остаётся нарушенным, и файл уходит в `needs_check`: правка данных синтетики скрыла дефект кода, а не данных; решается отдельно (см. отчёт task-6). **Отменён рулингом R-16:** данные s40–s42 возвращены к исходному тексту плана |
|
||
| 12 | R-12 | 19.10.2026 | Строка вида субконто не даёт группировку из ячеек-меток шапки («Субконто», «Счет»), меток валюты («Валюта БУ, НУ») и ячеек, на которые срабатывает паттерн `currency` | Ячейка между названием и периодом может быть меткой шапки, а не видом субконто; иначе в схему попали бы ложные группировки (T2.2, ревью задачи 2) | Метка, совпавшая с настоящим видом субконто, не станет группировкой; вид «Организации» уже учтён (R-10) |
|
||
| 13 | R-13 | 19.10.2026 | Предупреждение `OSV_ROWS` «вид субконто в шапке не назван…» — одно на каждый различный `account_code_raw` строк, принятых по плану счетов | План требовал «один раз на документ»; у листа с несколькими субсчетами (62.01, 62.02) заметка с точным кодом полезнее одной общей | Больше заметок на листах с несколькими субсчетами (лист `67 сч.` — четыре); на статус не влияют (`info`) |
|
||
| 14 | R-14 | 19.10.2026 | `Expect.data_type` в `test_synthetic_files.py` стал `str \| None`: у s40–s44 ожидается `None` | В шапке нет БУ/НУ; умолчание BU — зона трека A (`data_type`) | При слиянии трека A ожидания s40–s44 придётся поправить на `BU` |
|
||
| 15 | R-15 | 19.10.2026 | Предупреждение `OSV_TOTAL` при несходимости «Итого» без корня ACCOUNT, но со строками уровня 0, говорит «≠ сумме N строк уровня 0» (T4.1) | Прежний текст «корневого счёта нет» вводил в заблуждение для плоского списка | Текст в `metrics.warnings` изменился; снимков с прежним текстом нет |
|
||
| 16 | R-16 (уточнён R-19) | 19.10.2026 | Вторые итоговые строки (`total_row_secondary`, «Итого развернутое») исключены из проверки тождества строки; сверка по основной «Итого» сохранена; R-11 отменён | «Итого развернутое» перед «Итого» содержит только развёрнутые сальдо, без оборотов (подтверждено 7 реальными файлами контура: `638ecb3d`, `45e06f7d`, `f2e0e04e`, `7e49f8b5`, `270b41da`, `738bfcf5`, письмо); решение владельца: «Итого развернутое» — итоговая строка в любом порядке. Исключение оказалось шире обоснования: после «Итого» строка идёт с оборотами и сходящимся тождеством — см. R-19 | Тождество на вторых итогах без оборотов не проверяется: ошибка в сальдо этой строки не будет поймана; основная «Итого» по-прежнему проверяется тождеством и сверкой с корнем/суммой уровня 0 |
|
||
| 17 | R-17 (заменяет R-10) | 19.10.2026 | Синоним вида субконто сужен до множественного числа: `^организации\b` → COUNTERPARTY; единственное «Организация» остаётся OTHER и в схему не добавляется | «Организация» в шапке вариантов 1/2 — собственная организация учёта (измерение отчитывающейся организации); на контуре `dae56120` (сч. 83) и `02c5df51` (сч. 80) схема `[Счет, Организация]` из-за широкого паттерна стала `[ACCOUNT, COUNTERPARTY]` (финальное ревью I-1) | Прочие формы написания («Организации-контрагенты» и т. п.) не узнаются; вид во множественном числе с иным смыслом на нерасчётном счёте даст COUNTERPARTY (OQ-171) |
|
||
| 18 | R-18 | 19.10.2026 | Правило расчётных счетов (`counterparty_accounts`) не применяется к строке, которую словарь групп явно знает как не-COUNTERPARTY; паттерн валюты расширен до `^(в\s+)?валют` | Строки «Валюта», «Валюта CNY/EUR/USD/руб», «В валюте» под 76.55 становились контрагентами (`270b41da`, `738bfcf5`; финальное ревью I-2) | Контрагент с названием на «Валют…» («Валютные решения ООО») остаётся OTHER без имени и не попадёт в сопоставление |
|
||
| 19 | R-19 | 19.10.2026 | Тождество у второй итоговой строки пропускается, только если обе колонки оборотов пусты и на листе есть другая TOTAL; иначе строка проверяется как обычная | На контуре «Итого развернутое» перед «Итого» — без оборотов, после «Итого» — с оборотами и сходящимся тождеством; R-16 пропускал обе (финальное ревью I-4) | Вторая итоговая без оборотов и с ошибочным сальдо не ловится; единственная итоговая без оборотов проверяется, и пустое тождество может дать нарушение |
|
||
|
||
## Risks / Trade-offs
|
||
|
||
- [OQ-158] Контрагенты по плану счетов на 76 и прочих расчётных счетах могут включать физлиц: «Осталось N» в
|
||
сопоставлении вырастет. Смягчение: предупреждение `OSV_ROWS` на документе; список уже сужен (без 71/73, R-9).
|
||
- [Трек A] Слит в main (#58): строки без «Выводимые данные» — `data_type = BU` с предупреждением, `header.data_type`
|
||
остаётся `null`; golden s40–s44 обновлены при rebase (O-3 ledger).
|
||
- [Golden] Правило расчётных счетов срабатывает только при схеме из одной ACCOUNT без названия вида; у реальных файлов
|
||
и пакета ДКБ вид назван в шапке, поэтому снимки `osv/**` и `osv-dkb/**` не изменились (OTHER → COUNTERPARTY не
|
||
случилось). Эффект проверен на файлах контура.
|
||
|
||
## Open Questions
|
||
|
||
Решения R-8…R-10 приняты по рекомендации сессии при одобрении запуска 19.10.2026; вопросы **открыты** (docs: `07-open-questions.md`,
|
||
OQ-169…OQ-172), в коде действует допущение:
|
||
|
||
- OQ-169 — «Учредители» на 80 (O-29, `02c5df51`) остаются OTHER (R-8); гудвилл §4 п. 2 — вопрос владельцу.
|
||
- OQ-170 — счета 71 и 73 (подотчётные, персонал) исключены из списка расчётных (R-9).
|
||
- OQ-171 — «Организации» (множественное число) — синоним COUNTERPARTY (R-10, R-17); «Организация» — OTHER.
|
||
- OQ-172 — безымянная аналитика первого уровня на счёте 63 не входит в список расчётных счетов: нужен ли 63. На файлах
|
||
контура таких строк нет; 51 строка — в fixtures, golden `osv-dkb/nm/2025-Q3/НМ_ОСВ_63_Контрагенты_…json`, и имя файла
|
||
(«63_Контрагенты») — довод за включение счёта 63.
|