This commit is contained in:
zlwu committed 2026-06-14 17:20:52 +08:00
1 parent a79aee2614
commit 427bcb1551
274 files changed
+59416 -38342

No files matched your search

@@ -0,0 +1,72 @@
#!/usr/bin/env python3
"""执行质检物料盘点登记相关 DB 迁移(requisition_status + usage_id/batch_id)。
用法(在 backend 目录):
source venv/bin/activate
python scripts/migrate_qc_material_stock_check_requisition.py
"""
from __future__ import annotations
from sqlalchemy import text
from app.core.database import engine
MIGRATIONS = [
(
"biz_qc_material_usage.requisition_status",
"""ALTER TABLE `biz_qc_material_usage`
ADD COLUMN `requisition_status` varchar(16) NOT NULL DEFAULT 'open'
COMMENT '领用状态:open-待登记/有剩余 closed-已使用完'
AFTER `purpose`""",
"""UPDATE `biz_qc_material_usage`
SET `requisition_status` = 'open'
WHERE `source_type` = 'manual_out'
AND `change_type` = 'use'
AND (`requisition_status` IS NULL OR `requisition_status` = '')""",
),
(
"biz_qc_material_stock_check_unused_line.usage_id",
"""ALTER TABLE `biz_qc_material_stock_check_unused_line`
ADD COLUMN `usage_id` varchar(64) DEFAULT NULL COMMENT '关联领用记录ID(biz_qc_material_usage.id)' AFTER `check_id`""",
None,
),
(
"biz_qc_material_stock_check_unused_line.batch_id",
"""ALTER TABLE `biz_qc_material_stock_check_unused_line`
ADD COLUMN `batch_id` varchar(64) DEFAULT NULL COMMENT '关联入库批次ID(手工登记批次检索)' AFTER `usage_id`""",
None,
),
(
"idx_qc_check_unused_line_usage",
"""ALTER TABLE `biz_qc_material_stock_check_unused_line`
ADD KEY `idx_qc_check_unused_line_usage` (`usage_id`, `del_flag`)""",
None,
),
]
def _run(conn, sql: str, label: str) -> None:
try:
conn.execute(text(sql))
print(f"OK {label}")
except Exception as e:
msg = str(e)
if "Duplicate column name" in msg or "1060" in msg:
print(f"SKIP {label} (column exists)")
elif "Duplicate key name" in msg or "1061" in msg:
print(f"SKIP {label} (index exists)")
else:
raise
def main() -> None:
with engine.begin() as conn:
for label, ddl, follow_up in MIGRATIONS:
_run(conn, ddl, label)
if follow_up:
_run(conn, follow_up, f"{label} data backfill")
print("Migration completed.")
if __name__ == "__main__":
main()
@@ -0,0 +1,85 @@
#!/usr/bin/env python3
"""批量初始化细胞产品规格(biz_cell_product_spec),已存在编码则跳过。"""
from __future__ import annotations
import sys
from datetime import datetime
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from app.core.database import SessionLocal
from app.crud import pilot_service as ps_svc
from app.models.models import BizCellProductSpec
# 旧版仅细胞数规格,执行时软删除
LEGACY_SPEC_CODES = [
"CPS-100W",
"CPS-200W",
"CPS-500W",
"CPS-1000W",
"CPS-2000W",
"CPS-5000W",
]
# (spec_code, spec_name, order_num, remark) — 规格名称:细胞数/体积
CELL_PRODUCT_SPECS = [
("CPS-1.1E6-5ML", "1.1×10⁶/5ml", 1, "低剂量规格"),
("CPS-2.2E6-10ML", "2.2×10⁶/10ml", 2, "低剂量规格"),
("CPS-5.5E6-20ML", "5.5×10⁶/20ml", 3, "标准规格"),
("CPS-1.0E7-50ML", "1.0×10⁷/50ml", 4, "标准规格"),
("CPS-2.5E7-50ML", "2.5×10⁷/50ml", 5, "高剂量规格"),
("CPS-5.5E7-100ML", "5.5×10⁷/100ml", 6, "高剂量规格"),
]
def cleanup_legacy_specs(db, tenant_no: str = "zkxw") -> int:
rows = (
db.query(BizCellProductSpec)
.filter(
BizCellProductSpec.spec_code.in_(LEGACY_SPEC_CODES),
BizCellProductSpec.tenant_no == tenant_no,
BizCellProductSpec.del_flag == 0,
)
.all()
)
for row in rows:
row.del_flag = 1
row.update_time = datetime.now()
if rows:
db.commit()
return len(rows)
def seed(tenant_no: str = "zkxw", user_id: str = "d3000000000000000000000000000001") -> None:
db = SessionLocal()
created = 0
skipped = 0
try:
removed = cleanup_legacy_specs(db, tenant_no)
if removed:
print(f"已软删除旧版规格 {removed} 条。")
for code, name, order_num, remark in CELL_PRODUCT_SPECS:
if ps_svc.cell_product_spec_code_exists(db, code, tenant_no):
skipped += 1
continue
ps_svc.create_cell_product_spec(
db,
spec_code=code,
spec_name=name,
tenant_no=tenant_no,
order_num=order_num,
remark=remark,
user_id=user_id,
)
created += 1
print(f"细胞产品规格初始化完成:新增 {created} 条,跳过 {skipped} 条(编码已存在)。")
finally:
db.close()
if __name__ == "__main__":
seed()
+202
View File
@@ -0,0 +1,202 @@
#!/usr/bin/env python3
"""质检物料库测试数据(biz_qc_material + 入库批次 + 变动记录),已存在编码/入库编号则跳过。"""
from __future__ import annotations
import sys
from decimal import Decimal
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from app.core.database import SessionLocal
from app.crud import qc_material as material_svc
from app.crud import qc_material_stock_batch as batch_svc
# (material_code, material_name, spec_model, unit, warning_qty, order_num, remark,
# stock_in_no, stock_in_date, expire_date, catalog_no, batch_no, manufacturer,
# unit_price, qty)
QC_MATERIAL_SEEDS = [
(
"QC-MAT-001",
"DMEM培养基",
"500ml/瓶",
"瓶",
Decimal("5"),
1,
"细胞培养基础培养基",
"RK-20260601-001",
"2026-06-01",
"2027-06-01",
"CAT-DMEM-500",
"BN20260501",
"Gibco",
Decimal("128.00"),
Decimal("20"),
),
(
"QC-MAT-002",
"胎牛血清",
"500ml/瓶",
"瓶",
Decimal("3"),
2,
"细胞培养添加物",
"RK-20260601-002",
"2026-06-01",
"2027-03-01",
"CAT-FBS-500",
"BN20260415",
"Gibco",
Decimal("2680.00"),
Decimal("6"),
),
(
"QC-MAT-003",
"青霉素-链霉素双抗",
"100ml/瓶",
"瓶",
Decimal("5"),
3,
"细胞培养抗生素",
"RK-20260601-003",
"2026-06-02",
"2026-12-02",
"CAT-PS-100",
"BN20260520",
"HyClone",
Decimal("186.00"),
Decimal("10"),
),
(
"QC-MAT-004",
"胰酶消化液",
"100ml/瓶",
"瓶",
Decimal("4"),
4,
"贴壁细胞消化",
"RK-20260601-004",
"2026-06-02",
"2026-11-02",
"CAT-TRY-100",
"BN20260518",
"Sigma",
Decimal("95.00"),
Decimal("8"),
),
(
"QC-MAT-005",
"台盼蓝染色液",
"50ml/瓶",
"瓶",
Decimal("2"),
5,
"细胞活力检测",
"RK-20260601-005",
"2026-06-03",
"2027-06-03",
"CAT-TB-50",
"BN20260525",
"Bio-Rad",
Decimal("72.00"),
Decimal("5"),
),
]
def seed(tenant_no: str = "zkxw", user_id: str = "d3000000000000000000000000000001") -> None:
db = SessionLocal()
created_material = 0
skipped_material = 0
created_batch = 0
skipped_batch = 0
try:
for row in QC_MATERIAL_SEEDS:
(
code,
name,
spec,
unit,
warning_qty,
order_num,
remark,
stock_in_no,
stock_in_date,
expire_date,
catalog_no,
batch_no,
manufacturer,
unit_price,
qty,
) = row
amount = (unit_price * qty).quantize(Decimal("0.01"))
material = None
if material_svc.code_exists(db, code, tenant_no):
skipped_material += 1
from app.models.models import BizQcMaterial
material = (
db.query(BizQcMaterial)
.filter(
BizQcMaterial.material_code == code,
BizQcMaterial.tenant_no == tenant_no,
BizQcMaterial.del_flag == 0,
)
.first()
)
else:
material = material_svc.create_material(
db,
material_code=code,
material_name=name,
spec_model=spec,
unit=unit,
stock_qty=Decimal("0"),
warning_qty=warning_qty,
status="1",
tenant_no=tenant_no,
order_num=order_num,
remark=remark,
user_id=user_id,
)
created_material += 1
if not material:
print(f"跳过批次 {stock_in_no}:物料 {code} 不存在")
continue
if batch_svc.stock_in_no_exists(db, stock_in_no, tenant_no):
skipped_batch += 1
continue
batch_svc.stock_in_one(
db,
material.id,
stock_in_no=stock_in_no,
stock_in_date=stock_in_date,
expire_date=expire_date,
catalog_no=catalog_no,
batch_no=batch_no,
manufacturer=manufacturer,
unit_price=unit_price,
amount=amount,
qty=qty,
remark="测试数据初始化",
tenant_no=tenant_no,
user_id=user_id,
)
created_batch += 1
print(
f"质检物料库测试数据:物料新增 {created_material} 条、跳过 {skipped_material} 条;"
f"入库批次新增 {created_batch} 条、跳过 {skipped_batch} 条。"
)
finally:
db.close()
if __name__ == "__main__":
seed()