## ADDED Requirements ### Requirement: Уточнение шапки ОСВ `PATCH /api/v1/documents/{id}/osv-header` SHALL требовать `DOCUMENT_EDIT` и видимость ЮЛ, принимать частичный набор `period_year`, `period_quarter`, `is_cumulative`, `osv_account_code` и возвращать карточку документа с `is_modified`, `original_values`, `modified_by`, `modified_at`. Отсутствующие поля SHALL сохраняться. Правила — §9.5 п.12. Чужой и несуществующий документ SHALL возвращать одинаковый 404. #### Scenario: Изменение только счёта - **WHEN** аналитик меняет счёт ОСВ РБ с 60 на 62, не передавая период - **THEN** меняется только счёт; даже неопределённый квартал остаётся прежним #### Scenario: Нет доступа - **WHEN** пользователь без DOCUMENT_EDIT правит шапку либо документ находится вне видимости ЮЛ - **THEN** без права возвращается 403, при наличии права и отсутствии видимости — 404; данные не меняются ### Requirement: Проверки правки шапки ОСВ После проверки типов тела запроса Pydantic PATCH SHALL проверять последовательно: тип OSV (иначе 409 DOCUMENT_NOT_OSV), файл не SUPERSEDED (иначе 409 FILE_SUPERSEDED), корректность периода (422 INVALID_PERIOD), счёта (422 INVALID_ACCOUNT_CODE), отсутствие другой ОСВ с тем же нормализованным счётом в файле (409 DUPLICATE_ACCOUNT с detail.document_id), редактируемость привязанных версий (409 VERSION_NOT_EDITABLE с detail.version_ids). Все проверки SHALL предшествовать записи. #### Scenario: Частичная правка периода - **WHEN** передано хотя бы одно поле периода - **THEN** итоговые год, квартал и is_cumulative обязательны: год 1900–2099, квартал Q1–Q4, признак boolean; для Q1 признак true; иначе 422 INVALID_PERIOD без изменений #### Scenario: Нормализация и коллизия - **WHEN** передано 60.1, а у другой ОСВ этого файла уже счёт 60.01 - **THEN** нормализация даёт 60.01, возвращается 409 DUPLICATE_ACCOUNT с id другого документа #### Scenario: Пустой счёт - **WHEN** передан пустой счёт - **THEN** 422 INVALID_ACCOUNT_CODE, прежний счёт сохранён #### Scenario: Порядок конфликтов - **WHEN** у заменённого файла документ BALANCE и некорректный период в теле - **THEN** возвращается DOCUMENT_NOT_OSV; у OSV того же файла — FILE_SUPERSEDED ### Requirement: Исходная шапка и признак исправления При первой фактической правке ядро SHALL сохранить прежние значения всех четырёх полей в document.original_values; при последующих правках снимок MUST NOT меняться. Новые значения SHALL записываться в ту же строку document, is_modified SHALL отражать отличие от снимка, modified_by/at — последнюю правку. document.id, file_ordinal, строки osv_entry, файл и JSON результата в S3 MUST NOT изменяться. #### Scenario: Две правки - **WHEN** счёт ОСВ РБ меняется 60 → 62 → 76 - **THEN** текущий счёт 76, original_values.osv_account_code = 60, в original_values также исходный период; is_modified = true, id и количество документов и строк ОСВ прежние #### Scenario: Возврат к исходному - **WHEN** все четыре поля возвращены к значениям original_values - **THEN** is_modified = false, original_values остаётся заполненным, последняя правка атрибутирована #### Scenario: Запрос без изменений - **WHEN** нормализованная итоговая шапка совпадает с текущей - **THEN** 200 с карточкой без аудита, создания original_values, сброса подтверждения и изменения modified_by/at ### Requirement: Снятие подтверждения ОСВ после уточнения Фактическая правка CONFIRMED ОСВ SHALL установить NEEDS_CHECK, очистить confirmed_by/at и снять привязки consolidation_document из всех версий «в работе», сохранив привязки CANCELLED и статусы версий. Привязка к иной версии SHALL запретить правку. Неподтверждённый документ SHALL сохранить статус. consolidation_lock MUST NOT требоваться; автоподтверждение после PATCH MUST NOT запускаться. #### Scenario: Исправление автоподтверждённой ОСВ - **WHEN** ОСВ РБ привязана к версии DATA_CORRECTION, аналитик исправляет период - **THEN** NEEDS_CHECK, confirmed_by/at = null, привязка снята, версия остаётся DATA_CORRECTION; повторное ручное подтверждение привязывает документ по новому периоду и счёту #### Scenario: Завершённая версия - **WHEN** документ привязан к VERIFIED, даже если одновременно привязан к версии «в работе» - **THEN** 409 VERSION_NOT_EDITABLE со списком запрещающих version_ids, ни одна привязка и шапка не меняются #### Scenario: Отменённая версия - **WHEN** подтверждённый документ связан только с CANCELLED и версиями «в работе» - **THEN** правка разрешена без consolidation_lock, привязки CANCELLED остаются, остальные снимаются ### Requirement: Производные данные после уточнения ОСВ Подходящесть версиям, чек-лист и колонка «ОСВ» SHALL отражать новую шапку при следующем чтении; period_check, counterparty_check и строки osv_entry SHALL сохраняться. Правка MUST NOT запускать автозамену другого файла; замещение привязки с тем же ключом происходит при повторном подтверждении по §3 п.1. Перезагрузка файла MUST NOT переносить уточнения старых документов. Флаг CONSOLIDATION_PERIOD_FROM_VERSION сохраняется. #### Scenario: Смена счёта шапки - **WHEN** счёт ОСВ РБ исправлен с 60 на 62 - **THEN** колонка «ОСВ» и подходящие слоты чек-листа учитывают 62; account_code/account_id/account_root строк ОСВ, period_check и counterparty_check не изменились, другой файл ОСВ 62 не заменён #### Scenario: Перезагрузка - **WHEN** новый загруженный файл распознан с исходным счётом 60 - **THEN** новые документы созданы из нового результата, исправление 62 предыдущего документа не переносится ### Requirement: Строгие типы шапки ОСВ PATCH SHALL принимать period_year как integer, period_quarter и osv_account_code как string, is_cumulative как boolean, без приведения типов. Отсутствующие поля не меняются; null SHALL передаваться в доменную проверку явно. Неверные типы SHALL отклоняться стандартным 422 валидации тела до доменных проверок. Границы года, допустимые кварталы и совместимость полей SHALL проверяться доменом. #### Scenario: Неверный тип года - **WHEN** передан period_year как строка, дробное число, boolean, массив или объект - **THEN** стандартный 422 с detail-массивом ошибок валидации, доменная обработка не запускается #### Scenario: Год вне диапазона - **WHEN** у доступного незаменённого документа OSV передан period_year = 2100 - **THEN** тип integer принят, домен возвращает 422 INVALID_PERIOD #### Scenario: Неизвестный квартал - **WHEN** у доступного незаменённого документа OSV передан period_quarter = "Q0" - **THEN** тип string принят, домен возвращает 422 INVALID_PERIOD