Files
shanxia2.0/backend/app/crud/storage_occupancy.py
T
2026-05-22 11:43:41 +08:00

303 lines
9.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.
"""库位占用记录 CRUD(biz_storage_slot_occupancy)"""
from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, List, Optional
from sqlalchemy import and_, func
from sqlalchemy.orm import Session
from app.core.utils import generate_uuid
from app.models.models import BizStorageFacilitySlot, BizStorageSlotOccupancy
def _row_to_dict(c: BizStorageSlotOccupancy) -> Dict[str, Any]:
return {
"id": c.id,
"slotId": c.slot_id,
"itemType": c.item_type or "",
"itemId": c.item_id or "",
"itemCode": c.item_code or "",
"itemName": c.item_name or "",
"itemSource": c.item_source or "",
"storedAt": c.stored_at.strftime("%Y-%m-%d %H:%M:%S") if c.stored_at else "",
"removedAt": c.removed_at.strftime("%Y-%m-%d %H:%M:%S") if c.removed_at else "",
"occupancyStatus": c.occupancy_status or "",
"tenantNo": c.tenant_no,
"remark": c.remark or "",
"createBy": c.create_by or "",
"createTime": c.create_time.strftime("%Y-%m-%d %H:%M:%S") if c.create_time else "",
}
def _refresh_slot_cache(db: Session, slot_id: str) -> None:
"""刷新 slot 的 occupied_count / last_item_type / last_stored_at / slot_status"""
slot = db.query(BizStorageFacilitySlot).filter(
BizStorageFacilitySlot.id == slot_id,
BizStorageFacilitySlot.del_flag == 0,
).first()
if not slot:
return
count = db.query(func.count(BizStorageSlotOccupancy.id)).filter(
BizStorageSlotOccupancy.slot_id == slot_id,
BizStorageSlotOccupancy.occupancy_status == "stored",
BizStorageSlotOccupancy.del_flag == 0,
).scalar() or 0
slot.occupied_count = count
if count > 0:
slot.slot_status = "占用"
latest = db.query(BizStorageSlotOccupancy).filter(
BizStorageSlotOccupancy.slot_id == slot_id,
BizStorageSlotOccupancy.occupancy_status == "stored",
BizStorageSlotOccupancy.del_flag == 0,
).order_by(BizStorageSlotOccupancy.stored_at.desc()).first()
if latest:
slot.last_item_type = latest.item_type
slot.last_stored_at = latest.stored_at
else:
slot.slot_status = "空"
slot.last_item_type = None
slot.last_stored_at = None
slot.update_time = datetime.now()
db.add(slot)
db.flush()
def create_occupancy(
db: Session,
*,
slot_id: str,
item_type: str,
item_id: str,
item_code: str,
item_name: str,
item_source: str = "manual",
tenant_no: str = "",
user_id: Optional[str] = None,
remark: Optional[str] = None,
) -> BizStorageSlotOccupancy:
"""创建入库记录 + 刷新 slot 缓存"""
now = datetime.now()
obj = BizStorageSlotOccupancy(
id=generate_uuid(),
slot_id=slot_id,
item_type=item_type,
item_id=item_id,
item_code=(item_code or "").strip() or None,
item_name=(item_name or "").strip() or None,
item_source=(item_source or "manual").strip(),
stored_at=now,
removed_at=None,
occupancy_status="stored",
tenant_no=tenant_no,
order_num=0,
remark=(remark or "").strip() or None,
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(obj)
db.flush()
_refresh_slot_cache(db, slot_id)
db.commit()
db.refresh(obj)
return obj
def batch_create_occupancies(
db: Session,
*,
items: List[Dict[str, Any]],
tenant_no: str = "",
user_id: Optional[str] = None,
) -> List[BizStorageSlotOccupancy]:
"""批量入库"""
now = datetime.now()
created: List[BizStorageSlotOccupancy] = []
slot_ids_seen: set = set()
for it in items:
obj = BizStorageSlotOccupancy(
id=generate_uuid(),
slot_id=it["slot_id"],
item_type=it.get("item_type", "cell_product"),
item_id=it["item_id"],
item_code=(it.get("item_code") or "").strip() or None,
item_name=(it.get("item_name") or "").strip() or None,
item_source=(it.get("item_source") or "production").strip(),
stored_at=now,
removed_at=None,
occupancy_status="stored",
tenant_no=tenant_no,
order_num=0,
remark=(it.get("remark") or "").strip() or None,
create_by=user_id,
create_dept_id="",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(obj)
created.append(obj)
slot_ids_seen.add(it["slot_id"])
db.flush()
for sid in slot_ids_seen:
_refresh_slot_cache(db, sid)
db.commit()
for obj in created:
db.refresh(obj)
return created
def remove_occupancy(
db: Session,
occupancy_id: str,
*,
user_id: Optional[str] = None,
) -> BizStorageSlotOccupancy:
"""手动出库"""
obj = db.query(BizStorageSlotOccupancy).filter(
BizStorageSlotOccupancy.id == occupancy_id,
BizStorageSlotOccupancy.del_flag == 0,
).first()
if not obj:
raise ValueError("占用记录不存在")
if obj.occupancy_status == "removed":
raise ValueError("该物品已出库")
now = datetime.now()
obj.occupancy_status = "removed"
obj.removed_at = now
obj.update_by = user_id
obj.update_time = now
db.add(obj)
db.flush()
_refresh_slot_cache(db, obj.slot_id)
db.commit()
db.refresh(obj)
return obj
def remove_by_item(
db: Session,
item_type: str,
item_id: str,
*,
user_id: Optional[str] = None,
) -> None:
"""按物品类型+ID出库(细胞产品状态变更时调用)"""
obj = db.query(BizStorageSlotOccupancy).filter(
BizStorageSlotOccupancy.item_type == item_type,
BizStorageSlotOccupancy.item_id == item_id,
BizStorageSlotOccupancy.occupancy_status == "stored",
BizStorageSlotOccupancy.del_flag == 0,
).first()
if not obj:
return
now = datetime.now()
obj.occupancy_status = "removed"
obj.removed_at = now
obj.update_by = user_id
obj.update_time = now
db.add(obj)
db.flush()
_refresh_slot_cache(db, obj.slot_id)
db.commit()
def list_occupancies(
db: Session,
*,
slot_id: str,
occupancy_status: Optional[str] = "stored",
item_type: Optional[str] = None,
tenant_no: str = "",
page: int = 1,
page_size: int = 20,
) -> Dict[str, Any]:
"""分页查询占用记录"""
q = db.query(BizStorageSlotOccupancy).filter(
BizStorageSlotOccupancy.slot_id == slot_id,
BizStorageSlotOccupancy.del_flag == 0,
)
if tenant_no:
q = q.filter(BizStorageSlotOccupancy.tenant_no == tenant_no)
if occupancy_status:
q = q.filter(BizStorageSlotOccupancy.occupancy_status == occupancy_status)
if item_type:
q = q.filter(BizStorageSlotOccupancy.item_type == item_type)
total = q.order_by(None).with_entities(func.count(BizStorageSlotOccupancy.id)).scalar() or 0
page = max(1, page)
page_size = min(max(1, page_size), 100)
rows = (
q.order_by(BizStorageSlotOccupancy.stored_at.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
return {"items": [_row_to_dict(r) for r in rows], "total": int(total)}
def list_occupancy_history(
db: Session,
*,
slot_id: Optional[str] = None,
facility_node_id: Optional[str] = None,
item_type: Optional[str] = None,
start_date: Optional[str] = None,
end_date: Optional[str] = None,
tenant_no: str = "",
page: int = 1,
page_size: int = 20,
) -> Dict[str, Any]:
"""出入库历史"""
q = db.query(BizStorageSlotOccupancy).filter(
BizStorageSlotOccupancy.del_flag == 0,
)
if tenant_no:
q = q.filter(BizStorageSlotOccupancy.tenant_no == tenant_no)
if slot_id:
q = q.filter(BizStorageSlotOccupancy.slot_id == slot_id)
if item_type:
q = q.filter(BizStorageSlotOccupancy.item_type == item_type)
if start_date:
try:
sd = datetime.strptime(start_date, "%Y-%m-%d")
q = q.filter(BizStorageSlotOccupancy.stored_at >= sd)
except ValueError:
pass
if end_date:
try:
ed = datetime.strptime(end_date, "%Y-%m-%d")
ed = ed.replace(hour=23, minute=59, second=59)
q = q.filter(BizStorageSlotOccupancy.stored_at <= ed)
except ValueError:
pass
if facility_node_id:
slot_ids = [
r[0] for r in db.query(BizStorageFacilitySlot.id).filter(
BizStorageFacilitySlot.facility_node_id == facility_node_id,
BizStorageFacilitySlot.del_flag == 0,
).all()
]
if slot_ids:
q = q.filter(BizStorageSlotOccupancy.slot_id.in_(slot_ids))
else:
return {"items": [], "total": 0}
total = q.order_by(None).with_entities(func.count(BizStorageSlotOccupancy.id)).scalar() or 0
page = max(1, page)
page_size = min(max(1, page_size), 100)
rows = (
q.order_by(BizStorageSlotOccupancy.create_time.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
return {"items": [_row_to_dict(r) for r in rows], "total": int(total)}