24 KiB
Raw Blame History

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) Вторая итоговая без оборотов и с ошибочным сальдо не ловится; единственная итоговая без оборотов проверяется, и пустое тождество может дать нарушение
20 R-21 19.10.2026 Шаг вверх от «Итого» проходит строки-показатели, но включает их в итоговый блок, только если выше найдена вторая итоговая строка; показатели второй итоговой строки без оборотов наследуют пропуск тождества (R-19); счётчик TOTAL в тождестве не считает показатели 1С штатно даёт «Показатели БУ, НУ» + «Итого развернутое»: без шага «Итого развернутое» оставалась в теле и становилась фиктивным контрагентом, а НУ давала нарушение тождества (ревью интеграции I-6, I-7, M-13) Ошибка сальдо показателя второй итоговой строки не ловится

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.