234 lines
8.0 KiB
Python
234 lines
8.0 KiB
Python
import sqlite3
|
||
import json
|
||
from pathlib import Path
|
||
import os
|
||
from config import OUTPUT_DIR, DB_PATH
|
||
|
||
RECEIPT_JSON = OUTPUT_DIR / "receipt_details_full_clean.json"
|
||
BOM_JSON = OUTPUT_DIR / "bom_cost_full_tree_final.json"
|
||
|
||
def init_db():
|
||
"""初始化数据库并创建表"""
|
||
conn = sqlite3.connect(DB_PATH)
|
||
cursor = conn.cursor()
|
||
|
||
# 创建收货明细表
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS receipt_details (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
purchase_order_code TEXT,
|
||
row_no INTEGER,
|
||
material_code TEXT,
|
||
material_name TEXT,
|
||
material_specification TEXT,
|
||
warehouse_code TEXT,
|
||
warehouse_name TEXT,
|
||
supplier_code TEXT,
|
||
supplier_name TEXT,
|
||
unit_name TEXT,
|
||
conversion_unit TEXT,
|
||
receive_price REAL,
|
||
receipt_time TEXT,
|
||
purchase_qty REAL,
|
||
receive_qty REAL,
|
||
total_amount REAL
|
||
)
|
||
''')
|
||
|
||
# 为收货明细表创建索引以加速查询
|
||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_receipt_material_code ON receipt_details(material_code)')
|
||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_receipt_supplier_name ON receipt_details(supplier_name)')
|
||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_receipt_time ON receipt_details(receipt_time)')
|
||
|
||
# 注意:为了在打包部署时不丢失用户已抓取的数据,改为 IF NOT EXISTS
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS bom_parent (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
parent_material_code TEXT UNIQUE,
|
||
parent_material_name TEXT
|
||
)
|
||
''')
|
||
|
||
cursor.execute('''
|
||
CREATE TABLE IF NOT EXISTS bom_child (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
parent_material_code TEXT, -- 归属的最顶层父件
|
||
node_material_code TEXT,
|
||
node_material_name TEXT,
|
||
bom_level INTEGER,
|
||
parent_node_id INTEGER, -- 指向上一级子件的 id,如果是一级子件则为空
|
||
usage_qty REAL DEFAULT 1.0,
|
||
FOREIGN KEY(parent_material_code) REFERENCES bom_parent(parent_material_code),
|
||
FOREIGN KEY(parent_node_id) REFERENCES bom_child(id)
|
||
)
|
||
''')
|
||
|
||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_bom_child_parent_code ON bom_child(parent_material_code)')
|
||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_bom_child_node_code ON bom_child(node_material_code)')
|
||
|
||
conn.commit()
|
||
return conn
|
||
|
||
def import_receipt_details(conn):
|
||
"""导入收货明细数据"""
|
||
if not RECEIPT_JSON.exists():
|
||
print(f"找不到收货明细文件: {RECEIPT_JSON}")
|
||
return
|
||
|
||
print("开始导入收货明细数据...")
|
||
with open(RECEIPT_JSON, 'r', encoding='utf-8') as f:
|
||
data = json.load(f)
|
||
|
||
cursor = conn.cursor()
|
||
# 清空旧数据(如果需要重复运行),并且我们现在要更新表结构
|
||
cursor.execute('DROP TABLE IF EXISTS receipt_details')
|
||
cursor.execute('''
|
||
CREATE TABLE receipt_details (
|
||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||
purchase_order_code TEXT,
|
||
row_no INTEGER,
|
||
material_code TEXT,
|
||
material_name TEXT,
|
||
material_specification TEXT,
|
||
warehouse_code TEXT,
|
||
warehouse_name TEXT,
|
||
supplier_code TEXT,
|
||
supplier_name TEXT,
|
||
unit_name TEXT,
|
||
conversion_unit TEXT,
|
||
receive_price REAL,
|
||
receipt_time TEXT,
|
||
purchase_qty REAL,
|
||
receive_qty REAL,
|
||
total_amount REAL
|
||
)
|
||
''')
|
||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_receipt_material_code ON receipt_details(material_code)')
|
||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_receipt_supplier_name ON receipt_details(supplier_name)')
|
||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_receipt_time ON receipt_details(receipt_time)')
|
||
|
||
count = 0
|
||
for item in data:
|
||
p_qty = item.get("进货数量")
|
||
r_qty = item.get("收货数量")
|
||
|
||
cursor.execute('''
|
||
INSERT INTO receipt_details (
|
||
purchase_order_code, row_no, material_code, material_name,
|
||
material_specification, warehouse_code, warehouse_name,
|
||
supplier_code, supplier_name, unit_name, conversion_unit,
|
||
receive_price, receipt_time,
|
||
purchase_qty, receive_qty, total_amount
|
||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||
''', (
|
||
item.get("采购订单号"),
|
||
item.get("行号"),
|
||
item.get("物料代码"),
|
||
item.get("物料名称"),
|
||
item.get("物料规格"),
|
||
item.get("仓库代码"),
|
||
item.get("仓库名称"),
|
||
item.get("供应商代码"),
|
||
item.get("供应商名称"),
|
||
item.get("单位名称"),
|
||
item.get("转换单位"),
|
||
item.get("收货单价"),
|
||
item.get("收货时间"),
|
||
p_qty,
|
||
r_qty,
|
||
item.get("收货总金额")
|
||
))
|
||
count += 1
|
||
|
||
conn.commit()
|
||
print(f"成功导入 {count} 条收货明细数据!")
|
||
|
||
def _insert_bom_tree(cursor, parent_material_code, tree_nodes, parent_node_id=None):
|
||
"""递归插入 BOM 树节点"""
|
||
for node in tree_nodes:
|
||
# 提取当前节点信息
|
||
node_code = node.get("childMaterialCode")
|
||
node_name = node.get("childMaterialName")
|
||
bom_level = node.get("bomLevel")
|
||
usage_qty = float(node.get("usageQty") or 1.0)
|
||
|
||
# 插入当前节点
|
||
cursor.execute('''
|
||
INSERT INTO bom_child (
|
||
parent_material_code, node_material_code, node_material_name, bom_level, parent_node_id, usage_qty
|
||
) VALUES (?, ?, ?, ?, ?, ?)
|
||
''', (parent_material_code, node_code, node_name, bom_level, parent_node_id, usage_qty))
|
||
|
||
# 获取刚插入的节点 ID,作为其子节点的 parent_node_id
|
||
current_node_id = cursor.lastrowid
|
||
|
||
# 如果有子节点,递归插入
|
||
sub_items = node.get("sub_items", [])
|
||
if sub_items:
|
||
_insert_bom_tree(cursor, parent_material_code, sub_items, current_node_id)
|
||
|
||
def import_bom_data(conn):
|
||
"""导入 BOM 成本树状数据"""
|
||
if not BOM_JSON.exists():
|
||
print(f"找不到 BOM 成本文件: {BOM_JSON}")
|
||
return
|
||
|
||
print("开始导入 BOM 成本数据...")
|
||
with open(BOM_JSON, 'r', encoding='utf-8') as f:
|
||
data = json.load(f)
|
||
|
||
cursor = conn.cursor()
|
||
# 清空旧数据
|
||
cursor.execute('DELETE FROM bom_child')
|
||
cursor.execute('DELETE FROM bom_parent')
|
||
|
||
parent_count = 0
|
||
for parent in data:
|
||
parent_code = parent.get("parentMaterialCode")
|
||
parent_name = parent.get("parentMaterialName")
|
||
|
||
# 忽略空父件
|
||
if not parent_code:
|
||
continue
|
||
|
||
try:
|
||
cursor.execute('''
|
||
INSERT INTO bom_parent (parent_material_code, parent_material_name)
|
||
VALUES (?, ?)
|
||
''', (parent_code, parent_name))
|
||
parent_count += 1
|
||
|
||
# 递归处理这棵树
|
||
tree = parent.get("bom_cost_tree", [])
|
||
if tree:
|
||
_insert_bom_tree(cursor, parent_code, tree, parent_node_id=None)
|
||
|
||
except sqlite3.IntegrityError:
|
||
print(f"警告: 父件重复 {parent_code},跳过")
|
||
|
||
conn.commit()
|
||
|
||
# 统计插入的子件数量
|
||
cursor.execute('SELECT COUNT(*) FROM bom_child')
|
||
child_count = cursor.fetchone()[0]
|
||
print(f"成功导入 {parent_count} 个 BOM 父件,包含 {child_count} 个子件节点!")
|
||
|
||
if __name__ == "__main__":
|
||
import sys
|
||
print(f"数据库文件将保存在: {DB_PATH}")
|
||
conn = init_db()
|
||
|
||
# 允许通过命令行参数单独导入某一部分数据
|
||
args = sys.argv[1:]
|
||
|
||
if "--bom-only" in args:
|
||
import_bom_data(conn)
|
||
elif "--receipt-only" in args:
|
||
import_receipt_details(conn)
|
||
else:
|
||
# 默认全量导入
|
||
import_receipt_details(conn)
|
||
import_bom_data(conn)
|
||
|
||
conn.close()
|
||
print("全部导入完成!你可以使用 SQLite 客户端连接 erp_data.db 查看数据。") |