From c14349f0c8a6a2df9feba5d13d85044cc6ddab27 Mon Sep 17 00:00:00 2001 From: Andrey Epifancev Date: Tue, 27 Oct 2026 10:23:01 +0400 Subject: [PATCH] =?UTF-8?q?=C2=A71.1/=C2=A71.3/=C2=A71.5:=20=D0=B2=D1=8B?= =?UTF-8?q?=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0=20=D0=9E=D0=A1=D0=92=20?= =?UTF-8?q?=D0=BF=D0=BE=20=D1=81=D1=87=D1=91=D1=82=D1=83=20=D0=BF=D0=BE=20?= =?UTF-8?q?=D0=BA=D0=BE=D0=BD=D1=82=D1=80=D0=B0=D0=B3=D0=B5=D0=BD=D1=82?= =?UTF-8?q?=D0=B0=D0=BC=20=E2=80=94=20=D0=BC=D0=B5=D1=82=D0=BA=D0=B8=20?= =?UTF-8?q?=D1=81=D1=87=D1=91=D1=82=D0=B0=20=D0=B8=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B8=D0=BE=D0=B4=D0=B0,=20=D0=B1=D0=BB=D0=BE=D0=BA=D0=B8=20?= =?UTF-8?q?=D1=81=D1=83=D0=B1=D1=81=D1=87=D0=B5=D1=82=D0=BE=D0=B2=20=D1=81?= =?UTF-8?q?=20=D0=BA=D0=BE=D0=B4=D0=B0=D0=BC=D0=B8=20=D0=BA=D0=BE=D0=BD?= =?UTF-8?q?=D1=82=D1=80=D0=B0=D0=B3=D0=B5=D0=BD=D1=82=D0=BE=D0=B2=20(AUROR?= =?UTF-8?q?A-1714,=20#172)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Решение владельца 27.10.2026: счёт из соседней ячейки, «За период» с диапазоном .. и годом из двух цифр, вид субконто в таблице, субсчета с тремя цифрами, код контрагента + название + договоры, «ИТОГ по …» и «ИТОГ». Co-Authored-By: Claude Opus 5.5 --- context/05-domain-rules.md | 63 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/context/05-domain-rules.md b/context/05-domain-rules.md index 0930d21..8eb1ec4 100644 --- a/context/05-domain-rules.md +++ b/context/05-domain-rules.md @@ -101,6 +101,29 @@ r11 Август - Агро ООО | … ← сра - Организация — по порядку: хвост «План счетов <организация>» (`header_markers.chart_of_accounts`) → первая не-метка шапки вне строки вида субконто → хвост названия ведомости. +**Шапка выгрузки «ОСВ по счёту … по контрагентам» (AURORA-1714, решение владельца 27.10.2026; tabular-parser #71).** +Пример — выгрузка 1С «ОСВ по счету 60 по контрагентам» (в файле нет слов «по счету»): +``` +r1 MBC2009 | Оборотно-сальдовая ведомость +r2 15 июля 2025(18:40) | За период | 01.01.25..30.06.25 ← метка и значение в разных ячейках +r3 | Счет Поставщика | 60* ← метка и код счёта в разных ячейках +r4 | Счет Клиента | ← метка без значения +r6 Субконто | Начальное сальдо | … +r8 | | Поставщики ← строка вида субконто внутри таблицы +``` +- **Счёт шапки.** Метки `Счет Поставщика` / `Счет Клиента` (`header_markers.account`, данные) — как «по счету»; код — в той же + ячейке после метки либо, если там кода нет, **в следующей ячейке строки**. «60*» — все субсчета счёта: код «60» (знак `*` + кодом не является). Метка без значения пропускается; первая метка со значением определяет счёт шапки. +- **Период.** Метка `За период` (`header_markers.period`) со значением в **соседней ячейке** строки; ячейки метки и + значения организацией не считаются. Диапазон дат `01.01.25..30.06.25` (разделитель `..` — наравне с пробелом/«по»; + `periods.yaml`) разбирается как «с 1 января по конец квартала»; год из двух цифр — 20yy (год конца диапазона). Ожидаемо + для примера: 2025, Q2, нарастающий (§1.5); границы 01.01.2025–30.06.2025. +- **Вид субконто в таблице.** Если первая строка данных состоит из одной текстовой ячейки **вне колонки текста** (в колонке + суммы), а текст — известный словарю групп вид субконто (`osv_groups.yaml`: «Поставщики» — синоним `COUNTERPARTY`, как + «Контрагенты»), строка — не данные: она не получает суммы, предупреждение «нечисловое значение в колонке сумм» не + выдаётся, а вид субконто добавляется в `group_schema` после ACCOUNT так же, как строка вида субконто шапки плоской формы + выше (схема из одного ACCOUNT). Текст в колонке текста («Поставщики» как название контрагента) этим правилом не затрагивается. + Нормализация значений шапки в типы: | Текст в шапке | `group_type` | @@ -241,6 +264,41 @@ AURORA-1661; прикладной сценарий валютных строк «вид субконто в шапке не назван: строки аналитики счёта <код> приняты за контрагентов по плану счетов — проверьте». Риск: физлица и подотчётные на расчётных счетах без названия вида попадут в «Осталось N» сопоставления (OQ-158). +**Блоки субсчетов с кодами контрагентов (AURORA-1714, решение владельца 27.10.2026; tabular-parser #71).** Выгрузка 1С +«ОСВ по счету 60 по контрагентам» (плоский лист без outline и отступов, счёт — в шапке, см. §1.1) строит таблицу блоками: +``` +60.111 ← субсчёт, ячейки сумм пусты +П005482 | 0 | 108000 | 108000 | 0 | 0 | 0 ← код контрагента, суммы есть +ВЕСТ БИР ООО ← название контрагента, сумм нет +ДП87825;З-139/21-1 | 0 | 108000 | … ← договор контрагента, суммы есть +ИТОГ по 60.111 | … суммы блока … ← итог субсчёта +… +ИТОГ | … суммы листа … ← итог листа +``` +1. **Строка-счёт с тремя цифрами в сегменте.** Код вида `60.111`, `60.243` — строка-счёт (ACCOUNT, п. 1 выше), **только если + он начинается со счёта шапки** (`60.xxx` при шапке `60`; сегмент — 1–3 цифры). Без счёта шапки, а также для кода, не + начинающегося со счёта шапки, шаблон п. 1 прежний (сегмент — 1–2 цифры или заглавные буквы). Голый числовой код без + точки, не совпадающий со счётом шапки («441», «199»), при известном счёте шапки счётом не считается: это текст строки + аналитики (код контрагента, номер договора). +2. **«ИТОГ по <субсчёт>»** (`subtotal_row`, данные) — суммы субсчёта: строка-счёт с таким же кодом, у которой **нет ни одной + суммы**, получает шесть сумм из своей строки «ИТОГ по …»; сама строка «ИТОГ по …» отдельной строкой в `rows` не + выдаётся (контракт не меняется). Субсчёт сверяется «сумма детей = родитель» как обычный родитель (§1.4). Наличие + хотя бы одной такой пары включает п. 3–4 для листа. +3. **Код с суммами → название без сумм → договоры.** Внутри блока субсчёта (между строкой-счётом и следующей строкой-счётом + или итогом): + - строка **с суммами, за которой следует строка без единой суммы** — контрагент: `COUNTERPARTY` (вид субконто — «Контрагенты» + или его синоним из шапки/таблицы, §1.1), `row_value` — код как в файле; + - строка **без сумм**, идущая сразу за такой строкой, — название этого контрагента: **склеивается с ним** (как строка ИНН + с наименованием, OQ-163): в `rows` не попадает, текст — в `counterparty_name`; `counterparty_inn` — по общим правилам + (ИНН в файле нет — пустой ИНН штатен); + - остальные строки **с суммами** до следующего контрагента, субсчёта или итога — **договоры** этого контрагента: + `CONTRACT` (§1.3, п. 3: после COUNTERPARTY дочерние продолжают классифицироваться по схеме; вид договоров здесь не + назван в шапке, поэтому тип задаёт сама раскладка), на уровень глубже контрагента. Суммы контрагента = Σ его договоров. + Иерархия листа: субсчёт → контрагент → договор (стратегия каскада `code_blocks`, `OSV_HIERARCHY_BY_CODE_BLOCKS`). +4. **«ИТОГ» — итог листа.** Строка с текстом ровно «ИТОГ» (`total_row_exact`, наравне с «Итого») — TOTAL; таблица кончается + на ней, а не на первой «ИТОГ по …» (та не начинается с «Итого» и итогом листа не является). Сверка «Итого = Σ строк + уровня 0» (§1.4) идёт по субсчетам. + Нормализация кода счёта (ядро, `domain/osv_mapping.py`): `60.1 → 60.01`, `60.01.1 → 60.01.1`, `76.7.1 → 76.07.1` — числовой **второй** сегмент (1–3 цифры) дополняется нулём слева до двух цифр, первый и третий сегменты и буквенные (`76.АВ`, `МЦ.04`) остаются как в файле. `account_code` строки хранится как в результате (`account_code_raw`), @@ -329,6 +387,11 @@ OQ-131, PR #13 и 2b): парсер заполняет границы `period_st `is_cumulative = null`, границы по тексту и предупреждение «не с начала года» / «конец диапазона не совпадает с концом квартала»: ядро такой документ к версии не привязывает (OQ-131). +**Диапазон дат с точками и год из двух цифр (AURORA-1714, решение владельца 27.10.2026).** «За период | 01.01.25..30.06.25» +(метка в одной ячейке, диапазон в соседней, §1.1) разбирается как «01.01–30.06» по таблице выше: Q2, нарастающий; +разделитель `..` допустим наравне с пробелом и «по». Год из двух цифр в дате (`.25`) — 20yy; берётся год даты конца +диапазона. Год из четырёх цифр, если есть в тексте, имеет приоритет. + `ASSUMPTION(OQ-02)`: парсер определяет `period_quarter` и `is_cumulative` **только по тексту периода** (`rules/periods.yaml`, правила идут от специфичных к общим): квартальная ведомость Q2–Q4 считается не нарастающей; сверку по данным (`Q3.opening == Q1.opening`) выполняет ядро и может переопределить признак. `period_year` — четыре