equipment.py 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. """设备 CRUD(biz_equipment)"""
  2. from __future__ import annotations
  3. import math
  4. from datetime import date, datetime
  5. from decimal import Decimal
  6. from typing import Any, Dict, List, Optional, Tuple
  7. from sqlalchemy import and_, func, or_
  8. from sqlalchemy.orm import Session
  9. from app.core.utils import generate_uuid
  10. from app.models.models import BizEquipment
  11. _UNSET = object()
  12. def _dec(v: Any) -> Optional[float]:
  13. if v is None:
  14. return None
  15. return float(v)
  16. def _norm_status(v: Any) -> str:
  17. if v is None:
  18. return "1"
  19. if isinstance(v, bool):
  20. return "1" if v else "0"
  21. if isinstance(v, (int, float)):
  22. if int(v) == 0:
  23. return "0"
  24. if int(v) == 1:
  25. return "1"
  26. s = str(v).strip()
  27. if s in ("0", "1"):
  28. return s
  29. return "1"
  30. def _parse_date(v: Any) -> Optional[date]:
  31. if v is None or v == "":
  32. return None
  33. if isinstance(v, date) and not isinstance(v, datetime):
  34. return v
  35. if isinstance(v, datetime):
  36. return v.date()
  37. s = str(v).strip()
  38. if not s:
  39. return None
  40. for fmt in ("%Y-%m-%d", "%Y/%m/%d"):
  41. try:
  42. return datetime.strptime(s[:10], fmt).date()
  43. except ValueError:
  44. continue
  45. raise ValueError("购入日期格式无效,请使用 YYYY-MM-DD")
  46. def _row_to_dict(r: BizEquipment) -> Dict[str, Any]:
  47. return {
  48. "id": r.id,
  49. "equipmentCode": r.equipment_code,
  50. "equipmentName": r.equipment_name,
  51. "equipmentModel": r.equipment_model or "",
  52. "manufacturer": r.manufacturer or "",
  53. "purchaseDate": r.purchase_date.strftime("%Y-%m-%d") if r.purchase_date else "",
  54. "purchaseAmount": _dec(r.purchase_amount),
  55. "location": r.location or "",
  56. "serialNo": r.serial_no or "",
  57. "status": _norm_status(r.status),
  58. "tenantNo": r.tenant_no,
  59. "orderNum": r.order_num if r.order_num is not None else 0,
  60. "remark": r.remark or "",
  61. "createTime": r.create_time.strftime("%Y-%m-%d %H:%M:%S") if r.create_time else "",
  62. "updateTime": r.update_time.strftime("%Y-%m-%d %H:%M:%S") if r.update_time else "",
  63. }
  64. def list_equipments(
  65. db: Session,
  66. *,
  67. page: int = 1,
  68. page_size: int = 10,
  69. keyword: Optional[str] = None,
  70. status: Optional[str] = None,
  71. tenant_no: Optional[str] = None,
  72. user_info=None,
  73. ) -> Tuple[List[Dict[str, Any]], int]:
  74. def _filtered():
  75. q0 = db.query(BizEquipment).filter(BizEquipment.del_flag == 0)
  76. if tenant_no:
  77. q0 = q0.filter(BizEquipment.tenant_no == tenant_no)
  78. kw = (keyword or "").strip()
  79. if kw:
  80. like = f"%{kw}%"
  81. q0 = q0.filter(
  82. or_(
  83. BizEquipment.equipment_code.like(like),
  84. BizEquipment.equipment_name.like(like),
  85. BizEquipment.equipment_model.like(like),
  86. BizEquipment.manufacturer.like(like),
  87. )
  88. )
  89. st = (status or "").strip()
  90. if st in ("0", "1"):
  91. q0 = q0.filter(BizEquipment.status == st)
  92. if user_info:
  93. from app.core.data_scope import apply_data_scope
  94. q0 = apply_data_scope(q0, BizEquipment, user_info, db, tenant_no)
  95. return q0
  96. total = _filtered().order_by(None).with_entities(func.count(BizEquipment.id)).scalar() or 0
  97. page = max(1, page)
  98. page_size = min(max(1, page_size), 100)
  99. rows = (
  100. _filtered()
  101. .order_by(BizEquipment.create_time.desc())
  102. .offset((page - 1) * page_size)
  103. .limit(page_size)
  104. .all()
  105. )
  106. return [_row_to_dict(x) for x in rows], int(total)
  107. def get_equipment(db: Session, equipment_id: str) -> Optional[BizEquipment]:
  108. return (
  109. db.query(BizEquipment)
  110. .filter(and_(BizEquipment.id == equipment_id, BizEquipment.del_flag == 0))
  111. .first()
  112. )
  113. def equipment_is_referenced(db: Session, equipment_id: str) -> bool:
  114. from app.models.models import BizProductionTask
  115. eid = (equipment_id or "").strip()
  116. if not eid:
  117. return False
  118. task_hit = (
  119. db.query(BizProductionTask.id)
  120. .filter(
  121. BizProductionTask.del_flag == 0,
  122. func.find_in_set(eid, BizProductionTask.equipment_ids) > 0,
  123. )
  124. .first()
  125. )
  126. return task_hit is not None
  127. def equipment_code_exists(
  128. db: Session, code: str, tenant_no: str, exclude_id: Optional[str] = None
  129. ) -> bool:
  130. q = db.query(BizEquipment).filter(
  131. and_(
  132. BizEquipment.equipment_code == code.strip(),
  133. BizEquipment.tenant_no == tenant_no,
  134. BizEquipment.del_flag == 0,
  135. )
  136. )
  137. if exclude_id:
  138. q = q.filter(BizEquipment.id != exclude_id)
  139. return q.first() is not None
  140. def create_equipment(
  141. db: Session,
  142. *,
  143. equipment_code: str,
  144. equipment_name: str,
  145. equipment_model: Optional[str] = None,
  146. manufacturer: Optional[str] = None,
  147. purchase_date: Optional[Any] = None,
  148. purchase_amount: Optional[float] = None,
  149. location: Optional[str] = None,
  150. serial_no: Optional[str] = None,
  151. status: Optional[str] = None,
  152. tenant_no: str = "zkxw",
  153. order_num: Optional[int] = None,
  154. remark: Optional[str] = None,
  155. user_id: Optional[str] = None,
  156. ) -> BizEquipment:
  157. ec = (equipment_code or "").strip()
  158. if not ec:
  159. raise ValueError("设备编码不能为空")
  160. if equipment_code_exists(db, ec, tenant_no):
  161. raise ValueError("设备编码已存在")
  162. obj = BizEquipment(
  163. id=generate_uuid(),
  164. equipment_code=ec,
  165. equipment_name=(equipment_name or "").strip() or ec,
  166. equipment_model=(equipment_model or "").strip() or None,
  167. manufacturer=(manufacturer or "").strip() or None,
  168. purchase_date=_parse_date(purchase_date),
  169. purchase_amount=Decimal(str(purchase_amount)) if purchase_amount is not None else None,
  170. location=(location or "").strip() or None,
  171. serial_no=(serial_no or "").strip() or None,
  172. status=_norm_status(status),
  173. tenant_no=tenant_no,
  174. order_num=order_num if order_num is not None else 0,
  175. remark=(remark or "").strip() or None,
  176. create_by=user_id,
  177. create_dept_id="",
  178. create_time=datetime.now(),
  179. update_by=user_id,
  180. update_time=datetime.now(),
  181. del_flag=0,
  182. )
  183. db.add(obj)
  184. db.commit()
  185. db.refresh(obj)
  186. return obj
  187. def update_equipment(
  188. db: Session,
  189. equipment_id: str,
  190. *,
  191. equipment_code: Any = _UNSET,
  192. equipment_name: Any = _UNSET,
  193. equipment_model: Any = _UNSET,
  194. manufacturer: Any = _UNSET,
  195. purchase_date: Any = _UNSET,
  196. purchase_amount: Any = _UNSET,
  197. location: Any = _UNSET,
  198. serial_no: Any = _UNSET,
  199. status: Any = _UNSET,
  200. order_num: Any = _UNSET,
  201. remark: Any = _UNSET,
  202. user_id: Optional[str] = None,
  203. ) -> BizEquipment:
  204. obj = get_equipment(db, equipment_id)
  205. if not obj:
  206. raise ValueError("设备不存在")
  207. if equipment_code is not _UNSET:
  208. new_code = equipment_code.strip()
  209. if not new_code:
  210. raise ValueError("设备编码不能为空")
  211. if new_code != obj.equipment_code and equipment_code_exists(
  212. db, new_code, obj.tenant_no, exclude_id=equipment_id
  213. ):
  214. raise ValueError("设备编码已存在")
  215. obj.equipment_code = new_code
  216. if equipment_name is not _UNSET:
  217. obj.equipment_name = (equipment_name or "").strip() or obj.equipment_name
  218. if equipment_model is not _UNSET:
  219. obj.equipment_model = (equipment_model or "").strip() or None
  220. if manufacturer is not _UNSET:
  221. obj.manufacturer = (manufacturer or "").strip() or None
  222. if purchase_date is not _UNSET:
  223. obj.purchase_date = _parse_date(purchase_date)
  224. if purchase_amount is not _UNSET:
  225. obj.purchase_amount = Decimal(str(purchase_amount)) if purchase_amount not in (None, "") else None
  226. if location is not _UNSET:
  227. obj.location = (location or "").strip() or None
  228. if serial_no is not _UNSET:
  229. obj.serial_no = (serial_no or "").strip() or None
  230. if status is not _UNSET:
  231. obj.status = _norm_status(status)
  232. if order_num is not _UNSET:
  233. obj.order_num = order_num
  234. if remark is not _UNSET:
  235. obj.remark = (remark or "").strip() or None
  236. obj.update_by = user_id
  237. obj.update_time = datetime.now()
  238. db.add(obj)
  239. db.commit()
  240. db.refresh(obj)
  241. return obj
  242. def soft_delete_equipment(db: Session, equipment_id: str) -> None:
  243. obj = get_equipment(db, equipment_id)
  244. if not obj:
  245. raise ValueError("设备不存在")
  246. if equipment_is_referenced(db, equipment_id):
  247. raise ValueError("该设备已被工艺或工艺路线引用,无法删除")
  248. obj.del_flag = 1
  249. obj.update_time = datetime.now()
  250. db.add(obj)
  251. db.commit()
  252. def total_pages(total: int, page_size: int) -> int:
  253. if total <= 0:
  254. return 0
  255. return int(math.ceil(total / float(page_size)))