提交
This commit is contained in:
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()
|
||||
@@ -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()
|
||||
Reference in new issue
Block a user