dongguoliang преди 2 месеца
ревизия
065ac35c51

BIN
51050146686100001911.xls


BIN
__pycache__/main.cpython-311.pyc


BIN
__pycache__/main.cpython-312.pyc


+ 319 - 0
main.py

@@ -0,0 +1,319 @@
+import pandas as pd
+import tkinter as tk
+from tkinter import ttk, filedialog, messagebox
+from datetime import datetime
+import os
+from openpyxl.styles import Font
+
+# -------------------------- 全局配置 --------------------------
+# 定义参数表的sheet名称(根据实际情况调整,若不同需修改)
+ACCOUNT_PARAM_SHEET = "账户信息参数"  # 参数表中账户信息的sheet名
+CATEGORY_PARAM_SHEET = "收支分类参数"  # 参数表中收支分类的sheet名
+# 固定匹配的对手户名(结算收支)
+SETTLEMENT_COMPANIES = ["成都合煦商贸有限公司", "四川泗讯科技有限公司"]
+
+# -------------------------- 核心数据处理函数 --------------------------
+def load_parameter_table(param_file_path):
+    """加载参数表,返回账户信息和收支分类"""
+    try:
+        # 加载账户信息参数(列名在第1行,所以使用header=1)
+        account_params = pd.read_excel(param_file_path, sheet_name=ACCOUNT_PARAM_SHEET, header=1)
+        # 加载收支分类参数(列名在第1行,所以使用header=1)
+        category_params = pd.read_excel(param_file_path, sheet_name=CATEGORY_PARAM_SHEET, header=1)
+        return account_params, category_params
+    except Exception as e:
+        messagebox.showerror("错误", f"加载参数表失败:{str(e)}")
+        return None, None
+
+def parse_ccb_flow(flow_file_path):
+    """解析建行流水文件,返回标准化的流水数据"""
+    try:
+        # 读取建行流水(适配xls格式,列名在第9行)
+        flow_df = pd.read_excel(flow_file_path, dtype=str, header=9)  # 先以字符串读取避免格式丢失
+        
+        # 去除列名中的空格
+        flow_df.columns = flow_df.columns.str.strip()
+        
+        # 标准化字段名(根据建行流水实际列名调整,以下是常见建行流水列名,需核对)
+        # 你可根据实际流水列名修改key值
+        field_mapping = {
+            "交易时间": "交易时间",
+            "对方户名": "对方户名",
+            "摘要": "摘要",
+            "贷方发生额/元(收入)": "收入",
+            "借方发生额/元(支取)": "支出"
+        }
+        
+        # 检查必要列是否存在
+        missing_fields = [k for k in field_mapping.keys() if k not in flow_df.columns]
+        if missing_fields:
+            messagebox.showerror("错误", f"建行流水缺少必要列:{missing_fields}")
+            return None
+        
+        # 保留所有需要的列,并重命名部分列
+        required_columns = ["交易时间", "对方户名", "摘要", "贷方发生额/元(收入)", "借方发生额/元(支取)", "对方开户机构", "备注", "余额"]
+        standard_flow = flow_df[required_columns].rename(columns=field_mapping)
+        
+        # 处理空值和格式
+        standard_flow["收入"] = pd.to_numeric(standard_flow["收入"], errors='coerce').fillna(0)
+        standard_flow["支出"] = pd.to_numeric(standard_flow["支出"], errors='coerce').fillna(0)
+        
+        return standard_flow
+    except Exception as e:
+        messagebox.showerror("错误", f"解析建行流水失败:{str(e)}")
+        return None
+
+def generate_journal_data(company_name, bank_name, param_file, flow_file):
+    """核心:生成日记账数据"""
+    # 1. 加载参数表
+    account_params, category_params = load_parameter_table(param_file)
+    if account_params is None:
+        return None
+    
+    # 2. 解析建行流水
+    flow_df = parse_ccb_flow(flow_file)
+    if flow_df is None:
+        return None
+    
+    # 3. 匹配账户编号和简称(按公司+开户行)
+    # 先精确匹配公司,然后模糊匹配开户行
+    account_filter = account_params["公司"] == company_name
+    account_info = account_params[account_filter]
+    
+    # 如果开户行不是具体的支行名称,则进行模糊匹配
+    if not account_info.empty and bank_name not in account_info["开户行"].values:
+        # 模糊匹配:检查开户行是否包含银行名称
+        account_filter = account_filter & account_params["开户行"].str.contains(bank_name, na=False)
+        account_info = account_params[account_filter]
+    
+    if account_info.empty:
+        messagebox.showerror("错误", f"参数表中未找到{company_name}-{bank_name}的账户信息")
+        return None
+    
+    account_code = account_info["编号"].iloc[0]
+    account_short_name = account_info["简称"].iloc[0]
+    company_name_full = account_info["公司"].iloc[0]
+    
+    # 4. 构建日记账数据
+    journal_data = []
+    for _, row in flow_df.iterrows():
+        # 跳过空行(交易时间为空)
+        if pd.isna(row["交易时间"]) or str(row["交易时间"]).strip() == "":
+            continue
+        
+        # 计算收支金额
+        income = row["收入"] if pd.notna(row["收入"]) else 0
+        expense = row["支出"] if pd.notna(row["支出"]) else 0
+        
+        # 确定收支类型和金额
+        if income > 0:
+            shouzhi_type = "收入"
+            amount = income
+        elif expense > 0:
+            shouzhi_type = "支出"
+            amount = expense
+        else:
+            shouzhi_type = ""
+            amount = 0
+        
+        # 提取月份(从日期中提取)
+        try:
+            date_obj = pd.to_datetime(row["交易时间"], errors='coerce')
+            month = date_obj.month if pd.notna(date_obj) else ""
+        except:
+            month = ""
+        
+        # 格式化日期为 2026/1/26 格式
+        try:
+            date_obj = pd.to_datetime(row["交易时间"], errors='coerce')
+            if pd.notna(date_obj):
+                formatted_date = f"{date_obj.year}/{date_obj.month}/{date_obj.day}"
+            else:
+                formatted_date = row["交易时间"]
+        except:
+            formatted_date = row["交易时间"]
+        
+        # 判断对方户名是否为个人(简单判断:如果对方户名较短且不包含"公司"等关键词)
+        opponent_name = str(row["对方户名"]).strip()
+        is_personal = len(opponent_name) < 5 or "公司" not in opponent_name
+        
+        # 构建备注:对方户名+对方开户机构,如果是个人则用对方户名+备注
+        if is_personal:
+            remark = f"{opponent_name}{row['备注']}"
+        else:
+            remark = f"{opponent_name}{row['对方开户机构']}"
+        
+        # 基础字段
+        journal_row = {
+            "简称": account_short_name,
+            "企业": company_name_full,
+            "日期": formatted_date,
+            "月份": month,
+            "收支": shouzhi_type,
+            "资金分类-1级": "",
+            "资金分类-2级": "",
+            "资金分类-3级": "",
+            "对手户": opponent_name,
+            "备注": remark,
+            "发生额": amount,
+            "余额": row["余额"],
+            "备注.1": ""
+        }
+        
+        # 5. 匹配资金分类(核心规则)
+        # 规则1:对手户是参数表中的公司 或 指定的两家公司 → 结算收入/支出
+        if (opponent_name in account_params["公司"].values) or (opponent_name in SETTLEMENT_COMPANIES):
+            if income > 0:
+                # 结算收入
+                journal_row["资金分类-1级"] = "结算收入"
+                journal_row["资金分类-2级"] = "结算收入"
+                journal_row["资金分类-3级"] = "结算收入"
+            else:
+                # 结算支出
+                journal_row["资金分类-1级"] = "结算支出"
+                journal_row["资金分类-2级"] = "结算支出"
+                journal_row["资金分类-3级"] = "结算支出"
+        # 规则2:摘要含"手续费" → 财务费用-财务费用-手续费
+        elif "手续费" in str(row["摘要"]):
+            journal_row["资金分类-1级"] = "财务费用"
+            journal_row["资金分类-2级"] = "财务费用"
+            journal_row["资金分类-3级"] = "手续费"
+            # 对手户和备注都只写成手续费
+            journal_row["对手户"] = "手续费"
+            journal_row["备注"] = "手续费"
+        # 规则3:备注是"会长提现" → 结算支出-结算支出-会长提现支付
+        elif "会长提现" in str(row["备注"]):
+            journal_row["资金分类-1级"] = "结算支出"
+            journal_row["资金分类-2级"] = "结算支出"
+            journal_row["资金分类-3级"] = "会长提现支付"
+        # 其他情况(可后续补充规则)
+        else:
+            journal_row["资金分类-1级"] = "待分类"
+            journal_row["资金分类-2级"] = "待分类"
+            journal_row["资金分类-3级"] = "待分类"
+        
+        journal_data.append(journal_row)
+    
+    # 转换为DataFrame
+    journal_df = pd.DataFrame(journal_data)
+    # 确保列顺序匹配参考日记账
+    journal_columns = [
+        "简称", "企业", "日期", "月份", "收支",
+        "资金分类-1级", "资金分类-2级", "资金分类-3级", "对手户", "备注", "发生额", "余额", "备注.1"
+    ]
+    journal_df = journal_df[journal_columns]
+    
+    return journal_df
+
+# -------------------------- GUI界面 --------------------------
+def select_param_file():
+    """选择参数表文件"""
+    file_path = filedialog.askopenfilename(
+        title="选择参数表文件",
+        filetypes=[("Excel文件", "*.xlsx *.xls"), ("所有文件", "*.*")]
+    )
+    if file_path:
+        param_file_var.set(file_path)
+
+def select_flow_file():
+    """选择银行流水文件"""
+    file_path = filedialog.askopenfilename(
+        title="选择建行流水文件",
+        filetypes=[("Excel文件", "*.xlsx *.xls"), ("所有文件", "*.*")]
+    )
+    if file_path:
+        flow_file_var.set(file_path)
+
+def generate_journal():
+    """生成日记账"""
+    # 获取界面输入
+    company = company_var.get()
+    bank = bank_var.get()
+    param_file = param_file_var.get()
+    flow_file = flow_file_var.get()
+    
+    # 校验输入
+    if not param_file or not flow_file:
+        messagebox.showwarning("提示", "请先选择参数表和流水文件")
+        return
+    
+    # 生成日记账数据
+    journal_df = generate_journal_data(company, bank, param_file, flow_file)
+    if journal_df is None:
+        return
+    
+    # 保存文件
+    today = datetime.now().strftime("%Y年%m月%d日")
+    save_path = filedialog.asksaveasfilename(
+        title="保存日记账",
+        defaultextension=".xlsx",
+        initialfile=f"{company}-{bank}-日记账-{today}.xlsx",
+        filetypes=[("Excel文件", "*.xlsx"), ("所有文件", "*.*")]
+    )
+    if save_path:
+        # 保存时保持格式(匹配参考日记账)
+        with pd.ExcelWriter(save_path, engine="openpyxl") as writer:
+            journal_df.to_excel(writer, index=False, sheet_name="日记账")
+            
+            # 设置表头加粗
+            workbook = writer.book
+            worksheet = writer.sheets["日记账"]
+            
+            # 创建加粗字体样式
+            bold_font = Font(bold=True)
+            
+            # 设置表头行加粗
+            for cell in worksheet[1]:
+                cell.font = bold_font
+        
+        messagebox.showinfo("成功", f"日记账已生成:{save_path}")
+
+# 初始化GUI
+root = tk.Tk()
+root.title("出纳日记账生成工具(建行版)")
+root.geometry("600x300")
+
+# 变量定义
+company_var = tk.StringVar(value="成都锦高量科科技有限公司")
+bank_var = tk.StringVar(value="建设银行")
+param_file_var = tk.StringVar()
+flow_file_var = tk.StringVar()
+
+# 界面布局
+# 1. 公司选择
+frame_company = ttk.Frame(root, padding="10")
+frame_company.pack(fill=tk.X)
+ttk.Label(frame_company, text="公司名称:").pack(side=tk.LEFT)
+company_combo = ttk.Combobox(frame_company, textvariable=company_var, state="readonly")
+company_combo["values"] = ["成都锦高量科科技有限公司"]  # 后续可扩展其他公司
+company_combo.pack(side=tk.LEFT, padx=5)
+
+# 2. 银行选择
+frame_bank = ttk.Frame(root, padding="10")
+frame_bank.pack(fill=tk.X)
+ttk.Label(frame_bank, text="开户银行:").pack(side=tk.LEFT)
+bank_combo = ttk.Combobox(frame_bank, textvariable=bank_var, state="readonly")
+bank_combo["values"] = ["建设银行"]  # 后续可扩展其他银行
+bank_combo.pack(side=tk.LEFT, padx=5)
+
+# 3. 参数表选择
+frame_param = ttk.Frame(root, padding="10")
+frame_param.pack(fill=tk.X)
+ttk.Label(frame_param, text="参数表文件:").pack(side=tk.LEFT)
+ttk.Entry(frame_param, textvariable=param_file_var, width=50).pack(side=tk.LEFT, padx=5)
+ttk.Button(frame_param, text="选择", command=select_param_file).pack(side=tk.LEFT)
+
+# 4. 流水文件选择
+frame_flow = ttk.Frame(root, padding="10")
+frame_flow.pack(fill=tk.X)
+ttk.Label(frame_flow, text="流水文件:").pack(side=tk.LEFT)
+ttk.Entry(frame_flow, textvariable=flow_file_var, width=50).pack(side=tk.LEFT, padx=5)
+ttk.Button(frame_flow, text="选择", command=select_flow_file).pack(side=tk.LEFT)
+
+# 5. 生成按钮
+frame_generate = ttk.Frame(root, padding="10")
+frame_generate.pack(fill=tk.X)
+ttk.Button(frame_generate, text="生成日记账", command=generate_journal, style="Accent.TButton").pack()
+
+# 启动GUI
+root.mainloop()

BIN
参数表.xlsx


BIN
成都锦高量科科技有限公司-建设银行-4-日记账-2026年01月26日.xlsx


BIN
日记账.xlsx


BIN
锦高建行的流水.xls