# ========================================
# 🚚 在途未入库管理窗口(防重复打开)—— 完整修复版
# ✅ 增加 "已入库" 列,并正确读取 ImportedQty
# ========================================
in_transit_open = False
def open_in_transit_window():
if not cursor:
messagebox.showwarning("警告", "数据库未连接,无法管理在途数据")
return
global in_transit_open
if in_transit_open:
messagebox.showwarning("提示", "在途未入库窗口已打开。")
return
in_transit_open = True
window = tk.Toplevel(root)
window.title("在途未入库管理")
window.geometry("1180x650")
# --- 定义变量 ---
item_var = tk.StringVar()
qty_var = tk.StringVar()
so_var_local = tk.StringVar()
sn_var_local = tk.StringVar()
remark_var = tk.StringVar()
ship_date_var = tk.StringVar()
search_var = tk.StringVar()
# --- 创建 Treeview 表格 ---
tree = ttk.Treeview(
window,
columns=("物料编号", "数量", "销售订单", "序列号", "备注", "发货日期", "已入库"),
show="headings",
height=22
)
headers = ["物料编号", "数量", "销售订单", "序列号", "备注", "发货日期", "已入库"]
widths = [150, 80, 120, 180, 150, 120, 80]
for header, width in zip(headers, widths):
tree.heading(header, text=header)
tree.column(header, width=width, anchor='center')
tree.grid(row=8, column=0, columnspan=4, padx=10, pady=10, sticky="nsew")
# ========================================
# ✅ 功能函数定义
# ========================================
def refresh_list(filter_text=""):
"""刷新表格数据,可带搜索过滤"""
for row in tree.get_children():
tree.delete(row)
try:
if filter_text.strip():
like_filter = f"%{filter_text}%"
query = """
SELECT Item, Qty, SO, SN, 备注, 发货日期, ImportedQty
FROM [in_transit]
WHERE Item LIKE ? OR Qty LIKE ? OR SO LIKE ?
OR SN LIKE ? OR 备注 LIKE ? OR 发货日期 LIKE ? OR CAST(ImportedQty AS VARCHAR) LIKE ?
"""
params = (like_filter,) * 7 # 注意:现在是7个参数
cursor.execute(query, params)
else:
cursor.execute("SELECT Item, Qty, SO, SN, 备注, 发货日期, ImportedQty FROM [in_transit]")
for row in cursor.fetchall():
values = [
str(row[0] or ""), # Item
str(row[1]), # Qty
str(row[2] or ""), # SO
str(row[3] or ""), # SN
str(row[4] or ""), # 备注
str(row[5] or ""), # 发货日期
str(row[6] or "0") # ImportedQty → 显示为“已入库”
]
tree.insert("", tk.END, values=values)
except Exception as e:
messagebox.showerror("读取失败", f"无法加载在途数据:\n{e}")
def add_record():
"""添加新记录,默认 ImportedQty=0"""
item = item_var.get().strip()
qty = qty_var.get().strip()
so = so_var_local.get().strip()
sn = sn_var_local.get().strip()
remark = remark_var.get().strip()
ship_date = ship_date_var.get().strip()
if not item or not qty:
messagebox.showwarning("提示", "请至少输入【物料编号】和【数量】")
return
try:
float(qty)
except ValueError:
messagebox.showwarning("提示", "数量必须是数字")
return
try:
cursor.execute("""
INSERT INTO [in_transit] (Item, Qty, SO, SN, 备注, 发货日期, ImportedQty)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", (item, qty, so, sn, remark, ship_date, 0)) # 初始化 ImportedQty 为 0
conn.commit()
messagebox.showinfo("成功", "✅ 记录已添加")
clear_inputs()
refresh_list()
except Exception as e:
conn.rollback()
messagebox.showerror("错误", f"添加失败:\n{e}")
def delete_record():
"""删除选中的记录"""
selected = tree.selection()
if not selected:
messagebox.showwarning("提示", "请先选择要删除的记录")
return
item = tree.item(selected[0])['values'][0]
sn = tree.item(selected[0])['values'][3]
if messagebox.askyesno("确认删除", f"确定要删除该在途记录?\n物料编号:{item}\n序列号:{sn}"):
try:
cursor.execute("DELETE FROM [in_transit] WHERE Item = ? AND SN = ?", (item, sn))
conn.commit()
messagebox.showinfo("成功", "🗑️ 记录已删除")
refresh_list()
except Exception as e:
conn.rollback()
messagebox.showerror("错误", f"删除失败:\n{e}")
def on_double_click(event):
"""双击编辑记录"""
selected = tree.selection()
if not selected:
return
item_values = tree.item(selected[0])['values']
original_item, original_sn = item_values[0], item_values[3]
edit_win = tk.Toplevel(window)
edit_win.title("编辑在途记录")
edit_win.geometry("500x420")
v_item = tk.StringVar(value=item_values[0])
v_qty = tk.StringVar(value=item_values[1])
v_so = tk.StringVar(value=item_values[2])
v_sn = tk.StringVar(value=item_values[3])
v_remark = tk.StringVar(value=item_values[4])
v_ship_date = tk.StringVar(value=item_values[5])
fields = [
("物料编号", v_item),
("数量", v_qty),
("销售订单", v_so),
("序列号", v_sn),
("备注", v_remark),
("发货日期", v_ship_date),
]
for i, (label, var) in enumerate(fields):
ttk.Label(edit_win, text=label).grid(row=i, column=0, padx=15, pady=6, sticky='e')
ttk.Entry(edit_win, textvariable=var, width=30).grid(row=i, column=1, padx=15, pady=6, sticky='w')
def save_edit():
new_item = v_item.get().strip()
new_qty = v_qty.get().strip()
new_so = v_so.get().strip()
new_sn = v_sn.get().strip()
new_remark = v_remark.get().strip()
new_ship_date = v_ship_date.get().strip()
if not new_item or not new_qty:
messagebox.showwarning("提示", "物料编号和数量不能为空")
return
try:
float(new_qty)
except ValueError:
messagebox.showwarning("提示", "数量必须是有效数字")
return
try:
cursor.execute("""
UPDATE [in_transit]
SET Item = ?, Qty = ?, SO = ?, SN = ?, 备注 = ?, 发货日期 = ?
WHERE Item = ? AND SN = ?
""", (new_item, new_qty, new_so, new_sn, new_remark, new_ship_date,
original_item, original_sn))
conn.commit()
messagebox.showinfo("成功", "💾 修改已保存")
edit_win.destroy()
refresh_list()
window.lift()
except Exception as e:
conn.rollback()
messagebox.showerror("错误", f"更新失败:\n{e}")
ttk.Button(edit_win, text="保存修改", command=save_edit).grid(row=len(fields), column=0, columnspan=2, pady=20)
def clear_inputs():
"""清空输入框"""
item_var.set("")
qty_var.set("")
so_var_local.set("")
sn_var_local.set("")
remark_var.set("")
ship_date_var.set("")
def copy_selected():
"""复制选中行内容到剪贴板"""
selected = tree.selection()
if selected:
values = tree.item(selected[0])['values']
text = '\t'.join(str(v) for v in values)
window.clipboard_clear()
window.clipboard_append(text)
def show_context_menu(event):
"""右键弹出菜单"""
if tree.identify_row(event.y):
tree.selection_set(tree.identify_row(event.y))
context_menu.post(event.x_root, event.y_root)
def import_from_excel():
"""从 Excel 导入并覆盖所有在途数据"""
filepath = filedialog.askopenfilename(
title="选择在途数据Excel文件",
filetypes=[("Excel 文件", "*.xlsx"), ("所有文件", "*.*")]
)
if not filepath:
return
if not messagebox.askyesno("⚠️ 确认操作", "此操作将清空当前所有在途数据,\n并用Excel中的数据完全替换。\n\n是否继续?"):
return
try:
df = pd.read_excel(filepath)
required_columns = {'Item', 'Qty', 'SO', 'SN', '备注', '发货日期'}
missing = required_columns - set(df.columns)
if missing:
messagebox.showerror("格式错误", f"❌ 缺少必要列:{missing}\n请确保包含以下列:\n{required_columns}")
return
df['Item'] = df['Item'].astype(str).str.strip()
df['Qty'] = pd.to_numeric(df['Qty'], errors='coerce').fillna(0)
df['SO'] = df['SO'].astype(str).str.strip()
df['SN'] = df['SN'].astype(str).str.strip()
df['备注'] = df['备注'].astype(str).str.strip()
df['发货日期'] = df['发货日期'].astype(str).str.strip()
valid_df = df.dropna(subset=['Item']).query('Item != "nan" and Item != ""')
if valid_df.empty:
messagebox.showwarning("无有效数据", "Excel 中没有可导入的有效数据行。")
return
cursor.execute("DELETE FROM [in_transit]")
inserted_count = 0
for _, row in valid_df.iterrows():
cursor.execute("""
INSERT INTO [in_transit] (Item, Qty, SO, SN, 备注, 发货日期, ImportedQty)
VALUES (?, ?, ?, ?, ?, ?, ?)
""", (row['Item'], row['Qty'], row['SO'], row['SN'], row['备注'], row['发货日期'], 0))
inserted_count += 1
conn.commit()
messagebox.showinfo("导入成功", f"✅ 已成功导入 {inserted_count} 条在途记录!")
refresh_list()
except Exception as e:
conn.rollback()
messagebox.showerror("导入失败", f"发生错误:\n{str(e)}")
在途管理窗口中 手动创建的日期 改成自动填写当前日期 且不可在页面中编辑
最新发布