68 lines
2.4 KiB
Python
68 lines
2.4 KiB
Python
"""№ справочника компонента: автоназначение и проверка уникальности."""
|
|
|
|
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
|