"""Общий пролог операций над версией консолидации: блокировка строки версии и видимость (design D-8, D-9). Один порядок для всех операций, меняющих версию: `consolidation_version FOR UPDATE` → видимость (ГК консолидации в области пользователя; чужая и несуществующая версии — один и тот же 404 `VERSION_NOT_FOUND`) → для правок — активная блокировка и `ensure_version_editable` (409 `VERSION_NOT_EDITABLE` / `LOCK_REQUIRED` / `LOCKED_BY_OTHER`). Версию блокирует и правка, и перехват, и подтверждение документа (R-2), поэтому проверка статуса и владельца блокировки не может устареть до конца транзакции. Операции, которым редактируемость не нужна (взять в работу, снять блокировку), берут только `lock_visible_version`. """ import uuid from sqlalchemy.ext.asyncio import AsyncSession from src.common.exceptions import NotFoundError from src.domain.policy import VisibilityScope, ensure_version_editable from src.models.consolidation import Consolidation, ConsolidationVersion from src.repositories import consolidations as repo VERSION_NOT_FOUND = "версия консолидации не найдена" # одинаково для чужой и несуществующей CONSOLIDATION_NOT_FOUND = "консолидация не найдена" # одинаково для чужой и несуществующей async def lock_visible_version( session: AsyncSession, scope: VisibilityScope, version_id: uuid.UUID ) -> tuple[ConsolidationVersion, Consolidation]: """Версия под `FOR UPDATE` и её консолидация, если ГК видна пользователю; иначе 404 `VERSION_NOT_FOUND`. Видимость проверяется ДО блокировки (M-6): невидимая версия отвечает 404 сразу, не ожидая чужую транзакцию, которая держит её строку. Затем `FOR UPDATE` с обновлением состояния; исчезнувшая за это время версия — тоже 404. Повторной проверки видимости нет: ГК и филиал версия не меняет.""" found = await repo.get_version(session, scope, version_id) if found is None: raise NotFoundError(VERSION_NOT_FOUND) version = await repo.lock_version(session, version_id) if version is None: raise NotFoundError(VERSION_NOT_FOUND) return version, found[1] async def lock_editable_version( session: AsyncSession, scope: VisibilityScope, version_id: uuid.UUID, user_id: uuid.UUID ) -> tuple[ConsolidationVersion, Consolidation]: """`lock_visible_version` + `ensure_version_editable` по активной блокировке: версию вправе править `user_id`.""" version, consolidation = await lock_visible_version(session, scope, version_id) ensure_version_editable(version, await repo.active_lock(session, version.id), user_id) return version, consolidation