Files
zkxw/backend/app/crud/production_task.py
T
2026-05-22 07:16:53 +08:00

934 lines
38 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_production_task + 步骤 + 子表 + 投入/产出)"""
from __future__ import annotations
import math
from datetime import datetime
from typing import Any, Dict, List, Optional, Tuple
from sqlalchemy import and_, func
from sqlalchemy.orm import Session
from app.core.utils import generate_uuid
from app.models.models import (
BizProcess,
BizProcessConsumable,
BizProcessDataCollection,
BizProcessOutput,
BizProcessReagent,
BizProcessRoute,
BizProcessRouteStep,
BizProcessRouteStepConsumable,
BizProcessRouteStepDataCollection,
BizProcessRouteStepOutput,
BizProcessRouteStepReagent,
BizProductionTask,
BizProductionTaskInput,
BizProductionTaskOutput,
BizProductionTaskStep,
BizProductionTaskStepConsumable,
BizProductionTaskStepDataCollection,
BizProductionTaskStepOutput,
BizProductionTaskStepReagent,
)
def _fmt_dt(t: Optional[datetime]) -> str:
return t.strftime("%Y-%m-%d %H:%M:%S") if t else ""
def _generate_task_no(db: Session, tenant_no: str) -> str:
day = datetime.now().strftime("%Y%m%d")
like = f"RW{day}%"
cnt = (
db.query(func.count(BizProductionTask.id))
.filter(
BizProductionTask.tenant_no == tenant_no,
BizProductionTask.task_no.like(like),
)
.scalar()
)
return f"RW{day}{(cnt or 0) + 1:04d}"
def _task_to_dict(t: BizProductionTask) -> Dict[str, Any]:
return {
"id": t.id,
"taskNo": t.task_no,
"workOrderId": t.work_order_id,
"taskName": t.task_name or "",
"description": t.description or "",
"routeId": t.route_id or "",
"taskStatus": t.task_status,
"startedTime": _fmt_dt(t.started_time),
"completedTime": _fmt_dt(t.completed_time),
"remark": t.remark or "",
"attachments": t.attachments or "",
"tenantNo": t.tenant_no,
"orderNum": t.order_num if t.order_num is not None else 0,
"createTime": _fmt_dt(t.create_time),
"updateTime": _fmt_dt(t.update_time),
}
def _step_to_dict(s: BizProductionTaskStep) -> Dict[str, Any]:
return {
"id": s.id,
"taskId": s.task_id,
"stepOrder": s.step_order if s.step_order is not None else 0,
"processCode": s.process_code or "",
"categoryId": s.category_id or "",
"processName": s.process_name or "",
"description": s.description or "",
"equipmentName": s.equipment_name or "",
"estimatedDuration": s.estimated_duration or "",
"actualDuration": s.actual_duration or "",
"sopIds": s.sop_ids or "",
"stepStatus": s.step_status or "dzx",
"startedTime": _fmt_dt(s.started_time),
"completedTime": _fmt_dt(s.completed_time),
"remark": s.remark or "",
}
def _step_reagent_to_dict(r: BizProductionTaskStepReagent) -> Dict[str, Any]:
return {
"id": r.id,
"stepId": r.step_id,
"reagentCode": r.reagent_code or "",
"reagentName": r.reagent_name or "",
"spec": r.spec or "",
"standardUsage": r.standard_usage or "",
"actualUsage": r.actual_usage or "",
"unit": r.unit or "",
"isKeyMaterial": r.is_key_material if r.is_key_material is not None else "0",
"remark": r.remark or "",
}
def _step_consumable_to_dict(c: BizProductionTaskStepConsumable) -> Dict[str, Any]:
return {
"id": c.id,
"stepId": c.step_id,
"consumableCode": c.consumable_code or "",
"consumableName": c.consumable_name or "",
"specModel": c.spec_model or "",
"standardUsage": c.standard_usage or "",
"actualUsage": c.actual_usage or "",
"unit": c.unit or "",
"isRequired": c.is_required if c.is_required is not None else "1",
"remark": c.remark or "",
}
def _step_output_to_dict(o: BizProductionTaskStepOutput) -> Dict[str, Any]:
return {
"id": o.id,
"stepId": o.step_id,
"outputTypeId": o.output_type_id or "",
"outputName": o.output_name or "",
"outputBarcode": o.output_barcode or "",
"remark": o.remark or "",
}
def _step_data_collection_to_dict(d: BizProductionTaskStepDataCollection) -> Dict[str, Any]:
return {
"id": d.id,
"stepId": d.step_id,
"collectionName": d.collection_name or "",
"unit": d.unit or "",
"collectionValue": d.collection_value or "",
"remark": d.remark or "",
}
def _task_input_to_dict(i: BizProductionTaskInput) -> Dict[str, Any]:
return {
"id": i.id,
"taskId": i.task_id,
"inputType": i.input_type or "",
"inputId": i.input_id or "",
"inputCode": i.input_code or "",
"inputName": i.input_name or "",
"remark": i.remark or "",
}
def _task_output_to_dict(o: BizProductionTaskOutput) -> Dict[str, Any]:
return {
"id": o.id,
"taskId": o.task_id,
"cellProductId": o.cell_product_id or "",
"storageSlotId": o.storage_slot_id or "",
"outputName": o.output_name or "",
"outputBarcode": o.output_barcode or "",
"quantity": float(o.quantity) if o.quantity is not None else None,
"unit": o.unit or "",
"remark": o.remark or "",
"attachments": o.attachments or "",
}
def _soft_delete_sub(db: Session, model_class, parent_field: str, parent_id: str, now: datetime) -> None:
rows = db.query(model_class).filter(
and_(
getattr(model_class, parent_field) == parent_id,
model_class.del_flag == 0,
)
).all()
for r in rows:
r.del_flag = 1
r.update_time = now
db.add(r)
def _clone_step_reagents(
db: Session, src_step_id: str, new_step_id: str,
tenant_no: str, user_id: Optional[str], now: datetime,
) -> None:
for r in db.query(BizProcessRouteStepReagent).filter(
and_(BizProcessRouteStepReagent.step_id == src_step_id, BizProcessRouteStepReagent.del_flag == 0)
).all():
db.add(BizProductionTaskStepReagent(
id=generate_uuid(), step_id=new_step_id, tenant_no=tenant_no,
reagent_code=r.reagent_code, reagent_name=r.reagent_name,
spec=r.spec, standard_usage=r.standard_usage,
unit=r.unit, is_key_material=r.is_key_material,
remark=r.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
def _clone_step_consumables(
db: Session, src_step_id: str, new_step_id: str,
tenant_no: str, user_id: Optional[str], now: datetime,
) -> None:
for c in db.query(BizProcessRouteStepConsumable).filter(
and_(BizProcessRouteStepConsumable.step_id == src_step_id, BizProcessRouteStepConsumable.del_flag == 0)
).all():
db.add(BizProductionTaskStepConsumable(
id=generate_uuid(), step_id=new_step_id, tenant_no=tenant_no,
consumable_code=c.consumable_code, consumable_name=c.consumable_name,
spec_model=c.spec_model, standard_usage=c.standard_usage,
unit=c.unit, is_required=c.is_required,
remark=c.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
def _clone_step_outputs(
db: Session, src_step_id: str, new_step_id: str,
tenant_no: str, user_id: Optional[str], now: datetime,
) -> None:
for o in db.query(BizProcessRouteStepOutput).filter(
and_(BizProcessRouteStepOutput.step_id == src_step_id, BizProcessRouteStepOutput.del_flag == 0)
).all():
db.add(BizProductionTaskStepOutput(
id=generate_uuid(), step_id=new_step_id, tenant_no=tenant_no,
output_type_id=o.output_type_id, output_name=o.output_name,
output_barcode=o.output_barcode,
remark=o.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
def _clone_step_data_collections(
db: Session, src_step_id: str, new_step_id: str,
tenant_no: str, user_id: Optional[str], now: datetime,
) -> None:
for d in db.query(BizProcessRouteStepDataCollection).filter(
and_(BizProcessRouteStepDataCollection.step_id == src_step_id, BizProcessRouteStepDataCollection.del_flag == 0)
).all():
db.add(BizProductionTaskStepDataCollection(
id=generate_uuid(), step_id=new_step_id, tenant_no=tenant_no,
collection_name=d.collection_name, unit=d.unit,
remark=d.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
def _clone_route_to_task(
db: Session, task_id: str, route_id: str,
tenant_no: str, user_id: Optional[str], create_dept_id: str,
) -> None:
route_steps = (
db.query(BizProcessRouteStep)
.filter(and_(BizProcessRouteStep.route_id == route_id, BizProcessRouteStep.del_flag == 0))
.order_by(BizProcessRouteStep.step_order.asc(), BizProcessRouteStep.create_time.asc())
.all()
)
now = datetime.now()
for rs in route_steps:
new_step = BizProductionTaskStep(
id=generate_uuid(), task_id=task_id,
step_order=rs.step_order,
process_code=rs.process_code, category_id=rs.category_id,
process_name=rs.process_name, description=rs.description,
equipment_name=rs.equipment_name, estimated_duration=rs.estimated_duration,
sop_ids=rs.sop_ids,
step_status="dzx",
tenant_no=tenant_no, order_num=0,
remark=None, create_by=user_id, create_dept_id=create_dept_id or "",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
)
db.add(new_step)
db.flush()
_clone_step_reagents(db, rs.id, new_step.id, tenant_no, user_id, now)
_clone_step_consumables(db, rs.id, new_step.id, tenant_no, user_id, now)
_clone_step_outputs(db, rs.id, new_step.id, tenant_no, user_id, now)
_clone_step_data_collections(db, rs.id, new_step.id, tenant_no, user_id, now)
def create_production_task(
db: Session, *, tenant_no: str, user_id: Optional[str] = None,
create_dept_id: str = "", data: Dict[str, Any],
) -> BizProductionTask:
task_no = _generate_task_no(db, tenant_no)
now = datetime.now()
task = BizProductionTask(
id=generate_uuid(),
task_no=task_no,
work_order_id=data.get("work_order_id", ""),
task_name=(data.get("task_name") or "").strip() or None,
description=(data.get("description") or "").strip() or None,
route_id=data.get("route_id") or None,
task_status="dpz",
remark=(data.get("remark") or "").strip() or None,
attachments=(data.get("attachments") or "").strip() or None,
tenant_no=tenant_no,
order_num=0,
create_by=user_id,
create_dept_id=create_dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
db.add(task)
db.flush()
route_id = data.get("route_id")
if route_id:
_clone_route_to_task(db, task.id, route_id, tenant_no, user_id, create_dept_id)
db.flush()
return task
def list_production_tasks(
db: Session, *, tenant_no: str,
page: int = 1, page_size: int = 10,
work_order_id: Optional[str] = None,
task_no: Optional[str] = None,
task_status: Optional[str] = None,
user_info=None,
) -> Tuple[List[Dict[str, Any]], int]:
def _filtered():
q = db.query(BizProductionTask).filter(
BizProductionTask.tenant_no == tenant_no,
BizProductionTask.del_flag == 0,
)
if work_order_id and work_order_id.strip():
q = q.filter(BizProductionTask.work_order_id == work_order_id.strip())
if task_no and task_no.strip():
q = q.filter(BizProductionTask.task_no.like(f"%{task_no.strip()}%"))
if task_status and task_status.strip():
q = q.filter(BizProductionTask.task_status == task_status.strip())
if user_info:
from app.core.data_scope import apply_data_scope
q = apply_data_scope(q, BizProductionTask, user_info, db, tenant_no)
return q
total = _filtered().order_by(None).with_entities(func.count(BizProductionTask.id)).scalar() or 0
page = max(1, page)
page_size = min(max(1, page_size), 100)
rows = (
_filtered()
.order_by(BizProductionTask.create_time.desc())
.offset((page - 1) * page_size)
.limit(page_size)
.all()
)
items = [_task_to_dict(x) for x in rows]
# 批量补充路线名称
route_ids = [x.route_id for x in rows if x.route_id]
if route_ids:
route_map = {
r.id: r.route_name
for r in db.query(BizProcessRoute.id, BizProcessRoute.route_name)
.filter(BizProcessRoute.id.in_(route_ids))
.all()
}
for item, row in zip(items, rows):
item["routeName"] = route_map.get(row.route_id, "")
return items, int(total)
def get_production_task_detail(db: Session, task_id: str, tenant_no: str) -> Optional[Dict[str, Any]]:
task = db.query(BizProductionTask).filter(
and_(
BizProductionTask.id == task_id,
BizProductionTask.tenant_no == tenant_no,
BizProductionTask.del_flag == 0,
)
).first()
if not task:
return None
result = _task_to_dict(task)
steps = (
db.query(BizProductionTaskStep)
.filter(and_(BizProductionTaskStep.task_id == task_id, BizProductionTaskStep.del_flag == 0))
.order_by(BizProductionTaskStep.step_order.asc(), BizProductionTaskStep.create_time.asc())
.all()
)
step_list = []
for s in steps:
sd = _step_to_dict(s)
reagents = db.query(BizProductionTaskStepReagent).filter(
and_(BizProductionTaskStepReagent.step_id == s.id, BizProductionTaskStepReagent.del_flag == 0)
).order_by(BizProductionTaskStepReagent.order_num.asc(), BizProductionTaskStepReagent.create_time.asc()).all()
consumables = db.query(BizProductionTaskStepConsumable).filter(
and_(BizProductionTaskStepConsumable.step_id == s.id, BizProductionTaskStepConsumable.del_flag == 0)
).order_by(BizProductionTaskStepConsumable.order_num.asc(), BizProductionTaskStepConsumable.create_time.asc()).all()
outputs = db.query(BizProductionTaskStepOutput).filter(
and_(BizProductionTaskStepOutput.step_id == s.id, BizProductionTaskStepOutput.del_flag == 0)
).order_by(BizProductionTaskStepOutput.order_num.asc(), BizProductionTaskStepOutput.create_time.asc()).all()
data_collections = db.query(BizProductionTaskStepDataCollection).filter(
and_(BizProductionTaskStepDataCollection.step_id == s.id, BizProductionTaskStepDataCollection.del_flag == 0)
).order_by(BizProductionTaskStepDataCollection.order_num.asc(), BizProductionTaskStepDataCollection.create_time.asc()).all()
sd["reagents"] = [_step_reagent_to_dict(r) for r in reagents]
sd["consumables"] = [_step_consumable_to_dict(c) for c in consumables]
sd["outputs"] = [_step_output_to_dict(o) for o in outputs]
sd["dataCollections"] = [_step_data_collection_to_dict(d) for d in data_collections]
step_list.append(sd)
inputs = db.query(BizProductionTaskInput).filter(
and_(BizProductionTaskInput.task_id == task_id, BizProductionTaskInput.del_flag == 0)
).order_by(BizProductionTaskInput.create_time.asc()).all()
outputs = db.query(BizProductionTaskOutput).filter(
and_(BizProductionTaskOutput.task_id == task_id, BizProductionTaskOutput.del_flag == 0)
).order_by(BizProductionTaskOutput.create_time.asc()).all()
result["steps"] = step_list
result["inputs"] = [_task_input_to_dict(i) for i in inputs]
result["outputs"] = [_task_output_to_dict(o) for o in outputs]
return result
def update_production_task(
db: Session, task_id: str, tenant_no: str,
user_id: Optional[str], data: Dict[str, Any],
) -> BizProductionTask:
task = db.query(BizProductionTask).filter(
and_(
BizProductionTask.id == task_id,
BizProductionTask.tenant_no == tenant_no,
BizProductionTask.del_flag == 0,
)
).first()
if not task:
raise ValueError("任务不存在")
for field in ["task_name", "description", "remark", "attachments"]:
val = data.get(field)
if val is not None:
setattr(task, field, (val or "").strip() or None)
task.update_by = user_id
task.update_time = datetime.now()
db.add(task)
db.commit()
db.refresh(task)
return task
def update_task_status(
db: Session, task_id: str, tenant_no: str,
user_id: Optional[str], status: str,
) -> BizProductionTask:
task = db.query(BizProductionTask).filter(
and_(
BizProductionTask.id == task_id,
BizProductionTask.tenant_no == tenant_no,
BizProductionTask.del_flag == 0,
)
).first()
if not task:
raise ValueError("任务不存在")
now = datetime.now()
task.task_status = status
if status == "jxz" and not task.started_time:
task.started_time = now
if status == "ywc" and not task.completed_time:
task.completed_time = now
task.update_by = user_id
task.update_time = now
db.add(task)
db.commit()
db.refresh(task)
return task
def add_task_step(
db: Session, task_id: str, tenant_no: str,
user_id: Optional[str], data: Dict[str, Any],
) -> Dict[str, Any]:
task = db.query(BizProductionTask).filter(
and_(
BizProductionTask.id == task_id,
BizProductionTask.tenant_no == tenant_no,
BizProductionTask.del_flag == 0,
)
).first()
if not task:
raise ValueError("任务不存在")
now = datetime.now()
step_order = data.get("step_order")
if step_order is None:
max_order = db.query(func.max(BizProductionTaskStep.step_order)).filter(
and_(BizProductionTaskStep.task_id == task_id, BizProductionTaskStep.del_flag == 0)
).scalar()
step_order = (max_order or 0) + 1
# 如果传入了 process_id,从工艺主表深拷贝基本信息和子表数据
process_id = data.get("process_id")
if process_id:
proc = db.query(BizProcess).filter(
and_(BizProcess.id == process_id, BizProcess.del_flag == 0)
).first()
if not proc:
raise ValueError("工艺不存在")
step = BizProductionTaskStep(
id=generate_uuid(), task_id=task_id,
step_order=step_order,
process_code=proc.process_code,
category_id=proc.category_id,
process_name=proc.process_name,
description=proc.description,
equipment_name=proc.equipment_name,
estimated_duration=proc.estimated_duration,
sop_ids=proc.sop_ids,
step_status="dzx",
tenant_no=tenant_no, order_num=0,
remark=None,
create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
)
db.add(step)
db.flush()
# 深拷贝工艺子表 → 任务步骤子表
for r in db.query(BizProcessReagent).filter(
and_(BizProcessReagent.process_id == process_id, BizProcessReagent.del_flag == 0)
).all():
db.add(BizProductionTaskStepReagent(
id=generate_uuid(), step_id=step.id, tenant_no=tenant_no,
reagent_code=r.reagent_code, reagent_name=r.reagent_name,
spec=r.spec, standard_usage=r.standard_usage,
unit=r.unit, is_key_material=r.is_key_material,
remark=r.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
for c in db.query(BizProcessConsumable).filter(
and_(BizProcessConsumable.process_id == process_id, BizProcessConsumable.del_flag == 0)
).all():
db.add(BizProductionTaskStepConsumable(
id=generate_uuid(), step_id=step.id, tenant_no=tenant_no,
consumable_code=c.consumable_code, consumable_name=c.consumable_name,
spec_model=c.spec_model, standard_usage=c.standard_usage,
unit=c.unit, is_required=c.is_required,
remark=c.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
for o in db.query(BizProcessOutput).filter(
and_(BizProcessOutput.process_id == process_id, BizProcessOutput.del_flag == 0)
).all():
db.add(BizProductionTaskStepOutput(
id=generate_uuid(), step_id=step.id, tenant_no=tenant_no,
output_type_id=o.output_type_id, output_name=o.output_name,
output_barcode=o.output_barcode,
remark=o.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
for d in db.query(BizProcessDataCollection).filter(
and_(BizProcessDataCollection.process_id == process_id, BizProcessDataCollection.del_flag == 0)
).all():
db.add(BizProductionTaskStepDataCollection(
id=generate_uuid(), step_id=step.id, tenant_no=tenant_no,
collection_name=d.collection_name, unit=d.unit,
remark=d.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
db.commit()
else:
# 无 process_id 时,使用前端传来的字段直接创建(兼容旧逻辑)
step = BizProductionTaskStep(
id=generate_uuid(), task_id=task_id,
step_order=step_order,
process_code=(data.get("process_code") or "").strip() or None,
category_id=(data.get("category_id") or "").strip() or None,
process_name=(data.get("process_name") or "").strip() or None,
description=(data.get("description") or "").strip() or None,
equipment_name=(data.get("equipment_name") or "").strip() or None,
estimated_duration=(data.get("estimated_duration") or "").strip() or None,
actual_duration=(data.get("actual_duration") or "").strip() or None,
sop_ids=(data.get("sop_ids") or "").strip() or None,
step_status=(data.get("step_status") or "dzx"),
tenant_no=tenant_no, order_num=0,
remark=(data.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(step)
db.commit()
# 返回带子表的完整步骤信息(与 get_production_task_detail 格式一致)
result = _step_to_dict(step)
reagents = db.query(BizProductionTaskStepReagent).filter(
and_(BizProductionTaskStepReagent.step_id == step.id, BizProductionTaskStepReagent.del_flag == 0)
).order_by(BizProductionTaskStepReagent.order_num.asc(), BizProductionTaskStepReagent.create_time.asc()).all()
consumables = db.query(BizProductionTaskStepConsumable).filter(
and_(BizProductionTaskStepConsumable.step_id == step.id, BizProductionTaskStepConsumable.del_flag == 0)
).order_by(BizProductionTaskStepConsumable.order_num.asc(), BizProductionTaskStepConsumable.create_time.asc()).all()
outputs = db.query(BizProductionTaskStepOutput).filter(
and_(BizProductionTaskStepOutput.step_id == step.id, BizProductionTaskStepOutput.del_flag == 0)
).order_by(BizProductionTaskStepOutput.order_num.asc(), BizProductionTaskStepOutput.create_time.asc()).all()
data_collections = db.query(BizProductionTaskStepDataCollection).filter(
and_(BizProductionTaskStepDataCollection.step_id == step.id, BizProductionTaskStepDataCollection.del_flag == 0)
).order_by(BizProductionTaskStepDataCollection.order_num.asc(), BizProductionTaskStepDataCollection.create_time.asc()).all()
result["reagents"] = [_step_reagent_to_dict(r) for r in reagents]
result["consumables"] = [_step_consumable_to_dict(c) for c in consumables]
result["outputs"] = [_step_output_to_dict(o) for o in outputs]
result["dataCollections"] = [_step_data_collection_to_dict(d) for d in data_collections]
return result
def update_task_step(
db: Session, step_id: str, tenant_no: str,
user_id: Optional[str], data: Dict[str, Any],
) -> BizProductionTaskStep:
step = db.query(BizProductionTaskStep).filter(
and_(
BizProductionTaskStep.id == step_id,
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionTaskStep.del_flag == 0,
)
).first()
if not step:
raise ValueError("步骤不存在")
for field in [
"step_order", "process_code", "category_id", "process_name",
"description", "equipment_name", "estimated_duration", "actual_duration",
"sop_ids", "step_status", "remark",
]:
val = data.get(field)
if val is not None:
if field == "step_order":
setattr(step, field, int(val))
else:
setattr(step, field, (val or "").strip() or None)
if data.get("started_time"):
from dateutil.parser import parse as _parse_dt
step.started_time = _parse_dt(data["started_time"])
if data.get("completed_time"):
from dateutil.parser import parse as _parse_dt
step.completed_time = _parse_dt(data["completed_time"])
step.update_by = user_id
step.update_time = datetime.now()
db.add(step)
db.commit()
db.refresh(step)
return step
def delete_task_step(db: Session, step_id: str, tenant_no: str, user_id: Optional[str]) -> None:
step = db.query(BizProductionTaskStep).filter(
and_(
BizProductionTaskStep.id == step_id,
BizProductionTaskStep.tenant_no == tenant_no,
BizProductionTaskStep.del_flag == 0,
)
).first()
if not step:
raise ValueError("步骤不存在")
now = datetime.now()
step.del_flag = 1
step.update_by = user_id
step.update_time = now
db.add(step)
_soft_delete_sub(db, BizProductionTaskStepReagent, "step_id", step_id, now)
_soft_delete_sub(db, BizProductionTaskStepConsumable, "step_id", step_id, now)
_soft_delete_sub(db, BizProductionTaskStepOutput, "step_id", step_id, now)
_soft_delete_sub(db, BizProductionTaskStepDataCollection, "step_id", step_id, now)
db.commit()
def replace_step_reagents(
db: Session, step_id: str, tenant_no: str,
user_id: Optional[str], items: List[Dict[str, Any]],
) -> None:
now = datetime.now()
_soft_delete_sub(db, BizProductionTaskStepReagent, "step_id", step_id, now)
for idx, it in enumerate(items):
db.add(BizProductionTaskStepReagent(
id=generate_uuid(), step_id=step_id, tenant_no=tenant_no,
reagent_code=(it.get("reagent_code") or "").strip() or None,
reagent_name=(it.get("reagent_name") or "").strip() or None,
spec=(it.get("spec") or "").strip() or None,
standard_usage=(it.get("standard_usage") or "").strip() or None,
actual_usage=(it.get("actual_usage") or "").strip() or None,
unit=(it.get("unit") or "").strip() or None,
is_key_material=it.get("is_key_material", "0"),
remark=(it.get("remark") or "").strip() or None,
order_num=idx, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
db.commit()
def replace_step_consumables(
db: Session, step_id: str, tenant_no: str,
user_id: Optional[str], items: List[Dict[str, Any]],
) -> None:
now = datetime.now()
_soft_delete_sub(db, BizProductionTaskStepConsumable, "step_id", step_id, now)
for idx, it in enumerate(items):
db.add(BizProductionTaskStepConsumable(
id=generate_uuid(), step_id=step_id, tenant_no=tenant_no,
consumable_code=(it.get("consumable_code") or "").strip() or None,
consumable_name=(it.get("consumable_name") or "").strip() or None,
spec_model=(it.get("spec_model") or "").strip() or None,
standard_usage=(it.get("standard_usage") or "").strip() or None,
actual_usage=(it.get("actual_usage") or "").strip() or None,
unit=(it.get("unit") or "").strip() or None,
is_required=it.get("is_required", "1"),
remark=(it.get("remark") or "").strip() or None,
order_num=idx, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
db.commit()
def replace_step_outputs(
db: Session, step_id: str, tenant_no: str,
user_id: Optional[str], items: List[Dict[str, Any]],
) -> None:
now = datetime.now()
_soft_delete_sub(db, BizProductionTaskStepOutput, "step_id", step_id, now)
for idx, it in enumerate(items):
db.add(BizProductionTaskStepOutput(
id=generate_uuid(), step_id=step_id, tenant_no=tenant_no,
output_type_id=(it.get("output_type_id") or "").strip() or None,
output_name=(it.get("output_name") or "").strip() or None,
output_barcode=(it.get("output_barcode") or "").strip() or None,
remark=(it.get("remark") or "").strip() or None,
order_num=idx, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
db.commit()
def replace_step_data_collections(
db: Session, step_id: str, tenant_no: str,
user_id: Optional[str], items: List[Dict[str, Any]],
) -> None:
now = datetime.now()
_soft_delete_sub(db, BizProductionTaskStepDataCollection, "step_id", step_id, now)
for idx, it in enumerate(items):
db.add(BizProductionTaskStepDataCollection(
id=generate_uuid(), step_id=step_id, tenant_no=tenant_no,
collection_name=(it.get("collection_name") or "").strip() or None,
unit=(it.get("unit") or "").strip() or None,
collection_value=(it.get("collection_value") or "").strip() or None,
remark=(it.get("remark") or "").strip() or None,
order_num=idx, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
db.commit()
def set_task_inputs(
db: Session, task_id: str, tenant_no: str,
user_id: Optional[str], items: List[Dict[str, Any]],
) -> None:
now = datetime.now()
_soft_delete_sub(db, BizProductionTaskInput, "task_id", task_id, now)
for it in items:
db.add(BizProductionTaskInput(
id=generate_uuid(), task_id=task_id, tenant_no=tenant_no,
input_type=(it.get("input_type") or "").strip() or None,
input_id=(it.get("input_id") or "").strip() or None,
input_code=(it.get("input_code") or "").strip() or None,
input_name=(it.get("input_name") or "").strip() or None,
remark=(it.get("remark") or "").strip() or None,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
db.commit()
def set_task_outputs(
db: Session, task_id: str, tenant_no: str,
user_id: Optional[str], items: List[Dict[str, Any]],
) -> None:
now = datetime.now()
_soft_delete_sub(db, BizProductionTaskOutput, "task_id", task_id, now)
for it in items:
quantity = it.get("quantity")
db.add(BizProductionTaskOutput(
id=generate_uuid(), task_id=task_id, tenant_no=tenant_no,
cell_product_id=(it.get("cell_product_id") or "").strip() or None,
storage_slot_id=(it.get("storage_slot_id") or "").strip() or None,
output_name=(it.get("output_name") or "").strip() or None,
output_barcode=(it.get("output_barcode") or "").strip() or None,
quantity=quantity if quantity is not None else None,
unit=(it.get("unit") or "").strip() or None,
remark=(it.get("remark") or "").strip() or None,
attachments=(it.get("attachments") or "").strip() or None,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
db.commit()
def reorder_task_steps(db: Session, task_id: str, step_ids: List[str]) -> None:
now = datetime.now()
for idx, sid in enumerate(step_ids):
step = db.query(BizProductionTaskStep).filter(
and_(BizProductionTaskStep.id == sid, BizProductionTaskStep.task_id == task_id,
BizProductionTaskStep.del_flag == 0)
).first()
if step:
step.step_order = idx + 1
step.update_time = now
db.add(step)
db.commit()
def copy_task_step(db: Session, step_id: str, *, tenant_no: str = "zkxw",
user_id: Optional[str] = None) -> Dict[str, Any]:
"""复制生产任务步骤,插入到原步骤之后,名称追加(复制)"""
src = db.query(BizProductionTaskStep).filter(
and_(BizProductionTaskStep.id == step_id, BizProductionTaskStep.del_flag == 0)
).first()
if not src:
raise ValueError("步骤不存在")
now = datetime.now()
new_order = src.step_order + 1
# 后续步骤 step_order +1
later_steps = db.query(BizProductionTaskStep).filter(
and_(
BizProductionTaskStep.task_id == src.task_id,
BizProductionTaskStep.del_flag == 0,
BizProductionTaskStep.step_order > src.step_order,
)
).order_by(BizProductionTaskStep.step_order.asc()).all()
for ls in later_steps:
ls.step_order += 1
ls.update_time = now
db.add(ls)
# 创建新步骤
new_name = (src.process_name or "").strip()
if not new_name.endswith("(复制)"):
new_name += "(复制)"
new_step = BizProductionTaskStep(
id=generate_uuid(), task_id=src.task_id,
step_order=new_order,
process_code=src.process_code, category_id=src.category_id,
process_name=new_name, description=src.description,
equipment_name=src.equipment_name, estimated_duration=src.estimated_duration,
actual_duration=src.actual_duration, sop_ids=src.sop_ids,
step_status="dzx",
tenant_no=tenant_no, order_num=0,
remark=src.remark, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
)
db.add(new_step)
db.flush()
# 复制子表:试剂(从生产任务步骤试剂表复制)
for r in db.query(BizProductionTaskStepReagent).filter(
and_(BizProductionTaskStepReagent.step_id == step_id, BizProductionTaskStepReagent.del_flag == 0)
).all():
db.add(BizProductionTaskStepReagent(
id=generate_uuid(), step_id=new_step.id, tenant_no=tenant_no,
reagent_code=r.reagent_code, reagent_name=r.reagent_name,
spec=r.spec, standard_usage=r.standard_usage,
actual_usage=r.actual_usage,
unit=r.unit, is_key_material=r.is_key_material,
remark=r.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
# 复制子表:耗材
for c in db.query(BizProductionTaskStepConsumable).filter(
and_(BizProductionTaskStepConsumable.step_id == step_id, BizProductionTaskStepConsumable.del_flag == 0)
).all():
db.add(BizProductionTaskStepConsumable(
id=generate_uuid(), step_id=new_step.id, tenant_no=tenant_no,
consumable_code=c.consumable_code, consumable_name=c.consumable_name,
spec_model=c.spec_model, standard_usage=c.standard_usage,
actual_usage=c.actual_usage,
unit=c.unit, is_required=c.is_required,
remark=c.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
# 复制子表:产出
for o in db.query(BizProductionTaskStepOutput).filter(
and_(BizProductionTaskStepOutput.step_id == step_id, BizProductionTaskStepOutput.del_flag == 0)
).all():
db.add(BizProductionTaskStepOutput(
id=generate_uuid(), step_id=new_step.id, tenant_no=tenant_no,
output_type_id=o.output_type_id, output_name=o.output_name,
output_barcode=o.output_barcode,
remark=o.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
# 复制子表:数据采集
for d in db.query(BizProductionTaskStepDataCollection).filter(
and_(BizProductionTaskStepDataCollection.step_id == step_id, BizProductionTaskStepDataCollection.del_flag == 0)
).all():
db.add(BizProductionTaskStepDataCollection(
id=generate_uuid(), step_id=new_step.id, tenant_no=tenant_no,
collection_name=d.collection_name, unit=d.unit,
collection_value=d.collection_value,
remark=d.remark,
order_num=0, create_by=user_id, create_dept_id="",
create_time=now, update_by=user_id, update_time=now, del_flag=0,
))
db.commit()
return _step_to_dict(new_step)
def total_pages(total: int, page_size: int) -> int:
if total <= 0:
return 0
return int(math.ceil(total / float(page_size)))