seed_production_materials.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. #!/usr/bin/env python3
  2. """批量初始化生产物料示例数据(物料主数据 + 入库批次),已存在编码则跳过。"""
  3. from __future__ import annotations
  4. import sys
  5. from datetime import date, timedelta
  6. from decimal import Decimal
  7. from pathlib import Path
  8. ROOT = Path(__file__).resolve().parents[1]
  9. if str(ROOT) not in sys.path:
  10. sys.path.insert(0, str(ROOT))
  11. from app.core.database import SessionLocal
  12. from app.crud import production_material as mat_svc
  13. from app.crud import production_material_stock_batch as batch_svc
  14. # (material_type, material_code, material_name, spec_model, unit, warning_qty, remark)
  15. SAMPLE_MATERIALS = [
  16. (
  17. "reagent",
  18. "R001",
  19. "75% 乙醇溶液",
  20. "500mL/瓶",
  21. "瓶",
  22. 10.0,
  23. "实验室常用消毒剂",
  24. ),
  25. (
  26. "reagent",
  27. "R002",
  28. "PBS 缓冲液",
  29. "1×, pH 7.4",
  30. "瓶",
  31. 5.0,
  32. "细胞培养用磷酸盐缓冲液",
  33. ),
  34. (
  35. "reagent",
  36. "R003",
  37. "DMEM 高糖培养基",
  38. "500mL/瓶, 含双抗",
  39. "瓶",
  40. 8.0,
  41. "细胞培养基础培养基",
  42. ),
  43. (
  44. "consumable",
  45. "C001",
  46. "15mL 离心管",
  47. "50支/袋",
  48. "袋",
  49. 20.0,
  50. "实验室常规耗材",
  51. ),
  52. (
  53. "consumable",
  54. "C002",
  55. "T25 细胞培养瓶",
  56. "1个/包",
  57. "包",
  58. 15.0,
  59. "贴壁细胞培养用",
  60. ),
  61. ]
  62. # 入库批次示例数据(与 SAMPLE_MATERIALS 一一对应)
  63. TODAY = date.today()
  64. BATCH_ITEMS = [
  65. # R001: 乙醇,入库 20 瓶
  66. dict(
  67. material_id=None, # 创建后填充
  68. stockInNo="RK2025061501",
  69. stockInDate=TODAY.isoformat(),
  70. expireDate=(TODAY + timedelta(days=730)).isoformat(), # 2年后过期
  71. catalogNo="SP-ETH-500",
  72. batchNo="B20250601",
  73. manufacturer="国药集团化学试剂有限公司",
  74. storageLocation="试剂柜-A-01",
  75. unitPrice=Decimal("15.00"),
  76. amount=Decimal("300.00"),
  77. qty=Decimal("20"),
  78. ),
  79. # R002: PBS,入库 10 瓶
  80. dict(
  81. material_id=None,
  82. stockInNo="RK2025061502",
  83. stockInDate=TODAY.isoformat(),
  84. expireDate=(TODAY + timedelta(days=365)).isoformat(),
  85. catalogNo="SP-PBS-500",
  86. batchNo="B20250602",
  87. manufacturer="Gibco",
  88. storageLocation="试剂柜-B-02",
  89. unitPrice=Decimal("85.00"),
  90. amount=Decimal("850.00"),
  91. qty=Decimal("10"),
  92. ),
  93. # R003: DMEM,入库 8 瓶
  94. dict(
  95. material_id=None,
  96. stockInNo="RK2025061503",
  97. stockInDate=TODAY.isoformat(),
  98. expireDate=(TODAY + timedelta(days=180)).isoformat(),
  99. catalogNo="SP-DMEM-500",
  100. batchNo="B20250603",
  101. manufacturer="Gibco",
  102. storageLocation="试剂柜-B-03",
  103. unitPrice=Decimal("120.00"),
  104. amount=Decimal("960.00"),
  105. qty=Decimal("8"),
  106. ),
  107. # C001: 离心管,入库 50 袋
  108. dict(
  109. material_id=None,
  110. stockInNo="RK2025061504",
  111. stockInDate=TODAY.isoformat(),
  112. expireDate=(TODAY + timedelta(days=1825)).isoformat(), # 5年后过期
  113. catalogNo="SC-CF15-50",
  114. batchNo="B20250604",
  115. manufacturer="Axygen",
  116. storageLocation="耗材柜-C-01",
  117. unitPrice=Decimal("45.00"),
  118. amount=Decimal("2250.00"),
  119. qty=Decimal("50"),
  120. ),
  121. # C002: 培养瓶,入库 30 包
  122. dict(
  123. material_id=None,
  124. stockInNo="RK2025061505",
  125. stockInDate=TODAY.isoformat(),
  126. expireDate=(TODAY + timedelta(days=1095)).isoformat(), # 3年后过期
  127. catalogNo="SC-CF25-1",
  128. batchNo="B20250605",
  129. manufacturer="Corning",
  130. storageLocation="耗材柜-D-02",
  131. unitPrice=Decimal("28.00"),
  132. amount=Decimal("840.00"),
  133. qty=Decimal("30"),
  134. ),
  135. ]
  136. def seed(
  137. tenant_no: str = "zkxw",
  138. user_id: str = "d3000000000000000000000000000001",
  139. ) -> None:
  140. db = SessionLocal()
  141. mat_created = 0
  142. mat_skipped = 0
  143. batch_created = 0
  144. batch_errors = 0
  145. try:
  146. for i, (mt, code, name, spec, unit, warn_qty, remark) in enumerate(SAMPLE_MATERIALS):
  147. exists = mat_svc.code_exists(db, code, mt, tenant_no)
  148. if exists:
  149. # 找到已有物料的 ID
  150. rows, _ = mat_svc.list_materials(
  151. db, page=1, page_size=1, keyword=code, material_type=mt, tenant_no=tenant_no
  152. )
  153. mat_id = rows[0]["id"] if rows else None
  154. print(f" 跳过物料:{code} - {name}(已存在,ID: {mat_id})")
  155. mat_skipped += 1
  156. if mat_id:
  157. BATCH_ITEMS[i]["material_id"] = mat_id
  158. else:
  159. try:
  160. obj = mat_svc.create_material(
  161. db,
  162. material_type=mt,
  163. material_code=code,
  164. material_name=name,
  165. spec_model=spec,
  166. unit=unit,
  167. warning_qty=Decimal(str(warn_qty)),
  168. status="1",
  169. remark=remark,
  170. tenant_no=tenant_no,
  171. user_id=user_id,
  172. )
  173. BATCH_ITEMS[i]["material_id"] = obj.id
  174. print(f" 新增物料:{code} - {name}(ID: {obj.id})")
  175. mat_created += 1
  176. except Exception as e:
  177. print(f" 错误(物料):{code} - {name},原因:{e}")
  178. continue
  179. # 入库批次
  180. print("\n--- 开始入库批次 ---")
  181. for item in BATCH_ITEMS:
  182. mat_id = item.get("material_id")
  183. if not mat_id:
  184. print(f" 跳过批次:物料ID不存在")
  185. continue
  186. stock_in_no = item["stockInNo"]
  187. try:
  188. batch_svc.batch_stock_in(
  189. db,
  190. items=[item],
  191. tenant_no=tenant_no,
  192. user_id=user_id,
  193. )
  194. print(f" 新增批次:{stock_in_no}(物料ID: {mat_id},数量: {item['qty']})")
  195. batch_created += 1
  196. except Exception as e:
  197. print(f" 错误(批次):{stock_in_no},原因:{e}")
  198. batch_errors += 1
  199. print(
  200. f"\n完成:物料新增 {mat_created} 条、跳过 {mat_skipped} 条;"
  201. f"批次新增 {batch_created} 条、错误 {batch_errors} 条。"
  202. )
  203. finally:
  204. db.close()
  205. if __name__ == "__main__":
  206. seed()