@@ -0,0 +1,67 @@
|
||||
"""№ справочника компонента: автоназначение и проверка уникальности."""
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user