## MODIFIED Requirements ### Requirement: Предупреждения и нарушения документа — `document_issue` Ядро SHALL хранить предупреждения и нарушения документа в `document_issue`: вид (`warning` | `relation` | `soft`), код, уровень (`error` | `info`), сообщение, список `line_id`, `column_id` (колонка формы) или `column_index` (столбец предупреждения, которого нет среди колонок формы), поле шапки, источник (`service` | `core`); для нарушений — `codes` (коды цели и членов), текст соотношения, `expected`, `actual`. Нарушение из `checks.relations_failed` — `kind = relation`, `code = RELATION_FAILED`, `severity = error`; из `soft_relations_failed` — `kind = soft`, `code = RELATION_SOFT`, `severity = info`; `line_ids` — все строки `ok` кодов `codes` в документе. При загрузке пишутся предупреждения и `checks` результата (источник `service`) и проверки ядра уровня `info` (источник `core`): `CODE_NOT_IN_DICTIONARY` (кода нет в словаре имён редакции документа), `CODE_NOT_IN_EDITION` (код не из перечня редакции документа и его варианта — по перечню кодов библиотеки `arm_statements`), `NAME_MISMATCH`, `RESULT_STATUS_INCONSISTENT`. **Редакция документа** для этих проверок — `edition` точки входа пакета для пересчёта (`statement-normalization`): редакция с лучшей подпоследовательностью кодов строк `ok` документа, при равенстве — по эффективной дате; так бланк 66н, сданный в 2025 году, проверяется по `66n`, а не по дате. Поле `edition` ответа `GET …/statement` и выбор шаблонных строк по-прежнему — по дате периода (§7 «Полный состав формы»). `document_issue` ведётся у **любого** документа файла: у ОСВ — предупреждения `osvDocument.warnings` результата `2.0` (вид `warning`, источник `service`, `OSV_*`, `info`). При перезагрузке файла записи документа удаляются вместе с ним. При пересчёте после правки записи `relation` и `soft` заменяются целиком независимо от источника; записи `warning` источника `service` сохраняются, кроме снимаемых правкой: правка `header.report_date` (в том числе подтверждение тем же значением) снимает `PERIOD_UNKNOWN`, `REPORT_DATE_UNPARSED`, `REPORT_DATE_UNCERTAIN`; правка `header.inn` или `header.organization_name` — `NO_ORGANIZATION_NO_INN`; правка `columns[].period_date` — `COLUMN_WITHOUT_DATE` и `COLUMN_DATE_UNCERTAIN` этой колонки; прочие `error` сервиса снимаются только перезагрузкой файла. При удалении шаблонной строки её `id` SHALL убираться из `line_ids` всех записей документа. `GET …/statement` и `POST …/confirm` SHALL читать нарушения §7 только из `document_issue`. #### Scenario: Дата колонке - **WHEN** у колонки предупреждение `COLUMN_WITHOUT_DATE`, аналитик ставит ей `period_date` через PATCH - **THEN** предупреждение удаляется, статус пересчитывается #### Scenario: Неснимаемое предупреждение - **WHEN** у документа `SEGMENT_COLUMNS_MISMATCH`, аналитик правит значения - **THEN** предупреждение остаётся; документ можно подтвердить вручную (оно подтверждение не блокирует) #### Scenario: Бланк 66н в 2025 году без ложного info - **WHEN** форма Межениновской за 3 кв. 2025 на бланке 66н с кодом 1120 (в перечне `fsbu4` его нет) - **THEN** редакция документа для проверок ядра — `66n` (подпоследовательность длиннее), `CODE_NOT_IN_EDITION` у 1120 нет #### Scenario: Пропущенная колонка - **WHEN** в результате предупреждение `COLUMN_SKIPPED` с `column_index = 5`, колонки 5 среди `columns[]` нет - **THEN** запись `document_issue` с `column_index = 5` и `column_id = null`; экран показывает её у документа ### Requirement: Пересчёт после правки После каждого PATCH формы ядро SHALL пересчитывать `checks`, `needs_review`, `form_variant`, родителей строк `detail` и статус точкой входа пакета для пересчёта по сохранённым данным (`statement-normalization`; конвенция знака — по `source_text`), с правилами: `is_modified` или `is_confirmed` значения ⇒ `needs_review = false`; `line_code_modified` или `is_confirmed` строки ⇒ `needs_review` строки = `false`; значения строк `detail` — `needs_review = false` (Р-23), у строки, выведенной из `detail` назначением кода, `needs_review` её значений считается по общему правилу; акцепт ⇒ жёсткие нарушения не учитываются; правленые ячейки в голосование знака не входят. Результат пишется: `needs_review` строк и значений, `form_variant`, `parent_row_index` всех строк `detail` (ближайшая строка `ok` выше по `row_index`; нет такой — `null`, строка остаётся `detail`), записи `document_issue` видов `relation` / `soft`, статус. Отличие `statements_version` задания от версии библиотеки ядра (версия установленного дистрибутива `pylib-ai-arm-statements`) — `document_issue` `STATEMENTS_VERSION_MISMATCH` (`info`); у задания без версии (`1.x`) запись не пишется. Один PATCH — один пересчёт и одна запись аудита `RECOGNIZED_DATA_EDIT`. Акцепт SHALL сбрасываться правкой данных — суммы, кода или статуса строки, даты колонки, поля шапки; подтверждение или снятие подтверждения (`confirmed`) и подтверждение даты отчёта или даты колонки тем же значением акцепт MUST NOT сбрасывать (Р-25, S-47). #### Scenario: Подтверждение ячейки без правки - **WHEN** аналитик подтверждает ячейку с `needs_review = true` без изменения суммы - **THEN** `is_confirmed = true`, `confirmed_by/at`, `is_modified` не ставится, `needs_review = false`, аудит; акцепт проверок, если был, сохраняется #### Scenario: Снятие подтверждения - **WHEN** аналитик снимает подтверждение с ячейки (`confirmed: false`), которая не покрыта соотношением - **THEN** `is_confirmed = false`, `needs_review = true` после пересчёта, аудит #### Scenario: Родитель уходит в расшифровки - **WHEN** у строки 1150 две расшифровки (`detail`, `parent_row_index` = 1150), аналитик отмечает саму 1150 расшифровкой - **THEN** после пересчёта родитель всех трёх строк `detail` — ближайшая строка `ok` выше 1150; строк над ними нет — `null` (на экране — в начале раздела) ### Requirement: Словари ядра и достройка шаблонных строк Копия словаря §7 ядра (`api/rules/statement_checks.yaml`) SHALL удаляться: проверки ядро считает библиотекой `arm_statements`. `api/rules/statement_lines.yaml` SHALL оставаться только словарём имён (код → наименования по редакциям): у кода может быть несколько имён (синонимы и законные повторы кода в бланке, Р-20), загрузчик повтор кода в редакции MUST NOT запрещать; сверка наименования — с любым именем кода; шаблонная строка берёт первое имя. Порядок и состав редакций — из перечня кодов библиотеки; что все коды словаря имён есть в перечне своей редакции, сверяет тест ядра `test_name_dictionary_codes_in_code_orders` (в `make check`). В PR перехода ядра на v2.0 словарь имён SHALL быть дополнен: в редакции `fsbu4` — вторые имена законных повторов 1220 «НДС, начисленный по отгрузке», 1520 «Обязательство по аренде», 2410 «Налог при упрощенной системе налогообложения» (Р-20) и коды печатной формы 1С 1105 «Гудвил», 1130 «Нематериальные поисковые активы», 1140 «Материальные поисковые активы», 1530 «Доходы будущих периодов», 2460 «Прочее» (имена — по `fixtures/ocr` и шаблонам банка); без этого законные повторы РБ, Счастливочной и aep_6 и любой неуверенный 1105 дают `NAME_MISMATCH`. Шаблонные строки достраиваются по словарю имён (одна на код); код, у которого есть хотя бы одна строка `ok` из файла, шаблоном MUST NOT достраиваться; строки `unreadable` / `missing` не заменяются шаблонной строкой молча. #### Scenario: Код есть только у строки unreadable - **WHEN** у формы нет строки `ok` с кодом 1250, но есть строка `unreadable` «ИЧ,250» - **THEN** шаблонная строка 1250 достраивается пустой, строка `unreadable` показывается отдельно со статусом #### Scenario: Два имени у кода - **WHEN** в редакции `fsbu4` словаря имён код 1520 записан дважды — «Кредиторская задолженность» и «Обязательство по аренде» - **THEN** словарь загружается; шаблонная строка 1520 одна, с именем «Кредиторская задолженность» #### Scenario: Гудвил с заголовком раздела в имени - **WHEN** строка 1105 (уверенность 67) с наименованием «АКТИВ I. Внеоборотные активы Гудвил» - **THEN** справочное «Гудвил» (≥ 5 символов) входит в него — совпало, `NAME_MISMATCH` нет