Python 写的 智慧记 进销存 辅助 步伐 导入导出 excel 可打印 ...

王柳  金牌会员 | 2024-12-24 02:24:36 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 886|帖子 886|积分 2658

 图样:



 



就可以导入了
上代码

  1. import tkinter as tk
  2. from tkinter import ttk
  3. import sqlite3
  4. from datetime import datetime
  5. from tkinter import messagebox, filedialog
  6. import pandas as pd
  7. import re
  8. class OrderSystem:
  9.     def __init__(self, root):
  10.         self.root = root
  11.         self.root.title("订单记录系统")
  12.         
  13.         # 创建数据库连接
  14.         self.conn = sqlite3.connect('orders.db')
  15.         self.create_table()
  16.         
  17.         # 创建界面
  18.         self.create_ui()
  19.         
  20.         # 添加搜索框架
  21.         self.create_search_frame()
  22.         
  23.         # 添加更多功能按钮
  24.         self.add_function_buttons()
  25.         
  26.         # 加载所有订单数据
  27.         self.load_all_orders()
  28.         
  29.         # 设置默认值
  30.         self.set_default_values()
  31.         
  32.     def create_table(self):
  33.         cursor = self.conn.cursor()
  34.         cursor.execute('''
  35.         CREATE TABLE IF NOT EXISTS orders (
  36.             order_date TEXT,
  37.             order_number TEXT,
  38.             customer TEXT,
  39.             product TEXT,
  40.             unit TEXT,
  41.             quantity REAL,
  42.             price REAL,
  43.             discount REAL,
  44.             final_price REAL,
  45.             total REAL,
  46.             remarks TEXT,
  47.             discount_amount REAL,
  48.             discount_total REAL,
  49.             delivery TEXT,
  50.             payment_received REAL,
  51.             end_customer TEXT,
  52.             notes TEXT,
  53.             business TEXT
  54.         )
  55.         ''')
  56.         self.conn.commit()
  57.         
  58.     def create_ui(self):
  59.         # 创建主框架来容纳左右两部分
  60.         main_frame = ttk.Frame(self.root)
  61.         main_frame.pack(fill="both", expand=True)
  62.         
  63.         # 创建左侧框架
  64.         left_frame = ttk.Frame(main_frame)
  65.         left_frame.pack(side="left", fill="both", expand=True)
  66.         
  67.         # 创建右侧框架
  68.         right_frame = ttk.Frame(main_frame)
  69.         right_frame.pack(side="right", fill="y", padx=5)
  70.         
  71.         # 创建输入框架(放在左侧)
  72.         input_frame = ttk.LabelFrame(left_frame, text="订单信息")
  73.         input_frame.pack(padx=5, pady=5, fill="x")
  74.         
  75.         # 修改订单分类框架
  76.         order_group_frame = ttk.LabelFrame(right_frame, text="订单查询")
  77.         order_group_frame.pack(padx=5, pady=5, fill="both", expand=True)
  78.         
  79.         # 添加筛选框
  80.         filter_frame = ttk.Frame(order_group_frame)
  81.         filter_frame.pack(fill="x", padx=5, pady=5)
  82.         
  83.         # 单据���号筛选
  84.         ttk.Label(filter_frame, text="单据编号:").grid(row=0, column=0, padx=5)
  85.         self.order_number_filter = ttk.Combobox(filter_frame, width=15)
  86.         self.order_number_filter.grid(row=0, column=1, padx=5)
  87.         
  88.         # 客户名称筛选
  89.         ttk.Label(filter_frame, text="客户名称:").grid(row=0, column=2, padx=5)
  90.         self.customer_filter = ttk.Combobox(filter_frame, width=15)
  91.         self.customer_filter.grid(row=0, column=3, padx=5)
  92.         
  93.         # 筛选按钮
  94.         ttk.Button(filter_frame, text="筛选", command=self.filter_orders).grid(row=0, column=4, padx=5)
  95.         ttk.Button(filter_frame, text="重置", command=self.reset_filter).grid(row=0, column=5, padx=5)
  96.         ttk.Button(filter_frame, text="打印", command=self.print_filtered_data).grid(row=0, column=6, padx=5)
  97.         
  98.         # 绑定下拉框事件
  99.         self.order_number_filter.bind('<KeyRelease>', self.update_order_number_list)
  100.         self.customer_filter.bind('<KeyRelease>', self.update_customer_list)
  101.         
  102.         # 修改订单分类的树形视图列
  103.         self.group_tree = ttk.Treeview(order_group_frame, columns=[
  104.             "order_number", "customer", "product", "unit",
  105.             "quantity", "price", "total", "remarks"
  106.         ], show="headings", height=15)
  107.         
  108.         # 设置列标题和宽度
  109.         columns = [
  110.             ("order_number", "单据编号", 100),
  111.             ("customer", "客户名称", 100),
  112.             ("product", "品名规格", 120),
  113.             ("unit", "单位", 50),
  114.             ("quantity", "数量", 60),
  115.             ("price", "原价", 80),
  116.             ("total", "金额", 80),
  117.             ("remarks", "备注", 100)
  118.         ]
  119.         
  120.         for col, heading, width in columns:
  121.             self.group_tree.heading(col, text=heading)
  122.             self.group_tree.column(col, width=width)
  123.         
  124.         self.group_tree.pack(padx=5, pady=5, fill="both", expand=True)
  125.         
  126.         # 添加滚动条
  127.         group_scrollbar = ttk.Scrollbar(order_group_frame, orient="vertical", command=self.group_tree.yview)
  128.         group_scrollbar.pack(side="right", fill="y")
  129.         self.group_tree.configure(yscrollcommand=group_scrollbar.set)
  130.         
  131.         # 绑定点击事件
  132.         self.group_tree.bind('<<TreeviewSelect>>', self.on_group_select)
  133.         
  134.         # 修改输入字段列表,确保与数据库字段完全匹配
  135.         self.entries = {}
  136.         fields = [
  137.             ("order_date", "单据日期"),
  138.             ("order_number", "单据编号"),
  139.             ("customer", "客户名称"),
  140.             ("product", "品名规格"),
  141.             ("unit", "单位"),
  142.             ("quantity", "数量"),
  143.             ("price", "原价"),
  144.             ("discount", "单行折扣率(%)"),
  145.             ("final_price", "折后价"),
  146.             ("total", "金额"),
  147.             ("remarks", "备注"),
  148.             ("discount_amount", "整单折扣率(%)"),
  149.             ("discount_total", "折后金额"),
  150.             ("delivery", "运费"),
  151.             ("payment_received", "本单已收"),
  152.             ("end_customer", "结算账户"),
  153.             ("notes", "说明"),
  154.             ("business", "营业员")
  155.         ]
  156.         
  157.         for row, (field, label) in enumerate(fields):
  158.             ttk.Label(input_frame, text=label).grid(row=row//2, column=(row%2)*2, padx=5, pady=2)
  159.             self.entries[field] = ttk.Entry(input_frame)
  160.             self.entries[field].grid(row=row//2, column=(row%2)*2+1, padx=5, pady=2, sticky="ew")
  161.             
  162.         # 添加按钮
  163.         self.btn_frame = ttk.Frame(self.root)
  164.         self.btn_frame.pack(pady=5)
  165.         
  166.         ttk.Button(self.btn_frame, text="保存", command=self.save_order).pack(side="left", padx=5)
  167.         ttk.Button(self.btn_frame, text="清空", command=self.clear_fields).pack(side="left", padx=5)
  168.         ttk.Button(self.btn_frame, text="导入Excel", command=self.import_from_excel).pack(side="left", padx=5)
  169.         ttk.Button(self.btn_frame, text="导出模板", command=self.export_template).pack(side="left", padx=5)
  170.         
  171.         # 修改表格显示,显示所有列
  172.         self.tree = ttk.Treeview(self.root, columns=[
  173.             "order_date", "order_number", "customer", "product", "unit",
  174.             "quantity", "price", "discount", "final_price", "total",
  175.             "remarks", "discount_amount", "discount_total", "delivery",
  176.             "payment_received", "end_customer", "notes", "business"
  177.         ], show="headings")
  178.         
  179.         # 修改列标题定义,显示所有列
  180.         columns = [
  181.             ("order_date", "单据日期"),
  182.             ("order_number", "单据编号"),
  183.             ("customer", "客户名称"),
  184.             ("product", "品名规格"),
  185.             ("unit", "单位"),
  186.             ("quantity", "数量"),
  187.             ("price", "原价"),
  188.             ("discount", "单行折扣率(%)"),
  189.             ("final_price", "折后价"),
  190.             ("total", "金额"),
  191.             ("remarks", "备注"),
  192.             ("discount_amount", "整单折扣率(%)"),
  193.             ("discount_total", "折后金额"),
  194.             ("delivery", "运费"),
  195.             ("payment_received", "本单已收"),
  196.             ("end_customer", "结算账户"),
  197.             ("notes", "说明"),
  198.             ("business", "营业员")
  199.         ]
  200.         
  201.         for col, heading in columns:
  202.             self.tree.heading(col, text=heading)
  203.             self.tree.column(col, width=100)
  204.             
  205.         self.tree.pack(padx=5, pady=5, fill="both", expand=True)
  206.         
  207.         # 添加滚动条
  208.         scrollbar = ttk.Scrollbar(self.root, orient="vertical", command=self.tree.yview)
  209.         scrollbar.pack(side="right", fill="y")
  210.         self.tree.configure(yscrollcommand=scrollbar.set)
  211.         
  212.         # 添加自动计算绑定
  213.         self.entries['quantity'].bind('<KeyRelease>', self.calculate_total)
  214.         self.entries['price'].bind('<KeyRelease>', self.calculate_total)
  215.         self.entries['discount'].bind('<KeyRelease>', self.calculate_total)
  216.         
  217.         # 在订单分类框架底部添加合计标签
  218.         self.total_label = ttk.Label(order_group_frame, text="合计金额: ¥0.00")
  219.         self.total_label.pack(pady=5)
  220.     def calculate_total(self, event=None):
  221.         """计算折后价和金额"""
  222.         try:
  223.             quantity = float(self.entries['quantity'].get() or 0)
  224.             price = float(self.entries['price'].get() or 0)
  225.             discount = float(self.entries['discount'].get() or 100)
  226.             
  227.             # 计算折后价
  228.             final_price = price * discount / 100
  229.             self.entries['final_price'].delete(0, tk.END)
  230.             self.entries['final_price'].insert(0, f"{final_price:.2f}")
  231.             
  232.             # 计算金额
  233.             total = quantity * final_price
  234.             self.entries['total'].delete(0, tk.END)
  235.             self.entries['total'].insert(0, f"{total:.2f}")
  236.         except ValueError:
  237.             pass
  238.     def create_search_frame(self):
  239.         search_frame = ttk.LabelFrame(self.root, text="搜索")
  240.         search_frame.pack(padx=5, pady=5, fill="x")
  241.         
  242.         ttk.Label(search_frame, text="搜索条件:").pack(side="left", padx=5)
  243.         self.search_entry = ttk.Entry(search_frame)
  244.         self.search_entry.pack(side="left", padx=5, fill="x", expand=True)
  245.         
  246.         ttk.Button(search_frame, text="搜索", command=self.search_orders).pack(side="left", padx=5)
  247.         
  248.     def add_function_buttons(self):
  249.         # 在原有btn_frame中添加更多按钮
  250.         ttk.Button(self.btn_frame, text="编辑", command=self.edit_selected).pack(side="left", padx=5)
  251.         ttk.Button(self.btn_frame, text="删除", command=self.delete_selected).pack(side="left", padx=5)
  252.         ttk.Button(self.btn_frame, text="导出Excel", command=self.export_to_excel).pack(side="left", padx=5)
  253.         ttk.Button(self.btn_frame, text="统计报表", command=self.show_statistics).pack(side="left", padx=5)
  254.     def validate_data(self):
  255.         """数据验证"""
  256.         errors = []
  257.         
  258.         # 验证日期格式
  259.         date = self.entries['order_date'].get().strip()
  260.         if not date:
  261.             errors.append("单据日期不能为空")
  262.         elif not re.match(r'^\d{4}-\d{2}-\d{2}$', date):
  263.             errors.append("单据日期格式错误,应为 YYYY-MM-DD")
  264.         
  265.         # 验证必填字段
  266.         required_fields = {
  267.             'order_number': '单据编号',
  268.             'customer': '客户名称',
  269.             'product': '品名规格',
  270.             'unit': '单位',
  271.             'quantity': '数量',
  272.             'price': '原价'
  273.         }
  274.         
  275.         for field, name in required_fields.items():
  276.             value = self.entries[field].get().strip()
  277.             if not value:
  278.                 errors.append(f"{name}不能为空")
  279.         
  280.         # 验证数字字段
  281.         number_fields = {
  282.             'quantity': '数量',
  283.             'price': '原价',
  284.             'discount': '单行折扣率(%)',
  285.             'final_price': '折后价',
  286.             'total': '金额',
  287.             'discount_amount': '整单折扣率(%)',
  288.             'discount_total': '折后金额',
  289.             'payment_received': '本单已收'
  290.         }
  291.         
  292.         for field, name in number_fields.items():
  293.             value = self.entries[field].get().strip()
  294.             if value:  # 如果有值才验证
  295.                 try:
  296.                     num = float(value)
  297.                     if field in ['quantity', 'price'] and num <= 0:
  298.                         errors.append(f"{name}必须大于0")
  299.                     elif num < 0:
  300.                         errors.append(f"{name}不能为负数")
  301.                 except ValueError:
  302.                     errors.append(f"{name}必须是数字")
  303.         
  304.         if errors:
  305.             messagebox.showerror("验证错误", "\n".join(errors))
  306.             return False
  307.         return True
  308.     def save_order(self):
  309.         """保存订单数据"""
  310.         if not self.validate_data():
  311.             return
  312.         
  313.         try:
  314.             # 获取所有输入值
  315.             values = []
  316.             fields_order = [
  317.                 'order_date', 'order_number', 'customer', 'product', 'unit',
  318.                 'quantity', 'price', 'discount', 'final_price', 'total',
  319.                 'remarks', 'discount_amount', 'discount_total', 'delivery',
  320.                 'payment_received', 'end_customer', 'notes', 'business'
  321.             ]
  322.             
  323.             for field in fields_order:
  324.                 value = self.entries[field].get().strip()
  325.                
  326.                 # 对数字字段进行转换
  327.                 if field in ['quantity', 'price', 'discount', 'final_price', 'total',
  328.                             'discount_amount', 'discount_total', 'payment_received']:
  329.                     try:
  330.                         value = float(value) if value else 0.0
  331.                     except ValueError:
  332.                         value = 0.0
  333.                 elif not value:  # 对非数字字段,如果为空则设为空字符串
  334.                     value = ''
  335.                
  336.                 values.append(value)
  337.             
  338.             # 检查单据编号是否重复
  339.             cursor = self.conn.cursor()
  340.             cursor.execute('SELECT COUNT(*) FROM orders WHERE order_number = ?', (values[1],))
  341.             if cursor.fetchone()[0] > 0:
  342.                 if not messagebox.askyesno("警告", "单据编号已存在是否继续保存?"):
  343.                     return
  344.             
  345.             # 插入数据
  346.             try:
  347.                 cursor.execute('''
  348.                 INSERT INTO orders VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
  349.                 ''', values)
  350.                 self.conn.commit()
  351.                
  352.                 # 更新表格显示
  353.                 self.tree.insert("", "end", values=values)
  354.                
  355.                 # 清空输入框并设置默认值
  356.                 self.set_default_values()
  357.                
  358.                 # 显示成功消息
  359.                 messagebox.showinfo("成功", "订单保存成功!")
  360.                
  361.             except sqlite3.Error as e:
  362.                 self.conn.rollback()
  363.                 messagebox.showerror("数据库错误", f"保存失败:{str(e)}")
  364.                 return
  365.                
  366.         except Exception as e:
  367.             messagebox.showerror("错误", f"保存过程中出错:{str(e)}")
  368.             return
  369.         
  370.         self.update_order_groups()
  371.     def clear_fields(self):
  372.         """清空所有输入框"""
  373.         for field in self.entries:
  374.             self.entries[field].delete(0, tk.END)
  375.     def __del__(self):
  376.         self.conn.close()
  377.     def search_orders(self):
  378.         search_text = self.search_entry.get().strip()
  379.         if not search_text:
  380.             self.load_all_orders()
  381.             return
  382.             
  383.         cursor = self.conn.cursor()
  384.         cursor.execute('''
  385.         SELECT * FROM orders
  386.         WHERE order_date LIKE ? OR order_number LIKE ? OR customer LIKE ? OR product LIKE ?
  387.         ''', [f'%{search_text}%'] * 4)
  388.         
  389.         self.tree.delete(*self.tree.get_children())
  390.         for row in cursor.fetchall():
  391.             self.tree.insert("", "end", values=row)
  392.     def edit_selected(self):
  393.         selected = self.tree.selection()
  394.         if not selected:
  395.             messagebox.showwarning("提示", "请先选择一条记录")
  396.             return
  397.             
  398.         item = self.tree.item(selected[0])
  399.         values = item['values']
  400.         
  401.         # 填充表单
  402.         for field, value in zip(self.entries.keys(), values):
  403.             self.entries[field].delete(0, tk.END)
  404.             self.entries[field].insert(0, str(value))
  405.     def delete_selected(self):
  406.         selected = self.tree.selection()
  407.         if not selected:
  408.             messagebox.showwarning("提示", "请先选择一条记录")
  409.             return
  410.             
  411.         if messagebox.askyesno("确认", "确定要删除中的记录吗?"):
  412.             item = self.tree.item(selected[0])
  413.             order_number = item['values'][1]
  414.             
  415.             cursor = self.conn.cursor()
  416.             cursor.execute('DELETE FROM orders WHERE order_number = ?', (order_number,))
  417.             self.conn.commit()
  418.             
  419.             self.tree.delete(selected[0])
  420.         
  421.         self.update_order_groups()
  422.     def export_to_excel(self):
  423.         """导出数据到Excel"""
  424.         try:
  425.             # 先获取保存路径
  426.             filename = filedialog.asksaveasfilename(
  427.                 defaultextension=".xlsx",
  428.                 filetypes=[("Excel files", "*.xlsx")]
  429.             )
  430.             if not filename:
  431.                 return
  432.             
  433.             # 获取数据
  434.             cursor = self.conn.cursor()
  435.             cursor.execute('SELECT * FROM orders')
  436.             data = cursor.fetchall()
  437.             
  438.             # 准备列名
  439.             columns = [
  440.                 '单据日期', '单据编号', '客户名称', '品名规格',
  441.                 '单位', '数量', '原价', '单行折扣率(%)', '折后价',
  442.                 '金额', '备注', '整单折扣率(%)', '折后金额', '运费',
  443.                 '本单已收', '结算账户', '说明', '营业员'
  444.             ]
  445.             
  446.             # 创建DataFrame
  447.             df = pd.DataFrame(data, columns=columns)
  448.             
  449.             # 直接导出
  450.             df.to_excel(filename, index=False)
  451.             messagebox.showinfo("成功", "数据已导出到Excel文件")
  452.             
  453.         except PermissionError:
  454.             messagebox.showerror("错误", "无法保存文件,请确保:\n1. 文件未被其他程序打开\n2. 您有写入权限")
  455.         except Exception as e:
  456.             messagebox.showerror("错误", f"导出过程中出错:{str(e)}")
  457.     def show_statistics(self):
  458.         stats_window = tk.Toplevel(self.root)
  459.         stats_window.title("统计报表")
  460.         
  461.         cursor = self.conn.cursor()
  462.         
  463.         # 客户统计
  464.         cursor.execute('''
  465.         SELECT customer,
  466.                COUNT(*) as order_count,
  467.                SUM(total) as total_amount,
  468.                SUM(payment_received) as total_received
  469.         FROM orders
  470.         GROUP BY customer
  471.         ''')
  472.         
  473.         # 创建统计表格
  474.         tree = ttk.Treeview(stats_window, columns=["customer", "count", "amount", "received"], show="headings")
  475.         tree.heading("customer", text="客户")
  476.         tree.heading("count", text="订单数")
  477.         tree.heading("amount", text="总金额")
  478.         tree.heading("received", text="已收金额")
  479.         
  480.         for row in cursor.fetchall():
  481.             tree.insert("", "end", values=row)
  482.             
  483.         tree.pack(padx=5, pady=5, fill="both", expand=True)
  484.     def load_all_orders(self):
  485.         """加载所有订单到表格"""
  486.         cursor = self.conn.cursor()
  487.         cursor.execute('SELECT * FROM orders')
  488.         
  489.         self.tree.delete(*self.tree.get_children())
  490.         for row in cursor.fetchall():
  491.             self.tree.insert("", "end", values=row)
  492.         
  493.         # 更新筛选下拉列表
  494.         self.update_filter_lists()
  495.         self.update_order_groups()
  496.     def import_from_excel(self):
  497.         """从Excel文件导入数据"""
  498.         filename = filedialog.askopenfilename(
  499.             filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")]
  500.         )
  501.         if not filename:
  502.             return
  503.         
  504.         try:
  505.             # 读取Excel文件
  506.             df = pd.read_excel(filename)
  507.             
  508.             # 数字字段列表
  509.             numeric_columns = [
  510.                 '数量', '原价', '单行折扣率(%)', '折后价', '金额',
  511.                 '整单折扣率(%)', '折后金额', '运费', '本单已收'
  512.             ]
  513.             
  514.             # 转换数字列的数据类型
  515.             for col in numeric_columns:
  516.                 if col in df.columns:
  517.                     df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
  518.             
  519.             # 检查必需的列是否存在
  520.             required_columns = [
  521.                 '单据日期', '单据编号', '客户名称', '品名规格', '单位',
  522.                 '数量', '原价', '单行折扣率(%)', '折后价', '金额',
  523.                 '备注', '整单折扣率(%)', '折后金额', '运费',
  524.                 '本单已收', '结算账户', '说明', '营业员'
  525.             ]
  526.             
  527.             missing_columns = [col for col in required_columns if col not in df.columns]
  528.             if missing_columns:
  529.                 messagebox.showerror("错误", f"Excel文件缺少以下列:\n{', '.join(missing_columns)}")
  530.                 return
  531.             
  532.             # 创建预览窗口
  533.             preview_window = tk.Toplevel(self.root)
  534.             preview_window.title("导入数据预览")
  535.             preview_window.geometry("800x600")
  536.             
  537.             # 创建预览表格
  538.             preview_tree = ttk.Treeview(preview_window, columns=required_columns[:10], show="headings")
  539.             
  540.             # 设置列标题
  541.             for col in required_columns[:10]:
  542.                 preview_tree.heading(col, text=col)
  543.                 preview_tree.column(col, width=100)
  544.             
  545.             # 添加数据到预��表格
  546.             for _, row in df.iterrows():
  547.                 values = [row[col] for col in required_columns[:10]]
  548.                 preview_tree.insert("", "end", values=values)
  549.             
  550.             # 添加滚动条
  551.             scrollbar = ttk.Scrollbar(preview_window, orient="vertical", command=preview_tree.yview)
  552.             scrollbar.pack(side="right", fill="y")
  553.             preview_tree.configure(yscrollcommand=scrollbar.set)
  554.             preview_tree.pack(padx=5, pady=5, fill="both", expand=True)
  555.             
  556.             # 添加按钮框
  557.             btn_frame = ttk.Frame(preview_window)
  558.             btn_frame.pack(pady=5)
  559.             
  560.             def confirm_import():
  561.                 try:
  562.                     # 将数据插入数据库
  563.                     cursor = self.conn.cursor()
  564.                     
  565.                     # 定义字段映射
  566.                     field_mapping = {
  567.                         '单据日期': 'order_date',
  568.                         '单据编号': 'order_number',
  569.                         '客户名称': 'customer',
  570.                         '品名规格': 'product',
  571.                         '单位': 'unit',
  572.                         '数量': 'quantity',
  573.                         '原价': 'price',
  574.                         '单行折扣率(%)': 'discount',
  575.                         '折后价': 'final_price',
  576.                         '金额': 'total',
  577.                         '备注': 'remarks',
  578.                         '整单折扣率(%)': 'discount_amount',
  579.                         '折后金额': 'discount_total',
  580.                         '运费': 'delivery',
  581.                         '本单已收': 'payment_received',
  582.                         '结算账户': 'end_customer',
  583.                         '说明': 'notes',
  584.                         '营业员': 'business'
  585.                     }
  586.                     
  587.                     # 获取数据字段顺序
  588.                     db_fields = [
  589.                         'order_date', 'order_number', 'customer', 'product', 'unit',
  590.                         'quantity', 'price', 'discount', 'final_price', 'total',
  591.                         'remarks', 'discount_amount', 'discount_total', 'delivery',
  592.                         'payment_received', 'end_customer', 'notes', 'business'
  593.                     ]
  594.                     
  595.                     for _, row in df.iterrows():
  596.                         values = []
  597.                         for field in db_fields:
  598.                             # 从Excel列名映射到数据库字段
  599.                             excel_col = [k for k, v in field_mapping.items() if v == field][0]
  600.                             value = row[excel_col]
  601.                            
  602.                             # 处理数值
  603.                             if pd.isna(value):
  604.                                 value = 0 if field in ['quantity', 'price', 'discount', 'final_price',
  605.                                                      'total', 'discount_amount', 'discount_total',
  606.                                                      'payment_received'] else ''
  607.                             elif isinstance(value, (int, float)):
  608.                                 value = float(value)
  609.                             else:
  610.                                 value = str(value)
  611.                             values.append(value)
  612.                         
  613.                         try:
  614.                             cursor.execute('''
  615.                             INSERT INTO orders VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
  616.                             ''', values)
  617.                         except sqlite3.Error as e:
  618.                             self.conn.rollback()
  619.                             messagebox.showerror("错误", f"插入数据时出错:{str(e)}\n行数据{values}")
  620.                             return
  621.                     
  622.                     self.conn.commit()
  623.                     self.load_all_orders()
  624.                     messagebox.showinfo("成功", "数据导入成功!")
  625.                     preview_window.destroy()
  626.                     
  627.                 except Exception as e:
  628.                     self.conn.rollback()
  629.                     messagebox.showerror("错误", f"导入过程中出错:{str(e)}")
  630.             
  631.             def cancel_import():
  632.                 preview_window.destroy()
  633.             
  634.             # 添加确认和取消按钮
  635.             ttk.Button(btn_frame, text="确认导入", command=confirm_import).pack(side="left", padx=5)
  636.             ttk.Button(btn_frame, text="取消", command=cancel_import).pack(side="left", padx=5)
  637.             
  638.             # 显示导入数据的总数
  639.             ttk.Label(preview_window, text=f"共 {len(df)} 条数据").pack(pady=5)
  640.             
  641.         except Exception as e:
  642.             messagebox.showerror("错误", f"导入过程中出错:{str(e)}")
  643.         
  644.         self.update_order_groups()
  645.     def export_template(self):
  646.         """导出Excel模板"""
  647.         filename = filedialog.asksaveasfilename(
  648.             defaultextension=".xlsx",
  649.             filetypes=[("Excel files", "*.xlsx")],
  650.             initialfile="订单导出模板.xlsx"
  651.         )
  652.         if not filename:
  653.             return
  654.         
  655.         try:
  656.             # 创建示例数据 - 使用相同的列名
  657.             sample_data = {
  658.                 '单据日期': ['2024-01-01'],
  659.                 '单据编号': ['XSD202401001'],
  660.                 '客户名称': ['示例客户'],
  661.                 '品名规格': ['示例产品'],
  662.                 '单位': ['个'],
  663.                 '数量': [1],
  664.                 '原价': [100],
  665.                 '单行折扣率(%)': [100],
  666.                 '折后价': [100],
  667.                 '金额': [100],
  668.                 '备注': ['备注示例'],
  669.                 '整单折扣率(%)': [0],
  670.                 '折后金额': [100],
  671.                 '运费': [0],
  672.                 '本单已收': [0],
  673.                 '结算账户': ['结算账户示例'],
  674.                 '说明': ['说明示例'],
  675.                 '营业员': ['营业员示例']
  676.             }
  677.             
  678.             # 创建DataFrame
  679.             df = pd.DataFrame(sample_data)
  680.             
  681.             # 创建Excel写入器
  682.             with pd.ExcelWriter(filename, engine='openpyxl') as writer:
  683.                 # 写入数据
  684.                 df.to_excel(writer, index=False, sheet_name='订单数据')
  685.                
  686.                 # 获取工作表
  687.                 worksheet = writer.sheets['订单数据']
  688.                
  689.                 # 设置列宽
  690.                 for column in worksheet.columns:
  691.                     max_length = 0
  692.                     column = [cell for cell in column]
  693.                     for cell in column:
  694.                         try:
  695.                             if len(str(cell.value)) > max_length:
  696.                                 max_length = len(str(cell.value))
  697.                         except:
  698.                             pass
  699.                     adjusted_width = (max_length + 2)
  700.                     worksheet.column_dimensions[column[0].column_letter].width = adjusted_width
  701.                
  702.                 # 设置样式
  703.                 from openpyxl.styles import PatternFill, Font, Alignment, Border, Side
  704.                
  705.                 # 定义样式
  706.                 header_fill = PatternFill(start_color='CCE5FF', end_color='CCE5FF', fill_type='solid')
  707.                 header_font = Font(bold=True)
  708.                 center_aligned = Alignment(horizontal='center', vertical='center')
  709.                 border = Border(
  710.                     left=Side(style='thin'),
  711.                     right=Side(style='thin'),
  712.                     top=Side(style='thin'),
  713.                     bottom=Side(style='thin')
  714.                 )
  715.                
  716.                 # 应用表头样式
  717.                 for cell in worksheet[1]:
  718.                     cell.fill = header_fill
  719.                     cell.font = header_font
  720.                     cell.alignment = center_aligned
  721.                     cell.border = border
  722.                
  723.                 # 应用数据行样式
  724.                 for row in worksheet.iter_rows(min_row=2):
  725.                     for cell in row:
  726.                         cell.alignment = center_aligned
  727.                         cell.border = border
  728.             
  729.             messagebox.showinfo("成功", "模板导出成功!\n请按照模板格式准备数据后再进行导。")
  730.             
  731.         except Exception as e:
  732.             messagebox.showerror("错误", f"导出模板时出错:{str(e)}")
  733.     def set_default_values(self):
  734.         """设置默认值"""
  735.         # 清空所有输入框
  736.         self.clear_fields()
  737.         
  738.         # 只设置日期和折扣率的默认值
  739.         today = datetime.now().strftime('%Y-%m-%d')
  740.         self.entries['order_date'].insert(0, today)  # 默认日期为今天
  741.         self.entries['discount'].insert(0, '100')    # 默认折扣率为100%
  742.         
  743.         # 生成新的单据编号
  744.         cursor = self.conn.cursor()
  745.         cursor.execute('''
  746.         SELECT MAX(order_number) FROM orders
  747.         WHERE order_number LIKE ?
  748.         ''', [f'XSD{today.replace("-", "")}%'])
  749.         
  750.         last_number = cursor.fetchone()[0]
  751.         if last_number:
  752.             try:
  753.                 # 从最后一个单号提取序号并加1
  754.                 seq = int(last_number[-3:]) + 1
  755.                 new_number = f'XSD{today.replace("-", "")}{seq:03d}'
  756.             except ValueError:
  757.                 new_number = f'XSD{today.replace("-", "")}001'
  758.         else:
  759.             new_number = f'XSD{today.replace("-", "")}001'
  760.         
  761.         self.entries['order_number'].insert(0, new_number)  # 设置新单据编号
  762.     def update_order_groups(self):
  763.         """更新订单分类显示"""
  764.         cursor = self.conn.cursor()
  765.         cursor.execute('''
  766.         SELECT order_number, customer, product, unit,
  767.                quantity, price, total, remarks,
  768.                SUM(total) OVER () as total_sum
  769.         FROM orders
  770.         ORDER BY order_number DESC
  771.         ''')
  772.         
  773.         # 清空现有数据
  774.         self.group_tree.delete(*self.group_tree.get_children())
  775.         
  776.         total_sum = 0
  777.         # 插入新数据
  778.         for row in cursor.fetchall():
  779.             formatted_row = list(row[:8])  # 只取前8列显示
  780.             # 格式化数字列
  781.             formatted_row[4] = f"{row[4]:.2f}"  # 数量
  782.             formatted_row[5] = f"¥{row[5]:.2f}"  # 原价
  783.             formatted_row[6] = f"¥{row[6]:.2f}"  # 金额
  784.             self.group_tree.insert("", "end", values=formatted_row)
  785.             total_sum = row[8]  # 获取合计金额
  786.         
  787.         # 更新合计标签
  788.         self.total_label.config(text=f"合计金额: ¥{total_sum:,.2f}")
  789.     def on_group_select(self, event):
  790.         """当选择订单分类时的处理"""
  791.         selected = self.group_tree.selection()
  792.         if not selected:
  793.             return
  794.         
  795.         # 获取选中的单据编号
  796.         order_number = self.group_tree.item(selected[0])['values'][0]
  797.         
  798.         # 在主表格中查找并选中对应的记录
  799.         for item in self.tree.get_children():
  800.             if self.tree.item(item)['values'][1] == order_number:  # 假设单据编号是第二列
  801.                 self.tree.selection_set(item)
  802.                 self.tree.see(item)  # 确保选中的项可见
  803.                 break
  804.     def filter_orders(self):
  805.         """根据筛选条件过滤订单"""
  806.         order_number = self.order_number_filter.get().strip()
  807.         customer = self.customer_filter.get().strip()
  808.         
  809.         cursor = self.conn.cursor()
  810.         
  811.         # 构建查询条件
  812.         query = '''
  813.         SELECT order_number, customer, product, unit,
  814.                quantity, price, total, remarks,
  815.                SUM(total) OVER () as total_sum
  816.         FROM orders
  817.         WHERE 1=1
  818.         '''
  819.         params = []
  820.         
  821.         if order_number:
  822.             query += " AND order_number LIKE ?"
  823.             params.append(f"%{order_number}%")
  824.         
  825.         if customer:
  826.             query += " AND customer LIKE ?"
  827.             params.append(f"%{customer}%")
  828.         
  829.         query += " ORDER BY order_number DESC"
  830.         
  831.         cursor.execute(query, params)
  832.         
  833.         # 清空现有数据
  834.         self.group_tree.delete(*self.group_tree.get_children())
  835.         
  836.         total_sum = 0
  837.         # 插入新数据
  838.         for row in cursor.fetchall():
  839.             formatted_row = list(row[:8])  # 只取前8列显示
  840.             # 格式化数字列
  841.             formatted_row[4] = f"{row[4]:.2f}"  # 数量
  842.             formatted_row[5] = f"¥{row[5]:.2f}"  # 原价
  843.             formatted_row[6] = f"¥{row[6]:.2f}"  # 金额
  844.             self.group_tree.insert("", "end", values=formatted_row)
  845.             total_sum = row[8]  # 获取合计金额
  846.         
  847.         # 更新合计标签
  848.         self.total_label.config(text=f"合计金额: ¥{total_sum:,.2f}")
  849.     def reset_filter(self):
  850.         """重置筛选条件"""
  851.         self.order_number_filter.delete(0, tk.END)
  852.         self.customer_filter.delete(0, tk.END)
  853.         self.update_order_groups()
  854.     def update_order_number_list(self, event=None):
  855.         """更新单据编号下拉列表"""
  856.         search_text = self.order_number_filter.get().strip()
  857.         cursor = self.conn.cursor()
  858.         
  859.         if search_text:
  860.             cursor.execute('''
  861.             SELECT DISTINCT order_number FROM orders
  862.             WHERE order_number LIKE ?
  863.             ORDER BY order_number DESC
  864.             ''', [f'%{search_text}%'])
  865.         else:
  866.             cursor.execute('''
  867.             SELECT DISTINCT order_number FROM orders
  868.             ORDER BY order_number DESC
  869.             ''')
  870.         
  871.         order_numbers = [row[0] for row in cursor.fetchall()]
  872.         if order_numbers:
  873.             self.order_number_filter['values'] = order_numbers
  874.             if search_text:
  875.                 self.order_number_filter.event_generate('<Down>')
  876.     def update_customer_list(self, event=None):
  877.         """更新客户名称下拉列表"""
  878.         search_text = self.customer_filter.get().strip()
  879.         cursor = self.conn.cursor()
  880.         
  881.         if search_text:
  882.             cursor.execute('''
  883.             SELECT DISTINCT customer FROM orders
  884.             WHERE customer LIKE ?
  885.             ORDER BY customer
  886.             ''', [f'%{search_text}%'])
  887.         else:
  888.             cursor.execute('''
  889.             SELECT DISTINCT customer FROM orders
  890.             ORDER BY customer
  891.             ''')
  892.         
  893.         customers = [row[0] for row in cursor.fetchall()]
  894.         if customers:
  895.             self.customer_filter['values'] = customers
  896.             if search_text:
  897.                 self.customer_filter.event_generate('<Down>')
  898.     def update_filter_lists(self):
  899.         """更新所有筛选下拉列表"""
  900.         cursor = self.conn.cursor()
  901.         
  902.         # 更新单据编号列表
  903.         cursor.execute('SELECT DISTINCT order_number FROM orders ORDER BY order_number DESC')
  904.         self.order_number_filter['values'] = [row[0] for row in cursor.fetchall()]
  905.         
  906.         # 更新客户名称列表
  907.         cursor.execute('SELECT DISTINCT customer FROM orders ORDER BY customer')
  908.         self.customer_filter['values'] = [row[0] for row in cursor.fetchall()]
  909.     def print_filtered_data(self):
  910.         """打印筛选后的数据"""
  911.         try:
  912.             # 获取当前筛选条件下的数据
  913.             order_number = self.order_number_filter.get().strip()
  914.             customer = self.customer_filter.get().strip()
  915.             
  916.             # 构建查询条件
  917.             query = '''
  918.             SELECT order_number, customer, product, unit,
  919.                    quantity, price, total, remarks,
  920.                    SUM(total) OVER () as total_sum
  921.             FROM orders
  922.             WHERE 1=1
  923.             '''
  924.             params = []
  925.             
  926.             if order_number:
  927.                 query += " AND order_number LIKE ?"
  928.                 params.append(f"%{order_number}%")
  929.             
  930.             if customer:
  931.                 query += " AND customer LIKE ?"
  932.                 params.append(f"%{customer}%")
  933.             
  934.             query += " ORDER BY order_number DESC"
  935.             
  936.             cursor = self.conn.cursor()
  937.             cursor.execute(query, params)
  938.             rows = cursor.fetchall()
  939.             
  940.             if not rows:
  941.                 messagebox.showinfo("提示", "没有数据可打印")
  942.                 return
  943.             
  944.             # 生成HTML内容
  945.             html_content = f"""
  946.             <!DOCTYPE html>
  947.             <html>
  948.             <head>
  949.                 <meta charset="utf-8">
  950.                 <title>订单查询结果</title>
  951.                 <style>
  952.                     body {{ font-family: SimSun, serif; }}
  953.                     table {{ border-collapse: collapse; width: 100%; margin-top: 10px; }}
  954.                     th, td {{
  955.                         border: 1px solid black;
  956.                         padding: 8px;
  957.                         text-align: center;
  958.                     }}
  959.                     th {{ background-color: #f2f2f2; }}
  960.                     .total {{
  961.                         text-align: right;
  962.                         padding: 10px;
  963.                         font-weight: bold;
  964.                     }}
  965.                     .header-info {{
  966.                         margin: 10px 0;
  967.                         padding: 10px;
  968.                         border: 1px solid #ddd;
  969.                         background-color: #f9f9f9;
  970.                     }}
  971.                     .header-info p {{
  972.                         margin: 5px 0;
  973.                     }}
  974.                     @media print {{
  975.                         .no-print {{ display: none; }}
  976.                         body {{ margin: 0; }}
  977.                         table {{ page-break-inside: auto; }}
  978.                         tr {{ page-break-inside: avoid; }}
  979.                     }}
  980.                 </style>
  981.             </head>
  982.             <body>
  983.                 <h2 style="text-align: center;">订单查询结果</h2>
  984.                 <div class="header-info">
  985.                     <p><strong>单据编号:</strong>{order_number if order_number else "全部"}</p>
  986.                     <p><strong>客户名称:</strong>{customer if customer else "全部"}</p>
  987.                     <p><strong>打印时间:</strong>{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
  988.                 </div>
  989.                 <table>
  990.                     <tr>
  991.                         <th>品名规格</th>
  992.                         <th>单位</th>
  993.                         <th>数量</th>
  994.                         <th>原价</th>
  995.                         <th>金额</th>
  996.                         <th>备注</th>
  997.                     </tr>
  998.             """
  999.             
  1000.             # 添加数据行
  1001.             for row in rows:
  1002.                 html_content += f"""
  1003.                     <tr>
  1004.                         <td>{row[2]}</td>
  1005.                         <td>{row[3]}</td>
  1006.                         <td>{row[4]:.2f}</td>
  1007.                         <td>¥{row[5]:.2f}</td>
  1008.                         <td>¥{row[6]:.2f}</td>
  1009.                         <td>{row[7]}</td>
  1010.                     </tr>
  1011.                 """
  1012.             
  1013.             # 添加合计行
  1014.             total_sum = rows[0][8] if rows else 0
  1015.             html_content += f"""
  1016.                 </table>
  1017.                 <div class="total">
  1018.                     合计金额: ¥{total_sum:,.2f}
  1019.                 </div>
  1020.                 <div class="no-print" style="margin-top: 20px; text-align: center;">
  1021.                     <button onclick="window.print()">打印</button>
  1022.                 </div>
  1023.             </body>
  1024.             </html>
  1025.             """
  1026.             
  1027.             # 保存HTML文件
  1028.             temp_file = "订单查询结果.html"
  1029.             with open(temp_file, "w", encoding="utf-8") as f:
  1030.                 f.write(html_content)
  1031.             
  1032.             # 在默认浏览器中打开HTML文件
  1033.             import webbrowser
  1034.             webbrowser.open(temp_file)
  1035.             
  1036.         except Exception as e:
  1037.             messagebox.showerror("错误", f"打印过程中出错:{str(e)}")
  1038. if __name__ == "__main__":
  1039.     root = tk.Tk()
  1040.     app = OrderSystem(root)
  1041.     root.mainloop()
复制代码

 步伐简要阐明

   这是一个订单管理系统,紧张功能如下:
  1. 根本功能:
   - 订单录入和生存
   - 订单查询和筛选
   - 数据导入导出(Excel格式)
   - 打印功能(HTML格式)
  2. 界面布局:
   - 左侧:订单信息录入表单
   - 中间:订单数据列表
   - 右侧:订单查询面板(带筛选和统计)
  3. 紧张特点:
   - 自动天生票据编号(格式:XSD + 日期 + 3位序号)
   - 支持数字字段自动盘算(数量、单价、折扣等)
   - 提供下拉选择和手动输入的组合筛选
   - 实时显示筛选结果的合计金额
  4. 数据管理:
   - 使用SQLite数据库存储数据
   - 支持Excel导入导出
   - 提供数据验证和错误处理
  5. 使用方法:
   ```python
   if __name__ == "__main__":
       root = tk.Tk()
       app = OrderSystem(root)
       root.mainloop()
   ```
  6. 依赖库:
   - tkinter:GUI界面
   - sqlite3:数据库操作
   - pandas:Excel处理
   - datetime:日期处理
   - webbrowser:打印功能
  这个步伐适合小型企业或个人用于日常订单管理和记录。
 
  
1.1 1.1 版本



   添加以下功能:
  1. 步伐启动时需要输入密码才能进入  
  2. 默认密码为 "admin"  
  3. 可以通过界面修改登录密码  
  4. 数据库使用相同的密码举行加密保护
  5. 密码以哈希情势存储在 config.json 文件中  (放入 db目次里)
  使用阐明:
  1. 首次运行时,默认密码为 "admin"  
  2. 登录后可以点击"修改密码"按钮更改密码
  3. 新密码会同时用于步伐登录和数据库加密
  4. 假如忘记密码,需要删除 config.json 文件,步伐会重置为默认密码  
  注意事项:
  1. 修改密码后,请务必记住新密码,由于它同时用于步伐登录和数据库加密  
  2. 建议定期备份数据库文件(数据库也放在 db目次里)
  login.py

  1. import tkinter as tk
  2. from tkinter import ttk, messagebox
  3. import hashlib
  4. import json
  5. import os
  6. class LoginWindow:
  7.     def __init__(self):
  8.         self.window = tk.Tk()
  9.         self.window.title("登录")
  10.         self.window.geometry("300x150")
  11.         self.login_success = False  # 添加登录状态标志
  12.         
  13.         # 居中显示
  14.         self.window.update_idletasks()
  15.         width = self.window.winfo_width()
  16.         height = self.window.winfo_height()
  17.         x = (self.window.winfo_screenwidth() // 2) - (width // 2)
  18.         y = (self.window.winfo_screenheight() // 2) - (height // 2)
  19.         self.window.geometry(f"{width}x{height}+{x}+{y}")
  20.         
  21.         # 创建登录框架
  22.         frame = ttk.Frame(self.window, padding="20")
  23.         frame.pack(fill="both", expand=True)
  24.         
  25.         ttk.Label(frame, text="请输入密码:").pack(pady=5)
  26.         
  27.         self.password_var = tk.StringVar()
  28.         self.password_entry = ttk.Entry(frame, show="*", textvariable=self.password_var)
  29.         self.password_entry.pack(pady=5, fill="x")
  30.         
  31.         btn_frame = ttk.Frame(frame)
  32.         btn_frame.pack(pady=10)
  33.         
  34.         ttk.Button(btn_frame, text="登录", command=self.login).pack(side="left", padx=5)
  35.         ttk.Button(btn_frame, text="修改密码", command=self.change_password).pack(side="left", padx=5)
  36.         
  37.         self.password_entry.bind('<Return>', lambda e: self.login())
  38.         self.password_entry.focus()
  39.         
  40.         self.load_password()
  41.         
  42.     def load_password(self):
  43.         """加载保存的密码哈希"""
  44.         try:
  45.             if os.path.exists('db/config.json'):
  46.                 with open('db/config.json', 'r') as f:
  47.                     config = json.load(f)
  48.                     self.password_hash = config.get('password_hash', self.hash_password('admin'))
  49.             else:
  50.                 # 默认密码为 'admin'
  51.                 self.password_hash = self.hash_password('admin')
  52.                 self.save_password()
  53.         except Exception:
  54.             self.password_hash = self.hash_password('admin')
  55.             
  56.     def save_password(self):
  57.         """保存密码哈希到配置文件"""
  58.         config = {'password_hash': self.password_hash}
  59.         with open('db/config.json', 'w') as f:
  60.             json.dump(config, f)
  61.             
  62.     def hash_password(self, password):
  63.         """对密码进行哈希处理"""
  64.         return hashlib.sha256(password.encode()).hexdigest()
  65.         
  66.     def login(self):
  67.         """验证密码"""
  68.         if self.hash_password(self.password_var.get()) == self.password_hash:
  69.             self.login_success = True  # 设置登录成功标志
  70.             self.window.destroy()
  71.             return True
  72.         else:
  73.             messagebox.showerror("错误", "密码错误!")
  74.             self.password_var.set("")
  75.             return False
  76.             
  77.     def change_password(self):
  78.         """修改密码"""
  79.         change_window = tk.Toplevel(self.window)
  80.         change_window.title("修改密码")
  81.         change_window.geometry("300x400")
  82.         
  83.         # 居中显示
  84.         change_window.update_idletasks()
  85.         width = change_window.winfo_width()
  86.         height = change_window.winfo_height()
  87.         x = (change_window.winfo_screenwidth() // 2) - (width // 2)
  88.         y = (change_window.winfo_screenheight() // 2) - (height // 2)
  89.         change_window.geometry(f"{width}x{height}+{x}+{y}")
  90.         
  91.         frame = ttk.Frame(change_window, padding="20")
  92.         frame.pack(fill="both", expand=True)
  93.         
  94.         # 当前密码
  95.         ttk.Label(frame, text="当前密码:").pack(pady=5)
  96.         current_password = ttk.Entry(frame, show="*")
  97.         current_password.pack(pady=5, fill="x")
  98.         
  99.         # 新密码
  100.         ttk.Label(frame, text="新密码:").pack(pady=5)
  101.         new_password = ttk.Entry(frame, show="*")
  102.         new_password.pack(pady=5, fill="x")
  103.         
  104.         # 确认新密码
  105.         ttk.Label(frame, text="确认新密码:").pack(pady=5)
  106.         confirm_password = ttk.Entry(frame, show="*")
  107.         confirm_password.pack(pady=5, fill="x")
  108.         
  109.         def do_change():
  110.             if self.hash_password(current_password.get()) != self.password_hash:
  111.                 messagebox.showerror("错误", "当前密码错误!")
  112.                 return
  113.                
  114.             if new_password.get() != confirm_password.get():
  115.                 messagebox.showerror("错误", "两次输入的新密码不一致!")
  116.                 return
  117.                
  118.             if not new_password.get():
  119.                 messagebox.showerror("错误", "新密码不能为空!")
  120.                 return
  121.                
  122.             self.password_hash = self.hash_password(new_password.get())
  123.             self.save_password()
  124.             messagebox.showinfo("成功", "密码修改成功!")
  125.             change_window.destroy()
  126.             
  127.         ttk.Button(frame, text="确认修改", command=do_change).pack(pady=10)
  128.         
  129.     def run(self):
  130.         """运行登录窗口"""
  131.         self.window.protocol("WM_DELETE_WINDOW", self.on_closing)  # 添加窗口关闭事件处理
  132.         self.window.mainloop()
  133.         return self.login_success  # 返回登录状态
  134.         
  135.     def on_closing(self):
  136.         """窗口关闭事件处理"""
  137.         if messagebox.askokcancel("退出", "确定要退出程序吗?"):
  138.             self.login_success = False
  139.             self.window.destroy()
复制代码
 主步伐.py

  1. import tkinter as tk
  2. from tkinter import ttk
  3. import sqlite3
  4. from datetime import datetime
  5. from tkinter import messagebox, filedialog
  6. import pandas as pd
  7. import re
  8. import json
  9. import sys
  10. from login import LoginWindow
  11. class OrderSystem:
  12.     def __init__(self, root):
  13.         self.root = root
  14.         self.root.title("订单记录系统")
  15.         
  16.         # 创建数据库连接
  17.         self.db_password = self.get_db_password()  # 获取数据库密码
  18.         self.conn = self.create_db_connection()
  19.         self.create_table()
  20.         
  21.         # 创建界面
  22.         self.create_ui()
  23.         
  24.         # 添加搜索框架
  25.         self.create_search_frame()
  26.         
  27.         # 添加更多功能按钮
  28.         self.add_function_buttons()
  29.         
  30.         # 加载所有订单数据
  31.         self.load_all_orders()
  32.         
  33.         # 设置默认值
  34.         self.set_default_values()
  35.         
  36.     def get_db_password(self):
  37.         """从配置文件获取数据库密码"""
  38.         try:
  39.             with open('db/config.json', 'r') as f:
  40.                 config = json.load(f)
  41.                 return config.get('password_hash', '')
  42.         except Exception:
  43.             return ''
  44.     def create_db_connection(self):
  45.         """创建加密的数据库连接"""
  46.         try:
  47.             conn = sqlite3.connect('orders.db')
  48.             # 设置数据库密码
  49.             conn.execute(f"PRAGMA key = '{self.db_password}'")
  50.             return conn
  51.         except sqlite3.Error as e:
  52.             messagebox.showerror("错误", f"数据库连接失败:{str(e)}")
  53.             sys.exit(1)
  54.         
  55.     def create_table(self):
  56.         cursor = self.conn.cursor()
  57.         cursor.execute('''
  58.         CREATE TABLE IF NOT EXISTS orders (
  59.             order_date TEXT,
  60.             order_number TEXT,
  61.             customer TEXT,
  62.             product TEXT,
  63.             unit TEXT,
  64.             quantity REAL,
  65.             price REAL,
  66.             discount REAL,
  67.             final_price REAL,
  68.             total REAL,
  69.             remarks TEXT,
  70.             discount_amount REAL,
  71.             discount_total REAL,
  72.             delivery TEXT,
  73.             payment_received REAL,
  74.             end_customer TEXT,
  75.             notes TEXT,
  76.             business TEXT
  77.         )
  78.         ''')
  79.         self.conn.commit()
  80.         
  81.     def create_ui(self):
  82.         # 创建主框架来容纳左右两部分
  83.         main_frame = ttk.Frame(self.root)
  84.         main_frame.pack(fill="both", expand=True)
  85.         
  86.         # 创建左侧框架
  87.         left_frame = ttk.Frame(main_frame)
  88.         left_frame.pack(side="left", fill="both", expand=True)
  89.         
  90.         # 创建右侧框架
  91.         right_frame = ttk.Frame(main_frame)
  92.         right_frame.pack(side="right", fill="y", padx=5)
  93.         
  94.         # 创建输入框架(放在左侧)
  95.         input_frame = ttk.LabelFrame(left_frame, text="订单信息")
  96.         input_frame.pack(padx=5, pady=5, fill="x")
  97.         
  98.         # 修改订单分类框架
  99.         order_group_frame = ttk.LabelFrame(right_frame, text="订单查询")
  100.         order_group_frame.pack(padx=5, pady=5, fill="both", expand=True)
  101.         
  102.         # 添加筛选框
  103.         filter_frame = ttk.Frame(order_group_frame)
  104.         filter_frame.pack(fill="x", padx=5, pady=5)
  105.         
  106.         # 单据编号筛选
  107.         ttk.Label(filter_frame, text="单据编号:").grid(row=0, column=0, padx=5)
  108.         self.order_number_filter = ttk.Combobox(filter_frame, width=15)
  109.         self.order_number_filter.grid(row=0, column=1, padx=5)
  110.         
  111.         # 客户名称筛选
  112.         ttk.Label(filter_frame, text="客户名称:").grid(row=0, column=2, padx=5)
  113.         self.customer_filter = ttk.Combobox(filter_frame, width=15)
  114.         self.customer_filter.grid(row=0, column=3, padx=5)
  115.         
  116.         # 筛选按钮
  117.         ttk.Button(filter_frame, text="筛选", command=self.filter_orders).grid(row=0, column=4, padx=5)
  118.         ttk.Button(filter_frame, text="重置", command=self.reset_filter).grid(row=0, column=5, padx=5)
  119.         ttk.Button(filter_frame, text="打印", command=self.print_filtered_data).grid(row=0, column=6, padx=5)
  120.         
  121.         # 绑定下拉框事件
  122.         self.order_number_filter.bind('<KeyRelease>', self.update_order_number_list)
  123.         self.customer_filter.bind('<KeyRelease>', self.update_customer_list)
  124.         
  125.         # 修改订单分类的树形视图列
  126.         self.group_tree = ttk.Treeview(order_group_frame, columns=[
  127.             "order_number", "customer", "product", "unit",
  128.             "quantity", "price", "total", "remarks"
  129.         ], show="headings", height=15)
  130.         
  131.         # 设置列标题和宽度
  132.         columns = [
  133.             ("order_number", "单据编号", 100),
  134.             ("customer", "客户名称", 100),
  135.             ("product", "品名规格", 120),
  136.             ("unit", "单位", 50),
  137.             ("quantity", "数量", 60),
  138.             ("price", "原价", 80),
  139.             ("total", "金额", 80),
  140.             ("remarks", "备注", 100)
  141.         ]
  142.         
  143.         for col, heading, width in columns:
  144.             self.group_tree.heading(col, text=heading)
  145.             self.group_tree.column(col, width=width)
  146.         
  147.         self.group_tree.pack(padx=5, pady=5, fill="both", expand=True)
  148.         
  149.         # 添加滚动条
  150.         group_scrollbar = ttk.Scrollbar(order_group_frame, orient="vertical", command=self.group_tree.yview)
  151.         group_scrollbar.pack(side="right", fill="y")
  152.         self.group_tree.configure(yscrollcommand=group_scrollbar.set)
  153.         
  154.         # 绑定点击事件
  155.         self.group_tree.bind('<<TreeviewSelect>>', self.on_group_select)
  156.         
  157.         # 修改输入字段列表,确保与数据库字段完全匹配
  158.         self.entries = {}
  159.         fields = [
  160.             ("order_date", "单据日期"),
  161.             ("order_number", "单据编号"),
  162.             ("customer", "客户名称"),
  163.             ("product", "品名规格"),
  164.             ("unit", "单位"),
  165.             ("quantity", "数量"),
  166.             ("price", "原价"),
  167.             ("discount", "单行折扣率(%)"),
  168.             ("final_price", "折后价"),
  169.             ("total", "金额"),
  170.             ("remarks", "备注"),
  171.             ("discount_amount", "整单折扣率(%)"),
  172.             ("discount_total", "折后金额"),
  173.             ("delivery", "运费"),
  174.             ("payment_received", "本单已收"),
  175.             ("end_customer", "结算账户"),
  176.             ("notes", "说明"),
  177.             ("business", "营业员")
  178.         ]
  179.         
  180.         for row, (field, label) in enumerate(fields):
  181.             ttk.Label(input_frame, text=label).grid(row=row//2, column=(row%2)*2, padx=5, pady=2)
  182.             self.entries[field] = ttk.Entry(input_frame)
  183.             self.entries[field].grid(row=row//2, column=(row%2)*2+1, padx=5, pady=2, sticky="ew")
  184.             
  185.         # 添加按钮
  186.         self.btn_frame = ttk.Frame(self.root)
  187.         self.btn_frame.pack(pady=5)
  188.         
  189.         ttk.Button(self.btn_frame, text="保存", command=self.save_order).pack(side="left", padx=5)
  190.         ttk.Button(self.btn_frame, text="清空", command=self.clear_fields).pack(side="left", padx=5)
  191.         ttk.Button(self.btn_frame, text="导入Excel", command=self.import_from_excel).pack(side="left", padx=5)
  192.         ttk.Button(self.btn_frame, text="导出模板", command=self.export_template).pack(side="left", padx=5)
  193.         
  194.         # 修改表格显示,显示所有列
  195.         self.tree = ttk.Treeview(self.root, columns=[
  196.             "order_date", "order_number", "customer", "product", "unit",
  197.             "quantity", "price", "discount", "final_price", "total",
  198.             "remarks", "discount_amount", "discount_total", "delivery",
  199.             "payment_received", "end_customer", "notes", "business"
  200.         ], show="headings")
  201.         
  202.         # 修改列标题定义,显示所有列
  203.         columns = [
  204.             ("order_date", "单据日期"),
  205.             ("order_number", "单据编号"),
  206.             ("customer", "客户名称"),
  207.             ("product", "品名规格"),
  208.             ("unit", "单位"),
  209.             ("quantity", "数量"),
  210.             ("price", "原价"),
  211.             ("discount", "单行折扣率(%)"),
  212.             ("final_price", "折后价"),
  213.             ("total", "金额"),
  214.             ("remarks", "备注"),
  215.             ("discount_amount", "整单折扣率(%)"),
  216.             ("discount_total", "折后金额"),
  217.             ("delivery", "运费"),
  218.             ("payment_received", "本单已收"),
  219.             ("end_customer", "结算账户"),
  220.             ("notes", "说明"),
  221.             ("business", "营业员")
  222.         ]
  223.         
  224.         for col, heading in columns:
  225.             self.tree.heading(col, text=heading)
  226.             self.tree.column(col, width=100)
  227.             
  228.         self.tree.pack(padx=5, pady=5, fill="both", expand=True)
  229.         
  230.         # 添加滚动条
  231.         scrollbar = ttk.Scrollbar(self.root, orient="vertical", command=self.tree.yview)
  232.         scrollbar.pack(side="right", fill="y")
  233.         self.tree.configure(yscrollcommand=scrollbar.set)
  234.         
  235.         # 添加自动计算绑定
  236.         self.entries['quantity'].bind('<KeyRelease>', self.calculate_total)
  237.         self.entries['price'].bind('<KeyRelease>', self.calculate_total)
  238.         self.entries['discount'].bind('<KeyRelease>', self.calculate_total)
  239.         
  240.         # 在订单分类框架底部添加合计标签
  241.         self.total_label = ttk.Label(order_group_frame, text="合计金额: ¥0.00")
  242.         self.total_label.pack(pady=5)
  243.     def calculate_total(self, event=None):
  244.         """计算折后价和金额"""
  245.         try:
  246.             quantity = float(self.entries['quantity'].get() or 0)
  247.             price = float(self.entries['price'].get() or 0)
  248.             discount = float(self.entries['discount'].get() or 100)
  249.             
  250.             # 计算折后价
  251.             final_price = price * discount / 100
  252.             self.entries['final_price'].delete(0, tk.END)
  253.             self.entries['final_price'].insert(0, f"{final_price:.2f}")
  254.             
  255.             # 计算金额
  256.             total = quantity * final_price
  257.             self.entries['total'].delete(0, tk.END)
  258.             self.entries['total'].insert(0, f"{total:.2f}")
  259.         except ValueError:
  260.             pass
  261.     def create_search_frame(self):
  262.         search_frame = ttk.LabelFrame(self.root, text="搜索")
  263.         search_frame.pack(padx=5, pady=5, fill="x")
  264.         
  265.         ttk.Label(search_frame, text="搜索条件:").pack(side="left", padx=5)
  266.         self.search_entry = ttk.Entry(search_frame)
  267.         self.search_entry.pack(side="left", padx=5, fill="x", expand=True)
  268.         
  269.         ttk.Button(search_frame, text="搜索", command=self.search_orders).pack(side="left", padx=5)
  270.         
  271.     def add_function_buttons(self):
  272.         # 在原有btn_frame中添加更多按钮
  273.         ttk.Button(self.btn_frame, text="编辑", command=self.edit_selected).pack(side="left", padx=5)
  274.         ttk.Button(self.btn_frame, text="删除", command=self.delete_selected).pack(side="left", padx=5)
  275.         ttk.Button(self.btn_frame, text="导出Excel", command=self.export_to_excel).pack(side="left", padx=5)
  276.         ttk.Button(self.btn_frame, text="统计报表", command=self.show_statistics).pack(side="left", padx=5)
  277.     def validate_data(self):
  278.         """数据验证"""
  279.         errors = []
  280.         
  281.         # 验证日期格式
  282.         date = self.entries['order_date'].get().strip()
  283.         if not date:
  284.             errors.append("单据日期不能空")
  285.         elif not re.match(r'^\d{4}-\d{2}-\d{2}$', date):
  286.             errors.append("单据日期格式错误,应为 YYYY-MM-DD")
  287.         
  288.         # 验证必填字段
  289.         required_fields = {
  290.             'order_number': '单据编号',
  291.             'customer': '客户名称',
  292.             'product': '品名规格',
  293.             'unit': '单位',
  294.             'quantity': '数量',
  295.             'price': '原价'
  296.         }
  297.         
  298.         for field, name in required_fields.items():
  299.             value = self.entries[field].get().strip()
  300.             if not value:
  301.                 errors.append(f"{name}不能为空")
  302.         
  303.         # 验证数字字段
  304.         number_fields = {
  305.             'quantity': '数量',
  306.             'price': '原价',
  307.             'discount': '单行折扣率(%)',
  308.             'final_price': '折后价',
  309.             'total': '金额',
  310.             'discount_amount': '整单折扣率(%)',
  311.             'discount_total': '折后金额',
  312.             'payment_received': '本单已收'
  313.         }
  314.         
  315.         for field, name in number_fields.items():
  316.             value = self.entries[field].get().strip()
  317.             if value:  # 如果有值才验证
  318.                 try:
  319.                     num = float(value)
  320.                     if field in ['quantity', 'price'] and num <= 0:
  321.                         errors.append(f"{name}必须大于0")
  322.                     elif num < 0:
  323.                         errors.append(f"{name}不能为负数")
  324.                 except ValueError:
  325.                     errors.append(f"{name}必须是数字")
  326.         
  327.         if errors:
  328.             messagebox.showerror("验证错误", "\n".join(errors))
  329.             return False
  330.         return True
  331.     def save_order(self):
  332.         """保存订单数据"""
  333.         if not self.validate_data():
  334.             return
  335.         
  336.         try:
  337.             # 获取所有输入值
  338.             values = []
  339.             fields_order = [
  340.                 'order_date', 'order_number', 'customer', 'product', 'unit',
  341.                 'quantity', 'price', 'discount', 'final_price', 'total',
  342.                 'remarks', 'discount_amount', 'discount_total', 'delivery',
  343.                 'payment_received', 'end_customer', 'notes', 'business'
  344.             ]
  345.             
  346.             for field in fields_order:
  347.                 value = self.entries[field].get().strip()
  348.                
  349.                 # 对数字字段进行转换
  350.                 if field in ['quantity', 'price', 'discount', 'final_price', 'total',
  351.                             'discount_amount', 'discount_total', 'payment_received']:
  352.                     try:
  353.                         value = float(value) if value else 0.0
  354.                     except ValueError:
  355.                         value = 0.0
  356.                 elif not value:  # 对非数字字段,如果为空则设为空字��串
  357.                     value = ''
  358.                
  359.                 values.append(value)
  360.             
  361.             # 检查单据编号是否重复
  362.             cursor = self.conn.cursor()
  363.             cursor.execute('SELECT COUNT(*) FROM orders WHERE order_number = ?', (values[1],))
  364.             if cursor.fetchone()[0] > 0:
  365.                 if not messagebox.askyesno("警告", "单据编号已存在是否继续保存?"):
  366.                     return
  367.             
  368.             # 插入数据
  369.             try:
  370.                 cursor.execute('''
  371.                 INSERT INTO orders VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
  372.                 ''', values)
  373.                 self.conn.commit()
  374.                
  375.                 # 更新表格显示
  376.                 self.tree.insert("", "end", values=values)
  377.                
  378.                 # 清空输入框并设置默认值
  379.                 self.set_default_values()
  380.                
  381.                 # 显示成功消息
  382.                 messagebox.showinfo("成功", "订单保存成功!")
  383.                
  384.             except sqlite3.Error as e:
  385.                 self.conn.rollback()
  386.                 messagebox.showerror("数据库错误", f"保存失败:{str(e)}")
  387.                 return
  388.                
  389.         except Exception as e:
  390.             messagebox.showerror("错误", f"保存过程中出错:{str(e)}")
  391.             return
  392.         
  393.         self.update_order_groups()
  394.     def clear_fields(self):
  395.         """清空所有输入框"""
  396.         for field in self.entries:
  397.             self.entries[field].delete(0, tk.END)
  398.     def __del__(self):
  399.         self.conn.close()
  400.     def search_orders(self):
  401.         search_text = self.search_entry.get().strip()
  402.         if not search_text:
  403.             self.load_all_orders()
  404.             return
  405.             
  406.         cursor = self.conn.cursor()
  407.         cursor.execute('''
  408.         SELECT * FROM orders
  409.         WHERE order_date LIKE ? OR order_number LIKE ? OR customer LIKE ? OR product LIKE ?
  410.         ''', [f'%{search_text}%'] * 4)
  411.         
  412.         self.tree.delete(*self.tree.get_children())
  413.         for row in cursor.fetchall():
  414.             self.tree.insert("", "end", values=row)
  415.     def edit_selected(self):
  416.         selected = self.tree.selection()
  417.         if not selected:
  418.             messagebox.showwarning("提示", "请先选择一条记录")
  419.             return
  420.             
  421.         item = self.tree.item(selected[0])
  422.         values = item['values']
  423.         
  424.         # 填充表单
  425.         for field, value in zip(self.entries.keys(), values):
  426.             self.entries[field].delete(0, tk.END)
  427.             self.entries[field].insert(0, str(value))
  428.     def delete_selected(self):
  429.         selected = self.tree.selection()
  430.         if not selected:
  431.             messagebox.showwarning("提示", "请先选择一条记录")
  432.             return
  433.             
  434.         if messagebox.askyesno("确认", "确定要删除中的记录吗?"):
  435.             item = self.tree.item(selected[0])
  436.             order_number = item['values'][1]
  437.             
  438.             cursor = self.conn.cursor()
  439.             cursor.execute('DELETE FROM orders WHERE order_number = ?', (order_number,))
  440.             self.conn.commit()
  441.             
  442.             self.tree.delete(selected[0])
  443.         
  444.         self.update_order_groups()
  445.     def export_to_excel(self):
  446.         """导出数据到Excel"""
  447.         try:
  448.             # 先获取保存路径
  449.             filename = filedialog.asksaveasfilename(
  450.                 defaultextension=".xlsx",
  451.                 filetypes=[("Excel files", "*.xlsx")]
  452.             )
  453.             if not filename:
  454.                 return
  455.             
  456.             # 获取数据
  457.             cursor = self.conn.cursor()
  458.             cursor.execute('SELECT * FROM orders')
  459.             data = cursor.fetchall()
  460.             
  461.             # 准备列名
  462.             columns = [
  463.                 '单据日期', '单据编号', '客户名称', '品名规格',
  464.                 '单位', '数量', '原价', '单行折扣率(%)', '折后价',
  465.                 '金额', '备注', '整单折扣率(%)', '折后金额', '运费',
  466.                 '本单已收', '结算账户', '说明', '营业员'
  467.             ]
  468.             
  469.             # 创建DataFrame
  470.             df = pd.DataFrame(data, columns=columns)
  471.             
  472.             # 直接导出
  473.             df.to_excel(filename, index=False)
  474.             messagebox.showinfo("成功", "数据已导出到Excel文件")
  475.             
  476.         except PermissionError:
  477.             messagebox.showerror("错误", "无法保存文件,请确保:\n1. 文件未被其他程序打开\n2. 您有写入权限")
  478.         except Exception as e:
  479.             messagebox.showerror("错误", f"导出过程中出错:{str(e)}")
  480.     def show_statistics(self):
  481.         stats_window = tk.Toplevel(self.root)
  482.         stats_window.title("统计报表")
  483.         
  484.         cursor = self.conn.cursor()
  485.         
  486.         # 客户统计
  487.         cursor.execute('''
  488.         SELECT customer,
  489.                COUNT(*) as order_count,
  490.                SUM(total) as total_amount,
  491.                SUM(payment_received) as total_received
  492.         FROM orders
  493.         GROUP BY customer
  494.         ''')
  495.         
  496.         # 创建统计表格
  497.         tree = ttk.Treeview(stats_window, columns=["customer", "count", "amount", "received"], show="headings")
  498.         tree.heading("customer", text="客户")
  499.         tree.heading("count", text="订单数")
  500.         tree.heading("amount", text="总金额")
  501.         tree.heading("received", text="已收金额")
  502.         
  503.         for row in cursor.fetchall():
  504.             tree.insert("", "end", values=row)
  505.             
  506.         tree.pack(padx=5, pady=5, fill="both", expand=True)
  507.     def load_all_orders(self):
  508.         """加载所有订单到表格"""
  509.         cursor = self.conn.cursor()
  510.         cursor.execute('SELECT * FROM orders')
  511.         
  512.         self.tree.delete(*self.tree.get_children())
  513.         for row in cursor.fetchall():
  514.             self.tree.insert("", "end", values=row)
  515.         
  516.         # 更新筛选下拉列表
  517.         self.update_filter_lists()
  518.         self.update_order_groups()
  519.     def import_from_excel(self):
  520.         """从Excel文件导入数据"""
  521.         filename = filedialog.askopenfilename(
  522.             filetypes=[("Excel files", "*.xlsx"), ("All files", "*.*")]
  523.         )
  524.         if not filename:
  525.             return
  526.         
  527.         try:
  528.             # 读取Excel文件
  529.             df = pd.read_excel(filename)
  530.             
  531.             # 数字字段列表
  532.             numeric_columns = [
  533.                 '数量', '原价', '单行折扣率(%)', '折后价', '金额',
  534.                 '整单折扣率(%)', '折后金额', '运费', '本单已收'
  535.             ]
  536.             
  537.             # 转换数字列的数据类型
  538.             for col in numeric_columns:
  539.                 if col in df.columns:
  540.                     df[col] = pd.to_numeric(df[col], errors='coerce').fillna(0)
  541.             
  542.             # 检查必需的列是否存在
  543.             required_columns = [
  544.                 '单据日期', '单据编号', '客户名称', '品名规格', '单位',
  545.                 '数量', '原价', '单行折扣率(%)', '折后价', '金额',
  546.                 '备注', '整单折扣率(%)', '折后金额', '运费',
  547.                 '本单已收', '结算账户', '说明', '营业员'
  548.             ]
  549.             
  550.             missing_columns = [col for col in required_columns if col not in df.columns]
  551.             if missing_columns:
  552.                 messagebox.showerror("错误", f"Excel文件缺少以下列:\n{', '.join(missing_columns)}")
  553.                 return
  554.             
  555.             # 创建预览窗口
  556.             preview_window = tk.Toplevel(self.root)
  557.             preview_window.title("导入数据预览")
  558.             preview_window.geometry("800x600")
  559.             
  560.             # 创建预览表格
  561.             preview_tree = ttk.Treeview(preview_window, columns=required_columns[:10], show="headings")
  562.             
  563.             # 设置列标题
  564.             for col in required_columns[:10]:
  565.                 preview_tree.heading(col, text=col)
  566.                 preview_tree.column(col, width=100)
  567.             
  568.             # 添加数据到预览表格
  569.             for _, row in df.iterrows():
  570.                 values = [row[col] for col in required_columns[:10]]
  571.                 preview_tree.insert("", "end", values=values)
  572.             
  573.             # 添加滚动条
  574.             scrollbar = ttk.Scrollbar(preview_window, orient="vertical", command=preview_tree.yview)
  575.             scrollbar.pack(side="right", fill="y")
  576.             preview_tree.configure(yscrollcommand=scrollbar.set)
  577.             preview_tree.pack(padx=5, pady=5, fill="both", expand=True)
  578.             
  579.             # 添加按钮框
  580.             btn_frame = ttk.Frame(preview_window)
  581.             btn_frame.pack(pady=5)
  582.             
  583.             def confirm_import():
  584.                 try:
  585.                     # 将数据���入数据库
  586.                     cursor = self.conn.cursor()
  587.                     
  588.                     # 定义字段映射
  589.                     field_mapping = {
  590.                         '单据日期': 'order_date',
  591.                         '单据编号': 'order_number',
  592.                         '客户名称': 'customer',
  593.                         '品名规格': 'product',
  594.                         '单位': 'unit',
  595.                         '数量': 'quantity',
  596.                         '原价': 'price',
  597.                         '单行折扣率(%)': 'discount',
  598.                         '折后价': 'final_price',
  599.                         '金额': 'total',
  600.                         '备注': 'remarks',
  601.                         '整单折扣率(%)': 'discount_amount',
  602.                         '折后金额': 'discount_total',
  603.                         '运费': 'delivery',
  604.                         '本单已收': 'payment_received',
  605.                         '结算账户': 'end_customer',
  606.                         '说明': 'notes',
  607.                         '营业员': 'business'
  608.                     }
  609.                     
  610.                     # 获取数据字段顺序
  611.                     db_fields = [
  612.                         'order_date', 'order_number', 'customer', 'product', 'unit',
  613.                         'quantity', 'price', 'discount', 'final_price', 'total',
  614.                         'remarks', 'discount_amount', 'discount_total', 'delivery',
  615.                         'payment_received', 'end_customer', 'notes', 'business'
  616.                     ]
  617.                     
  618.                     for _, row in df.iterrows():
  619.                         values = []
  620.                         for field in db_fields:
  621.                             # 从Excel列名映射到数据库字段
  622.                             excel_col = [k for k, v in field_mapping.items() if v == field][0]
  623.                             value = row[excel_col]
  624.                            
  625.                             # 处理数值
  626.                             if pd.isna(value):
  627.                                 value = 0 if field in ['quantity', 'price', 'discount', 'final_price',
  628.                                                      'total', 'discount_amount', 'discount_total',
  629.                                                      'payment_received'] else ''
  630.                             elif isinstance(value, (int, float)):
  631.                                 value = float(value)
  632.                             else:
  633.                                 value = str(value)
  634.                             values.append(value)
  635.                         
  636.                         try:
  637.                             cursor.execute('''
  638.                             INSERT INTO orders VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
  639.                             ''', values)
  640.                         except sqlite3.Error as e:
  641.                             self.conn.rollback()
  642.                             messagebox.showerror("错误", f"插入数据时出错:{str(e)}\n行数据{values}")
  643.                             return
  644.                     
  645.                     self.conn.commit()
  646.                     self.load_all_orders()
  647.                     messagebox.showinfo("成功", "数据导入成功!")
  648.                     preview_window.destroy()
  649.                     
  650.                 except Exception as e:
  651.                     self.conn.rollback()
  652.                     messagebox.showerror("错误", f"导入过程中出错:{str(e)}")
  653.             
  654.             def cancel_import():
  655.                 preview_window.destroy()
  656.             
  657.             # 添加确认和取消按钮
  658.             ttk.Button(btn_frame, text="确认导入", command=confirm_import).pack(side="left", padx=5)
  659.             ttk.Button(btn_frame, text="取消", command=cancel_import).pack(side="left", padx=5)
  660.             
  661.             # 显示导入数据的总数
  662.             ttk.Label(preview_window, text=f"共 {len(df)} 条数据").pack(pady=5)
  663.             
  664.         except Exception as e:
  665.             messagebox.showerror("错误", f"导入过程中出错:{str(e)}")
  666.         
  667.         self.update_order_groups()
  668.     def export_template(self):
  669.         """导出Excel模板"""
  670.         filename = filedialog.asksaveasfilename(
  671.             defaultextension=".xlsx",
  672.             filetypes=[("Excel files", "*.xlsx")],
  673.             initialfile="订单导出模板.xlsx"
  674.         )
  675.         if not filename:
  676.             return
  677.         
  678.         try:
  679.             # 创建示例数据 - 使用相同的列名
  680.             sample_data = {
  681.                 '单据日期': ['2024-01-01'],
  682.                 '单据编号': ['XSD202401001'],
  683.                 '客户名称': ['示例客户'],
  684.                 '品名规格': ['示例产品'],
  685.                 '单位': ['个'],
  686.                 '数量': [1],
  687.                 '原价': [100],
  688.                 '单行折扣率(%)': [100],
  689.                 '折后价': [100],
  690.                 '金额': [100],
  691.                 '备注': ['备注示例'],
  692.                 '整单折扣率(%)': [0],
  693.                 '折后金额': [100],
  694.                 '运费': [0],
  695.                 '本单已收': [0],
  696.                 '结算账户': ['结算账户示例'],
  697.                 '说明': ['说明示例'],
  698.                 '营业员': ['营业员示例']
  699.             }
  700.             
  701.             # 创建DataFrame
  702.             df = pd.DataFrame(sample_data)
  703.             
  704.             # 创建Excel写入器
  705.             with pd.ExcelWriter(filename, engine='openpyxl') as writer:
  706.                 # 写入数据
  707.                 df.to_excel(writer, index=False, sheet_name='订单数据')
  708.                
  709.                 # 获取工作表
  710.                 worksheet = writer.sheets['订单数据']
  711.                
  712.                 # 设置列宽
  713.                 for column in worksheet.columns:
  714.                     max_length = 0
  715.                     column = [cell for cell in column]
  716.                     for cell in column:
  717.                         try:
  718.                             if len(str(cell.value)) > max_length:
  719.                                 max_length = len(str(cell.value))
  720.                         except:
  721.                             pass
  722.                     adjusted_width = (max_length + 2)
  723.                     worksheet.column_dimensions[column[0].column_letter].width = adjusted_width
  724.                
  725.                 # 设置样式
  726.                 from openpyxl.styles import PatternFill, Font, Alignment, Border, Side
  727.                
  728.                 # 定义样式
  729.                 header_fill = PatternFill(start_color='CCE5FF', end_color='CCE5FF', fill_type='solid')
  730.                 header_font = Font(bold=True)
  731.                 center_aligned = Alignment(horizontal='center', vertical='center')
  732.                 border = Border(
  733.                     left=Side(style='thin'),
  734.                     right=Side(style='thin'),
  735.                     top=Side(style='thin'),
  736.                     bottom=Side(style='thin')
  737.                 )
  738.                
  739.                 # 应用表头样式
  740.                 for cell in worksheet[1]:
  741.                     cell.fill = header_fill
  742.                     cell.font = header_font
  743.                     cell.alignment = center_aligned
  744.                     cell.border = border
  745.                
  746.                 # 应用数据行样式
  747.                 for row in worksheet.iter_rows(min_row=2):
  748.                     for cell in row:
  749.                         cell.alignment = center_aligned
  750.                         cell.border = border
  751.             
  752.             messagebox.showinfo("成功", "模板导出成功!\n请按照模板格式准备数据后再进行导。")
  753.             
  754.         except Exception as e:
  755.             messagebox.showerror("错误", f"导出模板时出错:{str(e)}")
  756.     def set_default_values(self):
  757.         """设置默认值"""
  758.         # 清空所有输入框
  759.         self.clear_fields()
  760.         
  761.         # 只设置日期和折扣率的默认值
  762.         today = datetime.now().strftime('%Y-%m-%d')
  763.         self.entries['order_date'].insert(0, today)  # 默认日期为今天
  764.         self.entries['discount'].insert(0, '100')    # 默认折扣率为100%
  765.         
  766.         # 生成新的单据编号
  767.         cursor = self.conn.cursor()
  768.         cursor.execute('''
  769.         SELECT MAX(order_number) FROM orders
  770.         WHERE order_number LIKE ?
  771.         ''', [f'XSD{today.replace("-", "")}%'])
  772.         
  773.         last_number = cursor.fetchone()[0]
  774.         if last_number:
  775.             try:
  776.                 # 从最后一个单号提取序号并加1
  777.                 seq = int(last_number[-3:]) + 1
  778.                 new_number = f'XSD{today.replace("-", "")}{seq:03d}'
  779.             except ValueError:
  780.                 new_number = f'XSD{today.replace("-", "")}001'
  781.         else:
  782.             new_number = f'XSD{today.replace("-", "")}001'
  783.         
  784.         self.entries['order_number'].insert(0, new_number)  # 设置新单据编号
  785.     def update_order_groups(self):
  786.         """更新订单分类显示"""
  787.         cursor = self.conn.cursor()
  788.         cursor.execute('''
  789.         SELECT order_number, customer, product, unit,
  790.                quantity, price, total, remarks,
  791.                SUM(total) OVER () as total_sum
  792.         FROM orders
  793.         ORDER BY order_number DESC
  794.         ''')
  795.         
  796.         # 清空现有数据
  797.         self.group_tree.delete(*self.group_tree.get_children())
  798.         
  799.         total_sum = 0
  800.         # 插入新数据
  801.         for row in cursor.fetchall():
  802.             formatted_row = list(row[:8])  # 只取前8列显示
  803.             # 格式化数字列
  804.             formatted_row[4] = f"{row[4]:.2f}"  # 数量
  805.             formatted_row[5] = f"¥{row[5]:.2f}"  # 原价
  806.             formatted_row[6] = f"¥{row[6]:.2f}"  # 金额
  807.             self.group_tree.insert("", "end", values=formatted_row)
  808.             total_sum = row[8]  # 获取合计金额
  809.         
  810.         # 更新合计标签
  811.         self.total_label.config(text=f"合计金额: ¥{total_sum:,.2f}")
  812.     def on_group_select(self, event):
  813.         """当选择订单分类时的处理"""
  814.         selected = self.group_tree.selection()
  815.         if not selected:
  816.             return
  817.         
  818.         # 获取选中的单据编号
  819.         order_number = self.group_tree.item(selected[0])['values'][0]
  820.         
  821.         # 在主表格中查找并选中对应的记录
  822.         for item in self.tree.get_children():
  823.             if self.tree.item(item)['values'][1] == order_number:  # 假设单据编号是第二列
  824.                 self.tree.selection_set(item)
  825.                 self.tree.see(item)  # 确保选中的项可见
  826.                 break
  827.     def filter_orders(self):
  828.         """根据筛选条件过滤订单"""
  829.         order_number = self.order_number_filter.get().strip()
  830.         customer = self.customer_filter.get().strip()
  831.         
  832.         cursor = self.conn.cursor()
  833.         
  834.         # 构建查询条件
  835.         query = '''
  836.         SELECT order_number, customer, product, unit,
  837.                quantity, price, total, remarks,
  838.                SUM(total) OVER () as total_sum
  839.         FROM orders
  840.         WHERE 1=1
  841.         '''
  842.         params = []
  843.         
  844.         if order_number:
  845.             query += " AND order_number LIKE ?"
  846.             params.append(f"%{order_number}%")
  847.         
  848.         if customer:
  849.             query += " AND customer LIKE ?"
  850.             params.append(f"%{customer}%")
  851.         
  852.         query += " ORDER BY order_number DESC"
  853.         
  854.         cursor.execute(query, params)
  855.         
  856.         # 清空现有数据
  857.         self.group_tree.delete(*self.group_tree.get_children())
  858.         
  859.         total_sum = 0
  860.         # 插入新数据
  861.         for row in cursor.fetchall():
  862.             formatted_row = list(row[:8])  # 只取前8列显示
  863.             # 格式化数字列
  864.             formatted_row[4] = f"{row[4]:.2f}"  # 数量
  865.             formatted_row[5] = f"¥{row[5]:.2f}"  # 原价
  866.             formatted_row[6] = f"¥{row[6]:.2f}"  # 金额
  867.             self.group_tree.insert("", "end", values=formatted_row)
  868.             total_sum = row[8]  # 获取合计金额
  869.         
  870.         # 更新合计标签
  871.         self.total_label.config(text=f"合计金额: ¥{total_sum:,.2f}")
  872.     def reset_filter(self):
  873.         """重置筛选条件"""
  874.         self.order_number_filter.delete(0, tk.END)
  875.         self.customer_filter.delete(0, tk.END)
  876.         self.update_order_groups()
  877.     def update_order_number_list(self, event=None):
  878.         """更新单据编号下拉列表"""
  879.         search_text = self.order_number_filter.get().strip()
  880.         cursor = self.conn.cursor()
  881.         
  882.         if search_text:
  883.             cursor.execute('''
  884.             SELECT DISTINCT order_number FROM orders
  885.             WHERE order_number LIKE ?
  886.             ORDER BY order_number DESC
  887.             ''', [f'%{search_text}%'])
  888.         else:
  889.             cursor.execute('''
  890.             SELECT DISTINCT order_number FROM orders
  891.             ORDER BY order_number DESC
  892.             ''')
  893.         
  894.         order_numbers = [row[0] for row in cursor.fetchall()]
  895.         if order_numbers:
  896.             self.order_number_filter['values'] = order_numbers
  897.             if search_text:
  898.                 self.order_number_filter.event_generate('<Down>')
  899.     def update_customer_list(self, event=None):
  900.         """更新客户名称下拉列表"""
  901.         search_text = self.customer_filter.get().strip()
  902.         cursor = self.conn.cursor()
  903.         
  904.         if search_text:
  905.             cursor.execute('''
  906.             SELECT DISTINCT customer FROM orders
  907.             WHERE customer LIKE ?
  908.             ORDER BY customer
  909.             ''', [f'%{search_text}%'])
  910.         else:
  911.             cursor.execute('''
  912.             SELECT DISTINCT customer FROM orders
  913.             ORDER BY customer
  914.             ''')
  915.         
  916.         customers = [row[0] for row in cursor.fetchall()]
  917.         if customers:
  918.             self.customer_filter['values'] = customers
  919.             if search_text:
  920.                 self.customer_filter.event_generate('<Down>')
  921.     def update_filter_lists(self):
  922.         """更新所有筛选下拉列表"""
  923.         cursor = self.conn.cursor()
  924.         
  925.         # 更新单据编号列表
  926.         cursor.execute('SELECT DISTINCT order_number FROM orders ORDER BY order_number DESC')
  927.         self.order_number_filter['values'] = [row[0] for row in cursor.fetchall()]
  928.         
  929.         # 更新客户名称列表
  930.         cursor.execute('SELECT DISTINCT customer FROM orders ORDER BY customer')
  931.         self.customer_filter['values'] = [row[0] for row in cursor.fetchall()]
  932.     def print_filtered_data(self):
  933.         """打印筛选后的数据"""
  934.         try:
  935.             # 获取当前筛选条件下的数据
  936.             order_number = self.order_number_filter.get().strip()
  937.             customer = self.customer_filter.get().strip()
  938.             
  939.             # 构建查询条件
  940.             query = '''
  941.             SELECT order_number, customer, product, unit,
  942.                    quantity, price, total, remarks,
  943.                    SUM(total) OVER () as total_sum
  944.             FROM orders
  945.             WHERE 1=1
  946.             '''
  947.             params = []
  948.             
  949.             if order_number:
  950.                 query += " AND order_number LIKE ?"
  951.                 params.append(f"%{order_number}%")
  952.             
  953.             if customer:
  954.                 query += " AND customer LIKE ?"
  955.                 params.append(f"%{customer}%")
  956.             
  957.             query += " ORDER BY order_number DESC"
  958.             
  959.             cursor = self.conn.cursor()
  960.             cursor.execute(query, params)
  961.             rows = cursor.fetchall()
  962.             
  963.             if not rows:
  964.                 messagebox.showinfo("提示", "没有数据可打印")
  965.                 return
  966.             
  967.             # 生成HTML内容
  968.             html_content = f"""
  969.             <!DOCTYPE html>
  970.             <html>
  971.             <head>
  972.                 <meta charset="utf-8">
  973.                 <title>订单查询结果</title>
  974.                 <style>
  975.                     body {{ font-family: SimSun, serif; }}
  976.                     table {{ border-collapse: collapse; width: 100%; margin-top: 10px; }}
  977.                     th, td {{
  978.                         border: 1px solid black;
  979.                         padding: 8px;
  980.                         text-align: center;
  981.                     }}
  982.                     th {{ background-color: #f2f2f2; }}
  983.                     .total {{
  984.                         text-align: right;
  985.                         padding: 10px;
  986.                         font-weight: bold;
  987.                     }}
  988.                     .header-info {{
  989.                         margin: 10px 0;
  990.                         padding: 10px;
  991.                         border: 1px solid #ddd;
  992.                         background-color: #f9f9f9;
  993.                     }}
  994.                     .header-info p {{
  995.                         margin: 5px 0;
  996.                     }}
  997.                     @media print {{
  998.                         .no-print {{ display: none; }}
  999.                         body {{ margin: 0; }}
  1000.                         table {{ page-break-inside: auto; }}
  1001.                         tr {{ page-break-inside: avoid; }}
  1002.                     }}
  1003.                 </style>
  1004.             </head>
  1005.             <body>
  1006.                 <h2 style="text-align: center;">订单查询结果</h2>
  1007.                 <div class="header-info">
  1008.                     <p><strong>单据编号:</strong>{order_number if order_number else "全部"}</p>
  1009.                     <p><strong>客户名称:</strong>{customer if customer else "全部"}</p>
  1010.                     <p><strong>��印时间:</strong>{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
  1011.                 </div>
  1012.                 <table>
  1013.                     <tr>
  1014.                         <th>品名规格</th>
  1015.                         <th>单位</th>
  1016.                         <th>数量</th>
  1017.                         <th>原价</th>
  1018.                         <th>金额</th>
  1019.                         <th>备注</th>
  1020.                     </tr>
  1021.             """
  1022.             
  1023.             # 添加数据行
  1024.             for row in rows:
  1025.                 html_content += f"""
  1026.                     <tr>
  1027.                         <td>{row[2]}</td>
  1028.                         <td>{row[3]}</td>
  1029.                         <td>{row[4]:.2f}</td>
  1030.                         <td>¥{row[5]:.2f}</td>
  1031.                         <td>¥{row[6]:.2f}</td>
  1032.                         <td>{row[7]}</td>
  1033.                     </tr>
  1034.                 """
  1035.             
  1036.             # 添加合计行
  1037.             total_sum = rows[0][8] if rows else 0
  1038.             html_content += f"""
  1039.                 </table>
  1040.                 <div class="total">
  1041.                     合计金额: ¥{total_sum:,.2f}
  1042.                 </div>
  1043.                 <div class="no-print" style="margin-top: 20px; text-align: center;">
  1044.                     <button onclick="window.print()">打印</button>
  1045.                 </div>
  1046.             </body>
  1047.             </html>
  1048.             """
  1049.             
  1050.             # 保存HTML文件
  1051.             temp_file = "订单查询结果.html"
  1052.             with open(temp_file, "w", encoding="utf-8") as f:
  1053.                 f.write(html_content)
  1054.             
  1055.             # 在默认浏览器中打开HTML文件
  1056.             import webbrowser
  1057.             webbrowser.open(temp_file)
  1058.             
  1059.         except Exception as e:
  1060.             messagebox.showerror("错误", f"打印过程中出错:{str(e)}")
  1061. if __name__ == "__main__":
  1062.     # 显示登录窗口
  1063.     login = LoginWindow()
  1064.     if login.run():  # 只有在登录成功时才继续
  1065.         root = tk.Tk()
  1066.         app = OrderSystem(root)
  1067.         root.mainloop()
  1068.     else:
  1069.         sys.exit(0)  # 登录失败或关闭登录窗口时退出程序
复制代码
多个文件 打包成EXE 

我可以帮你使用 PyInstaller 将这两个 Python 文件打包成一个单独的 exe 文件。以下是具体步骤:
   1. 首先安装 PyInstaller:
pip install pyinstaller
  2. 创建一个 spec 文件,定名为 `zhi.spec`:
  1. # -*- mode: python ; coding: utf-8 -*-
  2. block_cipher = None
  3. a = Analysis(
  4.     ['zhi.py'],  # 主程序文件
  5.     pathex=[],
  6.     binaries=[],
  7.     datas=[('login.py', '.'), ('config.json', '.')],  # 添加其他需要的文件
  8.     hiddenimports=[],
  9.     hookspath=[],
  10.     hooksconfig={},
  11.     runtime_hooks=[],
  12.     excludes=[],
  13.     win_no_prefer_redirects=False,
  14.     win_private_assemblies=False,
  15.     cipher=block_cipher,
  16.     noarchive=False,
  17. )
  18. pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
  19. exe = EXE(
  20.     pyz,
  21.     a.scripts,
  22.     a.binaries,
  23.     a.zipfiles,
  24.     a.datas,
  25.     [],
  26.     name='订单记录系统',  # exe文件名
  27.     debug=False,
  28.     bootloader_ignore_signals=False,
  29.     strip=False,
  30.     upx=True,
  31.     upx_exclude=[],
  32.     runtime_tmpdir=None,
  33.     console=False,  # 设置为False不显示控制台窗口
  34.     disable_windowed_traceback=False,
  35.     target_arch=None,
  36.     codesign_identity=None,
  37.     entitlements_file=None,
  38.     icon='icon.ico',  # 如果有图标文件的话
  39. )
复制代码
  3. 在下令行中执行打包下令:

pyinstaller --clean zhi.spec
 
    假如你想要更简单的方式,可以直接使用以下下令:

pyinstaller --noconfirm --onefile --windowed --icon=icon.ico --add-data "login.py;." --add-data "config.json;." zhi.py
 
  注意事项:
1. 确保全部需要的依赖包都已安装
2. 假如使用了图标,需要预备一个 .ico 文件
3. 打包后的文件会在 `dist` 目次下
4. 建议在打包前测试步伐是否正常运行
完备的打包步骤:
   1. 预备文件:
your_project_folder/
    ├── zhi.py
    ├── login.py
    ├── config.json
    ├── icon.ico (可选)
    └── zhi.spec
    2. 安装必要的包:

pip install pyinstaller
pip install pandas
pip install sqlite3
    3. 执行打包下令:
pyinstaller --clean zhi.spec
  4. 检查天生的文件:
- 打包完成后,在 `dist` 目次下会天生一个名为"订单记录系统.exe"的文件
-记得在"订单记录系统.exe" 同时目次 创建一个 db目次
- 这个 exe 文件包罗了全部必要的依赖和资源文件
- 可以直接双击运行,不需要安装 Python 环境


1.1 版本,其它 2025年后。。。。。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

正序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

王柳

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表