arm-core/api/src/services/consolidation_guard.py
2026-10-10 01:10:21 +04:00

48 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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