Files
zkxw/backend/app/crud/production_material_formula.py
T
2026-06-13 19:47:22 +08:00

170 lines
5.7 KiB
Python

"""配置试剂配方 BOM CRUD"""
from __future__ import annotations
from datetime import datetime
from decimal import Decimal
from typing import Any, Dict, List, Optional
from sqlalchemy import and_
from sqlalchemy.orm import Session
from app.core.utils import generate_uuid
from app.crud.production_material import _dec, _fmt_dt
from app.models.production_material import BizProductionMaterial, BizProductionMaterialFormula
COMPONENT_TYPES = ("reagent", "consumable")
def _line_to_dict(r: BizProductionMaterialFormula) -> Dict[str, Any]:
return {
"id": r.id,
"configuredMaterialId": r.configured_material_id,
"componentMaterialId": r.component_material_id,
"componentMaterialType": r.component_material_type or "",
"componentMaterialCode": r.component_material_code or "",
"componentMaterialName": r.component_material_name or "",
"standardQty": _dec(r.standard_qty),
"usageUnit": r.usage_unit or "",
"orderNum": r.order_num if r.order_num is not None else 0,
"remark": r.remark or "",
}
def _get_configured_material(
db: Session, material_id: str, tenant_no: str
) -> BizProductionMaterial:
row = (
db.query(BizProductionMaterial)
.filter(
BizProductionMaterial.id == material_id,
BizProductionMaterial.tenant_no == tenant_no,
BizProductionMaterial.del_flag == 0,
)
.first()
)
if not row:
raise ValueError("配置试剂不存在")
if (row.material_type or "").strip() != "configured_reagent":
raise ValueError("仅配置试剂目录可维护配方")
return row
def get_formula(
db: Session, configured_material_id: str, tenant_no: str
) -> List[Dict[str, Any]]:
_get_configured_material(db, configured_material_id, tenant_no)
rows = (
db.query(BizProductionMaterialFormula)
.filter(
BizProductionMaterialFormula.configured_material_id == configured_material_id,
BizProductionMaterialFormula.tenant_no == tenant_no,
BizProductionMaterialFormula.del_flag == 0,
)
.order_by(
BizProductionMaterialFormula.order_num.asc(),
BizProductionMaterialFormula.create_time.asc(),
)
.all()
)
return [_line_to_dict(r) for r in rows]
def replace_formula(
db: Session,
configured_material_id: str,
*,
lines: List[Dict[str, Any]],
tenant_no: str,
user_id: Optional[str] = None,
dept_id: str = "",
) -> List[Dict[str, Any]]:
configured = _get_configured_material(db, configured_material_id, tenant_no)
now = datetime.now()
if not lines:
raise ValueError("请至少添加一条配方明细")
seen_components: set = set()
new_rows: List[BizProductionMaterialFormula] = []
for idx, it in enumerate(lines):
comp_id = (
it.get("componentMaterialId") or it.get("component_material_id") or ""
).strip()
if not comp_id:
raise ValueError("请选择子物料")
if comp_id in seen_components:
raise ValueError("配方中存在重复的子物料")
seen_components.add(comp_id)
comp_type = (
it.get("componentMaterialType") or it.get("component_material_type") or ""
).strip().lower()
if comp_type not in COMPONENT_TYPES:
raise ValueError("子物料类型无效,应为试剂或耗材")
comp = (
db.query(BizProductionMaterial)
.filter(
BizProductionMaterial.id == comp_id,
BizProductionMaterial.tenant_no == tenant_no,
BizProductionMaterial.del_flag == 0,
)
.first()
)
if not comp:
raise ValueError("子物料不存在")
if (comp.material_type or "").strip() != comp_type:
raise ValueError(f"子物料「{comp.material_name}」类型与所选不一致")
qty_raw = it.get("standardQty") if it.get("standardQty") is not None else it.get("standard_qty")
if qty_raw is None or Decimal(str(qty_raw)) <= 0:
raise ValueError("标准用量必须大于0")
standard_qty = Decimal(str(qty_raw))
usage_unit = (
it.get("usageUnit") or it.get("usage_unit") or comp.unit or ""
).strip()[:32]
new_rows.append(
BizProductionMaterialFormula(
id=generate_uuid(),
configured_material_id=configured_material_id,
component_material_id=comp_id,
component_material_type=comp_type,
component_material_code=comp.material_code,
component_material_name=comp.material_name,
standard_qty=standard_qty,
usage_unit=usage_unit or None,
tenant_no=tenant_no,
order_num=idx,
remark=(it.get("remark") or "").strip()[:512] or None,
create_by=user_id,
create_dept_id=dept_id or configured.create_dept_id or "",
create_time=now,
update_by=user_id,
update_time=now,
del_flag=0,
)
)
existing = (
db.query(BizProductionMaterialFormula)
.filter(
BizProductionMaterialFormula.configured_material_id == configured_material_id,
BizProductionMaterialFormula.tenant_no == tenant_no,
BizProductionMaterialFormula.del_flag == 0,
)
.all()
)
for row in existing:
row.del_flag = 1
row.update_by = user_id
row.update_time = now
db.add(row)
for row in new_rows:
db.add(row)
db.commit()
return get_formula(db, configured_material_id, tenant_no)