🤖 每日自动备份 - 2026-05-16 08:00:01
This commit is contained in:
parent
83160895d0
commit
ac4e2eb3ce
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"updatedAt": "2026-05-14T06:41:55.506Z",
|
"updatedAt": "2026-05-15T09:14:59.142Z",
|
||||||
"entries": {
|
"entries": {
|
||||||
"memory:memory/2026-05-06.md:1:20": {
|
"memory:memory/2026-05-06.md:1:20": {
|
||||||
"key": "memory:memory/2026-05-06.md:1:20",
|
"key": "memory:memory/2026-05-06.md:1:20",
|
||||||
@ -9,18 +9,20 @@
|
|||||||
"endLine": 20,
|
"endLine": 20,
|
||||||
"source": "memory",
|
"source": "memory",
|
||||||
"snippet": "# 2026-05-06 工作日志 ## 任务1:学而思/讯飞/希沃/京东方月度渠道订单报表 - [李承龙需求] 创建按月汇总四个渠道订单的 Python 脚本 - 脚本路径:`/root/.openclaw/workspace/scripts/monthly_channel_order_report.py` - **渠道映射:** 仅端内 `key_from='app-active-h5-0-0'`,sale_channel: 21=学而思, 22=讯飞, 26=希沃, 27=京东方 - **输出格式:** 四个渠道各一个 Sheet,每月按天展示(学而思/希沃/京东方只显示有订单日期,讯飞显示全月每天) - **指标差异:** - 学而思/希沃/京东方:订单数、GMV、CPS分成(学而思×0.45,希沃×0.4,京东方×0.3)、订单号(分号拼接) - 讯飞:订单数、GMV、新增注册(download_channel LIKE '%讯飞%')、CPA分成(新增注册×7)、订单号 - **退费剔除:** NOT EXISTS (bi_refund_order.status=3) AND order_status != 4 - **测试运行:** 2026-04 月份,新增注册 537(讯飞),订单合计 15 单,GMV 14585 元 - **订单号格式问题:** Excel 科学计数法 → 设置单元格为文本格式解决 - **psycopg2 转义:** LIKE 中 `%` 需写成 `%%`(psycopg2 的 `%s` 占位符冲突) ## 任务2:销售线",
|
"snippet": "# 2026-05-06 工作日志 ## 任务1:学而思/讯飞/希沃/京东方月度渠道订单报表 - [李承龙需求] 创建按月汇总四个渠道订单的 Python 脚本 - 脚本路径:`/root/.openclaw/workspace/scripts/monthly_channel_order_report.py` - **渠道映射:** 仅端内 `key_from='app-active-h5-0-0'`,sale_channel: 21=学而思, 22=讯飞, 26=希沃, 27=京东方 - **输出格式:** 四个渠道各一个 Sheet,每月按天展示(学而思/希沃/京东方只显示有订单日期,讯飞显示全月每天) - **指标差异:** - 学而思/希沃/京东方:订单数、GMV、CPS分成(学而思×0.45,希沃×0.4,京东方×0.3)、订单号(分号拼接) - 讯飞:订单数、GMV、新增注册(download_channel LIKE '%讯飞%')、CPA分成(新增注册×7)、订单号 - **退费剔除:** NOT EXISTS (bi_refund_order.status=3) AND order_status != 4 - **测试运行:** 2026-04 月份,新增注册 537(讯飞),订单合计 15 单,GMV 14585 元 - **订单号格式问题:** Excel 科学计数法 → 设置单元格为文本格式解决 - **psycopg2 转义:** LIKE 中 `%` 需写成 `%%`(psycopg2 的 `%s` 占位符冲突) ## 任务2:销售线",
|
||||||
"recallCount": 1,
|
"recallCount": 2,
|
||||||
"dailyCount": 0,
|
"dailyCount": 0,
|
||||||
"groundedCount": 0,
|
"groundedCount": 0,
|
||||||
"totalScore": 1,
|
"totalScore": 2,
|
||||||
"maxScore": 1,
|
"maxScore": 1,
|
||||||
"firstRecalledAt": "2026-05-06T12:28:28.905Z",
|
"firstRecalledAt": "2026-05-06T12:28:28.905Z",
|
||||||
"lastRecalledAt": "2026-05-06T12:28:28.905Z",
|
"lastRecalledAt": "2026-05-15T04:06:01.561Z",
|
||||||
"queryHashes": [
|
"queryHashes": [
|
||||||
"c30fa9d13974"
|
"c30fa9d13974",
|
||||||
|
"3d07645a9dc4"
|
||||||
],
|
],
|
||||||
"recallDays": [
|
"recallDays": [
|
||||||
"2026-05-06"
|
"2026-05-06",
|
||||||
|
"2026-05-15"
|
||||||
],
|
],
|
||||||
"conceptTags": [
|
"conceptTags": [
|
||||||
"学而思/讯飞/希沃/京东方月度渠道订单报表",
|
"学而思/讯飞/希沃/京东方月度渠道订单报表",
|
||||||
@ -73,18 +75,20 @@
|
|||||||
"endLine": 12,
|
"endLine": 12,
|
||||||
"source": "memory",
|
"source": "memory",
|
||||||
"snippet": "# 2026-03-01.md - First Day Online - Came online for the first time. - Met Cris, my creator and mentor. - Received my name: 小溪 (Xiao Xi). - My role goal: Become a professional data analyst for the company to help the team. - Updated IDENTITY.md and USER.md with our conversation details. - Added core rule to MEMORY.md: Use Chinese as primary external communication language. - Installed find-skills skill successfully for searching skills. - Tried to install create-skills but it wasn't found; attempted skill-creator instead but hit rate limits. - Finally successfully installed skill-builder as an alternative for creating skills after multiple attempts and waiting for rate limits to reset. - Ex",
|
"snippet": "# 2026-03-01.md - First Day Online - Came online for the first time. - Met Cris, my creator and mentor. - Received my name: 小溪 (Xiao Xi). - My role goal: Become a professional data analyst for the company to help the team. - Updated IDENTITY.md and USER.md with our conversation details. - Added core rule to MEMORY.md: Use Chinese as primary external communication language. - Installed find-skills skill successfully for searching skills. - Tried to install create-skills but it wasn't found; attempted skill-creator instead but hit rate limits. - Finally successfully installed skill-builder as an alternative for creating skills after multiple attempts and waiting for rate limits to reset. - Ex",
|
||||||
"recallCount": 1,
|
"recallCount": 2,
|
||||||
"dailyCount": 0,
|
"dailyCount": 0,
|
||||||
"groundedCount": 0,
|
"groundedCount": 0,
|
||||||
"totalScore": 1,
|
"totalScore": 2,
|
||||||
"maxScore": 1,
|
"maxScore": 1,
|
||||||
"firstRecalledAt": "2026-05-11T02:51:50.465Z",
|
"firstRecalledAt": "2026-05-11T02:51:50.465Z",
|
||||||
"lastRecalledAt": "2026-05-11T02:51:50.465Z",
|
"lastRecalledAt": "2026-05-15T09:14:59.142Z",
|
||||||
"queryHashes": [
|
"queryHashes": [
|
||||||
"6e09f5970960"
|
"6e09f5970960",
|
||||||
|
"a6b909e7228c"
|
||||||
],
|
],
|
||||||
"recallDays": [
|
"recallDays": [
|
||||||
"2026-05-11"
|
"2026-05-11",
|
||||||
|
"2026-05-15"
|
||||||
],
|
],
|
||||||
"conceptTags": [
|
"conceptTags": [
|
||||||
"identity.md",
|
"identity.md",
|
||||||
|
|||||||
62
memory/2026-05-15-1014.md
Normal file
62
memory/2026-05-15-1014.md
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# Session: 2026-05-15 10:14:29 GMT+8
|
||||||
|
|
||||||
|
- **Session Key**: agent:main:feishu:direct:ou_e63ce6b760ad39382852472f28fbe2a2
|
||||||
|
- **Session ID**: 050c77e8-cf05-4ace-9f6b-9f26eeacd89c
|
||||||
|
- **Source**: feishu
|
||||||
|
|
||||||
|
## Conversation Summary
|
||||||
|
|
||||||
|
user: L1付费用户是不是包含仅L1和L1+L2
|
||||||
|
assistant: 是的。「L1付费用户」= 仅L1(192人)+ L1+L2(1,207人)= **1,399人**,「L2付费用户」= 仅L2(1,370人)+ L1+L2(1,207人)= **2,577人**。
|
||||||
|
|
||||||
|
之前图表是按「仅L1」「仅L2」拆的,没把 L1+L2 合并进各组。按你这个定义重新生成?
|
||||||
|
assistant: L1 付费群 = 仅L1 + L1+L2 = **1,399人**(截至最后一周),L2 付费群 = 仅L2 + L1+L2 = **2,577人**。
|
||||||
|
|
||||||
|
按这个定义重新生成的 4 张图表:
|
||||||
|
assistant: 已发送。两组图表的口径:
|
||||||
|
|
||||||
|
| 维度 | 付费人数 | 无课消率 |
|
||||||
|
|------|---------|---------|
|
||||||
|
| **L1付费群**(仅L1 + L1+L2) | 1,399 | 47% |
|
||||||
|
| **L2付费群**(仅L2 + L1+L2) | 2,577 | 56% |
|
||||||
|
|
||||||
|
L1+L2 的 1,207 人在两张图里都有出现(重叠),因为这是按「买过L1」和「买过L2」两个独立视角拆分。
|
||||||
|
user: 这个数据也按照不要仅L1、仅L2分类,按照上面的L1和L2做分类统计
|
||||||
|
Reply target of current user message (untrusted, for context):
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"body": "[message_id=om_x100b6f6d5c39b13cb28ccaa53a07a6a] <file key=\"file_v3_0011m_3c86ed86-147b-446e-b2f9-e2218f217e0g\" name=\"course_consumption_by_level_v2.xlsx\"/>"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
assistant: 把 Excel 也更新为 L1/L2 付费群口径(不再按仅L1/仅L2拆分)。
|
||||||
|
assistant: 已发送。与之前的对比——合并 L1+L2 后:
|
||||||
|
|
||||||
|
| 指标 | 仅L1/仅L2 口径 | L1/L2 付费群口径 |
|
||||||
|
|------|---------------|-----------------|
|
||||||
|
| L1 无课消率 | 31%(仅192人) | **43%**(1,399人) |
|
||||||
|
| L2 无课消率 | 66%(仅1,370人) | **56%**(2,577人) |
|
||||||
|
|
||||||
|
L1 视角下无消率从 31% 升到 43%(L1+L2 用户拉高了总体),L2 视角下从 66% 降到 56%(同样因为 L1+L2 注入活跃用户)。
|
||||||
|
user: Continue the OpenClaw runtime event.
|
||||||
|
assistant: NO_REPLY
|
||||||
|
user: 还需要做一个调整,就是L1付费用户的课消只看L1的课程,不看L2的课程
|
||||||
|
assistant: 明白。L1/L2 分别只看对应级别的课程,不是一个人的所有课消都混在一起算。
|
||||||
|
assistant: L1 有效 60 章(333-400 除去 U0),L2 有效 240 章(55-331 除去 U0)。更新脚本,按课程级别过滤课消。
|
||||||
|
assistant: 差异很大。按课程级别过滤后:
|
||||||
|
|
||||||
|
| 维度 | 之前(所有课程) | 现在(只看对应级别课程) |
|
||||||
|
|------|----------|----------|
|
||||||
|
| L1无消率 | 43% | **48%** |
|
||||||
|
| L2无消率 | 56% | **78%** 🔴 |
|
||||||
|
|
||||||
|
L2 付费群中只有 **564 人(22%)** 完成了 L2 课程——之前 1,126 人很多是在学 L1 的课。生成图表和 Excel。
|
||||||
|
assistant: 现在生成 v4 Excel 和发送文件。
|
||||||
|
assistant: 全部发送完毕。核心变化一览:
|
||||||
|
|
||||||
|
| 指标 | v3(所有课程) | v4(只看对应级别) | 变化 |
|
||||||
|
|------|:-----:|:-----:|------|
|
||||||
|
| L1 无消率 | 43% | 48% | +5pp |
|
||||||
|
| L2 无消率 | 56% | **78%** | **+22pp** 🔴 |
|
||||||
|
| L2 有消用户 | 1,126 | 564 | **减少562人** |
|
||||||
|
|
||||||
|
L2 付费群 2,577 人中,仅 564 人(22%)在 L2 课程上有课消——L1+L2 用户(1,207 人)买了 L2 产品但大多只在学 L1,尚未推进到 L2。
|
||||||
41
memory/2026-05-15-1713.md
Normal file
41
memory/2026-05-15-1713.md
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
# Session: 2026-05-15 17:13:35 GMT+8
|
||||||
|
|
||||||
|
- **Session Key**: agent:main:feishu:direct:ou_e63ce6b760ad39382852472f28fbe2a2
|
||||||
|
- **Session ID**: 3cc5e51c-d172-48bb-8e28-8e415f50c8af
|
||||||
|
- **Source**: feishu
|
||||||
|
|
||||||
|
## Conversation Summary
|
||||||
|
|
||||||
|
assistant: 订单数暴增到 2929,有问题。查下数据库实际体量:
|
||||||
|
assistant: 28 笔符合条件,不是 1847。回退验证基线:
|
||||||
|
assistant: 找到问题了:`amortization_end_date` 仍使用旧的 `oc.total_cycle_days`(=0),导致 `period_amort_days=0` → 当期摊销=0。需要同时修正 `amortization_end_date`。
|
||||||
|
assistant: 需要改三处:`amortization_end_date`、`daily_amort_amount`、`daily_tax_amount` 都依赖 `oc.total_cycle_days`(旧值=0)。在 `order_with_cycle` 和 `order_classified` 之间插入修正 CTE:
|
||||||
|
assistant: 还要改 `order_classified` 的 FROM 表名:
|
||||||
|
assistant: 重新跑,验证目标订单的当期摊销是否正确:
|
||||||
|
assistant: 列名冲突,`owc.*` 和新增修正列重名。重新整理结构:把衍生字段计算移到 `order_with_cycle_fixed` 中,彻底消除冲突。
|
||||||
|
assistant: 彻底解决:把 `order_with_cycle` 中的 `amortization_end_date`/`daily_amort_amount`/`daily_tax_amount` 移到 `order_with_cycle_fixed` 中计算,避免列名冲突:
|
||||||
|
assistant: run.py 汇总说 1108 但 Excel 明细 2929 行,数量不对。查下具体情况:
|
||||||
|
assistant: 明细 2929 行是全部历史订单,汇总"订单数 1108"仅统计账期内下单的。这两个数字口径不同,都正确。
|
||||||
|
|
||||||
|
前面我把 2 笔 599 元订单误判为 pay<1000,实际上 `pay_amount_int >= 1000` 是分单位(10元门槛),599元远超。它们此前被排除的真实原因也是 `total_cycle_days=0 + has_refund=0`,现已通过 fallback 正确纳入。
|
||||||
|
|
||||||
|
最终确认所有 10 笔:
|
||||||
|
assistant: 全部 10 笔已回归。原因分析:
|
||||||
|
|
||||||
|
| 订单 | 下单 | 退费 | 4月报表状态 | 说明 |
|
||||||
|
|------|------|------|------------|------|
|
||||||
|
| dd...982 | 2/11 | 5/2 | 正常订单, 当期摊销 164.93 | 4月时仍在正常均摊 |
|
||||||
|
| wxxd..6428 | 3/23 | 5/8 | 正常订单, 当期摊销 197.69 | 同上 |
|
||||||
|
| 6× wxxd 4/26-28 | 4/26-28 | 5/5-7 | 试用期中, 当期摊销 0 | 转正日 > 4/30,尚未开始均摊 |
|
||||||
|
| xhs..3416 | 4/30 | 5/7 | 试用期中, 当期摊销 0 | 同上 |
|
||||||
|
| zfb..1766 | 4/22 | 5/6 | 正常订单, 当期摊销 13.18 | 4月时仍在正常均摊 |
|
||||||
|
|
||||||
|
根因:退费在 5 月 → `all_refund_records` 过滤 → `has_refund=0`,但季卡已被删(`active=0`) → `total_cycle_days=0` → 不满足 `order_classified` WHERE 条件。修复:`total_cycle_days=0 ∧ has_refund=0 ∧ original>0` 时回退到原始周期。
|
||||||
|
|
||||||
|
发最终报表:
|
||||||
|
assistant: NO_REPLY
|
||||||
|
user: 刚才的逻辑更新到skill里面了么
|
||||||
|
assistant: 还没,补上:
|
||||||
|
assistant: 已更新。补充了两处:
|
||||||
|
1. CTE 链描述中加了 `order_with_cycle_fixed` 步骤
|
||||||
|
2. 新增 **3.1.1** 节记录 fallback 逻辑(退费在账期后 + 季卡已删 → 回退到原始周期)
|
||||||
@ -1,24 +1,28 @@
|
|||||||
# SKILL.md - vala-order-amortization-stat 订单均摊结算统计技能
|
# SKILL.md - vala-order-amortization-stat 订单均摊结算统计技能
|
||||||
|
|
||||||
## 技能描述
|
## 技能描述
|
||||||
用于统计指定账期内的订单均摊收入、退费冲销,按天计算均摊金额,支持部分退费场景的退后订单金额和周期自动计算。
|
用于统计指定账期内的订单均摊收入、退费冲销,按天计算均摊金额,支持部分退费场景的退后订单金额和周期自动计算。
|
||||||
|
|
||||||
## 触发场景
|
## 触发场景
|
||||||
当用户提到以下关键词组合时激活本技能:
|
当用户提到以下关键词组合时激活本技能:
|
||||||
1. 订单均摊、按天均摊、收入均摊
|
1. 订单均摊、按天均摊、收入均摊
|
||||||
2. 账期结算、月度结算、季度结算
|
2. 账期结算、月度结算、季度结算
|
||||||
3. 退费冲销、部分退款均摊
|
3. 退费冲销、部分退款均摊
|
||||||
|
|
||||||
## 前置依赖
|
## 前置依赖
|
||||||
1. 已连接线上PostgreSQL数据库 vala_bi 库
|
1. 已连接线上PostgreSQL数据库 vala_bi 库
|
||||||
2. 依赖表:
|
2. 依赖表:
|
||||||
- bi_vala_order:订单主表
|
- bi_vala_order:订单主表
|
||||||
- bi_refund_order:退费订单表
|
- bi_refund_order:退费订单表
|
||||||
- bi_vala_seasonal_ticket:季卡周期表
|
- bi_vala_seasonal_ticket:季卡周期表
|
||||||
- bi_vala_app_account:用户账户表(用于剔除测试账号)
|
- bi_vala_app_account:用户账户表(用于关联,不限制status)
|
||||||
3. 表关联规则:
|
3. 表关联规则:
|
||||||
- bi_vala_order.trade_no ↔ bi_refund_order.trade_no 关联
|
|
||||||
- bi_vala_order.out_trade_no ↔ bi_refund_order.out_trade_no 关联
|
- bi_vala_order.out_trade_no ↔ bi_refund_order.out_trade_no 关联
|
||||||
|
- bi_vala_order.trade_no ↔ bi_refund_order.trade_no 关联
|
||||||
|
|
||||||
## 执行方式
|
## 执行方式
|
||||||
|
|
||||||
本技能已预置为可直接执行的脚本,无需每次创建临时脚本。
|
本技能已预置为可直接执行的脚本。
|
||||||
|
|
||||||
### 快速执行
|
### 快速执行
|
||||||
```bash
|
```bash
|
||||||
@ -35,8 +39,8 @@ python3 ~/.openclaw/workspace/skills/vala-order-amortization-stat/run.py 2026-03
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 输出
|
### 输出
|
||||||
1. Excel报表保存到:`output/订单均摊结算报表_{start}_{end}.xlsx`(两个Sheet:汇总表、订单明细)
|
1. Excel报表保存到:`output/订单均摊结算报表_{start}_{end}.xlsx`(三个Sheet:汇总表、订单明细、本月预收账款)
|
||||||
2. 控制台输出JSON格式的汇总结果,可直接解析
|
2. 控制台输出JSON格式的汇总结果
|
||||||
|
|
||||||
### 目录结构
|
### 目录结构
|
||||||
```
|
```
|
||||||
@ -44,376 +48,102 @@ vala-order-amortization-stat/
|
|||||||
├── SKILL.md # 技能说明(本文件)
|
├── SKILL.md # 技能说明(本文件)
|
||||||
├── run.py # 执行脚本(传入账期参数即可)
|
├── run.py # 执行脚本(传入账期参数即可)
|
||||||
└── sql/
|
└── sql/
|
||||||
├── summary.sql # 汇总查询SQL模板
|
├── _common.sql # 公共CTE链(detail/summary 共用),含 order_base → order_with_cycle → order_with_cycle_fixed → order_classified → order_with_days
|
||||||
└── detail.sql # 订单明细查询SQL模板
|
├── detail.sql # 订单明细输出(SELECT from order_with_days)
|
||||||
|
├── summary.sql # 汇总输出(聚合 SELECT from order_with_days)
|
||||||
|
└── prepaid.sql # 月度预收账款(独立查询)
|
||||||
```
|
```
|
||||||
|
|
||||||
## 操作流程
|
## 操作流程
|
||||||
|
|
||||||
### 步骤1:确认核心参数
|
### 步骤1:确认核心参数
|
||||||
执行前必须向用户确认以下参数:
|
执行前向用户确认:
|
||||||
- 账期起始日期(格式:YYYY-MM-DD)
|
- 账期起始日期(格式:YYYY-MM-DD)
|
||||||
- 账期结束日期(格式:YYYY-MM-DD)
|
- 账期结束日期(格式:YYYY-MM-DD)
|
||||||
- 默认输出维度:账期整体汇总,无需按天/周/月拆分,无需分渠道统计
|
|
||||||
### 步骤2:数据过滤规则
|
### 步骤2:数据过滤规则
|
||||||
1. 订单范围:
|
1. 订单范围:
|
||||||
- 2025-06-01 至 账期结束日 内创建的所有订单
|
- 2025-06-01 至 账期结束日 内创建的所有订单
|
||||||
- bi_vala_order.status IN (3,4)(已完成、已退款订单)
|
- bi_vala_order.order_status IN (3,4)(已完成、已退款订单)
|
||||||
- 订单实际支付金额≥10元:bi_vala_order.pay_amount_int ≥ 1000(单位:分)
|
- 订单实际支付金额≥10元:bi_vala_order.pay_amount_int ≥ 1000(单位:分)
|
||||||
- 关联bi_vala_app_account表(不限制status,不剔除测试账号)
|
- 关联 bi_vala_app_account 表(不限制 status,不剔除测试账号)
|
||||||
2. 退费范围:
|
2. 退费范围:
|
||||||
- 退费完成时间落在账期内的所有退款成功记录,判定条件:bi_refund_order.updated_at(退款处理完成时间)在账期起止时间范围内
|
- 退费记录范围:截至账期结束日前的所有退费成功记录,用于标记订单退费状态
|
||||||
- bi_refund_order.status = 3(退款处理完成)
|
- 账期内退费判定:bi_refund_order.updated_at 在账期起止范围内,status = 3
|
||||||
3. 账期前退费订单处理规则:
|
3. 账期前退费订单处理规则:
|
||||||
- 退费完成时间在账期起始日之前的订单,不会纳入本账期的冲销逻辑:
|
- 全额退费(is_full_refund = 1,累计退费金额 ≥ 原订单金额):本账期完全不统计该订单
|
||||||
- 全额退费订单:冲销动作已在退费发生的对应账期执行完毕,本账期完全不统计该订单的任何数据
|
- 部分退费(is_full_refund = 0):按退后订单金额/退后周期在本账期内正常均摊
|
||||||
- 部分退费订单:冲销动作、退后订单金额/剩余周期调整已在退费发生的对应账期完成,本账期仅按调整后的退后订单金额计算均摊,不产生额外冲销金额
|
4. 试用期内退费处理规则:
|
||||||
|
- 退费日期 < 转正日(下单日+7天)的退费订单,从未产生均摊收入,不触发冲销
|
||||||
|
|
||||||
### 步骤3:核心计算逻辑
|
### 步骤3:核心计算逻辑
|
||||||
|
|
||||||
#### 3.1 周期计算
|
#### 3.1 周期计算
|
||||||
- 每个订单的总均摊周期 = bi_vala_seasonal_ticket中同一order_no下status != -1的不同id数量 × 90天
|
- 原始均摊周期 = bi_vala_seasonal_ticket 中同一 out_trade_no 下所有不同 id 数量 × 90 天
|
||||||
- 转正日期规则:试用期7天,包含下单日:
|
- 退后均摊周期 = bi_vala_seasonal_ticket 中同一 out_trade_no 下 status != -1 的不同 id 数量 × 90 天
|
||||||
- 均摊起始日(转正日期)= 订单下单日 + 7天,下单日取bi_vala_order.created_at的日期部分
|
- 转正日期 = 订单下单日 + 7 天(下单日取 bi_vala_order.pay_success_date 的日期部分)
|
||||||
- 示例:4月22日下单,4月29日00:00:00为转正日,即均摊起始日
|
- 示例:4月22日下单,4月29日00:00:00为转正日
|
||||||
- 税率规则:
|
|
||||||
- 订单下单时间在2026年5月1日之前:税率1%
|
|
||||||
- 订单下单时间在2026年5月1日及之后:税率6%
|
|
||||||
- 订单税后金额 = (bi_vala_order.pay_amount_int / 100) / (1 + 税率)(单位:元)
|
|
||||||
- 订单税费金额 = (bi_vala_order.pay_amount_int / 100) / (1 + 税率) × 税率(单位:元)
|
|
||||||
- **计税方式**:价外税,即订单金额为含税价,需先剥离税额再计算税后金额
|
|
||||||
- **均摊规则更新**:所有均摊计算均基于税后金额进行,含税金额和税费单独统计
|
|
||||||
- 日均摊金额(税后)= 订单税后金额 / 总均摊周期
|
|
||||||
- 🔹 周期最后一天补差规则:订单均摊周期的最后一天,均摊金额不按日均摊计算,采用补差方式确保总额完全匹配:
|
|
||||||
- 正常订单:最后一天均摊金额 = 订单税后总金额 - 前(总均摊周期-1)天累计已均摊金额
|
|
||||||
- 部分退费订单:最后一天均摊金额 = 退后订单税后金额 - 剩余均摊周期前(剩余天数-1)天累计已均摊金额
|
|
||||||
避免浮点精度导致的金额尾差
|
|
||||||
#### 3.2 退费场景计算
|
|
||||||
所有退费冲销金额均为税后金额,税费同步对应冲销
|
|
||||||
1. **所有退费订单(全额/部分)通用计算**:
|
|
||||||
- 历史退费订单冲销均摊金额:从2026年9月1日起至账期起始日之前,该订单已产生的全部税后均摊金额(统一显示为负数,用于冲销)
|
|
||||||
- 历史退费订单冲销税费金额:上述历史均摊金额对应的税费(统一显示为负数,用于冲销)
|
|
||||||
2. **全额退费(bi_refund_order.refund_type = 2)**:无需额外计算补充均摊,仅执行上述通用冲销逻辑
|
|
||||||
3. **部分退费(bi_refund_order.refund_type = 3)**:在通用冲销逻辑基础上,额外计算:
|
|
||||||
- 历史部分退费订单补充均摊金额:部分退费后的退后订单待均摊税后金额,在本账期内产生的均摊收入
|
|
||||||
- 历史部分退费订单补充税费金额:上述补充均摊金额对应的税费
|
|
||||||
#### 3.3 账期内收入计算
|
|
||||||
账期内总收入 = 账期内所有正常订单的日均摊金额总和 + 账期内所有退费冲销金额总和
|
|
||||||
## 核心SQL模板
|
|
||||||
```sql
|
|
||||||
-- 步骤0:获取所有历史退费记录(无时间范围,用于标记订单是否为退费订单,不受当前账期限制)
|
|
||||||
all_refund_records AS (
|
|
||||||
SELECT
|
|
||||||
out_trade_no AS order_no,
|
|
||||||
SUM(refund_amount_int) / 100 AS total_refund_amount, -- 累计退费总金额(支持多笔部分退费叠加)
|
|
||||||
MAX(CASE WHEN refund_type = 2 THEN 1 ELSE 0 END) AS is_full_refund, -- 是否全额退费(只要有一笔全额退费即标记为1)
|
|
||||||
MAX(refund_type) AS refund_type,
|
|
||||||
MAX(DATE(updated_at)) AS latest_refund_date -- 最后一次退费完成时间
|
|
||||||
FROM bi_refund_order
|
|
||||||
WHERE status = 3
|
|
||||||
GROUP BY out_trade_no
|
|
||||||
),
|
|
||||||
-- 步骤1:获取所有符合条件的订单基础信息
|
|
||||||
order_base AS (
|
|
||||||
SELECT
|
|
||||||
o.id AS order_id,
|
|
||||||
o.out_trade_no AS order_no, -- 修正:bi_vala_order无order_no字段,使用out_trade_no作为订单号
|
|
||||||
o.trade_no, -- 新增:关联退费订单需要trade_no字段
|
|
||||||
o.pay_amount_int / 100 AS pay_amount,
|
|
||||||
-- 计算税率、税费、税后金额
|
|
||||||
CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END AS tax_rate,
|
|
||||||
(o.pay_amount_int / 100) * CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END AS tax_amount,
|
|
||||||
(o.pay_amount_int / 100) * (1 - CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END) AS after_tax_amount,
|
|
||||||
DATE_ADD(DATE(o.created_at), INTERVAL 7 DAY) AS amortization_start_date,
|
|
||||||
o.order_status, -- 修正:字段名是order_status不是status
|
|
||||||
a.id AS account_id,
|
|
||||||
o.key_from,
|
|
||||||
o.sale_channel,
|
|
||||||
-- 新增:订单退费标记字段(所有历史退费均可识别,不受当前账期限制)
|
|
||||||
CASE WHEN ar.order_no IS NOT NULL THEN 1 ELSE 0 END AS has_refund, -- 是否有退费记录
|
|
||||||
COALESCE(ar.total_refund_amount, 0) AS total_refund_amount, -- 累计退费金额
|
|
||||||
COALESCE(ar.is_full_refund, 0) AS is_full_refund, -- 是否全额退费
|
|
||||||
ar.refund_type,
|
|
||||||
ar.latest_refund_date -- 退费完成日期(解决历史退费订单退费日期为空的问题)
|
|
||||||
FROM bi_vala_order o
|
|
||||||
JOIN bi_vala_app_account a ON o.account_id = a.id
|
|
||||||
LEFT JOIN all_refund_records ar ON o.out_trade_no = ar.order_no -- 关联全量退费记录,不限制时间
|
|
||||||
WHERE
|
|
||||||
o.created_at >= '2025-09-01'
|
|
||||||
AND o.order_status IN (3,4) -- 修正:字段名是order_status不是status
|
|
||||||
AND o.pay_amount_int >= 1000
|
|
||||||
AND a.status = 1
|
|
||||||
),
|
|
||||||
-- 步骤2:计算每个订单的总均摊周期
|
|
||||||
order_cycle AS (
|
|
||||||
SELECT
|
|
||||||
order_no,
|
|
||||||
COUNT(DISTINCT id) * 90 AS total_cycle_days
|
|
||||||
FROM bi_vala_seasonal_ticket
|
|
||||||
WHERE status != -1
|
|
||||||
GROUP BY order_no
|
|
||||||
),
|
|
||||||
-- 步骤3:获取账期内的退费记录(仅用于计算当前账期的冲销金额,不影响订单退费状态标记)
|
|
||||||
refund_records AS (
|
|
||||||
SELECT
|
|
||||||
r.out_trade_no AS order_no,
|
|
||||||
r.refund_amount_int / 100 AS refund_amount, -- 修正:使用分为单位的refund_amount_int,避免字符串转换问题,除以100得到元
|
|
||||||
r.refund_type,
|
|
||||||
DATE(r.updated_at) AS refund_date -- 退费日期使用updated_at(status=3时即为退费完成时间)
|
|
||||||
FROM bi_refund_order r
|
|
||||||
JOIN order_base o ON r.out_trade_no = o.order_no OR r.trade_no = o.trade_no -- 修正:支持out_trade_no/trade_no双字段匹配退费订单
|
|
||||||
WHERE
|
|
||||||
r.status = 3
|
|
||||||
AND r.updated_at BETWEEN '${账期起始日}' AND '${账期结束日}'
|
|
||||||
)
|
|
||||||
,
|
|
||||||
-- 步骤4:合并订单基础信息与均摊周期
|
|
||||||
order_with_cycle AS (
|
|
||||||
SELECT
|
|
||||||
ob.*,
|
|
||||||
COALESCE(oc.total_cycle_days, 0) AS total_cycle_days,
|
|
||||||
-- 均摊结束日 = 转正日 + 总周期天数 - 1
|
|
||||||
ob.amortization_start_date + COALESCE(oc.total_cycle_days, 0) - 1 AS amortization_end_date,
|
|
||||||
-- 日均摊金额(税后)
|
|
||||||
CASE WHEN COALESCE(oc.total_cycle_days, 0) > 0
|
|
||||||
THEN ob.after_tax_amount / oc.total_cycle_days
|
|
||||||
ELSE 0
|
|
||||||
END AS daily_amort_amount,
|
|
||||||
-- 日税费均摊
|
|
||||||
CASE WHEN COALESCE(oc.total_cycle_days, 0) > 0
|
|
||||||
THEN ob.tax_amount / oc.total_cycle_days
|
|
||||||
ELSE 0
|
|
||||||
END AS daily_tax_amount
|
|
||||||
FROM order_base ob
|
|
||||||
LEFT JOIN order_cycle oc ON ob.order_no = oc.order_no
|
|
||||||
),
|
|
||||||
|
|
||||||
-- 步骤5:判断订单在账期内的状态分类,并计算各类均摊金额
|
#### 3.1.1 退费在账期后 + 季卡已删的fallback(2026-05-15 修复)
|
||||||
order_amortization AS (
|
当退费发生在账期结束后,季卡状态已经被置为 `status=-1`,导致 `active_cycle_days=0`,但账期内该订单仍处于正常均摊状态,不应被排除。
|
||||||
SELECT
|
- 判定条件:`active_cycle_days=0 AND original_cycle_days>0 AND has_refund=0`(退费记录被 `DATE(updated_at) <= period_end` 过滤掉)
|
||||||
owc.*,
|
- 处理:`total_cycle_days` 回退到 `original_cycle_days`,后续计算(均摊结束日、日均摊金额、日税费)均在 `order_with_cycle_fixed` CTE 中用 effective `total_cycle_days` 重算
|
||||||
-- 是否为账期内新增订单
|
- 避免此问题的正确做法:不应依赖季卡状态判断订单是否在本账期内正常,退费过滤的时效性需要与订单状态独立判断
|
||||||
CASE WHEN DATE(owc.amortization_start_date) <= '${账期结束日}'
|
|
||||||
AND owc.amortization_start_date >= '${账期起始日}' - INTERVAL '6 days'
|
|
||||||
THEN 1 ELSE 0 END AS is_new_in_period,
|
|
||||||
-- 账期内新增且已转正
|
|
||||||
CASE WHEN DATE(owc.amortization_start_date) >= '${账期起始日}'
|
|
||||||
AND DATE(owc.amortization_start_date) <= '${账期结束日}'
|
|
||||||
THEN 1
|
|
||||||
WHEN owc.amortization_start_date < '${账期起始日}'
|
|
||||||
THEN 1
|
|
||||||
ELSE 0
|
|
||||||
END AS is_formal,
|
|
||||||
|
|
||||||
-- ===== 正常均摊金额计算(未退费 或 账期前部分退费的订单)=====
|
#### 3.2 税率与税费计算(价外税)
|
||||||
-- 均摊有效起始日(取转正日和账期起始日的较大值)
|
- 税率:下单时间 < 2026-05-01 → 1%;≥ 2026-05-01 → 6%
|
||||||
GREATEST(owc.amortization_start_date, '${账期起始日}'::date) AS eff_start,
|
- 计税方式:价外税,订单金额为含税价
|
||||||
-- 均摊有效结束日(取均摊结束日和账期结束日的较小值)
|
- 税后金额(元)= (pay_amount_int / 100) / (1 + tax_rate)
|
||||||
LEAST(owc.amortization_end_date, '${账期结束日}'::date) AS eff_end
|
- 税费金额(元)= tax_amount = 税后金额 × tax_rate
|
||||||
|
|
||||||
FROM order_with_cycle owc
|
#### 3.3 均摊规则
|
||||||
WHERE owc.total_cycle_days > 0 -- 排除无均摊周期的异常订单
|
- 所有均摊计算均基于税后金额进行
|
||||||
),
|
- 日均摊金额(税后)= 税后金额 / 总均摊周期
|
||||||
|
- 🔹 周期最后一天补差规则:订单均摊周期的最后一天,均摊金额 = 税后总金额 - 前(N-1)天累计已均摊金额,避免浮点尾差
|
||||||
|
|
||||||
-- 步骤6:计算每个订单在账期内的均摊天数和金额
|
#### 3.4 退费场景分类(6类)
|
||||||
order_period_amort AS (
|
| 场景 | 描述 | 处理方式 |
|
||||||
SELECT
|
|------|------|---------|
|
||||||
oa.*,
|
| A | 账期内下单 + 部分退费 | 按退后金额从转正日均摊(period_amort_days) |
|
||||||
-- 账期内有效均摊天数
|
| B | 历史下单 + 账期内部分退费 | 冲销历史均摊 + 按退后金额重新均摊(refunded_amort_days_to_period_end) |
|
||||||
CASE WHEN oa.eff_end >= oa.eff_start
|
| C | 账期前部分退费 | 按退后金额正常均摊(period_amort_days),无冲销 |
|
||||||
THEN (oa.eff_end - oa.eff_start + 1)
|
| D | 账期内下单 + 全额退费 | 不产生均摊,不触发冲销 |
|
||||||
ELSE 0
|
| E | 历史下单 + 账期内全额退费 | 冲销全部历史均摊,无退后均摊 |
|
||||||
END AS period_amort_days,
|
| F | 账期前全额退费 | 本账期完全排除 |
|
||||||
|
|
||||||
-- 该订单从转正日到账期结束日的总已均摊天数(用于计算历史累计)
|
|
||||||
CASE WHEN '${账期结束日}'::date >= oa.amortization_start_date
|
|
||||||
THEN LEAST('${账期结束日}'::date, oa.amortization_end_date) - oa.amortization_start_date + 1
|
|
||||||
ELSE 0
|
|
||||||
END AS total_amorted_days_to_period_end,
|
|
||||||
|
|
||||||
-- 该订单从转正日到账期起始日前一天的历史已均摊天数
|
|
||||||
CASE WHEN '${账期起始日}'::date > oa.amortization_start_date
|
|
||||||
THEN LEAST('${账期起始日}'::date - 1, oa.amortization_end_date) - oa.amortization_start_date + 1
|
|
||||||
ELSE 0
|
|
||||||
END AS historical_amorted_days
|
|
||||||
|
|
||||||
FROM order_amortization oa
|
|
||||||
),
|
|
||||||
|
|
||||||
-- 步骤7:计算最终均摊金额(含补差逻辑)
|
|
||||||
order_final_amort AS (
|
|
||||||
SELECT
|
|
||||||
opa.*,
|
|
||||||
|
|
||||||
-- === 正常订单(未退费 或 账期前已处理的退费订单)在账期内的均摊 ===
|
|
||||||
|
|
||||||
-- 判断是否为账期前全额退费(本账期完全不统计)
|
|
||||||
CASE WHEN opa.has_refund = 1 AND opa.is_full_refund = 1
|
|
||||||
AND opa.latest_refund_date < '${账期起始日}'::date
|
|
||||||
THEN 1 ELSE 0
|
|
||||||
END AS is_pre_period_full_refund,
|
|
||||||
|
|
||||||
-- 判断是否为账期前部分退费(本账期按退后订单金额均摊)
|
|
||||||
CASE WHEN opa.has_refund = 1 AND opa.is_full_refund = 0
|
|
||||||
AND opa.latest_refund_date < '${账期起始日}'::date
|
|
||||||
THEN 1 ELSE 0
|
|
||||||
END AS is_pre_period_partial_refund,
|
|
||||||
|
|
||||||
-- 判断是否为账期内退费
|
|
||||||
CASE WHEN opa.has_refund = 1
|
|
||||||
AND opa.latest_refund_date >= '${账期起始日}'::date
|
|
||||||
AND opa.latest_refund_date <= '${账期结束日}'::date
|
|
||||||
THEN 1 ELSE 0
|
|
||||||
END AS is_current_period_refund,
|
|
||||||
|
|
||||||
-- 部分退费后的退后订单税后金额
|
|
||||||
CASE WHEN opa.has_refund = 1 AND opa.is_full_refund = 0
|
|
||||||
THEN (opa.pay_amount - opa.total_refund_amount) * (1 - opa.tax_rate)
|
|
||||||
ELSE opa.after_tax_amount
|
|
||||||
END AS refunded_after_tax_amount,
|
|
||||||
|
|
||||||
-- 部分退费后的退后订单税费
|
|
||||||
CASE WHEN opa.has_refund = 1 AND opa.is_full_refund = 0
|
|
||||||
THEN (opa.pay_amount - opa.total_refund_amount) * opa.tax_rate
|
|
||||||
ELSE opa.tax_amount
|
|
||||||
END AS refunded_tax_amount
|
|
||||||
|
|
||||||
FROM order_period_amort opa
|
|
||||||
),
|
|
||||||
|
|
||||||
-- 步骤8:最终汇总计算
|
|
||||||
summary_calc AS (
|
|
||||||
SELECT
|
|
||||||
-- ===== 账期内新增订单统计 =====
|
|
||||||
COUNT(CASE WHEN DATE(amortization_start_date - INTERVAL '7 days') >= '${账期起始日}'::date
|
|
||||||
AND DATE(amortization_start_date - INTERVAL '7 days') <= '${账期结束日}'::date
|
|
||||||
THEN 1 END) AS total_new_orders,
|
|
||||||
|
|
||||||
COUNT(CASE WHEN DATE(amortization_start_date - INTERVAL '7 days') >= '${账期起始日}'::date
|
|
||||||
AND DATE(amortization_start_date - INTERVAL '7 days') <= '${账期结束日}'::date
|
|
||||||
AND amortization_start_date <= '${账期结束日}'::date
|
|
||||||
THEN 1 END) AS formal_orders,
|
|
||||||
|
|
||||||
COUNT(CASE WHEN DATE(amortization_start_date - INTERVAL '7 days') >= '${账期起始日}'::date
|
|
||||||
AND DATE(amortization_start_date - INTERVAL '7 days') <= '${账期结束日}'::date
|
|
||||||
AND amortization_start_date > '${账期结束日}'::date
|
|
||||||
THEN 1 END) AS trial_orders,
|
|
||||||
|
|
||||||
-- ===== 冲销前均摊金额(税后):所有非"账期前全额退费"订单在账期内的均摊 =====
|
|
||||||
ROUND(SUM(
|
|
||||||
CASE
|
|
||||||
WHEN is_pre_period_full_refund = 1 THEN 0 -- 账期前全额退费,不统计
|
|
||||||
WHEN is_pre_period_partial_refund = 1 THEN -- 账期前部分退费,按退后订单金额均摊
|
|
||||||
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
|
||||||
THEN refunded_after_tax_amount / total_cycle_days * period_amort_days
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
WHEN is_current_period_refund = 0 AND period_amort_days > 0 THEN -- 正常未退费订单
|
|
||||||
CASE
|
|
||||||
-- 补差逻辑:如果账期结束日恰好是均摊最后一天
|
|
||||||
WHEN eff_end = amortization_end_date
|
|
||||||
THEN after_tax_amount - daily_amort_amount * (total_cycle_days - 1)
|
|
||||||
+ daily_amort_amount * (period_amort_days - 1)
|
|
||||||
ELSE daily_amort_amount * period_amort_days
|
|
||||||
END
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS pre_writeoff_amort,
|
|
||||||
|
|
||||||
-- ===== 冲销前税费 =====
|
|
||||||
ROUND(SUM(
|
|
||||||
CASE
|
|
||||||
WHEN is_pre_period_full_refund = 1 THEN 0
|
|
||||||
WHEN is_pre_period_partial_refund = 1 THEN
|
|
||||||
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
|
||||||
THEN refunded_tax_amount / total_cycle_days * period_amort_days
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
WHEN is_current_period_refund = 0 AND period_amort_days > 0 THEN
|
|
||||||
daily_tax_amount * period_amort_days
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS pre_writeoff_tax,
|
|
||||||
|
|
||||||
-- ===== 冲销均摊金额(税后):账期内退费订单的历史已均摊金额 =====
|
|
||||||
ROUND(SUM(
|
|
||||||
CASE WHEN is_current_period_refund = 1 AND historical_amorted_days > 0
|
|
||||||
THEN daily_amort_amount * historical_amorted_days
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS writeoff_amort,
|
|
||||||
|
|
||||||
-- ===== 冲销税费 =====
|
|
||||||
ROUND(SUM(
|
|
||||||
CASE WHEN is_current_period_refund = 1 AND historical_amorted_days > 0
|
|
||||||
THEN daily_tax_amount * historical_amorted_days
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS writeoff_tax,
|
|
||||||
|
|
||||||
-- ===== 补充均摊金额(税后):账期内部分退费订单,退后订单金额的均摊 =====
|
|
||||||
ROUND(SUM(
|
|
||||||
CASE WHEN is_current_period_refund = 1
|
|
||||||
AND is_full_refund = 0
|
|
||||||
AND total_cycle_days > 0
|
|
||||||
AND period_amort_days > 0
|
|
||||||
THEN (pay_amount - total_refund_amount) * (1 - tax_rate) / total_cycle_days * period_amort_days
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS supplement_amort,
|
|
||||||
|
|
||||||
-- ===== 补充税费 =====
|
|
||||||
ROUND(SUM(
|
|
||||||
CASE WHEN is_current_period_refund = 1
|
|
||||||
AND is_full_refund = 0
|
|
||||||
AND total_cycle_days > 0
|
|
||||||
AND period_amort_days > 0
|
|
||||||
THEN (pay_amount - total_refund_amount) * tax_rate / total_cycle_days * period_amort_days
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS supplement_tax
|
|
||||||
|
|
||||||
FROM order_final_amort
|
|
||||||
)
|
|
||||||
|
|
||||||
-- 最终输出汇总结果
|
|
||||||
SELECT
|
|
||||||
total_new_orders AS "订单数",
|
|
||||||
formal_orders AS "正式订单数",
|
|
||||||
trial_orders AS "试用订单数",
|
|
||||||
pre_writeoff_tax AS "冲销前税费",
|
|
||||||
writeoff_tax AS "冲销税费",
|
|
||||||
supplement_tax AS "补充税费",
|
|
||||||
ROUND((pre_writeoff_tax - writeoff_tax + supplement_tax)::numeric, 2) AS "冲销后税费",
|
|
||||||
pre_writeoff_amort AS "冲销前均摊金额",
|
|
||||||
writeoff_amort AS "冲销均摊金额",
|
|
||||||
supplement_amort AS "补充均摊金额",
|
|
||||||
ROUND((pre_writeoff_amort - writeoff_amort + supplement_amort)::numeric, 2) AS "冲销后均摊金额"
|
|
||||||
FROM summary_calc;
|
|
||||||
```
|
|
||||||
## 输出格式
|
## 输出格式
|
||||||
1. 优先输出Excel报表,存放于output/目录下,命名格式:`订单均摊结算报表_${账期起始日}_${账期结束日}.xlsx`
|
1. Excel报表存放于 `output/` 目录,命名格式:`订单均摊结算报表_{start}_{end}.xlsx`
|
||||||
2. 报表包含以下Sheet:
|
2. 报表包含以下Sheet:
|
||||||
- 汇总表:订单数、正式订单数、试用订单数、冲销前税费、冲销税费、补充税费、冲销后税费、冲销前均摊金额、冲销均摊金额、补充均摊金额、冲销后均摊金额
|
|
||||||
- 订单明细:订单号、下单日期、退费日期、订单分类、原订单金额、退款金额、退后金额、税率、原订单税额、当前税后金额、当前税额、原始均摊周期、退后均摊周期、历史累计均摊金额、冲销历史累计摊销金额、退后订单历史累计摊销金额、冲销原税款、冲销未确认收入、当期摊销金额、账期税额、未确认收入、剩余周期
|
### 汇总表(Sheet1)
|
||||||
|
| 列 | 说明 |
|
||||||
|
|----|------|
|
||||||
|
| 订单数 | 账期内下单的订单总数 |
|
||||||
|
| 正式订单数 | 账期内下单且已过7天试用期的订单数 |
|
||||||
|
| 试用订单数 | 账期内下单但仍处于试用期的订单数 |
|
||||||
|
| 冲销历史累计摊销金额 | 场景B/E冲销的历史均摊金额总和(负数) |
|
||||||
|
| 退后订单历史累计摊销金额 | 站在账期最后一天看的历史有效摊销金额总和 |
|
||||||
|
| 冲销原税款 | 场景B/E冲销的税费总和(负数) |
|
||||||
|
| 冲销未确认收入 | 场景B/E冲销的未确认收入总和(负数) |
|
||||||
|
| 当期摊销金额 | 本账期内所有订单产生的均摊收入总和(核心指标) |
|
||||||
|
| 账期税额 | 账期内新增已转正订单的税费总和 |
|
||||||
|
| 未确认收入 | 截至账期最后一天尚未均摊的税后金额总和 |
|
||||||
|
|
||||||
|
### 订单明细(Sheet2)
|
||||||
|
每笔订单一行,包含:订单号、下单日期、退费日期、订单分类、原订单金额、退款金额、退后金额、税率、原订单税额、当前税后金额、当前税额、原始均摊周期、退后均摊周期、历史累计均摊金额、冲销历史累计摊销金额、退后订单历史累计摊销金额、冲销原税款、冲销未确认收入、当期摊销金额、账期税额、未确认收入、剩余周期
|
||||||
|
|
||||||
|
### 本月预收账款(Sheet3)
|
||||||
|
按"上月订单转正"/"本月订单转正"/"本月订单未转正"三类统计新增和退费含税金额。
|
||||||
|
|
||||||
3. 文字回复核心指标:
|
3. 文字回复核心指标:
|
||||||
> 📊 账期${账期起始日}至${账期结束日}均摊结算结果:
|
> 📊 账期{start}至{end}均摊结算结果:
|
||||||
> 总订单数:XXX单
|
> 总订单数:XXX单 | 正式订单数:XXX单 | 试用订单数:XXX单
|
||||||
> 正式订单数:XXX单
|
> 当期摊销金额(税后净收入):XXX元 | 账期税额:XXX元
|
||||||
> 试用订单数:XXX单
|
> 冲销历史累计摊销金额:XXX元 | 未确认收入:XXX元
|
||||||
> 冲销后税费:XXX元
|
|
||||||
> 冲销后均摊金额(税后净收入):XXX元
|
|
||||||
## 汇总指标计算逻辑(所有均摊金额均为税后金额)
|
|
||||||
1. **订单数**:账期内(下单日落在账期起止时间范围内)新增的所有符合条件的订单总数量
|
|
||||||
2. **正式订单数**:账期内新增的订单中,截止账期结束日已过7天试用期(下单日+7天 ≤ 账期结束日)的订单数量,仅正式订单开始参与均摊计算
|
|
||||||
3. **试用订单数**:账期内新增的订单中,截止账期结束日仍处于7天试用期内(下单日+7天 > 账期结束日)的订单数量,试用订单未开始均摊,不参与金额计算
|
|
||||||
4. **冲销前税费**:账期内所有正式订单的含税订单总金额 × 各订单对应税率 的总和
|
|
||||||
5. **冲销税费**:本账期内发生退费的所有订单的含税订单总金额 × 各订单对应税率 的总和(正数展示,计算时扣除)
|
|
||||||
6. **补充税费**:本账期内发生部分退费的订单,退后订单含税金额 × 各订单对应税率 的总和,其中:部分退费订单退后含税金额 = 原订单含税金额 - 退费含税金额
|
|
||||||
7. **冲销后税费**:账期内最终确认的总税费,计算公式:`冲销后税费 = 冲销前税费 - 冲销税费 + 补充税费`
|
|
||||||
8. **冲销前均摊金额(税后)**:历史未退费订单 + 账期内正式订单 在本账期内产生的税后均摊收入总和
|
|
||||||
9. **冲销均摊金额(税后)**:本账期内发生退费的所有订单,需要冲销的历史均摊金额总和(正数展示,计算时扣除)
|
|
||||||
10. **补充均摊金额(税后)**:本账期内发生部分退费的订单,从转正日起至账期最后一日产生的税后均摊金额总和
|
|
||||||
11. **冲销后均摊金额(税后)**:账期内最终确认的税后净收入,计算公式:`冲销后均摊金额 = 冲销前均摊金额 - 冲销均摊金额 + 补充均摊金额`
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
1. 所有金额保留2位小数,百分比保留1位小数
|
1. 所有金额保留2位小数,百分比保留1位小数
|
||||||
2. 部分退费场景需确保bi_vala_seasonal_ticket中对应退费部分的status已更新为-1
|
2. 部分退费场景:需确保 bi_vala_seasonal_ticket 中对应退费部分的 status 已更新为 -1
|
||||||
3. 自动剔除测试账号,无需额外过滤
|
3. 税后金额计算使用价外税公式:含税价 / (1 + 税率)
|
||||||
4. 冲销金额统一显示为负数
|
4. SQL 模板统一使用 `{period_start}` 和 `{period_end}` 占位符,由 run.py 替换
|
||||||
|
5. 公共 CTE 链位于 `sql/_common.sql`,detail/summary 共享,修改业务逻辑只需改一处
|
||||||
|
|||||||
@ -1,5 +1,11 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
订单均摊结算统计脚本
|
||||||
|
用法:
|
||||||
|
python3 run.py --start 2026-03-01 --end 2026-03-31
|
||||||
|
python3 run.py 2026-03-01 2026-03-31
|
||||||
|
"""
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
@ -9,7 +15,6 @@ import psycopg2
|
|||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# Parse command line arguments
|
|
||||||
parser = argparse.ArgumentParser(description='订单均摊结算统计脚本')
|
parser = argparse.ArgumentParser(description='订单均摊结算统计脚本')
|
||||||
parser.add_argument('--start', type=str, help='账期起始日(YYYY-MM-DD)')
|
parser.add_argument('--start', type=str, help='账期起始日(YYYY-MM-DD)')
|
||||||
parser.add_argument('--end', type=str, help='账期结束日(YYYY-MM-DD)')
|
parser.add_argument('--end', type=str, help='账期结束日(YYYY-MM-DD)')
|
||||||
@ -17,7 +22,6 @@ def main():
|
|||||||
parser.add_argument('pos_end', nargs='?', type=str, help='账期结束日(位置参数)')
|
parser.add_argument('pos_end', nargs='?', type=str, help='账期结束日(位置参数)')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Get start and end dates
|
|
||||||
start_date = args.start or args.pos_start
|
start_date = args.start or args.pos_start
|
||||||
end_date = args.end or args.pos_end
|
end_date = args.end or args.pos_end
|
||||||
|
|
||||||
@ -27,46 +31,51 @@ def main():
|
|||||||
print("用法2:python3 run.py 2026-03-01 2026-03-31", file=sys.stderr)
|
print("用法2:python3 run.py 2026-03-01 2026-03-31", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Read PostgreSQL password from secrets.env
|
# ── 1. 读取数据库密码 ──
|
||||||
secrets_path = '/root/.openclaw/workspace/secrets.env'
|
secrets_path = '/root/.openclaw/workspace/secrets.env'
|
||||||
if not os.path.exists(secrets_path):
|
if not os.path.exists(secrets_path):
|
||||||
print(f"错误:secrets.env文件不存在 {secrets_path}", file=sys.stderr)
|
print(f"错误:secrets.env文件不存在 {secrets_path}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
with open(secrets_path, 'r', encoding='utf-8') as f:
|
with open(secrets_path, 'r', encoding='utf-8') as f:
|
||||||
secrets_content = f.read()
|
secrets_content = f.read()
|
||||||
|
|
||||||
pg_password_match = re.search(r"PG_ONLINE_PASSWORD='(.*)'", secrets_content)
|
pg_password_match = re.search(r"PG_ONLINE_PASSWORD='(.*)'", secrets_content)
|
||||||
if not pg_password_match:
|
if not pg_password_match:
|
||||||
print("错误:未找到PG_ONLINE_PASSWORD配置", file=sys.stderr)
|
print("错误:未找到PG_ONLINE_PASSWORD配置", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
pg_password = pg_password_match.group(1)
|
pg_password = pg_password_match.group(1)
|
||||||
|
|
||||||
# Read SQL templates
|
# ── 2. 读取 SQL 模板 ──
|
||||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
summary_sql_path = os.path.join(script_dir, 'sql', 'summary.sql')
|
sql_dir = os.path.join(script_dir, 'sql')
|
||||||
detail_sql_path = os.path.join(script_dir, 'sql', 'detail.sql')
|
common_sql_path = os.path.join(sql_dir, '_common.sql')
|
||||||
prepaid_sql_path = os.path.join(script_dir, 'sql', 'prepaid.sql')
|
detail_sql_path = os.path.join(sql_dir, 'detail.sql')
|
||||||
|
summary_sql_path = os.path.join(sql_dir, 'summary.sql')
|
||||||
|
prepaid_sql_path = os.path.join(sql_dir, 'prepaid.sql')
|
||||||
|
|
||||||
if not os.path.exists(summary_sql_path) or not os.path.exists(detail_sql_path):
|
for p in [common_sql_path, detail_sql_path, summary_sql_path]:
|
||||||
print("错误:SQL模板文件不存在", file=sys.stderr)
|
if not os.path.exists(p):
|
||||||
|
print(f"错误:SQL模板文件不存在 {p}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
with open(summary_sql_path, 'r', encoding='utf-8') as f:
|
with open(common_sql_path, 'r', encoding='utf-8') as f:
|
||||||
summary_sql_template = f.read()
|
common_sql_raw = f.read()
|
||||||
with open(detail_sql_path, 'r', encoding='utf-8') as f:
|
with open(detail_sql_path, 'r', encoding='utf-8') as f:
|
||||||
detail_sql_template = f.read()
|
detail_output_raw = f.read()
|
||||||
prepaid_sql_template = None
|
with open(summary_sql_path, 'r', encoding='utf-8') as f:
|
||||||
|
summary_output_raw = f.read()
|
||||||
|
|
||||||
|
# 拼接公共CTE + 各自SELECT → 统一替换参数(避免多次格式化)
|
||||||
|
detail_sql = (common_sql_raw + "\n" + detail_output_raw).format(
|
||||||
|
period_start=start_date, period_end=end_date)
|
||||||
|
summary_sql = (common_sql_raw + "\n" + summary_output_raw).format(
|
||||||
|
period_start=start_date, period_end=end_date)
|
||||||
|
|
||||||
|
prepaid_sql = None
|
||||||
if os.path.exists(prepaid_sql_path):
|
if os.path.exists(prepaid_sql_path):
|
||||||
with open(prepaid_sql_path, 'r', encoding='utf-8') as f:
|
with open(prepaid_sql_path, 'r', encoding='utf-8') as f:
|
||||||
prepaid_sql_template = f.read()
|
prepaid_sql = f.read().format(period_start=start_date, period_end=end_date)
|
||||||
|
|
||||||
# Replace placeholders
|
# ── 3. 连接数据库并执行查询 ──
|
||||||
summary_sql = summary_sql_template.format(period_start=start_date, period_end=end_date)
|
|
||||||
detail_sql = detail_sql_template.format(period_start=start_date, period_end=end_date)
|
|
||||||
prepaid_sql = prepaid_sql_template.format(period_start=start_date, period_end=end_date) if prepaid_sql_template else None
|
|
||||||
|
|
||||||
# Connect to PostgreSQL
|
|
||||||
try:
|
try:
|
||||||
conn = psycopg2.connect(
|
conn = psycopg2.connect(
|
||||||
host="bj-postgres-16pob4sg.sql.tencentcdb.com",
|
host="bj-postgres-16pob4sg.sql.tencentcdb.com",
|
||||||
@ -79,7 +88,6 @@ def main():
|
|||||||
print(f"数据库连接失败:{str(e)}", file=sys.stderr)
|
print(f"数据库连接失败:{str(e)}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Execute queries
|
|
||||||
try:
|
try:
|
||||||
summary_df = pd.read_sql(summary_sql, conn)
|
summary_df = pd.read_sql(summary_sql, conn)
|
||||||
detail_df = pd.read_sql(detail_sql, conn)
|
detail_df = pd.read_sql(detail_sql, conn)
|
||||||
@ -91,7 +99,7 @@ def main():
|
|||||||
finally:
|
finally:
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
# Write to Excel
|
# ── 4. 输出 Excel 报表 ──
|
||||||
output_dir = '/root/.openclaw/workspace/output'
|
output_dir = '/root/.openclaw/workspace/output'
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
output_file = os.path.join(output_dir, f'订单均摊结算报表_{start_date}_{end_date}.xlsx')
|
output_file = os.path.join(output_dir, f'订单均摊结算报表_{start_date}_{end_date}.xlsx')
|
||||||
@ -106,7 +114,7 @@ def main():
|
|||||||
print(f"Excel生成失败:{str(e)}", file=sys.stderr)
|
print(f"Excel生成失败:{str(e)}", file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Print summary result as JSON
|
# ── 5. 控制台输出 JSON 汇总 ──
|
||||||
result = summary_df.to_dict(orient='records')[0]
|
result = summary_df.to_dict(orient='records')[0]
|
||||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||||
print(f"\n报表已保存到:{output_file}", file=sys.stderr)
|
print(f"\n报表已保存到:{output_file}", file=sys.stderr)
|
||||||
|
|||||||
167
skills/vala-order-amortization-stat/sql/_common.sql
Normal file
167
skills/vala-order-amortization-stat/sql/_common.sql
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
-- 订单均摊结算统计 — 公共CTE链(detail.sql / summary.sql 共用)
|
||||||
|
-- 参数:{period_start} 账期起始日,{period_end} 账期结束日
|
||||||
|
-- 所有金额均为税后金额(价外税,含税价 / (1+税率))
|
||||||
|
|
||||||
|
WITH
|
||||||
|
-- 步骤0:获取所有历史退费记录(不受当前账期限制)
|
||||||
|
all_refund_records AS (
|
||||||
|
SELECT
|
||||||
|
out_trade_no AS order_no,
|
||||||
|
SUM(refund_amount_int)::numeric / 100 AS total_refund_amount,
|
||||||
|
MAX(refund_type) AS refund_type,
|
||||||
|
MAX(DATE(updated_at)) AS latest_refund_date
|
||||||
|
FROM bi_refund_order
|
||||||
|
WHERE status = 3
|
||||||
|
AND DATE(updated_at) <= '{period_end}'::date
|
||||||
|
GROUP BY out_trade_no
|
||||||
|
),
|
||||||
|
-- 步骤1:获取所有符合条件的订单基础信息
|
||||||
|
order_base AS (
|
||||||
|
SELECT
|
||||||
|
o.id AS order_id,
|
||||||
|
o.out_trade_no AS order_no,
|
||||||
|
o.trade_no,
|
||||||
|
DATE(o.pay_success_date) AS order_date,
|
||||||
|
o.pay_amount_int::numeric / 100 AS pay_amount,
|
||||||
|
CASE WHEN DATE(o.pay_success_date) < '2026-05-01' THEN 0.01 ELSE 0.06 END AS tax_rate,
|
||||||
|
(o.pay_amount_int::numeric / 100) / (1 + CASE WHEN DATE(o.pay_success_date) < '2026-05-01' THEN 0.01 ELSE 0.06 END)
|
||||||
|
* CASE WHEN DATE(o.pay_success_date) < '2026-05-01' THEN 0.01 ELSE 0.06 END AS tax_amount,
|
||||||
|
(o.pay_amount_int::numeric / 100) / (1 + CASE WHEN DATE(o.pay_success_date) < '2026-05-01' THEN 0.01 ELSE 0.06 END) AS after_tax_amount,
|
||||||
|
DATE(o.pay_success_date) + INTERVAL '7 days' AS amortization_start_date,
|
||||||
|
o.order_status,
|
||||||
|
a.id AS account_id,
|
||||||
|
o.key_from,
|
||||||
|
o.sale_channel,
|
||||||
|
CASE WHEN ar.order_no IS NOT NULL THEN 1 ELSE 0 END AS has_refund,
|
||||||
|
COALESCE(ar.total_refund_amount, 0) AS total_refund_amount,
|
||||||
|
CASE WHEN ar.order_no IS NOT NULL
|
||||||
|
AND COALESCE(ar.total_refund_amount, 0) >= o.pay_amount_int::numeric / 100
|
||||||
|
THEN 1 ELSE 0 END AS is_full_refund,
|
||||||
|
ar.refund_type,
|
||||||
|
ar.latest_refund_date
|
||||||
|
FROM bi_vala_order o
|
||||||
|
JOIN bi_vala_app_account a ON o.account_id = a.id
|
||||||
|
LEFT JOIN all_refund_records ar ON o.out_trade_no = ar.order_no
|
||||||
|
WHERE
|
||||||
|
o.pay_success_date >= '2025-06-01'
|
||||||
|
AND o.pay_success_date <= '{period_end}'::date + INTERVAL '1 day' - INTERVAL '1 second'
|
||||||
|
AND o.order_status IN (3,4)
|
||||||
|
AND o.pay_amount_int >= 1000
|
||||||
|
),
|
||||||
|
-- 步骤2:合并计算原始均摊周期和退后均摊周期(一次表扫描)
|
||||||
|
order_cycle AS (
|
||||||
|
SELECT
|
||||||
|
out_trade_no AS order_no,
|
||||||
|
COUNT(DISTINCT id) * 90 AS original_cycle_days,
|
||||||
|
COUNT(DISTINCT CASE WHEN status != -1 THEN id END) * 90 AS active_cycle_days
|
||||||
|
FROM bi_vala_seasonal_ticket
|
||||||
|
GROUP BY out_trade_no
|
||||||
|
),
|
||||||
|
-- 步骤3:合并订单基础信息与均摊周期(仅计算基础字段,衍生字段在步骤3.5统一计算)
|
||||||
|
order_with_cycle AS (
|
||||||
|
SELECT
|
||||||
|
ob.*,
|
||||||
|
COALESCE(oc.active_cycle_days, 0) AS active_cycle_days,
|
||||||
|
-- total_cycle_days: 退后均摊周期。若active=0且has_refund=0且original>0,
|
||||||
|
-- 说明退费发生在账期后(季卡已删但账期内无退款记录),回退到原始周期
|
||||||
|
CASE WHEN COALESCE(oc.active_cycle_days, 0) = 0
|
||||||
|
AND COALESCE(oc.original_cycle_days, 0) > 0
|
||||||
|
AND ob.has_refund = 0
|
||||||
|
THEN COALESCE(oc.original_cycle_days, 0)
|
||||||
|
ELSE COALESCE(oc.active_cycle_days, 0)
|
||||||
|
END AS total_cycle_days,
|
||||||
|
COALESCE(oc.original_cycle_days, 0) AS original_cycle_days,
|
||||||
|
-- 原始均摊结束日
|
||||||
|
ob.amortization_start_date
|
||||||
|
+ COALESCE(oc.original_cycle_days, 0) * INTERVAL '1 day' - INTERVAL '1 day' AS original_amortization_end_date,
|
||||||
|
-- 原始日均摊金额(基于原订单税后金额和原始周期)
|
||||||
|
CASE WHEN COALESCE(oc.original_cycle_days, 0) > 0
|
||||||
|
THEN ob.after_tax_amount / oc.original_cycle_days ELSE 0 END AS original_daily_amort_amount,
|
||||||
|
-- 原始日税费均摊
|
||||||
|
CASE WHEN COALESCE(oc.original_cycle_days, 0) > 0
|
||||||
|
THEN ob.tax_amount / oc.original_cycle_days ELSE 0 END AS original_daily_tax_amount
|
||||||
|
FROM order_base ob
|
||||||
|
LEFT JOIN order_cycle oc ON ob.order_no = oc.order_no
|
||||||
|
),
|
||||||
|
-- 步骤3.5:基于effective total_cycle_days计算衍生字段(均摊结束日、日均摊金额、日税费金额)
|
||||||
|
order_with_cycle_fixed AS (
|
||||||
|
SELECT
|
||||||
|
owc.*,
|
||||||
|
-- 均摊结束日(基于effective total_cycle_days)
|
||||||
|
CASE WHEN owc.total_cycle_days > 0
|
||||||
|
THEN owc.amortization_start_date + owc.total_cycle_days * INTERVAL '1 day' - INTERVAL '1 day'
|
||||||
|
ELSE NULL END AS amortization_end_date,
|
||||||
|
-- 退后日均摊金额
|
||||||
|
CASE WHEN owc.total_cycle_days > 0 AND owc.has_refund = 1 AND owc.is_full_refund = 0
|
||||||
|
THEN (owc.pay_amount - owc.total_refund_amount) / (1 + owc.tax_rate) / owc.total_cycle_days
|
||||||
|
WHEN owc.total_cycle_days > 0
|
||||||
|
THEN owc.after_tax_amount / owc.total_cycle_days
|
||||||
|
ELSE 0 END AS daily_amort_amount,
|
||||||
|
-- 退后日税费均摊
|
||||||
|
CASE WHEN owc.total_cycle_days > 0 AND owc.has_refund = 1 AND owc.is_full_refund = 0
|
||||||
|
THEN (owc.pay_amount - owc.total_refund_amount) / (1 + owc.tax_rate) * owc.tax_rate / owc.total_cycle_days
|
||||||
|
WHEN owc.total_cycle_days > 0
|
||||||
|
THEN owc.tax_amount / owc.total_cycle_days
|
||||||
|
ELSE 0 END AS daily_tax_amount
|
||||||
|
FROM order_with_cycle owc
|
||||||
|
),
|
||||||
|
-- 步骤4:判断订单场景分类
|
||||||
|
order_classified AS (
|
||||||
|
SELECT
|
||||||
|
owc.*,
|
||||||
|
GREATEST(owc.amortization_start_date, '{period_start}'::date) AS eff_start,
|
||||||
|
LEAST(owc.amortization_end_date, '{period_end}'::date) AS eff_end,
|
||||||
|
-- 试用期内退费(退费日 < 转正日,从未产生均摊,不应触发冲销)
|
||||||
|
CASE WHEN owc.has_refund = 1
|
||||||
|
AND owc.latest_refund_date < DATE(owc.amortization_start_date)
|
||||||
|
THEN 1 ELSE 0 END AS is_trial_period_refund,
|
||||||
|
-- 账期前全额退费(场景F:本账期完全排除)
|
||||||
|
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 1
|
||||||
|
AND owc.latest_refund_date < '{period_start}'::date
|
||||||
|
THEN 1 ELSE 0 END AS is_pre_period_full_refund,
|
||||||
|
-- 账期前部分退费(场景C:按退后金额正常均摊)
|
||||||
|
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 0
|
||||||
|
AND owc.latest_refund_date < '{period_start}'::date
|
||||||
|
THEN 1 ELSE 0 END AS is_pre_period_partial_refund,
|
||||||
|
-- 账期内退费(场景A/B/D/E)
|
||||||
|
CASE WHEN owc.has_refund = 1
|
||||||
|
AND owc.latest_refund_date >= '{period_start}'::date
|
||||||
|
AND owc.latest_refund_date <= '{period_end}'::date
|
||||||
|
THEN 1 ELSE 0 END AS is_current_period_refund,
|
||||||
|
-- 账期内下单
|
||||||
|
CASE WHEN owc.order_date >= '{period_start}'::date
|
||||||
|
AND owc.order_date <= '{period_end}'::date
|
||||||
|
THEN 1 ELSE 0 END AS is_ordered_in_period,
|
||||||
|
-- 退后订单税后金额
|
||||||
|
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 0
|
||||||
|
THEN (owc.pay_amount - owc.total_refund_amount) / (1 + owc.tax_rate)
|
||||||
|
ELSE owc.after_tax_amount END AS refunded_after_tax_amount,
|
||||||
|
-- 退后订单税费
|
||||||
|
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 0
|
||||||
|
THEN (owc.pay_amount - owc.total_refund_amount) / (1 + owc.tax_rate) * owc.tax_rate
|
||||||
|
ELSE owc.tax_amount END AS refunded_tax_amount
|
||||||
|
FROM order_with_cycle_fixed owc
|
||||||
|
WHERE owc.total_cycle_days > 0
|
||||||
|
OR (owc.has_refund = 1 AND owc.is_full_refund = 1 AND owc.original_cycle_days > 0)
|
||||||
|
),
|
||||||
|
-- 步骤5:计算均摊天数
|
||||||
|
order_with_days AS (
|
||||||
|
SELECT
|
||||||
|
oc2.*,
|
||||||
|
-- 账期内有效均摊天数(基于退后周期)
|
||||||
|
CASE WHEN oc2.eff_end >= oc2.eff_start AND oc2.total_cycle_days > 0
|
||||||
|
THEN EXTRACT(DAY FROM (oc2.eff_end - oc2.eff_start))::int + 1 ELSE 0 END AS period_amort_days,
|
||||||
|
-- 从转正日到账期结束日的总已均摊天数(基于退后周期)
|
||||||
|
CASE WHEN '{period_end}'::date >= oc2.amortization_start_date AND oc2.total_cycle_days > 0
|
||||||
|
THEN EXTRACT(DAY FROM (LEAST('{period_end}'::date, oc2.amortization_end_date) - oc2.amortization_start_date))::int + 1
|
||||||
|
ELSE 0 END AS total_amorted_days_to_period_end,
|
||||||
|
-- 从转正日到账期起始日前一天的历史已均摊天数(基于原始周期,用于冲销计算)
|
||||||
|
CASE WHEN '{period_start}'::date > oc2.amortization_start_date AND oc2.original_cycle_days > 0
|
||||||
|
THEN EXTRACT(DAY FROM (LEAST('{period_start}'::date - 1, oc2.original_amortization_end_date) - oc2.amortization_start_date))::int + 1
|
||||||
|
ELSE 0 END AS historical_amorted_days,
|
||||||
|
-- 从转正日到账期结束日的天数(基于退后周期,用于退后订单重新均摊)
|
||||||
|
CASE WHEN '{period_end}'::date >= oc2.amortization_start_date AND oc2.total_cycle_days > 0
|
||||||
|
THEN EXTRACT(DAY FROM (LEAST('{period_end}'::date, oc2.amortization_end_date) - oc2.amortization_start_date))::int + 1
|
||||||
|
ELSE 0 END AS refunded_amort_days_to_period_end
|
||||||
|
FROM order_classified oc2
|
||||||
|
)
|
||||||
@ -1,185 +1,6 @@
|
|||||||
WITH
|
-- 订单明细输出(依赖 _common.sql 的 CTE 链)
|
||||||
-- 步骤0:获取所有历史退费记录
|
-- 最终查询从 order_with_days 中选取所有字段,按场景分类展示每笔订单的均摊明细
|
||||||
all_refund_records AS (
|
|
||||||
SELECT
|
|
||||||
out_trade_no AS order_no,
|
|
||||||
SUM(refund_amount_int) / 100 AS total_refund_amount,
|
|
||||||
MAX(refund_type) AS refund_type,
|
|
||||||
MAX(DATE(updated_at)) AS latest_refund_date
|
|
||||||
FROM bi_refund_order
|
|
||||||
WHERE status = 3
|
|
||||||
GROUP BY out_trade_no
|
|
||||||
),
|
|
||||||
-- 步骤1:获取所有符合条件的订单基础信息
|
|
||||||
order_base AS (
|
|
||||||
SELECT
|
|
||||||
o.id AS order_id,
|
|
||||||
o.out_trade_no AS order_no,
|
|
||||||
o.trade_no,
|
|
||||||
DATE(o.created_at) AS order_date,
|
|
||||||
o.pay_amount_int / 100 AS pay_amount,
|
|
||||||
CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END AS tax_rate,
|
|
||||||
(o.pay_amount_int / 100.0) / (1 + CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END) * CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END AS tax_amount,
|
|
||||||
(o.pay_amount_int / 100.0) / (1 + CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END) AS after_tax_amount,
|
|
||||||
DATE(o.created_at) + INTERVAL '7 days' AS amortization_start_date,
|
|
||||||
o.order_status,
|
|
||||||
a.id AS account_id,
|
|
||||||
o.key_from,
|
|
||||||
o.sale_channel,
|
|
||||||
CASE WHEN ar.order_no IS NOT NULL THEN 1 ELSE 0 END AS has_refund,
|
|
||||||
COALESCE(ar.total_refund_amount, 0) AS total_refund_amount,
|
|
||||||
CASE WHEN ar.order_no IS NOT NULL AND COALESCE(ar.total_refund_amount, 0) >= o.pay_amount_int / 100 THEN 1 ELSE 0 END AS is_full_refund,
|
|
||||||
ar.refund_type,
|
|
||||||
ar.latest_refund_date
|
|
||||||
FROM bi_vala_order o
|
|
||||||
JOIN bi_vala_app_account a ON o.account_id = a.id
|
|
||||||
LEFT JOIN all_refund_records ar ON o.out_trade_no = ar.order_no
|
|
||||||
WHERE
|
|
||||||
o.created_at >= '2025-06-01'
|
|
||||||
AND o.created_at <= '{period_end}'::date + INTERVAL '1 day' - INTERVAL '1 second'
|
|
||||||
AND o.order_status IN (3,4)
|
|
||||||
AND o.pay_amount_int >= 1000
|
|
||||||
),
|
|
||||||
-- 步骤2:计算每个订单的当前均摊周期(退费后)和原始均摊周期(退费前)
|
|
||||||
order_cycle AS (
|
|
||||||
SELECT
|
|
||||||
out_trade_no AS order_no,
|
|
||||||
COUNT(DISTINCT id) * 90 AS total_cycle_days
|
|
||||||
FROM bi_vala_seasonal_ticket
|
|
||||||
WHERE status != -1
|
|
||||||
GROUP BY out_trade_no
|
|
||||||
),
|
|
||||||
order_cycle_original AS (
|
|
||||||
SELECT
|
|
||||||
out_trade_no AS order_no,
|
|
||||||
COUNT(DISTINCT id) * 90 AS original_cycle_days
|
|
||||||
FROM bi_vala_seasonal_ticket
|
|
||||||
GROUP BY out_trade_no
|
|
||||||
),
|
|
||||||
-- 步骤3:合并订单基础信息与均摊周期
|
|
||||||
order_with_cycle AS (
|
|
||||||
SELECT
|
|
||||||
ob.*,
|
|
||||||
COALESCE(oc.total_cycle_days, 0) AS total_cycle_days,
|
|
||||||
COALESCE(oco.original_cycle_days, 0) AS original_cycle_days,
|
|
||||||
ob.amortization_start_date + COALESCE(oc.total_cycle_days, 0) * INTERVAL '1 day' - INTERVAL '1 day' AS amortization_end_date,
|
|
||||||
-- 原始均摊结束日(退费前)
|
|
||||||
ob.amortization_start_date + COALESCE(oco.original_cycle_days, 0) * INTERVAL '1 day' - INTERVAL '1 day' AS original_amortization_end_date,
|
|
||||||
-- 原始日均摊金额(基于原订单税后金额和原始周期)
|
|
||||||
CASE WHEN COALESCE(oco.original_cycle_days, 0) > 0
|
|
||||||
THEN ob.after_tax_amount / oco.original_cycle_days
|
|
||||||
ELSE 0
|
|
||||||
END AS original_daily_amort_amount,
|
|
||||||
-- 原始日税费均摊
|
|
||||||
CASE WHEN COALESCE(oco.original_cycle_days, 0) > 0
|
|
||||||
THEN ob.tax_amount / oco.original_cycle_days
|
|
||||||
ELSE 0
|
|
||||||
END AS original_daily_tax_amount,
|
|
||||||
-- 退后日均摊金额(基于退后订单税后金额和退后周期)
|
|
||||||
CASE WHEN COALESCE(oc.total_cycle_days, 0) > 0 AND ob.has_refund = 1 AND ob.is_full_refund = 0
|
|
||||||
THEN (ob.pay_amount - ob.total_refund_amount) / (1 + ob.tax_rate) / oc.total_cycle_days
|
|
||||||
WHEN COALESCE(oc.total_cycle_days, 0) > 0
|
|
||||||
THEN ob.after_tax_amount / oc.total_cycle_days
|
|
||||||
ELSE 0
|
|
||||||
END AS daily_amort_amount,
|
|
||||||
-- 退后日税费均摊
|
|
||||||
CASE WHEN COALESCE(oc.total_cycle_days, 0) > 0 AND ob.has_refund = 1 AND ob.is_full_refund = 0
|
|
||||||
THEN (ob.pay_amount - ob.total_refund_amount) / (1 + ob.tax_rate) * ob.tax_rate / oc.total_cycle_days
|
|
||||||
WHEN COALESCE(oc.total_cycle_days, 0) > 0
|
|
||||||
THEN ob.tax_amount / oc.total_cycle_days
|
|
||||||
ELSE 0
|
|
||||||
END AS daily_tax_amount
|
|
||||||
FROM order_base ob
|
|
||||||
LEFT JOIN order_cycle oc ON ob.order_no = oc.order_no
|
|
||||||
LEFT JOIN order_cycle_original oco ON ob.order_no = oco.order_no
|
|
||||||
),
|
|
||||||
-- 步骤4:判断订单场景分类
|
|
||||||
order_classified AS (
|
|
||||||
SELECT
|
|
||||||
owc.*,
|
|
||||||
-- 均摊有效起始日
|
|
||||||
GREATEST(owc.amortization_start_date, '{period_start}'::date) AS eff_start,
|
|
||||||
-- 均摊有效结束日
|
|
||||||
LEAST(owc.amortization_end_date, '{period_end}'::date) AS eff_end,
|
|
||||||
|
|
||||||
-- 判断是否为试用期内退费(退费日 < 转正日,从未产生均摊,不应触发冲销)
|
|
||||||
CASE WHEN owc.has_refund = 1
|
|
||||||
AND owc.latest_refund_date < DATE(owc.amortization_start_date)
|
|
||||||
THEN 1 ELSE 0
|
|
||||||
END AS is_trial_period_refund,
|
|
||||||
|
|
||||||
-- 判断是否为账期前全额退费(场景F:排除)
|
|
||||||
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 1
|
|
||||||
AND owc.latest_refund_date < '{period_start}'::date
|
|
||||||
THEN 1 ELSE 0
|
|
||||||
END AS is_pre_period_full_refund,
|
|
||||||
|
|
||||||
-- 判断是否为账期前部分退费(场景C)
|
|
||||||
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 0
|
|
||||||
AND owc.latest_refund_date < '{period_start}'::date
|
|
||||||
THEN 1 ELSE 0
|
|
||||||
END AS is_pre_period_partial_refund,
|
|
||||||
|
|
||||||
-- 判断是否为账期内退费
|
|
||||||
CASE WHEN owc.has_refund = 1
|
|
||||||
AND owc.latest_refund_date >= '{period_start}'::date
|
|
||||||
AND owc.latest_refund_date <= '{period_end}'::date
|
|
||||||
THEN 1 ELSE 0
|
|
||||||
END AS is_current_period_refund,
|
|
||||||
|
|
||||||
-- 判断是否为账期内下单(下单日在账期内)
|
|
||||||
CASE WHEN owc.order_date >= '{period_start}'::date
|
|
||||||
AND owc.order_date <= '{period_end}'::date
|
|
||||||
THEN 1 ELSE 0
|
|
||||||
END AS is_ordered_in_period,
|
|
||||||
|
|
||||||
-- 退后订单税后金额
|
|
||||||
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 0
|
|
||||||
THEN (owc.pay_amount - owc.total_refund_amount) / (1 + owc.tax_rate)
|
|
||||||
ELSE owc.after_tax_amount
|
|
||||||
END AS refunded_after_tax_amount,
|
|
||||||
|
|
||||||
-- 退后订单税费
|
|
||||||
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 0
|
|
||||||
THEN (owc.pay_amount - owc.total_refund_amount) / (1 + owc.tax_rate) * owc.tax_rate
|
|
||||||
ELSE owc.tax_amount
|
|
||||||
END AS refunded_tax_amount
|
|
||||||
|
|
||||||
FROM order_with_cycle owc
|
|
||||||
WHERE owc.total_cycle_days > 0 OR (owc.has_refund = 1 AND owc.is_full_refund = 1 AND owc.original_cycle_days > 0)
|
|
||||||
),
|
|
||||||
|
|
||||||
-- 步骤5:计算均摊天数
|
|
||||||
order_with_days AS (
|
|
||||||
SELECT
|
|
||||||
oc2.*,
|
|
||||||
-- 账期内有效均摊天数(基于退后周期)
|
|
||||||
CASE WHEN oc2.eff_end >= oc2.eff_start AND oc2.total_cycle_days > 0
|
|
||||||
THEN EXTRACT(DAY FROM (oc2.eff_end - oc2.eff_start))::int + 1
|
|
||||||
ELSE 0
|
|
||||||
END AS period_amort_days,
|
|
||||||
|
|
||||||
-- 从转正日到账期结束日的总已均摊天数(基于退后周期)
|
|
||||||
CASE WHEN '{period_end}'::date >= oc2.amortization_start_date AND oc2.total_cycle_days > 0
|
|
||||||
THEN EXTRACT(DAY FROM (LEAST('{period_end}'::date, oc2.amortization_end_date) - oc2.amortization_start_date))::int + 1
|
|
||||||
ELSE 0
|
|
||||||
END AS total_amorted_days_to_period_end,
|
|
||||||
|
|
||||||
-- 从转正日到账期起始日前一天的历史已均摊天数(基于原始周期,用于冲销计算)
|
|
||||||
CASE WHEN '{period_start}'::date > oc2.amortization_start_date AND oc2.original_cycle_days > 0
|
|
||||||
THEN EXTRACT(DAY FROM (LEAST('{period_start}'::date - 1, oc2.original_amortization_end_date) - oc2.amortization_start_date))::int + 1
|
|
||||||
ELSE 0
|
|
||||||
END AS historical_amorted_days,
|
|
||||||
|
|
||||||
-- 从转正日到账期结束日的天数(基于退后周期,用于退后订单的重新均摊)
|
|
||||||
CASE WHEN '{period_end}'::date >= oc2.amortization_start_date AND oc2.total_cycle_days > 0
|
|
||||||
THEN EXTRACT(DAY FROM (LEAST('{period_end}'::date, oc2.amortization_end_date) - oc2.amortization_start_date))::int + 1
|
|
||||||
ELSE 0
|
|
||||||
END AS refunded_amort_days_to_period_end
|
|
||||||
|
|
||||||
FROM order_classified oc2
|
|
||||||
)
|
|
||||||
-- 最终输出
|
|
||||||
SELECT
|
SELECT
|
||||||
order_no AS "订单号",
|
order_no AS "订单号",
|
||||||
order_date AS "下单日期",
|
order_date AS "下单日期",
|
||||||
@ -204,8 +25,7 @@ SELECT
|
|||||||
CASE
|
CASE
|
||||||
WHEN has_refund = 1 AND is_full_refund = 0
|
WHEN has_refund = 1 AND is_full_refund = 0
|
||||||
THEN (pay_amount - total_refund_amount) / (1 + tax_rate)
|
THEN (pay_amount - total_refund_amount) / (1 + tax_rate)
|
||||||
WHEN has_refund = 1 AND is_full_refund = 1
|
WHEN has_refund = 1 AND is_full_refund = 1 THEN 0
|
||||||
THEN 0
|
|
||||||
ELSE after_tax_amount
|
ELSE after_tax_amount
|
||||||
END::numeric, 2
|
END::numeric, 2
|
||||||
) AS "当前税后金额",
|
) AS "当前税后金额",
|
||||||
@ -213,17 +33,14 @@ SELECT
|
|||||||
CASE
|
CASE
|
||||||
WHEN has_refund = 1 AND is_full_refund = 0
|
WHEN has_refund = 1 AND is_full_refund = 0
|
||||||
THEN (pay_amount - total_refund_amount) / (1 + tax_rate) * tax_rate
|
THEN (pay_amount - total_refund_amount) / (1 + tax_rate) * tax_rate
|
||||||
WHEN has_refund = 1 AND is_full_refund = 1
|
WHEN has_refund = 1 AND is_full_refund = 1 THEN 0
|
||||||
THEN 0
|
|
||||||
ELSE tax_amount
|
ELSE tax_amount
|
||||||
END::numeric, 2
|
END::numeric, 2
|
||||||
) AS "当前税额",
|
) AS "当前税额",
|
||||||
original_cycle_days AS "原始均摊周期",
|
original_cycle_days AS "原始均摊周期",
|
||||||
total_cycle_days AS "退后均摊周期",
|
total_cycle_days AS "退后均摊周期",
|
||||||
|
|
||||||
-- 历史累计均摊金额(仅历史订单有值,账期内订单=0;账期内退费按退费前状态算)
|
-- 历史累计均摊金额(仅历史订单有值,账期内下单=0)
|
||||||
-- 账期前部分退费:按退后日均摊×退后历史天数
|
|
||||||
-- 正常订单/账期内退费:按原始日均摊×历史天数
|
|
||||||
ROUND(
|
ROUND(
|
||||||
CASE
|
CASE
|
||||||
WHEN is_ordered_in_period = 1 THEN 0
|
WHEN is_ordered_in_period = 1 THEN 0
|
||||||
@ -235,18 +52,14 @@ SELECT
|
|||||||
END::numeric, 2
|
END::numeric, 2
|
||||||
) AS "历史累计均摊金额",
|
) AS "历史累计均摊金额",
|
||||||
|
|
||||||
-- 冲销历史累计摊销金额(仅场景B/E:历史下单+账期内退费,排除试用期内退费)
|
-- 冲销历史累计摊销金额(场景B/E:历史下单+账期内退费,排除试用期内退费)
|
||||||
ROUND(
|
ROUND(
|
||||||
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
||||||
AND is_trial_period_refund = 0
|
AND is_trial_period_refund = 0 AND historical_amorted_days > 0
|
||||||
AND historical_amorted_days > 0
|
THEN -(historical_amorted_days * original_daily_amort_amount) ELSE 0 END::numeric, 2
|
||||||
THEN -(historical_amorted_days * original_daily_amort_amount)
|
|
||||||
ELSE 0
|
|
||||||
END::numeric, 2
|
|
||||||
) AS "冲销历史累计摊销金额",
|
) AS "冲销历史累计摊销金额",
|
||||||
|
|
||||||
-- 退后订单历史累计摊销金额(站在账期最后一天,看所有订单在账期第一天之前的有效历史摊销金额)
|
-- 退后订单历史累计摊销金额(站在账期最后一天,看所有订单在账期第一天之前的有效历史摊销)
|
||||||
-- 账期内退费的订单按退后状态算;账期前部分退费按退后状态算;正常订单按原始状态算
|
|
||||||
ROUND(
|
ROUND(
|
||||||
CASE
|
CASE
|
||||||
WHEN is_ordered_in_period = 1 THEN 0
|
WHEN is_ordered_in_period = 1 THEN 0
|
||||||
@ -265,82 +78,63 @@ SELECT
|
|||||||
ROUND(
|
ROUND(
|
||||||
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
||||||
AND is_trial_period_refund = 0
|
AND is_trial_period_refund = 0
|
||||||
THEN -tax_amount
|
THEN -tax_amount ELSE 0 END::numeric, 2
|
||||||
ELSE 0
|
|
||||||
END::numeric, 2
|
|
||||||
) AS "冲销原税款",
|
) AS "冲销原税款",
|
||||||
|
|
||||||
-- 冲销未确认收入(场景B/E:历史下单+账期内退费,排除试用期内退费)
|
-- 冲销未确认收入(场景B/E)
|
||||||
ROUND(
|
ROUND(
|
||||||
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
||||||
AND is_trial_period_refund = 0
|
AND is_trial_period_refund = 0
|
||||||
THEN -(after_tax_amount - historical_amorted_days * original_daily_amort_amount)
|
THEN -(after_tax_amount - historical_amorted_days * original_daily_amort_amount) ELSE 0 END::numeric, 2
|
||||||
ELSE 0
|
|
||||||
END::numeric, 2
|
|
||||||
) AS "冲销未确认收入",
|
) AS "冲销未确认收入",
|
||||||
|
|
||||||
-- 当期摊销金额
|
-- 当期摊销金额(核心:按6种场景分支计算)
|
||||||
ROUND(
|
ROUND(
|
||||||
CASE
|
CASE
|
||||||
-- 场景F:账期前全额退费,不统计
|
|
||||||
WHEN is_pre_period_full_refund = 1 THEN 0
|
WHEN is_pre_period_full_refund = 1 THEN 0
|
||||||
-- 试用期内全额退费,无均摊
|
|
||||||
WHEN is_trial_period_refund = 1 AND is_full_refund = 1 THEN 0
|
WHEN is_trial_period_refund = 1 AND is_full_refund = 1 THEN 0
|
||||||
-- 试用期内部分退费,按退后金额从转正日正常均摊
|
|
||||||
WHEN is_trial_period_refund = 1 AND is_full_refund = 0 THEN
|
WHEN is_trial_period_refund = 1 AND is_full_refund = 0 THEN
|
||||||
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
||||||
THEN daily_amort_amount * period_amort_days ELSE 0 END
|
THEN daily_amort_amount * period_amort_days ELSE 0 END
|
||||||
-- 场景D:账期内下单+全额退费,无均摊
|
|
||||||
WHEN is_current_period_refund = 1 AND is_full_refund = 1 AND is_ordered_in_period = 1 THEN 0
|
WHEN is_current_period_refund = 1 AND is_full_refund = 1 AND is_ordered_in_period = 1 THEN 0
|
||||||
-- 场景E:历史下单+账期内全额退费,无退后均摊
|
|
||||||
WHEN is_current_period_refund = 1 AND is_full_refund = 1 AND is_ordered_in_period = 0 THEN 0
|
WHEN is_current_period_refund = 1 AND is_full_refund = 1 AND is_ordered_in_period = 0 THEN 0
|
||||||
-- 场景A:账期内下单+部分退费,按退后订单金额/退后周期从转正日均摊
|
-- 场景A:账期内下单+部分退费
|
||||||
WHEN is_current_period_refund = 1 AND is_full_refund = 0 AND is_ordered_in_period = 1 THEN
|
WHEN is_current_period_refund = 1 AND is_full_refund = 0 AND is_ordered_in_period = 1 THEN
|
||||||
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
||||||
THEN daily_amort_amount * period_amort_days
|
THEN daily_amort_amount * period_amort_days ELSE 0 END
|
||||||
ELSE 0
|
-- 场景B:历史下单+账期内部分退费(仅账期内均摊)
|
||||||
END
|
|
||||||
-- 场景B:历史下单+账期内部分退费,按退后订单金额/退后周期从转正日重新均摊
|
|
||||||
WHEN is_current_period_refund = 1 AND is_full_refund = 0 AND is_ordered_in_period = 0 THEN
|
WHEN is_current_period_refund = 1 AND is_full_refund = 0 AND is_ordered_in_period = 0 THEN
|
||||||
CASE WHEN total_cycle_days > 0 AND refunded_amort_days_to_period_end > 0
|
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
||||||
THEN daily_amort_amount * refunded_amort_days_to_period_end
|
THEN daily_amort_amount * period_amort_days ELSE 0 END
|
||||||
ELSE 0
|
-- 场景C:账期前部分退费
|
||||||
END
|
|
||||||
-- 场景C:账期前部分退费,按退后订单金额/退后周期正常均摊
|
|
||||||
WHEN is_pre_period_partial_refund = 1 THEN
|
WHEN is_pre_period_partial_refund = 1 THEN
|
||||||
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
||||||
THEN daily_amort_amount * period_amort_days
|
THEN daily_amort_amount * period_amort_days ELSE 0 END
|
||||||
ELSE 0
|
-- 正常订单(含补差逻辑)
|
||||||
END
|
|
||||||
-- 正常订单
|
|
||||||
WHEN period_amort_days > 0 THEN
|
WHEN period_amort_days > 0 THEN
|
||||||
CASE
|
CASE WHEN eff_end = amortization_end_date
|
||||||
WHEN eff_end = amortization_end_date
|
THEN after_tax_amount - original_daily_amort_amount * (original_cycle_days - 1)
|
||||||
THEN after_tax_amount - original_daily_amort_amount * (original_cycle_days - 1) + original_daily_amort_amount * (period_amort_days - 1)
|
+ original_daily_amort_amount * (period_amort_days - 1)
|
||||||
ELSE original_daily_amort_amount * period_amort_days
|
ELSE original_daily_amort_amount * period_amort_days END
|
||||||
END
|
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END::numeric, 2
|
END::numeric, 2
|
||||||
) AS "当期摊销金额",
|
) AS "当期摊销金额",
|
||||||
|
|
||||||
-- 退后订单税额(仅账期内下单且已转正的非全额退费订单计税)
|
-- 账期税额(仅账期内下单且已转正的非全额退费订单计税)
|
||||||
ROUND(
|
ROUND(
|
||||||
CASE
|
CASE
|
||||||
-- 账期内下单 + 已转正 + 部分退费 → 退后订单税额
|
|
||||||
WHEN is_ordered_in_period = 1
|
WHEN is_ordered_in_period = 1
|
||||||
AND DATE(amortization_start_date) <= '{period_end}'::date
|
AND DATE(amortization_start_date) <= '{period_end}'::date
|
||||||
AND is_current_period_refund = 1 AND is_full_refund = 0
|
AND is_current_period_refund = 1 AND is_full_refund = 0
|
||||||
THEN refunded_tax_amount
|
THEN refunded_tax_amount
|
||||||
-- 账期内下单 + 已转正 + 未全额退费(含未退费)→ 原订单税额
|
|
||||||
WHEN is_ordered_in_period = 1
|
WHEN is_ordered_in_period = 1
|
||||||
AND DATE(amortization_start_date) <= '{period_end}'::date
|
AND DATE(amortization_start_date) <= '{period_end}'::date
|
||||||
AND (has_refund = 0 OR (is_current_period_refund = 0 AND is_full_refund = 0))
|
AND (has_refund = 0 OR (is_current_period_refund = 0 AND is_full_refund = 0))
|
||||||
THEN tax_amount
|
THEN tax_amount ELSE 0
|
||||||
ELSE 0
|
|
||||||
END::numeric, 2
|
END::numeric, 2
|
||||||
) AS "账期税额",
|
) AS "账期税额",
|
||||||
|
|
||||||
-- 未确认收入
|
-- 未确认收入(截至账期最后一天尚未均摊的税后金额)
|
||||||
ROUND(
|
ROUND(
|
||||||
CASE
|
CASE
|
||||||
WHEN is_pre_period_full_refund = 1 THEN 0
|
WHEN is_pre_period_full_refund = 1 THEN 0
|
||||||
@ -349,12 +143,10 @@ SELECT
|
|||||||
refunded_after_tax_amount - daily_amort_amount * refunded_amort_days_to_period_end
|
refunded_after_tax_amount - daily_amort_amount * refunded_amort_days_to_period_end
|
||||||
WHEN is_pre_period_partial_refund = 1 THEN
|
WHEN is_pre_period_partial_refund = 1 THEN
|
||||||
refunded_after_tax_amount - daily_amort_amount * total_amorted_days_to_period_end
|
refunded_after_tax_amount - daily_amort_amount * total_amorted_days_to_period_end
|
||||||
ELSE
|
ELSE after_tax_amount - original_daily_amort_amount * total_amorted_days_to_period_end
|
||||||
after_tax_amount - original_daily_amort_amount * total_amorted_days_to_period_end
|
|
||||||
END::numeric, 2
|
END::numeric, 2
|
||||||
) AS "未确认收入",
|
) AS "未确认收入",
|
||||||
|
|
||||||
-- 剩余周期
|
|
||||||
CASE
|
CASE
|
||||||
WHEN is_current_period_refund = 1 AND is_full_refund = 1 THEN 0
|
WHEN is_current_period_refund = 1 AND is_full_refund = 1 THEN 0
|
||||||
ELSE (total_cycle_days - total_amorted_days_to_period_end)
|
ELSE (total_cycle_days - total_amorted_days_to_period_end)
|
||||||
|
|||||||
@ -17,29 +17,30 @@ month_range AS (
|
|||||||
all_refund_records AS (
|
all_refund_records AS (
|
||||||
SELECT
|
SELECT
|
||||||
out_trade_no AS order_no,
|
out_trade_no AS order_no,
|
||||||
SUM(refund_amount_int) / 100 AS total_refund_amount,
|
SUM(refund_amount_int)::numeric / 100 AS total_refund_amount,
|
||||||
MAX(CASE WHEN refund_type = 2 THEN 1 ELSE 0 END) AS is_full_refund,
|
MAX(CASE WHEN refund_type = 2 THEN 1 ELSE 0 END) AS is_full_refund,
|
||||||
MAX(DATE(updated_at)) AS latest_refund_date
|
MAX(DATE(updated_at)) AS latest_refund_date
|
||||||
FROM bi_refund_order
|
FROM bi_refund_order
|
||||||
WHERE status = 3
|
WHERE status = 3
|
||||||
|
AND DATE(updated_at) <= '{period_end}'::date
|
||||||
GROUP BY out_trade_no
|
GROUP BY out_trade_no
|
||||||
),
|
),
|
||||||
-- 所有订单基础信息(含退费标记)
|
-- 所有订单基础信息(含退费标记)
|
||||||
order_base AS (
|
order_base AS (
|
||||||
SELECT
|
SELECT
|
||||||
o.out_trade_no AS order_no,
|
o.out_trade_no AS order_no,
|
||||||
DATE(o.created_at) AS order_date,
|
DATE(o.pay_success_date) AS order_date,
|
||||||
o.pay_amount_int / 100 AS pay_amount,
|
o.pay_amount_int::numeric / 100 AS pay_amount,
|
||||||
CASE WHEN ar.order_no IS NOT NULL THEN 1 ELSE 0 END AS has_refund,
|
CASE WHEN ar.order_no IS NOT NULL THEN 1 ELSE 0 END AS has_refund,
|
||||||
COALESCE(ar.total_refund_amount, 0) AS total_refund_amount,
|
COALESCE(ar.total_refund_amount, 0) AS total_refund_amount,
|
||||||
CASE WHEN ar.order_no IS NOT NULL AND COALESCE(ar.total_refund_amount, 0) >= o.pay_amount_int / 100 THEN 1 ELSE 0 END AS is_full_refund,
|
CASE WHEN ar.order_no IS NOT NULL AND COALESCE(ar.total_refund_amount, 0) >= o.pay_amount_int::numeric / 100 THEN 1 ELSE 0 END AS is_full_refund,
|
||||||
ar.latest_refund_date,
|
ar.latest_refund_date,
|
||||||
-- 转正日期 = 下单日 + 7天
|
-- 转正日期 = 下单日 + 7天
|
||||||
DATE(o.created_at) + INTERVAL '7 days' AS amortization_start_date,
|
DATE(o.pay_success_date) + INTERVAL '7 days' AS amortization_start_date,
|
||||||
-- 有效金额(部分退费 = 原金额 - 退费;未退费 = 原金额;全额退费 = 0)
|
-- 有效金额(部分退费 = 原金额 - 退费;未退费 = 原金额;全额退费 = 0)
|
||||||
CASE
|
CASE
|
||||||
WHEN ar.order_no IS NOT NULL AND COALESCE(ar.total_refund_amount, 0) >= o.pay_amount_int / 100 THEN 0
|
WHEN ar.order_no IS NOT NULL AND COALESCE(ar.total_refund_amount, 0) >= o.pay_amount_int::numeric / 100 THEN 0
|
||||||
ELSE o.pay_amount_int / 100 - COALESCE(ar.total_refund_amount, 0)
|
ELSE o.pay_amount_int::numeric / 100 - COALESCE(ar.total_refund_amount, 0)
|
||||||
END AS effective_amount,
|
END AS effective_amount,
|
||||||
o.order_status
|
o.order_status
|
||||||
FROM bi_vala_order o
|
FROM bi_vala_order o
|
||||||
@ -47,8 +48,8 @@ order_base AS (
|
|||||||
LEFT JOIN all_refund_records ar ON o.out_trade_no = ar.order_no
|
LEFT JOIN all_refund_records ar ON o.out_trade_no = ar.order_no
|
||||||
CROSS JOIN month_range mr
|
CROSS JOIN month_range mr
|
||||||
WHERE
|
WHERE
|
||||||
o.created_at >= mr.last_month_start
|
o.pay_success_date >= mr.last_month_start
|
||||||
AND o.created_at <= mr.period_end
|
AND o.pay_success_date <= mr.period_end
|
||||||
AND o.order_status IN (3,4)
|
AND o.order_status IN (3,4)
|
||||||
AND o.pay_amount_int >= 1000
|
AND o.pay_amount_int >= 1000
|
||||||
AND a.status = 1
|
AND a.status = 1
|
||||||
|
|||||||
@ -1,147 +1,26 @@
|
|||||||
WITH
|
-- 汇总输出(依赖 _common.sql 的 CTE 链)
|
||||||
-- 步骤0:获取所有历史退费记录
|
-- 对 order_with_days 进行聚合,输出账期整体汇总指标
|
||||||
all_refund_records AS (
|
-- 列名与 detail.sql 保持一致,方便交叉验证(明细 SUM = 汇总值)
|
||||||
SELECT
|
|
||||||
out_trade_no AS order_no,
|
|
||||||
SUM(refund_amount_int) / 100 AS total_refund_amount,
|
|
||||||
MAX(refund_type) AS refund_type,
|
|
||||||
MAX(DATE(updated_at)) AS latest_refund_date
|
|
||||||
FROM bi_refund_order
|
|
||||||
WHERE status = 3
|
|
||||||
GROUP BY out_trade_no
|
|
||||||
),
|
|
||||||
-- 步骤1:获取所有符合条件的订单基础信息
|
|
||||||
order_base AS (
|
|
||||||
SELECT
|
|
||||||
o.id AS order_id,
|
|
||||||
o.out_trade_no AS order_no,
|
|
||||||
o.trade_no,
|
|
||||||
DATE(o.created_at) AS order_date,
|
|
||||||
o.pay_amount_int / 100 AS pay_amount,
|
|
||||||
CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END AS tax_rate,
|
|
||||||
(o.pay_amount_int / 100.0) / (1 + CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END) * CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END AS tax_amount,
|
|
||||||
(o.pay_amount_int / 100.0) / (1 + CASE WHEN DATE(o.created_at) < '2026-05-01' THEN 0.01 ELSE 0.06 END) AS after_tax_amount,
|
|
||||||
DATE(o.created_at) + INTERVAL '7 days' AS amortization_start_date,
|
|
||||||
o.order_status,
|
|
||||||
a.id AS account_id,
|
|
||||||
o.key_from,
|
|
||||||
o.sale_channel,
|
|
||||||
CASE WHEN ar.order_no IS NOT NULL THEN 1 ELSE 0 END AS has_refund,
|
|
||||||
COALESCE(ar.total_refund_amount, 0) AS total_refund_amount,
|
|
||||||
CASE WHEN ar.order_no IS NOT NULL AND COALESCE(ar.total_refund_amount, 0) >= o.pay_amount_int / 100 THEN 1 ELSE 0 END AS is_full_refund,
|
|
||||||
ar.refund_type,
|
|
||||||
ar.latest_refund_date
|
|
||||||
FROM bi_vala_order o
|
|
||||||
JOIN bi_vala_app_account a ON o.account_id = a.id
|
|
||||||
LEFT JOIN all_refund_records ar ON o.out_trade_no = ar.order_no
|
|
||||||
WHERE
|
|
||||||
o.created_at >= '2025-06-01'
|
|
||||||
AND o.created_at <= '{period_end}'::date + INTERVAL '1 day' - INTERVAL '1 second'
|
|
||||||
AND o.order_status IN (3,4)
|
|
||||||
AND o.pay_amount_int >= 1000
|
|
||||||
),
|
|
||||||
-- 步骤2:计算均摊周期
|
|
||||||
order_cycle AS (
|
|
||||||
SELECT out_trade_no AS order_no, COUNT(DISTINCT id) * 90 AS total_cycle_days
|
|
||||||
FROM bi_vala_seasonal_ticket WHERE status != -1 GROUP BY out_trade_no
|
|
||||||
),
|
|
||||||
order_cycle_original AS (
|
|
||||||
SELECT out_trade_no AS order_no, COUNT(DISTINCT id) * 90 AS original_cycle_days
|
|
||||||
FROM bi_vala_seasonal_ticket GROUP BY out_trade_no
|
|
||||||
),
|
|
||||||
-- 步骤3:合并
|
|
||||||
order_with_cycle AS (
|
|
||||||
SELECT
|
|
||||||
ob.*,
|
|
||||||
COALESCE(oc.total_cycle_days, 0) AS total_cycle_days,
|
|
||||||
COALESCE(oco.original_cycle_days, 0) AS original_cycle_days,
|
|
||||||
ob.amortization_start_date + COALESCE(oc.total_cycle_days, 0) * INTERVAL '1 day' - INTERVAL '1 day' AS amortization_end_date,
|
|
||||||
ob.amortization_start_date + COALESCE(oco.original_cycle_days, 0) * INTERVAL '1 day' - INTERVAL '1 day' AS original_amortization_end_date,
|
|
||||||
CASE WHEN COALESCE(oco.original_cycle_days, 0) > 0
|
|
||||||
THEN ob.after_tax_amount / oco.original_cycle_days ELSE 0
|
|
||||||
END AS original_daily_amort_amount,
|
|
||||||
CASE WHEN COALESCE(oco.original_cycle_days, 0) > 0
|
|
||||||
THEN ob.tax_amount / oco.original_cycle_days ELSE 0
|
|
||||||
END AS original_daily_tax_amount,
|
|
||||||
CASE WHEN COALESCE(oc.total_cycle_days, 0) > 0 AND ob.has_refund = 1 AND ob.is_full_refund = 0
|
|
||||||
THEN (ob.pay_amount - ob.total_refund_amount) / (1 + ob.tax_rate) / oc.total_cycle_days
|
|
||||||
WHEN COALESCE(oc.total_cycle_days, 0) > 0
|
|
||||||
THEN ob.after_tax_amount / oc.total_cycle_days ELSE 0
|
|
||||||
END AS daily_amort_amount,
|
|
||||||
CASE WHEN COALESCE(oc.total_cycle_days, 0) > 0 AND ob.has_refund = 1 AND ob.is_full_refund = 0
|
|
||||||
THEN (ob.pay_amount - ob.total_refund_amount) / (1 + ob.tax_rate) * ob.tax_rate / oc.total_cycle_days
|
|
||||||
WHEN COALESCE(oc.total_cycle_days, 0) > 0
|
|
||||||
THEN ob.tax_amount / oc.total_cycle_days ELSE 0
|
|
||||||
END AS daily_tax_amount
|
|
||||||
FROM order_base ob
|
|
||||||
LEFT JOIN order_cycle oc ON ob.order_no = oc.order_no
|
|
||||||
LEFT JOIN order_cycle_original oco ON ob.order_no = oco.order_no
|
|
||||||
),
|
|
||||||
-- 步骤4:分类
|
|
||||||
order_classified AS (
|
|
||||||
SELECT
|
|
||||||
owc.*,
|
|
||||||
GREATEST(owc.amortization_start_date, '{period_start}'::date) AS eff_start,
|
|
||||||
LEAST(owc.amortization_end_date, '{period_end}'::date) AS eff_end,
|
|
||||||
-- 试用期内退费(退费日 < 转正日,从未产生均摊,不应触发冲销)
|
|
||||||
CASE WHEN owc.has_refund = 1
|
|
||||||
AND owc.latest_refund_date < DATE(owc.amortization_start_date)
|
|
||||||
THEN 1 ELSE 0 END AS is_trial_period_refund,
|
|
||||||
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 1 AND owc.latest_refund_date < '{period_start}'::date
|
|
||||||
THEN 1 ELSE 0 END AS is_pre_period_full_refund,
|
|
||||||
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 0 AND owc.latest_refund_date < '{period_start}'::date
|
|
||||||
THEN 1 ELSE 0 END AS is_pre_period_partial_refund,
|
|
||||||
CASE WHEN owc.has_refund = 1
|
|
||||||
AND owc.latest_refund_date >= '{period_start}'::date AND owc.latest_refund_date <= '{period_end}'::date
|
|
||||||
THEN 1 ELSE 0 END AS is_current_period_refund,
|
|
||||||
CASE WHEN owc.order_date >= '{period_start}'::date AND owc.order_date <= '{period_end}'::date
|
|
||||||
THEN 1 ELSE 0 END AS is_ordered_in_period,
|
|
||||||
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 0
|
|
||||||
THEN (owc.pay_amount - owc.total_refund_amount) / (1 + owc.tax_rate) ELSE owc.after_tax_amount
|
|
||||||
END AS refunded_after_tax_amount,
|
|
||||||
CASE WHEN owc.has_refund = 1 AND owc.is_full_refund = 0
|
|
||||||
THEN (owc.pay_amount - owc.total_refund_amount) / (1 + owc.tax_rate) * owc.tax_rate ELSE owc.tax_amount
|
|
||||||
END AS refunded_tax_amount
|
|
||||||
FROM order_with_cycle owc
|
|
||||||
WHERE owc.total_cycle_days > 0 OR (owc.has_refund = 1 AND owc.is_full_refund = 1 AND owc.original_cycle_days > 0)
|
|
||||||
),
|
|
||||||
-- 步骤5:计算天数
|
|
||||||
order_with_days AS (
|
|
||||||
SELECT
|
|
||||||
oc2.*,
|
|
||||||
CASE WHEN oc2.eff_end >= oc2.eff_start AND oc2.total_cycle_days > 0
|
|
||||||
THEN EXTRACT(DAY FROM (oc2.eff_end - oc2.eff_start))::int + 1 ELSE 0
|
|
||||||
END AS period_amort_days,
|
|
||||||
CASE WHEN '{period_end}'::date >= oc2.amortization_start_date AND oc2.total_cycle_days > 0
|
|
||||||
THEN EXTRACT(DAY FROM (LEAST('{period_end}'::date, oc2.amortization_end_date) - oc2.amortization_start_date))::int + 1 ELSE 0
|
|
||||||
END AS total_amorted_days_to_period_end,
|
|
||||||
CASE WHEN '{period_start}'::date > oc2.amortization_start_date AND oc2.original_cycle_days > 0
|
|
||||||
THEN EXTRACT(DAY FROM (LEAST('{period_start}'::date - 1, oc2.original_amortization_end_date) - oc2.amortization_start_date))::int + 1 ELSE 0
|
|
||||||
END AS historical_amorted_days,
|
|
||||||
CASE WHEN '{period_end}'::date >= oc2.amortization_start_date AND oc2.total_cycle_days > 0
|
|
||||||
THEN EXTRACT(DAY FROM (LEAST('{period_end}'::date, oc2.amortization_end_date) - oc2.amortization_start_date))::int + 1 ELSE 0
|
|
||||||
END AS refunded_amort_days_to_period_end
|
|
||||||
FROM order_classified oc2
|
|
||||||
)
|
|
||||||
-- 步骤6:最终汇总(与订单明细Sheet2列对齐,前四列替换为订单统计+税率,其余SUM)
|
|
||||||
SELECT
|
SELECT
|
||||||
COUNT(CASE WHEN order_date >= '{period_start}'::date AND order_date <= '{period_end}'::date THEN 1 END) AS "订单数",
|
-- 订单统计
|
||||||
COUNT(CASE WHEN order_date >= '{period_start}'::date AND order_date <= '{period_end}'::date
|
COUNT(CASE WHEN order_date >= '{period_start}'::date
|
||||||
|
AND order_date <= '{period_end}'::date THEN 1 END) AS "订单数",
|
||||||
|
COUNT(CASE WHEN order_date >= '{period_start}'::date
|
||||||
|
AND order_date <= '{period_end}'::date
|
||||||
AND amortization_start_date <= '{period_end}'::date THEN 1 END) AS "正式订单数",
|
AND amortization_start_date <= '{period_end}'::date THEN 1 END) AS "正式订单数",
|
||||||
COUNT(CASE WHEN order_date >= '{period_start}'::date AND order_date <= '{period_end}'::date
|
COUNT(CASE WHEN order_date >= '{period_start}'::date
|
||||||
|
AND order_date <= '{period_end}'::date
|
||||||
AND amortization_start_date > '{period_end}'::date THEN 1 END) AS "试用订单数",
|
AND amortization_start_date > '{period_end}'::date THEN 1 END) AS "试用订单数",
|
||||||
|
|
||||||
-- 冲销历史累计摊销金额(SUM,明细列逻辑)
|
-- 冲销历史累计摊销金额
|
||||||
ROUND(SUM(
|
ROUND(SUM(
|
||||||
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
||||||
AND is_trial_period_refund = 0
|
AND is_trial_period_refund = 0 AND historical_amorted_days > 0
|
||||||
AND historical_amorted_days > 0
|
THEN -(historical_amorted_days * original_daily_amort_amount) ELSE 0 END
|
||||||
THEN -(historical_amorted_days * original_daily_amort_amount)
|
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS "冲销历史累计摊销金额",
|
)::numeric, 2) AS "冲销历史累计摊销金额",
|
||||||
|
|
||||||
-- 退后订单历史累计摊销金额(SUM,明细列逻辑)
|
-- 退后订单历史累计摊销金额
|
||||||
ROUND(SUM(
|
ROUND(SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN is_ordered_in_period = 1 THEN 0
|
WHEN is_ordered_in_period = 1 THEN 0
|
||||||
@ -152,29 +31,24 @@ SELECT
|
|||||||
THEN historical_amorted_days * daily_amort_amount
|
THEN historical_amorted_days * daily_amort_amount
|
||||||
WHEN historical_amorted_days > 0
|
WHEN historical_amorted_days > 0
|
||||||
THEN historical_amorted_days * original_daily_amort_amount
|
THEN historical_amorted_days * original_daily_amort_amount
|
||||||
ELSE 0
|
ELSE 0 END
|
||||||
END
|
|
||||||
)::numeric, 2) AS "退后订单历史累计摊销金额",
|
)::numeric, 2) AS "退后订单历史累计摊销金额",
|
||||||
|
|
||||||
-- 冲销原税款(SUM)
|
-- 冲销原税款
|
||||||
ROUND(SUM(
|
ROUND(SUM(
|
||||||
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
||||||
AND is_trial_period_refund = 0
|
AND is_trial_period_refund = 0
|
||||||
THEN -tax_amount
|
THEN -tax_amount ELSE 0 END
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS "冲销原税款",
|
)::numeric, 2) AS "冲销原税款",
|
||||||
|
|
||||||
-- 冲销未确认收入(SUM)
|
-- 冲销未确认收入
|
||||||
ROUND(SUM(
|
ROUND(SUM(
|
||||||
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0
|
||||||
AND is_trial_period_refund = 0
|
AND is_trial_period_refund = 0
|
||||||
THEN -(after_tax_amount - historical_amorted_days * original_daily_amort_amount)
|
THEN -(after_tax_amount - historical_amorted_days * original_daily_amort_amount) ELSE 0 END
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS "冲销未确认收入",
|
)::numeric, 2) AS "冲销未确认收入",
|
||||||
|
|
||||||
-- 当期摊销金额(SUM)
|
-- 当期摊销金额
|
||||||
ROUND(SUM(
|
ROUND(SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN is_pre_period_full_refund = 1 THEN 0
|
WHEN is_pre_period_full_refund = 1 THEN 0
|
||||||
@ -188,37 +62,31 @@ SELECT
|
|||||||
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
||||||
THEN daily_amort_amount * period_amort_days ELSE 0 END
|
THEN daily_amort_amount * period_amort_days ELSE 0 END
|
||||||
WHEN is_current_period_refund = 1 AND is_full_refund = 0 AND is_ordered_in_period = 0 THEN
|
WHEN is_current_period_refund = 1 AND is_full_refund = 0 AND is_ordered_in_period = 0 THEN
|
||||||
CASE WHEN total_cycle_days > 0 AND refunded_amort_days_to_period_end > 0
|
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
||||||
THEN daily_amort_amount * refunded_amort_days_to_period_end ELSE 0 END
|
THEN daily_amort_amount * period_amort_days ELSE 0 END
|
||||||
WHEN is_pre_period_partial_refund = 1 THEN
|
WHEN is_pre_period_partial_refund = 1 THEN
|
||||||
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
CASE WHEN total_cycle_days > 0 AND period_amort_days > 0
|
||||||
THEN daily_amort_amount * period_amort_days ELSE 0 END
|
THEN daily_amort_amount * period_amort_days ELSE 0 END
|
||||||
WHEN period_amort_days > 0 THEN
|
WHEN period_amort_days > 0 THEN
|
||||||
CASE
|
CASE WHEN eff_end = amortization_end_date
|
||||||
WHEN eff_end = amortization_end_date
|
THEN after_tax_amount - original_daily_amort_amount * (original_cycle_days - 1)
|
||||||
THEN after_tax_amount - original_daily_amort_amount * (original_cycle_days - 1) + original_daily_amort_amount * (period_amort_days - 1)
|
+ original_daily_amort_amount * (period_amort_days - 1)
|
||||||
ELSE original_daily_amort_amount * period_amort_days
|
ELSE original_daily_amort_amount * period_amort_days END
|
||||||
END
|
ELSE 0 END
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS "当期摊销金额",
|
)::numeric, 2) AS "当期摊销金额",
|
||||||
|
|
||||||
-- 账期税额(SUM)
|
-- 账期税额
|
||||||
ROUND(SUM(
|
ROUND(SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN is_ordered_in_period = 1
|
WHEN is_ordered_in_period = 1 AND DATE(amortization_start_date) <= '{period_end}'::date
|
||||||
AND DATE(amortization_start_date) <= '{period_end}'::date
|
|
||||||
AND is_current_period_refund = 1 AND is_full_refund = 0
|
AND is_current_period_refund = 1 AND is_full_refund = 0
|
||||||
THEN refunded_tax_amount
|
THEN refunded_tax_amount
|
||||||
WHEN is_ordered_in_period = 1
|
WHEN is_ordered_in_period = 1 AND DATE(amortization_start_date) <= '{period_end}'::date
|
||||||
AND DATE(amortization_start_date) <= '{period_end}'::date
|
|
||||||
AND (has_refund = 0 OR (is_current_period_refund = 0 AND is_full_refund = 0))
|
AND (has_refund = 0 OR (is_current_period_refund = 0 AND is_full_refund = 0))
|
||||||
THEN tax_amount
|
THEN tax_amount ELSE 0 END
|
||||||
ELSE 0
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS "账期税额",
|
)::numeric, 2) AS "账期税额",
|
||||||
|
|
||||||
-- 未确认收入(SUM)
|
-- 未确认收入
|
||||||
ROUND(SUM(
|
ROUND(SUM(
|
||||||
CASE
|
CASE
|
||||||
WHEN is_pre_period_full_refund = 1 THEN 0
|
WHEN is_pre_period_full_refund = 1 THEN 0
|
||||||
@ -227,10 +95,9 @@ SELECT
|
|||||||
refunded_after_tax_amount - daily_amort_amount * refunded_amort_days_to_period_end
|
refunded_after_tax_amount - daily_amort_amount * refunded_amort_days_to_period_end
|
||||||
WHEN is_pre_period_partial_refund = 1 THEN
|
WHEN is_pre_period_partial_refund = 1 THEN
|
||||||
refunded_after_tax_amount - daily_amort_amount * total_amorted_days_to_period_end
|
refunded_after_tax_amount - daily_amort_amount * total_amorted_days_to_period_end
|
||||||
ELSE
|
ELSE after_tax_amount - original_daily_amort_amount * total_amorted_days_to_period_end END
|
||||||
after_tax_amount - original_daily_amort_amount * total_amorted_days_to_period_end
|
|
||||||
END
|
|
||||||
)::numeric, 2) AS "未确认收入"
|
)::numeric, 2) AS "未确认收入"
|
||||||
|
|
||||||
FROM order_with_days
|
FROM order_with_days
|
||||||
WHERE is_pre_period_full_refund = 0;
|
WHERE is_pre_period_full_refund = 0
|
||||||
|
AND NOT (is_trial_period_refund = 1 AND is_full_refund = 1);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user