# directory-policy Specification ## Purpose Политика видимости справочников поверх прав: что именно из ГК/ЮЛ/ГСК видит пользователь, определяется данными о его филиале, а не кодом роли (§10.1 `05-domain-rules.md`). ## Requirements ### Requirement: Область видимости вычисляется из филиала пользователя Для каждого запроса ядро SHALL вычислять область видимости из `Principal`: если `regional_office.is_head_office = true` — вся система; иначе — ГК с `company_group.base_regional_office_id = app_user.regional_office_id`, их ЮЛ и ГСК/ГСЗ, а также монозаёмщики с `legal_entity.regional_office_id = app_user.regional_office_id`. Код роли (`role.code`) MUST NOT участвовать в вычислении; `business_segment` в Срезе 1 не используется (ASSUMPTION(OQ-86)). #### Scenario: Пользователь филиала видит только своё - **WHEN** есть ГК А (филиал X) и ГК Б (филиал Y), пользователь `KA_RF` филиала X запрашивает список ГК - **THEN** в ответе только ГК А, `total = 1` #### Scenario: Пользователь ГО видит всё - **WHEN** пользователь с `is_head_office = true` запрашивает список ГК - **THEN** в ответе обе ГК ### Requirement: Объект вне видимости неотличим от несуществующего Чтение, изменение и любые операции над ГК/ЮЛ/ГСК вне области видимости SHALL возвращать 404 с тем же телом, что и для несуществующего идентификатора; 403 для этого случая MUST NOT использоваться. #### Scenario: Чужая ГК по id - **WHEN** пользователь филиала X запрашивает `GET /directories/company-groups/{id}` ГК Б - **THEN** ответ 404, текст совпадает с ответом на случайный `uuid` ### Requirement: Политика применяется до прав на изменение Проверка права (`require`) и проверка видимости SHALL выполняться обе; порядок: право → видимость → операция. Создание ГК пользователем филиала SHALL привязывать её к его филиалу, если `base_regional_office_id` не указан; указание чужого филиала пользователем филиала → 422 `OFFICE_NOT_ALLOWED`. #### Scenario: Создание ГК из филиала - **WHEN** пользователь филиала X с `DIRECTORY_EDIT` создаёт ГК без филиала - **THEN** у ГК `base_regional_office_id` = X, и она видна создателю ### Requirement: Видимость консолидаций и редактируемость версии Консолидация и её версии SHALL быть видимы тем же пользователям, что и её ГК (`company_group`); чужие и несуществующие — 404 с одинаковым телом (код `NOT_FOUND`, сообщение `CONSOLIDATION_NOT_FOUND` / `VERSION_NOT_FOUND`; так устроены все 404 этого среза: `NotFoundError` несёт только текст сообщения). Правка версии (периметр, ручная смена статуса, сопоставление контрагента) SHALL проходить проверки в порядке: право → видимость → статус версии ∈ {`INITIATED`, `DOCUMENTS_PROCESSING`, `DATA_CORRECTION`} (иначе 409 `VERSION_NOT_EDITABLE`) → активная `consolidation_lock` есть (иначе 409 `LOCK_REQUIRED`) и принадлежит пользователю (иначе 409 `LOCKED_BY_OTHER` с владельцем). Проверка реализуется чистой функцией в `domain/policy.py` без обращения к коду роли. Чтение (карточка, свод, чек-лист, экспорт, кандидаты) и загрузка/подтверждение документов от статуса блокировки MUST NOT зависеть. Зоны ответственности не учитываются (ASSUMPTION(OQ-86)). #### Scenario: Чужая блокировка - **WHEN** версию держит А, Б с `CONSOLIDATION_EDIT` вызывает `PUT …/members` - **THEN** 409 `LOCKED_BY_OTHER`, `detail.locked_by = A` #### Scenario: Аннулированная версия - **WHEN** версия в `CANCELLED`, владелец прежней блокировки вызывает `POST …/osv/entries/{id}/counterparty` - **THEN** 409 `VERSION_NOT_EDITABLE` #### Scenario: Куратор ГО читает - **WHEN** `curator_go` (только `CONSOLIDATION_VIEW`) запрашивает свод версии любого филиала - **THEN** 200