feat: 库存管理系统 MaterialWMS 初始版本

- Flask + SQLAlchemy 物料/入库/出库/BOM/生产订单全模块管理
- PyInstaller 打包为单文件 EXE (双击运行, 自动建库)
- 物料维度: 入库总数量 / 消耗总数量 / 逐次入库流水(含时间)
- BOM 详情展示组成物料的规格型号与单套用量
- 支持 Altium / 嘉立创 BOM 批量导入(文本与 Excel 双通道)
- 内置端到端验证脚本(tools/_exe_util.py 等, 清理进程树防残留)
This commit is contained in:
2026-09-11 15:41:56 +08:00
commit 7d34a481b3
38 changed files with 3601 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
"""首次初始化: 建表 + 管理员 + 从桌面 Excel 导入真实物料(含期初库存)
用法: 用 venv 的 python 运行
python init_db.py
"""
import hashlib
from datetime import date
from app import app, db
from models import User, Material, Inbound
EXCEL = r"C:/Users/wps18/Desktop/物料明细表.xlsx"
def main():
with app.app_context():
db.create_all()
# 管理员
if not db.session.scalar(db.select(User).where(User.username == "admin")):
db.session.add(User(
username="admin",
password=hashlib.sha256("admin123".encode("utf-8")).hexdigest(),
role="admin",
display_name="管理员",
))
print("已创建管理员 admin / admin123")
# 导入物料
import openpyxl
wb = openpyxl.load_workbook(EXCEL, data_only=True)
ws = wb["物料入库"]
imported = 0
skipped = 0
for row in ws.iter_rows(min_row=2, values_only=True):
pn = row[1] # B 列 Part Number
if pn is None:
continue
pn = str(pn).strip()
if not pn:
continue
if db.session.scalar(db.select(Material).where(Material.part_number == pn)):
skipped += 1
continue
man = str(row[2]) if row[2] else ""
m = Material(part_number=pn, manufacturer=man, unit="个", safety_stock=0)
db.session.add(m)
db.session.flush()
# 期初库存: 优先 E 列(库存数量), 其次 D 列(总数量)
init_qty = row[4] if row[4] not in (None, "") else row[3]
try:
init_qty = float(init_qty)
except (TypeError, ValueError):
init_qty = 0
if init_qty > 0:
db.session.add(Inbound(
material_id=m.id, qty=init_qty,
source="期初", occur_date=date.today(), operator="系统",
))
imported += 1
db.session.commit()
print(f"导入物料 {imported} 条, 跳过已存在 {skipped} 条")
if __name__ == "__main__":
main()