48 lines
3.3 KiB
Python
48 lines
3.3 KiB
Python
"""Общий пролог операций над версией консолидации: блокировка строки версии и видимость (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
|