# skills 详细内容 ## 常见对账场景 ### 场景1: 月末银行对账 ```python # 1. 加载数据 bank_df = load_bank_statement('银行流水_202601.xlsx') system_df = load_system_records('收付款记录_202601.xlsx') # 2. 执行对账 result = reconcile(bank_df, system_df) # 3. 查看汇总 print(f"已匹配: {result['summary']['matched_count']} 笔") print(f"银行未达: {result['summary']['bank_only_count']} 笔, 金额 {result['summary']['bank_only_amount']:,.2f}") print(f"企业未达: {result['summary']['system_only_count']} 笔, 金额 {result['summary']['system_only_amount']:,.2f}") # 4. 生成调节表 report = generate_bank_reconciliation_report( bank_balance=150000.00, # 银行对账单余额 book_balance=145000.00, # 企业账面余额 bank_only=result['bank_only'], system_only=result['system_only'], as_of_date='2026-01-31' ) print(report) # 5. 导出 Excel export_reconciliation_to_excel(result, '银行对账_202601.xlsx', '2026-01-31') ``` ### 场景2: 客户对账 ```python # 加载客户交易明细 ar_transactions = pd.read_excel('应收明细.xlsx') # 筛选特定客户 customer_data = ar_transactions[ar_transactions['customer'] == '深圳XX公司'] # 生成对账单 statement = generate_ar_statement( customer_name='深圳XX公司', transactions=customer_data, as_of_date='2026-01-31' ) print(statement) ``` ### 场景3: 账龄分析 ```python # 加载未收款数据 receivables = pd.read_excel('应收账款.xlsx') # 账龄分析 aging = aging_analysis( receivables=receivables, as_of_date='2026-01-31', aging_buckets=[30, 60, 90, 180, 365] ) print(aging) # 导出 aging.to_excel('账龄分析_202601.xlsx') ``` ### 场景4: 扫描单据OCR识别与对账匹配(诉讼证据核对) 适用于:承运商/供应商提供扫描件作为诉讼证据,需要与我司对账单进行匹配核对。 ```python from pdf2image import convert_from_path import pytesseract import pandas as pd import re def ocr_pdf_extract_orders(pdf_path: str, dpi: int = 150) -> pd.DataFrame: """ 从 PDF 扫描件中 OCR 提取订单信息 Args: pdf_path: PDF 文件路径 dpi: 图像分辨率,越高识别越准但速度越慢 Returns: DataFrame 包含 order_no, page, tc_no 等字段 """ print(f"正在转换 PDF...") images = convert_from_path(pdf_path, dpi=dpi) print(f"共 {len(images)} 页,开始 OCR 识别...") all_records = [] for i, img in enumerate(images, 1): if i % 10 == 0: print(f" 处理进度: {i}/{len(images)}") text = pytesseract.image_to_string(img, lang='chi_sim+eng') # 提取多种订单号格式(根据实际业务调整) hm_orders = re.findall(r'(HM\d{12,15})(?:-\d+)?', text) # 华住订单 hpo_orders = re.findall(r'(HPO-\d{8}-\d+)', text) # 华住采购订单 fy_orders = re.findall(r'(FY\d{9,})', text) # 凤悦订单 phgco_orders = re.findall(r'(PHGCO\d{9,})', text) # 锦江订单 tc_orders = re.findall(r'(TC\d{8,})', text) # 运单号 # 提取日期 dates = re.findall(r'(\d{4}-\d{2}-\d{2})', text) all_orders = hm_orders + hpo_orders + fy_orders + phgco_orders for order in all_orders: all_records.append({ 'order_no': order, 'page': i, 'tc_no': tc_orders[0] if tc_orders else None, 'date': dates[0] if dates else None, 'raw_text': text[:500] # 保留部分原文用于核查 }) df = pd.DataFrame(all_records) df = df.drop_duplicates(subset=['order_no']) print(f"提取完成,共 {len(df)} 条唯一订单") return df def extract_base_order_no(order_no: str) -> str: """ 提取基础订单号(移除后缀如 -1, -2) 例如: HM202505227320-1 -> HM202505227320 """ if pd.isna(order_no): return None order_str = str(order_no) match = re.match(r'(HM\d{12,15}|HPO-\d{8}-\d+|FY\d{9,}|PHGCO\d{9,})', order_str) return match.group(1) if match else order_str def reconcile_scanned_with_excel( scanned_df: pd.DataFrame, excel_path: str, main_sheet: str, pending_sheet: str = None, order_column: str = '华住/锦江订单号' ) -> dict: """ 扫描单据与 Excel 对账单匹配 Args: scanned_df: OCR 提取的订单 DataFrame excel_path: Excel 对账单路径 main_sheet: 主数据 Sheet 名称 pending_sheet: 待结算/次月结 Sheet 名称(可选) order_column: 订单号列名 Returns: { 'matched_main': 在主表中匹配的订单, 'matched_pending': 在待结算表中匹配的订单, 'not_found': 未找到的订单, 'summary': 汇总统计 } """ # 加载主表 df_main = pd.read_excel(excel_path, sheet_name=main_sheet) # 处理表头(如果第一行是表头) if df_main.iloc[0].astype(str).str.contains(order_column).any(): df_main.columns = df_main.iloc[0] df_main = df_main.iloc[1:].reset_index(drop=True) # 提取基础订单号 main_orders = set(df_main[order_column].apply(extract_base_order_no).dropna()) # 加载待结算表(如有) pending_orders = set() df_pending = None if pending_sheet: try: df_pending = pd.read_excel(excel_path, sheet_name=pending_sheet) pending_orders = set(df_pending[order_column].apply(extract_base_order_no).dropna()) except: pass all_excel_orders = main_orders | pending_orders # 扫描件订单号 scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna()) # 匹配分析 matched_main = scan_orders & main_orders matched_pending = scan_orders & pending_orders not_found = scan_orders - all_excel_orders # 汇总 summary = { 'scan_total': len(scan_orders), 'matched_main_count': len(matched_main), 'matched_pending_count': len(matched_pending), 'not_found_count': len(not_found), 'match_rate': (len(matched_main) + len(matched_pending)) / len(scan_orders) * 100 if scan_orders else 0 } return { 'matched_main': matched_main, 'matched_pending': matched_pending, 'not_found': not_found, 'summary': summary, 'df_main': df_main, 'df_pending': df_pending } def generate_litigation_reconciliation_report( result: dict, scanned_df: pd.DataFrame, output_path: str ): """ 生成诉讼对账分析报告 """ summary = result['summary'] with pd.ExcelWriter(output_path, engine='openpyxl') as writer: # Sheet1: 汇总 summary_df = pd.DataFrame({ '项目': [ '扫描件订单数', '在主对账单中找到', '在待结算表中找到', '完全未找到', '匹配率' ], '数值': [ summary['scan_total'], summary['matched_main_count'], summary['matched_pending_count'], summary['not_found_count'], f"{summary['match_rate']:.1f}%" ] }) summary_df.to_excel(writer, sheet_name='对账汇总', index=False) # Sheet2: 未找到的订单 if result['not_found']: not_found_df = scanned_df[ scanned_df['order_no'].apply(extract_base_order_no).isin(result['not_found']) ][['order_no', 'page', 'tc_no', 'date']] not_found_df.columns = ['订单号', 'PDF页码', '运单号', '日期'] not_found_df.to_excel(writer, sheet_name='未找到订单', index=False) # Sheet3: 待结算订单详情 if result['matched_pending'] and result['df_pending'] is not None: pending_detail = result['df_pending'][ result['df_pending']['华住/锦江订单号'].apply(extract_base_order_no).isin(result['matched_pending']) ] pending_detail.to_excel(writer, sheet_name='待结算订单', index=False) print(f"报告已保存: {output_path}") ``` #### 使用示例 ```python # 1. 从 PDF 扫描件提取订单 pdf_path = "承运商证据_6月业务.pdf" scanned_df = ocr_pdf_extract_orders(pdf_path, dpi=150) # 2. 与 Excel 对账单匹配 result = reconcile_scanned_with_excel( scanned_df=scanned_df, excel_path="对账单_2025年6月.xlsx", main_sheet="2025.6", pending_sheet="次月结", order_column="华住/锦江订单号" ) # 3. 查看匹配结果 print(f"匹配率: {result['summary']['match_rate']:.1f}%") print(f"未找到: {result['summary']['not_found_count']} 条") if result['not_found']: print("\n未找到的订单:") for order in result['not_found']: print(f" {order}") # 4. 生成报告 generate_litigation_reconciliation_report( result=result, scanned_df=scanned_df, output_path="诉讼对账分析报告.xlsx" ) ``` #### OCR 识别注意事项 1. **环境依赖**: ```bash # macOS brew install tesseract tesseract-lang poppler pip install pdf2image pytesseract pandas openpyxl ``` 2. **识别精度**: - DPI 150 适合快速扫描,DPI 200-250 适合精确识别 - 中文识别需要 `chi_sim` 语言包 3. **订单号格式差异**: - PDF 中可能是 `HM202505227320` - Excel 中可能是 `HM202505227320-1`、`HM202505227320-2`(带后缀) - 匹配时应使用基础订单号 4. **常见问题**: - 空白页(签收单背面)会被自动跳过 - OCR 可能误识别字符(如 5→S, 0→O),建议人工抽查 - 大 PDF 处理较慢,可先用低 DPI 预览 5. **"次月结"/"待结算" 订单**: - 这类订单通常标记为"没有报价"、"未建单"等 - 虽然在系统中有记录,但可能未计入正式结算金额 - 诉讼中需特别关注这类订单的状态 6. **多承运商订单归属校验(重要风险点)**: - 承运商可能将其他公司的订单混入证据,企图多收运费 - 必须检查PDF订单是否出现在其他承运商的对账单中 - 如果订单同时出现在多个承运商对账单中,则为异常订单 #### 多承运商订单归属校验 ```python def check_order_ownership( scanned_df: pd.DataFrame, my_company_excel: str, other_carrier_excels: list[str], order_column: str = '华住/锦江订单号' ) -> dict: """ 检查订单归属,识别是否有其他承运商的订单被混入 Args: scanned_df: OCR 提取的订单 DataFrame my_company_excel: 我司对账单路径 other_carrier_excels: 其他承运商对账单路径列表 order_column: 订单号列名 Returns: { 'my_orders': 属于我司的订单, 'other_carrier_orders': 属于其他承运商的订单(异常), 'disputed_orders': 同时出现在多处的争议订单, 'summary': 汇总 } """ # 提取PDF订单 scan_orders = set(scanned_df['order_no'].apply(extract_base_order_no).dropna()) # 加载我司对账单订单 my_orders = set() try: xl = pd.ExcelFile(my_company_excel) for sheet in xl.sheet_names: df = pd.read_excel(my_company_excel, sheet_name=sheet) for col in df.columns: if '订单' in str(col): orders = df[col].apply(extract_base_order_no).dropna() my_orders.update(orders) except Exception as e: print(f"加载我司对账单失败: {e}") # 加载其他承运商对账单订单 other_orders = {} # {carrier_name: set of orders} for excel_path in other_carrier_excels: carrier_name = excel_path.split('/')[-1] carrier_orders = set() try: xl = pd.ExcelFile(excel_path) for sheet in xl.sheet_names: df = pd.read_excel(excel_path, sheet_name=sheet) for col in df.columns: if '订单' in str(col): orders = df[col].apply(extract_base_order_no).dropna() carrier_orders.update(orders) other_orders[carrier_name] = carrier_orders except: pass # 分析订单归属 all_other_orders = set() for orders in other_orders.values(): all_other_orders.update(orders) # 分类 only_mine = scan_orders & my_orders - all_other_orders # 仅在我司 only_others = scan_orders & all_other_orders - my_orders # 仅在其他承运商(异常!) in_both = scan_orders & my_orders & all_other_orders # 同时出现(争议) nowhere = scan_orders - my_orders - all_other_orders # 哪里都没有 # 识别异常订单属于哪个承运商 abnormal_details = [] for order in only_others: for carrier, orders in other_orders.items(): if order in orders: page = scanned_df[scanned_df['order_no'].apply(extract_base_order_no) == order]['page'].values abnormal_details.append({ 'order_no': order, 'belongs_to': carrier, 'pdf_page': page[0] if len(page) > 0 else None }) summary = { 'scan_total': len(scan_orders), 'my_orders_count': len(only_mine), 'other_carrier_count': len(only_others), # 异常订单数 'disputed_count': len(in_both), 'not_found_count': len(nowhere) } return { 'my_orders': only_mine, 'other_carrier_orders': only_others, 'abnormal_details': abnormal_details, 'disputed_orders': in_both, 'not_found': nowhere, 'summary': summary } ``` #### 使用示例(多承运商校验) ```python # 检查订单归属 result = check_order_ownership( scanned_df=scanned_df, my_company_excel="智慧云彩对账单_2025年6月.xlsx", other_carrier_excels=[ "友利速运对账单_2025年6月.xlsx", "其他承运商对账单.xlsx" ] ) # 检查是否有异常订单 if result['summary']['other_carrier_count'] > 0: print(f"⚠️ 发现 {result['summary']['other_carrier_count']} 个异常订单!") print("这些订单属于其他承运商,不应由我司支付:") for item in result['abnormal_details']: print(f" {item['order_no']} -> 属于 {item['belongs_to']} (PDF第{item['pdf_page']}页)") else: print("✓ 未发现异常订单,所有订单归属正确") ``` ---