diff --git a/context/05-domain-rules.md b/context/05-domain-rules.md index f15c9eb..ae813da 100644 --- a/context/05-domain-rules.md +++ b/context/05-domain-rules.md @@ -155,16 +155,35 @@ r11 Договоры ← Из текста ИНН отрезается всегда: `counterparty_name` без ИНН и разделителя, `row_value` — исходный текст; только для строк `COUNTERPARTY`, договоры и прочее не трогаются; правила — `osv_headers.yaml` (`counterparty_inn_suffix`, `inn_only_row`, `inn_kpp_cell`); **реализовано**, OQ-100, OQ-163 (строки ИНН; ранее OQ-130, tabular-parser #9, PR #15; синтетика `s17`–`s19`), -`data_type` (строки «USD», «Вал.», «Кол.», «Кол-во» под контрагентом → CURRENCY/QUANTITY; по умолчанию — из шапки). +`data_type` (строки «USD», «Вал.», «Кол.», «Кол-во», «В валюте» под контрагентом → CURRENCY/QUANTITY; метка колонки +«Показатели» — ниже; по умолчанию — из шапки, а если шапка вид данных не называет — BU с предупреждением). «Вал» **без точки** — не метка валюты: так называется материал (счёт 10); валютная строка — «Вал.» с точкой или код валюты из словаря (**реализовано**, tabular-parser #47, AURORA-1663). «Кол-во» — строка количества, как «Кол.» и «Количество» (там же). -Строки «Вал.», «Кол.», «Кол-во» и трёхбуквенный код валюты («USD») — не уровень группировки, а вид сумм родителя: -`group_type = OTHER`, `data_type = CURRENCY | QUANTITY`, позицию в `group_schema` не занимают, **не наследуют** -`counterparty_name`/`counterparty_inn` (контрагента потребитель находит по родителю), `account_code_raw` наследуют от -ветки; их суммы остаются в шести полях, но в проверки родителя не входят (§1.4). +Строки «Вал.», «Кол.», «Кол-во», «В валюте» и трёхбуквенный код валюты («USD») — **строки-показатели**: не уровень +группировки, а вид сумм родителя: `group_type = OTHER`, `data_type = CURRENCY | QUANTITY`, позицию в `group_schema` не +занимают, **не наследуют** `counterparty_name`/`counterparty_inn` (контрагента потребитель находит по родителю), +`account_code_raw` наследуют от ветки; их суммы остаются в шести полях, но в проверки родителя и «Итого» не входят +(§1.4). Родитель строки-показателя — **предыдущая строка данных** (ближайшая строка выше, которая сама не показатель); +уровень — на один глубже родителя; в каскад иерархии строки-показатели не передаются, так что не ломают дерево строк +БУ, даже если стоят на одном outline-уровне с ним. Строка «Валюта <код>» («Валюта USD») — **не** показатель: обычная +строка аналитики (OTHER) с рублёвыми суммами; код валюты отдельно не хранится (решение владельца 19.10.2026, +AURORA-1661; прикладной сценарий валютных строк — аналитика AURORA-1670). + +**Колонка «Показатели»** (решение владельца 19.10.2026, AURORA-1613). 1С печатает вид суммы строки отдельной колонкой +между колонкой текста и суммами (заголовок «Показатели», часто с переносом «Показа-тели»; словарь +`osv_headers.yaml` — `service_columns.indicator`): на каждую строку аналитики — строка «БУ», под ней «НУ», «Кол.», +«Вал.», иногда «ПР»/«ВР» (постоянные и временные разницы); текст строк-показателей в колонке текста пуст или повторяет +объединённую ячейку строки БУ. Метка читается по словарю `data_types.yaml` — `indicator_column`: «БУ» — обычная строка с +`data_type = BU`; «НУ» → NU, «Кол.» → QUANTITY, «Вал.» → CURRENCY — строки-показатели (как выше); «ПР», «ВР» и метка не +из словаря — строки-показатели с `data_type = null` (в контракте нет значений для разниц) и, для метки не из словаря, +предупреждением. `row_value` строки-показателя — метка. Строки НУ хранятся как пришли (OQ-45), собственных проверок +«дети = родитель» и «Итого» у них нет (прикладной сценарий НУ — аналитика AURORA-1669); тождество строки (начало + +обороты = конец) проверяется у всех строк, как и раньше. Итоговые строки «Итого» с меткой показателя («НУ», «Кол.») +сразу за основной «Итого» — тоже TOTAL, в подвал не уходят и в сверку «Итого = корень» не входят. Колонка +«Показатели» остаётся в `extra` строки под своим заголовком. Классификация строки (п. 6–10 «Парсинг ОСВ»): 1. Строка — счёт, если текст матчится `^\d{2,3}(\.(\d{1,2}|[А-ЯЁA-Z]{1,3})){0,2}(,\s*.*)?$` (флаги `re.ASCII | re.DOTALL`: @@ -197,17 +216,18 @@ r11 Договоры ← Допуск ±0.01, пустое значение = 0. - По каждой строке: `opening_debit − opening_credit + turnover_debit − turnover_credit == closing_debit − closing_credit` (±0.01). - **Исключение — счета без сальдо по аналитике** (решение владельца 07.10.2026, OQ-132): у 91 и 99 строки аналитики несут + **Исключение — счета без сальдо по аналитике** (решение владельца 07.10.2026, OQ-132): у 90, 91 и 99 строки аналитики несут только обороты, сальдо закрывается на субсчёт закрытия (91.09), поэтому тождество строк на них не проверяется; список - счетов — данные в `rules/*.yaml` tabular-parser (начальный состав 91, 99; 90 не входит — внутри года у его субсчетов - сальдо есть). Проверки «сумма детей = родитель» и «Итого» на этих счетах сохраняются в таком виде (R-3 PR + счетов — данные в `rules/*.yaml` tabular-parser (состав 90, 91, 99; счёт 90 добавлен решением владельца 19.10.2026: 4 файла + контура с колонкой «Показатели» оставались `needs_check` на счёте 90 (667d1ee1, 7983e91b, 3e74526e, ec075a82) — у + родителей 90.xx.y сальдо на конец есть, у аналитики его нет; tabular-parser#58, O-1). Проверки «сумма детей = родитель» и «Итого» на этих счетах сохраняются в таком виде (R-3 PR arm-tabular-parser#26, подтверждено владельцем 08.10.2026): **обороты** родителя сверяются с суммой детей всегда; **сальдо** — только у родителя с дочерними субсчетами (91 → 91.01/91.02/91.09); у субсчёта, под которым одна аналитика, сальдо с детьми не сверяется (у аналитики оно пусто, у субсчёта — равно оборотам). Счёт строки сопоставляется со - списком по ближайшему счёту ветки: запись `91` покрывает `91`, `91.01`, `91.09`, но не `910` и не `90.01`; строка + списком по ближайшему счёту ветки: запись `91` покрывает `91`, `91.01`, `91.09`, но не `910`; строка «Итого» проверяется как обычно. -- По каждому родителю: суммы родителя = Σ детей того же `data_type`. Строки CURRENCY/QUANTITY в суммирование не входят - и сами родителями не проверяются. **Обороты** сверяются по колонкам (дебет с дебетом, кредит с кредитом). **Сальдо** +- По каждому родителю: суммы родителя = Σ детей того же `data_type`. Строки-показатели (§1.3: «Вал.», «Кол.», код + валюты, «В валюте», метки колонки «Показатели» кроме «БУ») в суммирование не входят и сами родителями не проверяются. **Обороты** сверяются по колонкам (дебет с дебетом, кредит с кредитом). **Сальдо** (начало, конец) — по колонкам либо, **если у родителя сальдо свёрнуто** (не больше одной ненулевой стороны), по нетто `дебет − кредит` (R-21): 1С показывает счёт свёрнутым сальдо (62 = 16,1 млн Дт), а субсчета и контрагентов — развёрнутым (62.01 = 40,2 млн Дт, 62.02 = 24,1 млн Кт). Без этого правила 32 из 66 реальных файлов не сходились бы diff --git a/context/06-test-data.md b/context/06-test-data.md index 5136092..ce1753e 100644 --- a/context/06-test-data.md +++ b/context/06-test-data.md @@ -290,6 +290,11 @@ fixtures/ `s20`–`s27` — по одной на раскладку строки ИНН рядом с наименованием (над / под × в дереве / не в дереве × с суммами / без; ИНН с верной контрольной суммой), `s28` — строка ИНН с неверной контрольной суммой (остаётся строкой, `needs_check`); правило — `05-domain-rules.md` §1.3, OQ-163. +`s33`–`s36` — строки-показатели (`05-domain-rules.md` §1.3, AURORA-1613, AURORA-1661, tabular-parser#58): +`s33_indicator_column_bu_nu` — счёт 02, колонка «Показатели» БУ/НУ, шапка без «Выводимые данные», «Итого» по БУ и НУ; +`s34_indicator_column_qty_val_diff` — метки «Вал.», «Кол.», «ПР», «ВР» и неизвестная «Ед.»: показатели вне сверки родителя; +`s35_currency_slices` — пары «Валюта USD/EUR» (рубли) и «В валюте» (показатель `CURRENCY`) на одном уровне; +`s36_no_data_type_header` — шапка без вида данных и без колонки «Показатели»: строки `BU` по умолчанию с предупреждением. Формы ББ/ОФР из Excel — подраздел 4б. `s14`–`s16` добавлены тестом покрытия словарей: каждая запись `rules/*.yaml` обязана сработать на реальных или синтетических файлах. diff --git a/context/07-open-questions.md b/context/07-open-questions.md index b770b6e..c96a891 100644 --- a/context/07-open-questions.md +++ b/context/07-open-questions.md @@ -137,7 +137,7 @@ | OQ-129 | Формулировка сценария «сопоставление и автоприменение» (spec consolidated-osv): «`unmapped_total` падает на 1 + propagated» неверна на реальных данных. `propagated` считает записи `osv_entry` (включая пары-пробелы и строки, не дающие строк свода), а свод склеивает кварталы, поэтому `unmapped_total` падает на число **склеенных** строк свода (R-15, design №80). Уточнить текст spec при архивации | arm-core Срез 4: Task 13 (T13.M1, R-15) | spec `consolidated-osv` при архивации change | допущение принято: тест сверяет обе величины отдельно; текст spec уточнён в change, `openspec/specs/` — при архивации | | OQ-130 | ОСВ произвольного формата: ИНН в ячейке **на строку выше** названия контрагента (строка только с ИНН, без сумм). Сейчас такая строка уходит в OTHER, ИНН для резолва теряется. Решение владельца 07.10.2026: склеивать со следующей строкой контрагента; синтетические фикстуры на этот случай, на ИНН в тексте и на «ИНН/КПП» в одной ячейке | владелец, 07.10.2026 | tabular-parser §1.0 п. 4, §1.3; резолв контрагента в ядре | **реализовано** (RSHB/arm-tabular-parser#9, PR #15): строка только с ИНН над названием склеивается со следующей строкой контрагента (`inn_only_row`, предупреждение «ИНН … над строкой … отнесён к ней»), «ИНН/КПП» в одной ячейке (`inn_kpp_cell`); тесты — `test_classify.py` (`test_inn_only_row_above_is_glued_to_next_counterparty`, `test_inn_with_kpp_in_one_column_cell_gives_inn`), синтетика `s18_inn_row_above`, `s19_inn_kpp_cell`; `05-domain-rules.md` §1.3. Правило строки ИНН заменено OQ-163 (13.10.2026). | | OQ-131 | Скользящий период ОСВ «Октябрь 2024 г. – Сентябрь 2025 г.» (НПЗ 60/62/76, Агрокомплекс 76, `osv-dkb/`): парсер берёт первый месяц → `2024 Q4`, нарастающий. Нужен разбор диапазона дат и признак «не с начала года»; как ядру относиться к такому документу при привязке к версии (не нарастающий и не квартальный) | пакет ДКБ 06.10.2026, прогон парсера | tabular-parser `periods.yaml`/`header.py`; ядро `fits` (D-5 Среза 4) | **реализовано в парсере** (tabular-parser PR #13 + Срез 2b): диапазон месяцев разбирается (`periods.yaml`: `months`, `range`), границы `period_start`/`period_end`/`is_from_year_start` — в контракте v1.2 (docs change 2b), скользящий период → `period_quarter = null`, `is_cumulative = null` и предупреждение «не с начала года» (`05-domain-rules.md` §1.5). **Открыто для ядра:** привязка скользящего документа к версии (допущение — не привязывать, показывать в чек-листе как «период не соответствует») | -| OQ-132 | Тождество сальдо по строкам (§1.4) на счёте 91 (и, вероятно, 90, 99): строки аналитики доходов/расходов несут только обороты, сальдо закрывается на 91.09 → `needs_check` на каждой ОСВ 91 (Цимлянский, Приманыческий). Исключать ли счета без сальдо по аналитике из проверки тождества строк | пакет ДКБ 06.10.2026 | tabular-parser `checks.py`, статус документа | **закрыт решением владельца 07.10.2026 (вариант Б):** счета без сальдо по аналитике исключаются из тождества строк §1.4; список — данные `rules/*.yaml` tabular-parser, начальный состав 91 и 99 (90 не включать: внутри года у субсчетов 90 сальдо есть); «Итого» и сумма детей = родитель сверяются. Реализация — arm-tabular-parser#11 п. 2 | +| OQ-132 | Тождество сальдо по строкам (§1.4) на счёте 91 (и, вероятно, 90, 99): строки аналитики доходов/расходов несут только обороты, сальдо закрывается на 91.09 → `needs_check` на каждой ОСВ 91 (Цимлянский, Приманыческий). Исключать ли счета без сальдо по аналитике из проверки тождества строк | пакет ДКБ 06.10.2026 | tabular-parser `checks.py`, статус документа | **закрыт решением владельца 07.10.2026 (вариант Б):** счета без сальдо по аналитике исключаются из тождества строк §1.4; список — данные `rules/*.yaml` tabular-parser, начальный состав 91 и 99; «Итого» и сумма детей = родитель сверяются. Реализация — arm-tabular-parser#11 п. 2. **Дополнение владельца 19.10.2026 (заменяет «90 не включать»):** счёт 90 добавлен в список (состав 90, 91, 99): 4 файла контура с колонкой «Показатели» оставались `needs_check` на счёте 90 (667d1ee1, 7983e91b, 3e74526e, ec075a82) — у родителей 90.xx.y сальдо на конец есть, у аналитики его нет (tabular-parser#58, O-1) | | OQ-133 | ОСВ варианта 2 без строки корневого счёта: верхний уровень — субсчета (58.01, 58.03), «Итого» = их сумме, парсер сверяет «Итого» с первым корнем → `needs_check` на каждом листе (Новоросметалл, Агрокомплекс; уточняет OQ-106). Сверять «Итого» с суммой строк уровня 0 | пакет ДКБ 06.10.2026 | tabular-parser `checks.py` | **реализовано** (tabular-parser PR #13, arm-tabular-parser#11 п. 3): при нескольких корнях уровня 0 «Итого» сверяется с суммой строк уровня 0 (сальдо по колонкам либо по нетто, как у родителя, R-21); `checks.py`, `05-domain-rules.md` §1.4 | | OQ-134 | Шапка ОСВ без строки организации и с кодом/названием счёта в двух колонках («Счет: Код / Наименование», строка «Бухгалтерский \| 9 месяцев 2025 г.»; Новоросметалл ОСВ 63): парсер не находит организацию, счёт, период, тип данных. Новый вариант заголовка — словарь или код | пакет ДКБ 06.10.2026 | tabular-parser `detect.py`/`header.py`, `osv_headers.yaml` | **реализовано** (tabular-parser PR #13, arm-tabular-parser#11 п. 4): шапка без метки организации (организация — хвост названия ведомости), период и тип данных отдельными ячейками (`periods.yaml: standalone`, `data_types.yaml: standalone_header`), подписи «Код»/«Наименование» не группировки (`osv_headers.yaml: column_captions`), код счёта — из первой строки-счёта (с предупреждением); `05-domain-rules.md` §1.1; тест `test_header_without_labels_and_code_name_columns_are_found` | | OQ-135 | Отчёт 1С «Анализ счета» с отбором по контрагентам группы (`fixtures/analysis-reports/`, в именах файлов «ОСВ_…ПоКомпаниямГруппы»): не ОСВ, но содержит ВГО по корреспонденции счетов. Нужен ли отдельный тип документа для Среза 5, или ВГО считаются только из ОСВ | пакет ДКБ 06.10.2026 | `document_type`, Срез 5 | открыт: допущение — не разбирать; парсер отвечает `INVALID_DOCUMENT` |