抓取生产工单,赚取发料异常
This commit is contained in:
@@ -6,6 +6,7 @@ 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"
|
||||
ISSUE_JSON = OUTPUT_DIR / "issue_receipt_details_full.json"
|
||||
|
||||
def init_db():
|
||||
"""初始化数据库并创建表"""
|
||||
@@ -40,6 +41,68 @@ def init_db():
|
||||
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)')
|
||||
|
||||
# 创建发料明细表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS issue_receipt_details (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
production_order_no TEXT,
|
||||
product_material_code TEXT,
|
||||
product_material_name TEXT,
|
||||
product_material_specification TEXT,
|
||||
work_orders_number TEXT,
|
||||
status INTEGER,
|
||||
material_specification TEXT,
|
||||
material_name TEXT,
|
||||
material_code TEXT,
|
||||
issue_number REAL,
|
||||
has_issue_number REAL,
|
||||
amount REAL,
|
||||
cost_price REAL,
|
||||
issue_amount REAL,
|
||||
production_order_remark TEXT,
|
||||
detailed_remark TEXT,
|
||||
unit_name TEXT,
|
||||
warehouse_name TEXT,
|
||||
line_number INTEGER,
|
||||
work_orders_remark TEXT,
|
||||
executor_user_name TEXT,
|
||||
material_model TEXT,
|
||||
execution_time TEXT,
|
||||
materials_user_name TEXT,
|
||||
product_material_model TEXT,
|
||||
custom_field TEXT,
|
||||
department_information_code TEXT,
|
||||
department_information_name TEXT,
|
||||
image_file TEXT,
|
||||
issue_amount_total REAL,
|
||||
material_group_code TEXT,
|
||||
material_group_name TEXT,
|
||||
numnber_of_reserved_digits INTEGER,
|
||||
place_ment_strategy INTEGER,
|
||||
price REAL,
|
||||
sales_order_code TEXT
|
||||
)
|
||||
''')
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_issue_material_code ON issue_receipt_details(material_code)')
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_issue_execution_time ON issue_receipt_details(execution_time)')
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_issue_work_orders_number ON issue_receipt_details(work_orders_number)')
|
||||
|
||||
# 删除历史可能存在的重复数据,确保唯一索引能够成功创建
|
||||
cursor.execute('''
|
||||
DELETE FROM issue_receipt_details
|
||||
WHERE id NOT IN (
|
||||
SELECT MIN(id)
|
||||
FROM issue_receipt_details
|
||||
GROUP BY work_orders_number, line_number, material_code
|
||||
)
|
||||
''')
|
||||
|
||||
# 为发料明细表创建唯一索引,用于 UPSERT 冲突检测
|
||||
cursor.execute('''
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_issue_unique
|
||||
ON issue_receipt_details(work_orders_number, line_number, material_code)
|
||||
''')
|
||||
|
||||
# 注意:为了在打包部署时不丢失用户已抓取的数据,改为 IF NOT EXISTS
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS bom_parent (
|
||||
@@ -66,6 +129,81 @@ def init_db():
|
||||
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)')
|
||||
|
||||
# 创建生产工单表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS work_orders (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
work_orders_number TEXT,
|
||||
line_number INTEGER,
|
||||
material_code TEXT,
|
||||
material_name TEXT,
|
||||
material_specification TEXT,
|
||||
status INTEGER,
|
||||
unit_name TEXT,
|
||||
cost_price REAL,
|
||||
issue_number REAL,
|
||||
total_issue_number REAL,
|
||||
issue_amount REAL,
|
||||
issue_amount_total REAL,
|
||||
executor_user_name TEXT,
|
||||
execution_time TEXT,
|
||||
production_order_no TEXT,
|
||||
warehouse_name TEXT,
|
||||
materials_user_name TEXT,
|
||||
work_orders_remark TEXT
|
||||
)
|
||||
''')
|
||||
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_work_orders_number ON work_orders(work_orders_number)')
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_work_orders_material_code ON work_orders(material_code)')
|
||||
|
||||
# 删除可能存在的重复数据
|
||||
cursor.execute('''
|
||||
DELETE FROM work_orders
|
||||
WHERE id NOT IN (
|
||||
SELECT MIN(id)
|
||||
FROM work_orders
|
||||
GROUP BY work_orders_number, line_number, material_code
|
||||
)
|
||||
''')
|
||||
|
||||
# 唯一索引
|
||||
cursor.execute('''
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_work_orders_unique
|
||||
ON work_orders(work_orders_number, line_number, material_code)
|
||||
''')
|
||||
|
||||
# 创建发料异常报表表
|
||||
cursor.execute('''
|
||||
CREATE TABLE IF NOT EXISTS abnormal_report (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
work_orders_number TEXT,
|
||||
product_code TEXT,
|
||||
product_name TEXT,
|
||||
status TEXT,
|
||||
completed_qty REAL,
|
||||
order_date TEXT,
|
||||
workshop TEXT,
|
||||
material_code TEXT,
|
||||
material_name TEXT,
|
||||
material_specification TEXT,
|
||||
unit_qty REAL,
|
||||
total_demand_qty REAL,
|
||||
warehouse_issue_qty REAL,
|
||||
theoretical_issue_qty REAL,
|
||||
issue_method TEXT,
|
||||
issue_status TEXT
|
||||
)
|
||||
''')
|
||||
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_abnormal_work_orders_number ON abnormal_report(work_orders_number)')
|
||||
cursor.execute('CREATE INDEX IF NOT EXISTS idx_abnormal_material_code ON abnormal_report(material_code)')
|
||||
|
||||
cursor.execute('''
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_abnormal_unique
|
||||
ON abnormal_report(work_orders_number, material_code)
|
||||
''')
|
||||
|
||||
conn.commit()
|
||||
return conn
|
||||
|
||||
@@ -80,68 +218,74 @@ def import_receipt_details(conn):
|
||||
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
|
||||
)
|
||||
DELETE FROM receipt_details
|
||||
WHERE id NOT IN (
|
||||
SELECT MIN(id)
|
||||
FROM receipt_details
|
||||
GROUP BY purchase_order_code, row_no, material_code
|
||||
)
|
||||
''')
|
||||
|
||||
# 为了避免没有唯一索引导致 UPSERT 报错,这里显式创建一次
|
||||
cursor.execute('''
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_receipt_unique
|
||||
ON receipt_details(purchase_order_code, row_no, material_code)
|
||||
''')
|
||||
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
|
||||
count_inserted = 0
|
||||
|
||||
for item in data:
|
||||
p_qty = item.get("进货数量")
|
||||
r_qty = item.get("收货数量")
|
||||
po_code = item.get("采购订单号")
|
||||
row_no = item.get("行号")
|
||||
mat_code = 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
|
||||
# 容错:如果关键字段为空则跳过
|
||||
if not po_code or not row_no or not mat_code:
|
||||
continue
|
||||
|
||||
try:
|
||||
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 (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(purchase_order_code, row_no, material_code) DO UPDATE SET
|
||||
receive_price=excluded.receive_price,
|
||||
receive_qty=excluded.receive_qty,
|
||||
total_amount=excluded.total_amount,
|
||||
receipt_time=excluded.receipt_time
|
||||
''', (
|
||||
po_code,
|
||||
row_no,
|
||||
mat_code,
|
||||
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_inserted += 1
|
||||
except sqlite3.Error as e:
|
||||
print(f"入库报错 收货单:{po_code} 行号:{row_no} 错误:{e}")
|
||||
|
||||
conn.commit()
|
||||
print(f"成功导入 {count} 条收货明细数据!")
|
||||
print(f"成功处理(新增或更新) {count_inserted} 条收货明细数据!")
|
||||
|
||||
def _insert_bom_tree(cursor, parent_material_code, tree_nodes, parent_node_id=None):
|
||||
"""递归插入 BOM 树节点"""
|
||||
@@ -213,6 +357,128 @@ def import_bom_data(conn):
|
||||
child_count = cursor.fetchone()[0]
|
||||
print(f"成功导入 {parent_count} 个 BOM 父件,包含 {child_count} 个子件节点!")
|
||||
|
||||
def import_issue_receipt_details(conn):
|
||||
"""导入发料明细数据"""
|
||||
if not ISSUE_JSON.exists():
|
||||
print(f"找不到发料明细文件: {ISSUE_JSON}")
|
||||
return
|
||||
|
||||
print("开始导入发料明细数据...")
|
||||
with open(ISSUE_JSON, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 为了避免没有唯一索引导致 UPSERT 报错,这里显式创建一次
|
||||
cursor.execute('''
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_issue_unique
|
||||
ON issue_receipt_details(work_orders_number, line_number, material_code)
|
||||
''')
|
||||
|
||||
count_inserted = 0
|
||||
count_updated = 0
|
||||
|
||||
for item in data:
|
||||
# 提取关键字段
|
||||
wo_number = item.get("发料单号")
|
||||
line_no = item.get("行号")
|
||||
mat_code = item.get("物料代码")
|
||||
|
||||
# 容错:如果关键字段为空则跳过
|
||||
if not wo_number or not line_no or not mat_code:
|
||||
continue
|
||||
|
||||
try:
|
||||
cursor.execute('''
|
||||
INSERT INTO issue_receipt_details (
|
||||
production_order_no, product_material_code, product_material_name, product_material_specification,
|
||||
work_orders_number, status, material_specification, material_name, material_code,
|
||||
issue_number, has_issue_number, amount, cost_price, issue_amount,
|
||||
production_order_remark, detailed_remark, unit_name, warehouse_name, line_number,
|
||||
work_orders_remark, executor_user_name, material_model, execution_time, materials_user_name,
|
||||
product_material_model, custom_field, department_information_code, department_information_name,
|
||||
image_file, issue_amount_total, material_group_code, material_group_name,
|
||||
numnber_of_reserved_digits, place_ment_strategy, price, sales_order_code
|
||||
) VALUES (
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,
|
||||
?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
|
||||
)
|
||||
ON CONFLICT(work_orders_number, line_number, material_code) DO UPDATE SET
|
||||
status=excluded.status,
|
||||
issue_number=excluded.issue_number,
|
||||
has_issue_number=excluded.has_issue_number,
|
||||
amount=excluded.amount,
|
||||
cost_price=excluded.cost_price,
|
||||
issue_amount=excluded.issue_amount,
|
||||
warehouse_name=excluded.warehouse_name,
|
||||
executor_user_name=excluded.executor_user_name,
|
||||
execution_time=excluded.execution_time,
|
||||
materials_user_name=excluded.materials_user_name,
|
||||
issue_amount_total=excluded.issue_amount_total
|
||||
''', (
|
||||
item.get("生产任务单号"), item.get("生产物料代码"), item.get("生产物料名称"), item.get("生产物料规格"),
|
||||
wo_number, item.get("状态"), item.get("物料规格"), item.get("物料名称"), mat_code,
|
||||
item.get("发料数量"), item.get("已发料数量"), item.get("金额"), item.get("成本价"), item.get("发料金额"),
|
||||
item.get("生产订单备注"), item.get("明细备注"), item.get("单位名称"), item.get("仓库名称"), line_no,
|
||||
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("物料组名称"),
|
||||
item.get("单价小数位数"), item.get("单价进位策略"), item.get("单价"), item.get("销售订单号")
|
||||
))
|
||||
|
||||
# 由于 sqlite3 在 UPSERT 时,如果发生了 UPDATE,rowcount 也是 1
|
||||
# 但我们可以通过比较变化前后的总数来粗略估计,或者统一提示处理成功
|
||||
count_inserted += 1
|
||||
|
||||
except sqlite3.Error as e:
|
||||
print(f"入库报错 发料单:{wo_number} 行号:{line_no} 错误:{e}")
|
||||
|
||||
conn.commit()
|
||||
print(f"成功处理(新增或更新) {count_inserted} 条发料明细数据!")
|
||||
|
||||
def import_abnormal_report_data(items):
|
||||
"""直接将 API 获取到的异常报表 items 数组存入数据库"""
|
||||
conn = init_db()
|
||||
cursor = conn.cursor()
|
||||
|
||||
count_inserted = 0
|
||||
|
||||
for item in items:
|
||||
wo_number = item.get("生产工单号")
|
||||
mat_code = item.get("需求物料代码")
|
||||
|
||||
if not wo_number or not mat_code:
|
||||
continue
|
||||
|
||||
try:
|
||||
cursor.execute('''
|
||||
INSERT INTO abnormal_report (
|
||||
work_orders_number, product_code, product_name, status, completed_qty,
|
||||
order_date, workshop, material_code, material_name, material_specification,
|
||||
unit_qty, total_demand_qty, warehouse_issue_qty, theoretical_issue_qty,
|
||||
issue_method, issue_status
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(work_orders_number, material_code) DO UPDATE SET
|
||||
status=excluded.status,
|
||||
completed_qty=excluded.completed_qty,
|
||||
warehouse_issue_qty=excluded.warehouse_issue_qty,
|
||||
theoretical_issue_qty=excluded.theoretical_issue_qty,
|
||||
issue_status=excluded.issue_status
|
||||
''', (
|
||||
wo_number, item.get("产品代码"), item.get("产品名称"), item.get("工单状态"),
|
||||
item.get("已完工数量"), item.get("下单日期"), item.get("生产车间"),
|
||||
mat_code, item.get("需求物料名称"), item.get("需求物料规格"),
|
||||
item.get("单机用量"), item.get("需求总量"), item.get("仓库发放数量"),
|
||||
item.get("理论仓库出料数量"), item.get("发料方式"), item.get("发料情况")
|
||||
))
|
||||
count_inserted += 1
|
||||
except sqlite3.Error as e:
|
||||
print(f"异常报表入库报错 工单:{wo_number} 物料:{mat_code} 错误:{e}")
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return count_inserted
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
print(f"数据库文件将保存在: {DB_PATH}")
|
||||
@@ -225,10 +491,13 @@ if __name__ == "__main__":
|
||||
import_bom_data(conn)
|
||||
elif "--receipt-only" in args:
|
||||
import_receipt_details(conn)
|
||||
elif "--issue-only" in args:
|
||||
import_issue_receipt_details(conn)
|
||||
else:
|
||||
# 默认全量导入
|
||||
import_receipt_details(conn)
|
||||
import_bom_data(conn)
|
||||
import_issue_receipt_details(conn)
|
||||
|
||||
conn.close()
|
||||
print("全部导入完成!你可以使用 SQLite 客户端连接 erp_data.db 查看数据。")
|
||||
Reference in New Issue
Block a user