Files
ai-proj-helper/skills-biz/finance-plugin/skills/references/details.md
T
johnQ 237631147a chore(skill): 清理全部 23 条历史校验警告
- 11 个超 20KB 的 SKILL.md 拆分,大章节移入 references/details.md
- 7 处非标准字段(arguments/user_invocable)移入 metadata
- 修 req-dev 4 条相对链接;validate 跳过行内代码避免 ![](...) 误报
- validate-skills.py: 23 warns -> 0 warns,0 errors
2026-10-31 22:21:07 +08:00

14 KiB

skills 详细内容

常见对账场景

场景1: 月末银行对账

# 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: 客户对账

# 加载客户交易明细
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: 账龄分析

# 加载未收款数据
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识别与对账匹配(诉讼证据核对)

适用于:承运商/供应商提供扫描件作为诉讼证据,需要与我司对账单进行匹配核对。

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}")

使用示例

# 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. 环境依赖:

    # 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订单是否出现在其他承运商的对账单中
    • 如果订单同时出现在多个承运商对账单中,则为异常订单

多承运商订单归属校验

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
    }

使用示例(多承运商校验)

# 检查订单归属
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("✓ 未发现异常订单,所有订单归属正确")