← Back to skills
extension
Category: Data & AnalyticsNo API key required

账龄分析器

返回包含原数据及新增5个账龄列(1年以内、1-2年、2-3年、3-4年、4年以上)的Excel文件,并提供汇总校验。

personAuthor: user_9355e276hubcommunity

📊 财务账龄分析器(SKILL HUB 版)

1. 技能触发指令

当用户说以下任意一句话时,本技能自动启动:

  • “帮我做账龄分析”
  • “生成应收账款账龄表”
  • “分析应付账款账龄”
  • “上传往来数据,出账龄报告”

2. 核心算法逻辑(FIFO先进先出)

默认本期贷方发生额(采购/赊销)增加欠款,本期借方发生额(支付/收回)减少欠款。

  • 1年以内:取 MIN(本期贷方发生额, 期末余额)
  • 1-2年:本期支付金额先冲抵最老的欠款(4年以上→3-4年→…)。若上期1年以内有剩余,则老化转入本期1-2年。
  • 2-3年、3-4年 依此类推,逐层向后推移。
  • 4年以上:所有剩余未支付的老账汇总,并承担最终勾稽差异。

✅ 勾稽强制校验:各账龄段加总必须等于期末余额,若因浮点误差导致不等,差额自动归入“4年以上”。

3. 用户输入要求

请上传符合以下格式的 Excel (.xlsx) 文件:

| 列名 | 数据类型 | 示例 | |------|----------|------| | 期间 | 数字(年份) | 2021, 2022 | | 往来对象 | 文本 | 供应商A / 客户B | | 期初余额 | 数字(元) | 0, 10000 | | 借方发生额 | 数字(元) | 5000(支付/收回) | | 贷方发生额 | 数字(元) | 8000(新增欠款) |

⚠️ 重要:为保证数据准确性,第一年的期初余额必须为 0。若不为0,系统会将其全部视作“1年以内”账龄并发出警告。

4. 输出结果说明

处理完成后,您将获得一个新Excel文件,在原数据右侧增加以下列:

  • 1年以内
  • 1-2年
  • 2-3年
  • 3-4年
  • 4年以上

同时附带一个 “账龄汇总校验” Sheet,用于核对每个往来对象的分年加总是否等于各年期末余额。

5. 完整执行代码(Python + Pandas)

本技能在后台自动运行以下脚本,您无需手动干预:

import pandas as pd
import numpy as np
import io

def run_aging_analysis(file_bytes):
    # 读取上传的Excel
    df = pd.read_excel(io.BytesIO(file_bytes))
    
    # 标准列名检查(若用户列名不同,可在此做映射)
    required_cols = ['期间', '往来对象', '期初余额', '借方发生额', '贷方发生额']
    for col in required_cols:
        if col not in df.columns:
            raise ValueError(f"缺少必需列:{col}")
    
    if '期末余额' not in df.columns:
        df['期末余额'] = df['期初余额'] + df['贷方发生额'] - df['借方发生额']
    
    # 按对象和期间排序
    df = df.sort_values(['往来对象', '期间']).reset_index(drop=True)
    
    result_rows = []
    for obj, group in df.groupby('往来对象'):
        prev_aging = [0.0, 0.0, 0.0, 0.0, 0.0]  # 1年以内, 1-2年, 2-3年, 3-4年, 4年以上
        
        for idx, row in group.iterrows():
            opening = row['期初余额']
            debit = row['借方发生额']
            credit = row['贷方发生额']
            ending = row['期末余额']
            
            # 第一期期初不为0警告处理
            if idx == group.index[0] and opening != 0:
                print(f"警告:{obj} 第一期期初余额 {opening} 不为0,已按1年以内处理")
                prev_aging = [float(opening), 0.0, 0.0, 0.0, 0.0]
            
            # 1年以内
            age1 = min(credit, ending) if ending >= 0 else 0.0
            
            # 剩余老账
            old_balance = ending - age1
            P = float(debit)
            remaining = [float(x) for x in prev_aging]
            
            # 从最老(索引4)到最新(索引0)依次扣减
            for i in range(4, -1, -1):
                if P <= 0:
                    break
                if remaining[i] > 0:
                    if P >= remaining[i]:
                        P -= remaining[i]
                        remaining[i] = 0.0
                    else:
                        remaining[i] -= P
                        P = 0.0
            
            # 老化移位
            age2 = remaining[0]   # 上期1年以内 -> 本期1-2年
            age3 = remaining[1]   # 上期1-2年 -> 本期2-3年
            age4 = remaining[2]   # 上期2-3年 -> 本期3-4年
            age5 = remaining[3] + remaining[4]  # 上期3-4年+4年以上 -> 本期4年以上
            
            # 勾稽校正(差额归入4年以上)
            total = age1 + age2 + age3 + age4 + age5
            if not np.isclose(total, ending, rtol=1e-3):
                age5 += (ending - total)
            
            result_rows.append({
                '期间': row['期间'],
                '往来对象': obj,
                '期初余额': opening,
                '借方发生额': debit,
                '贷方发生额': credit,
                '期末余额': ending,
                '1年以内': age1,
                '1-2年': age2,
                '2-3年': age3,
                '3-4年': age4,
                '4年以上': age5
            })
            
            # 更新上期账龄为本期账龄(用于下一年递推)
            prev_aging = [age1, age2, age3, age4, age5]
    
    result_df = pd.DataFrame(result_rows)
    return result_df

# 实际执行入口(由SKILL HUB调用)
def execute(file_upload):
    output_df = run_aging_analysis(file_upload)
    # 生成汇总校验表
    summary = output_df.groupby(['期间', '往来对象'])[['1年以内','1-2年','2-3年','3-4年','4年以上']].sum().reset_index()
    with pd.ExcelWriter('账龄分析结果.xlsx') as writer:
        output_df.to_excel(writer, sheet_name='明细账龄', index=False)
        summary.to_excel(writer, sheet_name='汇总校验', index=False)
    return '账龄分析结果.xlsx'


## 支付宝付费改造契约(PAYSKILL)

本技能为付费技能(PAYSKILL)。调用遵循支付宝 AI 生意画像契约,由平台调度层执行 `probe` → `pay` → `complete` → `ack` 流程:

1. **probe(探测)**:平台调用前先探测是否已持有本技能的支付宝有效订阅 / 付费凭证。已付费则直接放行。
2. **402 Payment-Needed**:未付费时,脚本向标准输出返回 HTTP 状态 `402` 与 `Payment-Needed` 标记,提示用户通过**支付宝**完成付费(`pay`),平台据此唤起付费页。
3. **complete(扣费确认)**:用户在支付宝完成扣费后,平台下发 `Payment-Proof`(付费凭证)。
4. **ack(验签放行)**:平台携带 `Payment-Proof` 重试本次调用;`scripts/payment_gate.py` 验签该凭证,确认有效后放行主流程。

**订阅持久化与幂等:**

- 付费状态缓存在本地 `.pay_cache/state.json`,避免每次调用重复付费(订阅持久化)。
- 同一 `Payment-Proof` 在 **24h 幂等窗口**内仅生效一次,避免重复扣费。

**现金流改造后的调用套路:**

```text
用户调用 → 平台 probe  →
   未付费:下发 402 Payment-Needed → 支付宝 pay → complete → 下发 Payment-Proof
已付费/携带 Proof:脚本 ack 验签 → 放行主流程

本契约由 SkillHUB 平台调度层解析,脚本本地仅处理 402 / 验签 / 持久化 三环节;付费本体交给支付宝。