Andrey Epifancev fb491a3e93 openspec: архив statement-template-lines (#80)
Спеки documents-api и statement-checks обновлены дельтой change'а; change перенесён в
openspec/changes/archive/2026-10-09-statement-template-lines/. Реализация — PR #81.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:55:14 +04:00

77 lines
6.5 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.

# statement-checks Specification
## Purpose
Логические проверки форм ББ/ОФР §7 в ядре: чистые функции над текущими суммами документа, без хранения результата;
акцепт непройденных проверок аналитиком.
## Requirements
### Requirement: Соотношения §7 по колонкам
Проверки SHALL считаться чистой функцией по текущим `financial_statement_value.amount` документа для каждой колонки
отдельно по словарю `api/rules/statement_checks.yaml` (копия словаря tabular-parser): соотношение проверяется, только
если присутствует его левая часть; отсутствующий код и пустая сумма — 0; выполнено, если `|левая − правая| ≤ tolerance`
(1, в единицах формы) хотя бы в одном варианте правой части — как записано или с кодами `abs_terms` по модулю. Нарушение
жёсткого (`strict`) соотношения, не превышающее `rounding_tolerance_percent` (0,001 %) от наибольшего по модулю числа
соотношения, SHALL считаться мягким («в пределах округления»). Мягкие: соотношения `strict: false` и «сумма пятизначных
подстрок = четырёхзначный родитель». Результат — `{failed: [...], soft_failed: [...]}`, элемент — `{column_index,
relation, expected, actual}`; MUST NOT храниться в БД.
Присутствующими SHALL считаться строки формы из БД, кроме строк, созданных правкой аналитика
(`line_code_original_value IS NULL`), у которых все суммы пустые; строки, достроенные в ответе по справочнику
(`documents-api`, `id = null`), в проверках MUST NOT участвовать. Созданная правкой строка с хотя бы одной непустой
суммой проверяется по всем колонкам, пустые суммы — 0.
#### Scenario: Сходящийся баланс
- **WHEN** проверяется форма `mezheninovskaya/2025-Q3/1_Форма1_бух_09`
- **THEN** `failed` пуст
#### Scenario: Нарушение жёсткого соотношения
- **WHEN** у баланса 1600 = 1000, 1100 = 300, 1200 = 600 в колонке 0
- **THEN** `failed` содержит `{column_index: 0, relation: "1600 = 1100 + 1200", expected: 1000, actual: 900}`
#### Scenario: Расходы со знаком минус
- **WHEN** ОФР 2110 = 100, 2120 = −60, 2100 = 40
- **THEN** `2100 = 2110 − 2120` выполнено через вариант с `abs_terms`
#### Scenario: Правка исправляет нарушение
- **WHEN** аналитик правит 1200 с 600 на 700 в форме из предыдущего сценария
- **THEN** проверка по текущим суммам: `failed` пуст
#### Scenario: Пустая шаблонная строка не проверяется
- **WHEN** в форме нет строки 1700 (в ответе она достроена пустой), 1300 + 1400 + 1500 = 2 371 333 в колонке 0
- **THEN** соотношения с целью 1700 (`1700 = 1300 + 1400 + 1500`) не проверяются; `1600 = 1700` (цель 1600 есть в
форме, 1700 = 0) нарушено — как и без достройки: потерянная строка «БАЛАНС» сама по себе даёт `NEEDS_CHECK`
#### Scenario: Заполненная шаблонная строка проверяется
- **WHEN** аналитик заполнил 1700 = 2 371 333 только в колонке 0, а в колонке 1 1300 + 1400 + 1500 = 2 000 000
- **THEN** в колонке 0 соотношение выполнено; в колонке 1 `failed` содержит `1700 = 1300 + 1400 + 1500` с `expected 0`,
`actual 2000000`
#### Scenario: Очищенная шаблонная строка
- **WHEN** затем аналитик очищает 1700 в колонке 0 (`amount = null`)
- **THEN** строка 1700 снова не участвует в проверках
### Requirement: Межформенная проверка
Для документа `BALANCE` или `INCOME` SHALL считаться мягкая проверка `1370(колонка отчётной даты ББ) − 1370(колонка
31 декабря прошлого года ББ) = 2400(колонка отчётного периода ОФР)` с допуском `tolerance`, если найдена форма-пара:
документ другого типа формы того же ЮЛ, `period_year` и `period_quarter`, файла не `SUPERSEDED` (подтверждённый, затем
самый поздний по `created_at`), и все три колонки. Нарушение SHALL попадать в `cross_form` и MUST NOT блокировать
подтверждение.
#### Scenario: Пара найдена
- **WHEN** у ЮЛ ББ за 2025 Q3 (1370 = 500 на 30.09.2025, 300 на 31.12.2024) и ОФР за 2025 Q3 (2400 = 150)
- **THEN** `cross_form` — нарушение `expected 200, actual 150`; подтверждение ББ разрешено
#### Scenario: Пары нет
- **WHEN** у ЮЛ есть только ББ за период
- **THEN** `cross_form` — `null`
### Requirement: Акцепт непройденных проверок
Акцепт SHALL записывать `financial_statement_meta.checks_accepted_by`, `checks_accepted_at`,
`checks_accepted_comment`; документ с акцептом SHALL считаться прошедшим жёсткие проверки для статуса и подтверждения.
Любая правка значений или шапки формы SHALL сбрасывать акцепт (три поля — `NULL`).
#### Scenario: Акцепт и правка
- **WHEN** аналитик акцептовал нарушение 1600 и затем поправил любое значение
- **THEN** поля акцепта пусты, при непройденной проверке документ `NEEDS_CHECK`