Files
inventory/backend/app/services/objects.py
jze9 d6a7ac0a68 Replace box picker with free-text location in audit scan
"Where found" during inventory audit is now a plain text field
(e.g. "каб. 305") stored on the object instead of assigning it to a
box, since audit walks often use room/office labels that don't map
to the box/shelf hierarchy used elsewhere. Box assignment via the
regular object form is unaffected.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 15:41:30 +05:00

80 lines
2.7 KiB
Python

import uuid
from datetime import datetime, timezone
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload
from app.models.object import InventoryObject
from app.schemas.object import BoxRef, ObjectCreate, ObjectRead, ObjectUpdate
from app.services.photos import build_photo_read, delete_photo
from app.services.qrcodes import object_public_url
def build_object_read(obj: InventoryObject) -> ObjectRead:
return ObjectRead(
id=obj.id,
inventory_number=obj.inventory_number,
name=obj.name,
description=obj.description,
box_id=obj.box_id,
box=BoxRef.model_validate(obj.box) if obj.box else None,
created_at=obj.created_at,
updated_at=obj.updated_at,
last_seen_at=obj.last_seen_at,
last_seen_location=obj.last_seen_location,
qr_code_url=object_public_url(obj.id),
photos=[build_photo_read(p) for p in obj.photos],
)
def _select_with_relations():
return select(InventoryObject).options(
selectinload(InventoryObject.photos), selectinload(InventoryObject.box)
)
async def list_objects(db: AsyncSession, search: str | None = None) -> list[InventoryObject]:
stmt = _select_with_relations().order_by(InventoryObject.created_at.desc())
if search:
like = f"%{search}%"
stmt = stmt.where(
(InventoryObject.inventory_number.ilike(like)) | (InventoryObject.name.ilike(like))
)
result = await db.execute(stmt)
return list(result.scalars().all())
async def get_object(db: AsyncSession, object_id: uuid.UUID) -> InventoryObject | None:
stmt = _select_with_relations().where(InventoryObject.id == object_id)
result = await db.execute(stmt)
return result.scalar_one_or_none()
async def create_object(db: AsyncSession, data: ObjectCreate) -> InventoryObject:
obj = InventoryObject(**data.model_dump())
db.add(obj)
await db.commit()
return await get_object(db, obj.id)
async def update_object(db: AsyncSession, obj: InventoryObject, data: ObjectUpdate) -> InventoryObject:
for field, value in data.model_dump(exclude_unset=True).items():
setattr(obj, field, value)
await db.commit()
return await get_object(db, obj.id)
async def mark_object_found(db: AsyncSession, obj: InventoryObject, location: str | None) -> InventoryObject:
obj.last_seen_location = location
obj.last_seen_at = datetime.now(timezone.utc)
await db.commit()
return await get_object(db, obj.id)
async def delete_object(db: AsyncSession, minio_client, obj: InventoryObject) -> None:
for photo in list(obj.photos):
await delete_photo(db, minio_client, photo)
await db.delete(obj)
await db.commit()