"""№ справочника компонента: автоназначение и проверка уникальности.""" from __future__ import annotations from sqlalchemy import func, select from app import db from app.models import Component def parse_manual_external_no(raw) -> tuple[int | None, str | None]: """Разбор ручного ввода. None без ошибки — поле пустое (нужно автоназначение).""" if raw is None: return None, None if isinstance(raw, str): text = raw.strip() if not text: return None, None raw = text try: number = int(raw) except (TypeError, ValueError): return None, "Номер справочника должен быть целым числом" if number <= 0: return None, "Номер справочника должен быть больше нуля" return number, None def external_no_in_use(number: int, *, exclude_component_id: str | None = None) -> bool: stmt = select(Component.id).where( Component.external_no == number, Component.is_deleted.is_(False), ) if exclude_component_id: stmt = stmt.where(Component.id != exclude_component_id) return db.session.scalar(stmt.limit(1)) is not None def next_available_external_no() -> int: current_max = db.session.scalar( select(func.max(Component.external_no)).where(Component.is_deleted.is_(False)) ) return int(current_max or 0) + 1 def resolve_component_external_no( raw, *, exclude_component_id: str | None = None, current_external_no: int | None = None, ) -> tuple[int | None, str | None]: """ Пустое значение → автоназначение, если current_external_no ещё нет; иначе сохраняем текущий номер. """ manual, parse_error = parse_manual_external_no(raw) if parse_error: return None, parse_error if manual is None: if current_external_no is not None: return current_external_no, None return next_available_external_no(), None if external_no_in_use(manual, exclude_component_id=exclude_component_id): return None, f"Номер справочника {manual} уже используется другим компонентом" return manual, None