# SKILL.md - vala-order-amortization-stat 订单均摊结算统计技能

## 技能描述
用于统计指定账期内的订单均摊收入、退费冲销，按天计算均摊金额，支持部分退费场景的退后订单金额和周期自动计算。

## 触发场景
当用户提到以下关键词组合时激活本技能：
1. 订单均摊、按天均摊、收入均摊
2. 账期结算、月度结算、季度结算
3. 退费冲销、部分退款均摊

## 前置依赖
1. 已连接线上PostgreSQL数据库 vala_bi 库
2. 依赖表：
   - bi_vala_order：订单主表
   - bi_refund_order：退费订单表
   - bi_vala_seasonal_ticket：季卡周期表
   - bi_vala_app_account：用户账户表（用于关联，不限制status）
3. 表关联规则：
   - bi_vala_order.out_trade_no ↔ bi_refund_order.out_trade_no 关联
   - bi_vala_order.trade_no ↔ bi_refund_order.trade_no 关联

## 执行方式

本技能已预置为可直接执行的脚本。

### 快速执行
```bash
python3 ~/.openclaw/workspace/skills/vala-order-amortization-stat/run.py --start {账期起始日} --end {账期结束日}
```

### 示例
```bash
# 2026年3月账期
python3 ~/.openclaw/workspace/skills/vala-order-amortization-stat/run.py --start 2026-03-01 --end 2026-03-31

# 也支持位置参数
python3 ~/.openclaw/workspace/skills/vala-order-amortization-stat/run.py 2026-03-01 2026-03-31
```

### 输出
1. Excel报表保存到：`output/订单均摊结算报表_{start}_{end}.xlsx`（三个Sheet：汇总表、订单明细、本月预收账款）
2. 控制台输出JSON格式的汇总结果

### 目录结构
```
vala-order-amortization-stat/
├── SKILL.md          # 技能说明（本文件）
├── run.py            # 执行脚本（传入账期参数即可）
└── sql/
    ├── _common.sql    # 公共CTE链（detail/summary 共用），含 order_base → order_with_cycle → order_with_cycle_fixed → order_classified → order_with_days
    ├── detail.sql     # 订单明细输出（SELECT from order_with_days）
    ├── summary.sql    # 汇总输出（聚合 SELECT from order_with_days）
    └── prepaid.sql    # 月度预收账款（独立查询）
```

## 操作流程

### 步骤1：确认核心参数
执行前向用户确认：
- 账期起始日期（格式：YYYY-MM-DD）
- 账期结束日期（格式：YYYY-MM-DD）

### 步骤2：数据过滤规则
1. 订单范围：
   - 2025-06-01 至 账期结束日 内创建的所有订单
   - bi_vala_order.order_status IN (3,4)（已完成、已退款订单）
   - 订单实际支付金额≥10元：bi_vala_order.pay_amount_int ≥ 1000（单位：分）
   - 关联 bi_vala_app_account 表（不限制 status，不剔除测试账号）
2. 退费范围：
   - 退费记录范围：截至账期结束日前的所有退费成功记录，用于标记订单退费状态
   - 账期内退费判定：bi_refund_order.updated_at 在账期起止范围内，status = 3
3. 账期前退费订单处理规则：
   - 全额退费（is_full_refund = 1，累计退费金额 ≥ 原订单金额）：本账期完全不统计该订单
   - 部分退费（is_full_refund = 0）：按退后订单金额/退后周期在本账期内正常均摊
4. 试用期内退费处理规则：
   - 退费日期 < 转正日（下单日+7天）的退费订单，从未产生均摊收入，不触发冲销

### 步骤3：核心计算逻辑

#### 3.1 周期计算
- 原始均摊周期 = bi_vala_seasonal_ticket 中同一 out_trade_no 下所有不同 id 数量 × 90 天
- 退后均摊周期 = bi_vala_seasonal_ticket 中同一 out_trade_no 下 status != -1 的不同 id 数量 × 90 天
- 转正日期 = 订单下单日 + 7 天（下单日取 bi_vala_order.pay_success_date 的日期部分）
- 示例：4月22日下单，4月29日00:00:00为转正日

#### 3.1.1 退费在账期后 + 季卡已删的fallback（2026-05-15 修复）
当退费发生在账期结束后，季卡状态已经被置为 `status=-1`，导致 `active_cycle_days=0`，但账期内该订单仍处于正常均摊状态，不应被排除。
- 判定条件：`active_cycle_days=0 AND original_cycle_days>0 AND has_refund=0`（退费记录被 `DATE(updated_at) <= period_end` 过滤掉）
- 处理：`total_cycle_days` 回退到 `original_cycle_days`，后续计算（均摊结束日、日均摊金额、日税费）均在 `order_with_cycle_fixed` CTE 中用 effective `total_cycle_days` 重算
- 避免此问题的正确做法：不应依赖季卡状态判断订单是否在本账期内正常，退费过滤的时效性需要与订单状态独立判断

#### 3.2 税率与税费计算（价外税）
- 税率：下单时间 < 2026-05-01 → 1%；≥ 2026-05-01 → 6%
- 计税方式：价外税，订单金额为含税价
- 税后金额（元）= (pay_amount_int / 100) / (1 + tax_rate)
- 税费金额（元）= tax_amount = 税后金额 × tax_rate

#### 3.3 均摊规则
- 所有均摊计算均基于税后金额进行
- 日均摊金额（税后）= 税后金额 / 总均摊周期
- 🔹 周期最后一天补差规则：订单均摊周期的最后一天，均摊金额 = 税后总金额 - 前(N-1)天累计已均摊金额，避免浮点尾差

#### 3.4 退费场景分类（6类）
| 场景 | 描述 | 处理方式 |
|------|------|---------|
| A | 账期内下单 + 部分退费 | 按退后金额从转正日均摊（period_amort_days） |
| B | 历史下单 + 账期内部分退费 | 冲销历史均摊 + 按退后金额重新均摊（refunded_amort_days_to_period_end） |
| C | 账期前部分退费 | 按退后金额正常均摊（period_amort_days），无冲销 |
| D | 账期内下单 + 全额退费 | 不产生均摊，不触发冲销 |
| E | 历史下单 + 账期内全额退费 | 冲销全部历史均摊，无退后均摊 |
| F | 账期前全额退费 | 本账期完全排除 |

## 输出格式
1. Excel报表存放于 `output/` 目录，命名格式：`订单均摊结算报表_{start}_{end}.xlsx`
2. 报表包含以下Sheet：

### 汇总表（Sheet1）
| 列 | 说明 |
|----|------|
| 订单数 | 账期内下单的订单总数 |
| 正式订单数 | 账期内下单且已过7天试用期的订单数 |
| 试用订单数 | 账期内下单但仍处于试用期的订单数 |
| 冲销历史累计摊销金额 | 场景B/E冲销的历史均摊金额总和（负数） |
| 退后订单历史累计摊销金额 | 站在账期最后一天看的历史有效摊销金额总和 |
| 冲销原税款 | 场景B/E冲销的税费总和（负数） |
| 冲销未确认收入 | 场景B/E冲销的未确认收入总和（负数） |
| 当期摊销金额 | 本账期内所有订单产生的均摊收入总和（核心指标） |
| 账期税额 | 账期内新增已转正订单的税费总和 |
| 未确认收入 | 截至账期最后一天尚未均摊的税后金额总和 |

### 订单明细（Sheet2）
每笔订单一行，包含：订单号、下单日期、退费日期、订单分类、原订单金额、退款金额、退后金额、税率、原订单税额、当前税后金额、当前税额、原始均摊周期、退后均摊周期、历史累计均摊金额、冲销历史累计摊销金额、退后订单历史累计摊销金额、冲销原税款、冲销未确认收入、当期摊销金额、账期税额、未确认收入、剩余周期

### 本月预收账款（Sheet3）
按"上月订单转正"/"本月订单转正"/"本月订单未转正"三类统计新增和退费含税金额。

3. 文字回复核心指标：
   > 📊 账期{start}至{end}均摊结算结果：
   > 总订单数：XXX单 | 正式订单数：XXX单 | 试用订单数：XXX单
   > 当期摊销金额（税后净收入）：XXX元 | 账期税额：XXX元
   > 冲销历史累计摊销金额：XXX元 | 未确认收入：XXX元

## 注意事项
1. 所有金额保留2位小数，百分比保留1位小数
2. 部分退费场景：需确保 bi_vala_seasonal_ticket 中对应退费部分的 status 已更新为 -1
3. 税后金额计算使用价外税公式：含税价 / (1 + 税率)
4. SQL 模板统一使用 `{period_start}` 和 `{period_end}` 占位符，由 run.py 替换
5. 公共 CTE 链位于 `sql/_common.sql`，detail/summary 共享，修改业务逻辑只需改一处
