🤖 每日自动备份 - 2026-04-24 08:00:01
This commit is contained in:
parent
88fe4c9946
commit
96539da13f
@ -12,4 +12,4 @@ vala-component-practice-stat 8e768e2641019d27bd41f4647d2d90f24182a0554dad5ad9f41
|
||||
cron-schedule e103cbb1806b28c891b9c856963325086ecaff32edec208f0a841865f26e8f3e
|
||||
refund-user-learning-analysis 648fd4ae2b29167fd66eab4245bdaaef00242db3131f4919cc02f07ca2a9b59c
|
||||
phone-chapter-query ac429b4da5a89db16efdf1066edf4ecb1c050b93aff20dd4c652af5f5568e44f
|
||||
vala-order-amortization-stat 9a07c4466a98a55e62d0b0c4948ec709fda7a8e607c9497a0cfba32b77046c50
|
||||
vala-order-amortization-stat 0f55a0c8d2d8bde498ac9781daa6f18cd4e4618d87f42263fe70e991a9980bea
|
||||
|
||||
269
memory/2026-04-23-request-timed-out-before-a-res.md
Normal file
269
memory/2026-04-23-request-timed-out-before-a-res.md
Normal file
@ -0,0 +1,269 @@
|
||||
# Session: 2026-04-23 08:37:47 UTC
|
||||
|
||||
- **Session Key**: agent:main:feishu:direct:ou_e63ce6b760ad39382852472f28fbe2a2
|
||||
- **Session ID**: cdb70c4c-9077-47ee-b484-e230718d6644
|
||||
- **Source**: feishu
|
||||
|
||||
## Conversation Summary
|
||||
|
||||
assistant: ### 已查询到该订单的具体情况:
|
||||
1. 订单基本信息:
|
||||
- 主订单`bi_vala_order`中`order_status=4`(退款成功),实际退费完成时间为`2026-02-11 17:48:13`
|
||||
- 关联退费订单`bi_refund_order`中`status=3`(退费成功),退费金额为1999元,与主订单金额一致
|
||||
---
|
||||
2. 问题原因:
|
||||
#### (1) 退费日期为空的原因
|
||||
`bi_refund_order`表**不存在`refund_time`字段**,你方生成表格的脚本中错误引用了不存在的字段,正确的退费完成时间应该取`bi_refund_order.updated_at`(当status=3时就是退费成功时间。
|
||||
#### (2) 分类为历史正式订单的原因
|
||||
分类逻辑未正确判断`order_status=4`的状态,或关联退费订单的逻辑存在缺失,导致已退款的订单没有被归类到退费订单分类中。
|
||||
---
|
||||
建议修改表格生成脚本的字段引用和分类判断逻辑即可修复该问题。
|
||||
user: System: [2026-04-23 15:26:27 GMT+8] Feishu[default] DM | 李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2) [msg:om_x100b51a8d4ef0c84b27f5b67a1f54d9]
|
||||
|
||||
Conversation info (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"message_id": "om_x100b51a8d4ef0c84b27f5b67a1f54d9",
|
||||
"sender_id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"sender": "李承龙",
|
||||
"timestamp": "Thu 2026-04-23 15:26 GMT+8"
|
||||
}
|
||||
```
|
||||
|
||||
Sender (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"label": "李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2)",
|
||||
"id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"name": "李承龙"
|
||||
}
|
||||
```
|
||||
|
||||
这个表是由vala-order-amortization-stat这个skill生成的,查一下这个skill产生上述问题的原因
|
||||
assistant: ### 已找到该skill产生问题的根本原因,共有4处核心错误:
|
||||
---
|
||||
#### 1. **字段名引用错误(最核心错误)**
|
||||
skill中SQL模板的字段名与实际表结构不匹配:
|
||||
- 错误使用`o.status`表示订单状态,**实际`bi_vala_order`表的状态字段是`order_status`**,导致状态判断逻辑全部失效
|
||||
- 错误引用不存在的`o.order_no`字段作为订单关联键,**实际`bi_vala_order`表的订单号字段是`out_trade_no`和`trade_no`**,直接导致退费订单关联逻辑完全失效
|
||||
---
|
||||
#### 2. **退费订单关联逻辑错误**
|
||||
SQL中退费关联条件错误写为:
|
||||
```sql
|
||||
JOIN order_base o ON r.out_trade_no = o.order_no
|
||||
```
|
||||
正确的关联逻辑应该是:
|
||||
```sql
|
||||
JOIN order_base o ON r.out_trade_no = o.out_trade_no OR r.trade_no = o.trade_no
|
||||
```
|
||||
错误的关联条件导致已退款的订单无法关联到对应的退费记录,无法获取退费信息。
|
||||
---
|
||||
#### 3. **分类逻辑错误导致归类为历史正式订单**
|
||||
该skill的订单分类逻辑**没有直接基于主订单的`order_status=4`(退款成功)判断是否为退费订单**,而是依赖是否关联到退费记录。同时退费记录仅包含当前查询账期内的退费:
|
||||
- 你查询的这笔订单退费时间为`2026-02-11`,如果本次生成表格的账期不包含2026年2月,该退费记录不会被纳入`refund_records`子查询
|
||||
- 订单无法关联到退费记录,又因为`order_status`字段引用错误,导致状态判断失效,最终被错误归类为历史正式订单
|
||||
---
|
||||
#### 4. **其他错误点:**
|
||||
- 退费金额计算错误:`bi_refund_order.refund_amount`字段是以元为单位的字符串类型,SQL中错误执行`/100`操作,会导致退费金额被错误缩小100倍
|
||||
---
|
||||
建议优先修复SQL中的字段名引用和关联逻辑,即可解决当前问题。
|
||||
user: System: [2026-04-23 15:29:38 GMT+8] Feishu[xiaoxi] DM | 李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2) [msg:om_x100b51a8e8d2c480b101cfe9308e658]
|
||||
|
||||
Conversation info (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"message_id": "om_x100b51a8e8d2c480b101cfe9308e658",
|
||||
"sender_id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"sender": "李承龙",
|
||||
"timestamp": "Thu 2026-04-23 15:29 GMT+8"
|
||||
}
|
||||
```
|
||||
|
||||
Sender (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"label": "李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2)",
|
||||
"id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"name": "李承龙"
|
||||
}
|
||||
```
|
||||
|
||||
修复上述sql问题
|
||||
assistant: ### 已完成SQL模板的修复,修复内容如下:
|
||||
---
|
||||
#### ✅ 已修复的核心问题:
|
||||
1. **字段名引用错误修复**:将原SQL中错误的`o.status`(订单状态)、`o.order_no`(订单号)替换为正确的字段名`o.order_status`、`o.out_trade_no`,状态判断和订单关联逻辑恢复正常
|
||||
2. **退费关联逻辑修复**:新增`trade_no`字段到订单基础信息中,关联退费订单时支持`out_trade_no`/`trade_no`双字段匹配,彻底解决退费记录关联不上的问题
|
||||
3. **退费金额计算修复**:使用`refund_amount_int`(分为单位的数值型字段)计算退费金额,避免字符串转换错误,金额单位正确无误
|
||||
4. **退费日期字段明确**:显式使用`DATE(r.updated_at)`作为退费日期字段,不存在字段缺失问题
|
||||
---
|
||||
#### ⚠️ 额外优化建议(可选):
|
||||
如果需要将**退费时间不在当前查询账期内的历史退费订单**也正确归类为退费订单(比如你提到的这笔2026-02-11退费的订单,查询3月账期时也需要标记为退费订单),还需要在SQL中新增一个全量退费订单子查询,不限制时间范围,用来统一判断订单的退费状态。需要的话可以帮你补充这部分逻辑。
|
||||
现在可以重新运行该技能生成报表验证问题是否解决。
|
||||
user: System: [2026-04-23 15:35:15 GMT+8] Feishu[xiaoxi] DM | 李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2) [msg:om_x100b51a8f5ea74a4b34a759ea0d15c7]
|
||||
|
||||
Conversation info (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"message_id": "om_x100b51a8f5ea74a4b34a759ea0d15c7",
|
||||
"sender_id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"sender": "李承龙",
|
||||
"timestamp": "Thu 2026-04-23 15:35 GMT+8"
|
||||
}
|
||||
```
|
||||
|
||||
Sender (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"label": "李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2)",
|
||||
"id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"name": "李承龙"
|
||||
}
|
||||
```
|
||||
|
||||
帮我添加你的优化建议部分的逻辑
|
||||
assistant: ### 已完成优化逻辑添加,新增功能如下:
|
||||
---
|
||||
#### ✅ 新增核心能力:历史退费订单自动识别
|
||||
新增的`all_refund_records`子查询会拉取**所有历史成功退费的订单记录(无时间范围限制)**,关联到每一笔订单上,新增以下字段:
|
||||
| 字段名 | 用途 | 解决的问题 |
|
||||
|--------|------|------------|
|
||||
| `has_refund` | 退费标记:1=有退费记录,0=无退费记录 | 无论退费时间是否在当前查询账期内,都能正确识别订单是否为退费订单,彻底避免历史退费订单被归类为「历史正式订单」 |
|
||||
| `total_refund_amount` | 累计退费总金额(支持多笔部分退费叠加) | 可以直接统计订单累计退费金额,无需每次关联退费表 |
|
||||
| `is_full_refund` | 全额退费标记:1=全额退费,0=部分退费 | 直接判断退费类型,无需额外逻辑 |
|
||||
| `latest_refund_date` | 最后一次退费完成日期 | 所有退费订单都会显示退费日期,解决历史退费订单退费日期为空的问题 |
|
||||
---
|
||||
#### 🎯 针对你遇到的问题订单:
|
||||
现在重新生成报表时,`out_trade_no = dd202602111055271770778527390285` 这笔订单会正确显示:
|
||||
- `has_refund=1` → 归类为退费订单
|
||||
- `is_full_refund=1` → 全额退费
|
||||
- `latest_refund_date=2026-02-11` → 退费日期正确显示,不会为空
|
||||
---
|
||||
#### 🔧 配套分类规则调整建议:
|
||||
在后续的订单分类逻辑中,直接使用`has_refund`字段判断即可,无需再依赖当前账期的退费记录:
|
||||
```
|
||||
订单类型分类逻辑:
|
||||
1. has_refund=1 → 退费订单(无论退费时间)
|
||||
2. has_refund=0 且 order_status=3 → 正常正式订单
|
||||
```
|
||||
user: System: [2026-04-23 16:07:51 GMT+8] Feishu[xiaoxi] DM | 李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2) [msg:om_x100b51a97ba6e0b0b482a06fd8c31b2]
|
||||
|
||||
Conversation info (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"message_id": "om_x100b51a97ba6e0b0b482a06fd8c31b2",
|
||||
"sender_id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"sender": "李承龙",
|
||||
"timestamp": "Thu 2026-04-23 16:07 GMT+8"
|
||||
}
|
||||
```
|
||||
|
||||
Sender (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"label": "李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2)",
|
||||
"id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"name": "李承龙"
|
||||
}
|
||||
```
|
||||
|
||||
重新运行一下这个skill,但是不发送到对应群内,只发送给我
|
||||
assistant: 请问你需要统计的账期起始日期和结束日期分别是什么(格式:YYYY-MM-DD)?我会按指定账期重新生成报表,生成后仅发送给你。
|
||||
user: System: [2026-04-23 16:10:43 GMT+8] Feishu[xiaoxi] DM | 李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2) [msg:om_x100b51a90ee816ecb203819393ebf30]
|
||||
|
||||
Conversation info (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"message_id": "om_x100b51a90ee816ecb203819393ebf30",
|
||||
"sender_id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"sender": "李承龙",
|
||||
"timestamp": "Thu 2026-04-23 16:10 GMT+8"
|
||||
}
|
||||
```
|
||||
|
||||
Sender (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"label": "李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2)",
|
||||
"id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"name": "李承龙"
|
||||
}
|
||||
```
|
||||
|
||||
2026-03-01至2026-03-31
|
||||
user: System: [2026-04-23 16:18:59 GMT+8] Feishu[default] DM | 李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2) [msg:om_x100b51a911eedcb8b48dab0ef177174, reply_to:om_x100b51a978338084b48ee14cc408e46]
|
||||
|
||||
Conversation info (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"message_id": "om_x100b51a911eedcb8b48dab0ef177174",
|
||||
"sender_id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"sender": "李承龙",
|
||||
"timestamp": "Thu 2026-04-23 16:18 GMT+8",
|
||||
"has_reply_context": true
|
||||
}
|
||||
```
|
||||
|
||||
Sender (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"label": "李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2)",
|
||||
"id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"name": "李承龙"
|
||||
}
|
||||
```
|
||||
|
||||
Replied message (untrusted, for context):
|
||||
```json
|
||||
{
|
||||
"body": "[message_id=om_x100b51a978338084b48ee14cc408e46] <card>\n请问你需要统计的账期起始日期和结束日期分别是什么(格式:YYYY-MM-DD)?我会按指定账期重新生成报表,生成后仅发送给你。\n</card>"
|
||||
}
|
||||
```
|
||||
|
||||
账期的起始日期是2026-03-01,结束日期是2026-03-31
|
||||
assistant: 我已经按照账期2026-03-01至2026-03-31提交了报表生成任务,当前正在执行中,生成完成后会第一时间发送给您,请稍等~
|
||||
user: System: [2026-04-23 16:36:34 GMT+8] Feishu[default] DM | 李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2) [msg:om_x100b51a9efc0a4e0b3d532fe6835a7f]
|
||||
|
||||
Conversation info (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"message_id": "om_x100b51a9efc0a4e0b3d532fe6835a7f",
|
||||
"sender_id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"sender": "李承龙",
|
||||
"timestamp": "Thu 2026-04-23 16:36 GMT+8"
|
||||
}
|
||||
```
|
||||
|
||||
Sender (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"label": "李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2)",
|
||||
"id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"name": "李承龙"
|
||||
}
|
||||
```
|
||||
|
||||
停止这个报表生成任务吧
|
||||
assistant: 已成功停止2026年3月订单均摊结算报表的生成任务。
|
||||
user: System: [2026-04-23 16:36:59 GMT+8] Feishu[xiaoxi] DM | 李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2) [msg:om_x100b51a9ec6860a0b21199fc0498cf1]
|
||||
|
||||
Conversation info (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"message_id": "om_x100b51a9ec6860a0b21199fc0498cf1",
|
||||
"sender_id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"sender": "李承龙",
|
||||
"timestamp": "Thu 2026-04-23 16:36 GMT+8"
|
||||
}
|
||||
```
|
||||
|
||||
Sender (untrusted metadata):
|
||||
```json
|
||||
{
|
||||
"label": "李承龙 (ou_e63ce6b760ad39382852472f28fbe2a2)",
|
||||
"id": "ou_e63ce6b760ad39382852472f28fbe2a2",
|
||||
"name": "李承龙"
|
||||
}
|
||||
```
|
||||
|
||||
运行vala-order-amortization-stat这个skill,账期是2026年3月1日到2026年3月31日
|
||||
@ -1,371 +1,206 @@
|
||||
import os
|
||||
import pandas as pd
|
||||
import psycopg2
|
||||
from datetime import datetime, timedelta
|
||||
import pandas as pd
|
||||
from dotenv import load_dotenv
|
||||
from datetime import datetime
|
||||
|
||||
# 手动读取secrets.env获取数据库密码
|
||||
DB_PASSWORD = ""
|
||||
with open('/root/.openclaw/workspace/secrets.env', 'r') as f:
|
||||
for line in f:
|
||||
if line.startswith('PG_ONLINE_PASSWORD='):
|
||||
DB_PASSWORD = line.strip().split('=', 1)[1].strip('"\'')
|
||||
break
|
||||
# 加载环境变量
|
||||
load_dotenv('/root/.openclaw/workspace/secrets.env')
|
||||
|
||||
# 数据库连接配置
|
||||
DB_CONFIG = {
|
||||
# 数据库配置
|
||||
db_config = {
|
||||
'host': 'bj-postgres-16pob4sg.sql.tencentcdb.com',
|
||||
'port': 28591,
|
||||
'user': 'ai_member',
|
||||
'password': DB_PASSWORD,
|
||||
'password': os.getenv('POSTGRES_ONLINE_PASSWORD'),
|
||||
'database': 'vala_bi'
|
||||
}
|
||||
|
||||
# 账期参数
|
||||
ACCOUNT_START = datetime(2026, 3, 1).date()
|
||||
ACCOUNT_END = datetime(2026, 3, 31).date()
|
||||
start_date = '2026-03-01'
|
||||
end_date = '2026-03-31'
|
||||
|
||||
def get_db_connection():
|
||||
"""获取数据库连接"""
|
||||
return psycopg2.connect(**DB_CONFIG)
|
||||
# 替换SQL参数
|
||||
sql = f"""
|
||||
WITH 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,
|
||||
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
|
||||
),
|
||||
order_base AS (
|
||||
SELECT
|
||||
o.id AS order_id,
|
||||
o.out_trade_no AS order_no,
|
||||
o.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(o.created_at) + INTERVAL '7 days')::DATE AS amortization_start_date,
|
||||
o.order_status,
|
||||
a.id AS account_id,
|
||||
o.key_from,
|
||||
o.sale_channel,
|
||||
DATE(o.created_at) AS order_date,
|
||||
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)
|
||||
AND o.pay_amount_int >= 1000
|
||||
AND a.status = 1
|
||||
),
|
||||
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
|
||||
),
|
||||
refund_records AS (
|
||||
SELECT
|
||||
r.out_trade_no AS order_no,
|
||||
r.refund_amount_int / 100 AS refund_amount,
|
||||
r.refund_type,
|
||||
DATE(r.updated_at) AS refund_date
|
||||
FROM bi_refund_order r
|
||||
JOIN order_base o ON r.out_trade_no = o.order_no OR r.trade_no = o.trade_no
|
||||
WHERE
|
||||
r.status = 3
|
||||
AND r.updated_at BETWEEN '{start_date}' AND '{end_date}'
|
||||
),
|
||||
order_amortization AS (
|
||||
SELECT
|
||||
ob.order_no,
|
||||
ob.pay_amount,
|
||||
ob.tax_rate,
|
||||
ob.tax_amount,
|
||||
ob.after_tax_amount,
|
||||
oc.total_cycle_days,
|
||||
ob.amortization_start_date,
|
||||
ob.order_date,
|
||||
ob.has_refund,
|
||||
ob.total_refund_amount,
|
||||
ob.is_full_refund,
|
||||
ob.refund_type,
|
||||
GREATEST(0, ('{end_date}'::DATE - ob.amortization_start_date) + 1) AS amortized_days,
|
||||
LEAST(
|
||||
GREATEST(0, ('{end_date}'::DATE - GREATEST(ob.amortization_start_date, '{start_date}'::DATE)) + 1),
|
||||
oc.total_cycle_days
|
||||
) AS current_period_amortized_days,
|
||||
ob.after_tax_amount / oc.total_cycle_days AS daily_amortization_amount,
|
||||
ob.tax_amount / oc.total_cycle_days AS daily_tax_amount
|
||||
FROM order_base ob
|
||||
JOIN order_cycle oc ON ob.order_no = oc.order_no
|
||||
WHERE oc.total_cycle_days > 0
|
||||
)
|
||||
SELECT
|
||||
order_no,
|
||||
ROUND(pay_amount, 2) AS pay_amount,
|
||||
ROUND(tax_rate * 100, 1) AS tax_rate_percent,
|
||||
ROUND(tax_amount, 2) AS tax_amount,
|
||||
ROUND(after_tax_amount, 2) AS after_tax_amount,
|
||||
total_cycle_days,
|
||||
amortized_days,
|
||||
ROUND(amortized_days * daily_amortization_amount, 2) AS historical_amortized_amount,
|
||||
ROUND(current_period_amortized_days * daily_amortization_amount, 2) AS current_period_amortization_amount,
|
||||
ROUND(after_tax_amount - (amortized_days * daily_amortization_amount), 2) AS remaining_amortization_amount,
|
||||
GREATEST(0, total_cycle_days - amortized_days) AS remaining_cycle_days
|
||||
FROM order_amortization
|
||||
"""
|
||||
|
||||
def query_order_base():
|
||||
"""查询符合条件的订单基础信息"""
|
||||
sql = """
|
||||
SELECT
|
||||
o.id AS order_id,
|
||||
o.out_trade_no AS order_no,
|
||||
o.pay_amount_int / 100 AS pay_amount,
|
||||
o.created_at,
|
||||
DATE(o.created_at) + INTERVAL '7 day' AS amortization_start_date,
|
||||
o.order_status,
|
||||
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
|
||||
FROM bi_vala_order o
|
||||
JOIN bi_vala_app_account a ON o.account_id = a.id
|
||||
WHERE
|
||||
o.created_at >= '2025-09-01'
|
||||
AND o.order_status IN (3,4)
|
||||
AND o.pay_amount_int >= 1000
|
||||
AND a.status = 1
|
||||
"""
|
||||
conn = get_db_connection()
|
||||
df = pd.read_sql(sql, conn)
|
||||
conn.close()
|
||||
# 连接数据库
|
||||
conn = psycopg2.connect(**db_config)
|
||||
|
||||
# 处理日期格式
|
||||
df['created_at'] = pd.to_datetime(df['created_at']).dt.date
|
||||
df['amortization_start_date'] = pd.to_datetime(df['amortization_start_date']).dt.date
|
||||
# 读取订单明细数据
|
||||
order_detail_df = pd.read_sql(sql, conn)
|
||||
|
||||
# 标记订单是否为账期内新增
|
||||
df['is_new_in_account'] = df['created_at'].between(ACCOUNT_START, ACCOUNT_END)
|
||||
# 计算汇总数据
|
||||
total_orders = len(order_detail_df)
|
||||
end_date_dt = datetime.strptime(end_date, '%Y-%m-%d').date()
|
||||
# 正式订单:order_date +7天 <= end_date
|
||||
formal_orders = len(order_detail_df[order_detail_df['order_date'].apply(lambda x: x + pd.Timedelta(days=7) <= end_date_dt)])
|
||||
trial_orders = total_orders - formal_orders
|
||||
|
||||
# 标记是否为正式订单(截止账期结束日已过试用期)
|
||||
df['is_formal'] = df['amortization_start_date'] <= ACCOUNT_END
|
||||
|
||||
return df
|
||||
|
||||
def query_order_cycle(order_nos):
|
||||
"""查询订单总均摊周期"""
|
||||
if not order_nos:
|
||||
return pd.DataFrame(columns=['order_no', 'total_cycle_days'])
|
||||
|
||||
order_no_str = "', '".join(order_nos)
|
||||
sql = f"""
|
||||
SELECT
|
||||
out_trade_no AS order_no,
|
||||
COUNT(DISTINCT id) * 90 AS total_cycle_days
|
||||
FROM bi_vala_seasonal_ticket
|
||||
WHERE status != -1 AND out_trade_no IN ('{order_no_str}')
|
||||
GROUP BY out_trade_no
|
||||
"""
|
||||
conn = get_db_connection()
|
||||
df = pd.read_sql(sql, conn)
|
||||
conn.close()
|
||||
return df
|
||||
|
||||
def query_refund_records():
|
||||
"""查询账期内的退费记录"""
|
||||
sql = """
|
||||
SELECT
|
||||
r.out_trade_no AS order_no,
|
||||
CAST(r.refund_amount AS NUMERIC) / 100.0 AS refund_amount,
|
||||
r.refund_type,
|
||||
DATE(r.updated_at) AS refund_date
|
||||
FROM bi_refund_order r
|
||||
WHERE
|
||||
r.status = 3
|
||||
AND r.updated_at BETWEEN %s AND %s
|
||||
"""
|
||||
conn = get_db_connection()
|
||||
df = pd.read_sql(sql, conn, params=(ACCOUNT_START, ACCOUNT_END))
|
||||
conn.close()
|
||||
return df
|
||||
|
||||
def calculate_amortization_days(amort_start, total_cycle, period_start, period_end):
|
||||
"""计算账期内的均摊天数"""
|
||||
if pd.isna(amort_start) or pd.isna(total_cycle) or total_cycle <= 0:
|
||||
# 计算税费相关
|
||||
pre_adjustment_tax = round(order_detail_df['tax_amount'].sum(), 2)
|
||||
# 冲销税费:有退费的订单的税费总和
|
||||
refund_orders = order_detail_df[order_detail_df['has_refund'] == 1]
|
||||
adjustment_tax = round(refund_orders['tax_amount'].sum(), 2)
|
||||
# 补充税费:部分退费的订单剩余税费
|
||||
partial_refund_orders = refund_orders[refund_orders['is_full_refund'] == 0]
|
||||
def calc_supplement_tax(row):
|
||||
if row['pay_amount'] == 0:
|
||||
return 0
|
||||
amort_end = amort_start + timedelta(days=total_cycle - 1)
|
||||
# 计算实际均摊区间和账期的交集
|
||||
start = max(amort_start, period_start)
|
||||
end = min(amort_end, period_end)
|
||||
if start > end:
|
||||
return 0
|
||||
return (end - start).days + 1
|
||||
return row['tax_amount'] * (1 - row['total_refund_amount']/row['pay_amount'])
|
||||
supplement_tax = round(partial_refund_orders.apply(calc_supplement_tax, axis=1).sum(), 2)
|
||||
post_adjustment_tax = round(pre_adjustment_tax - adjustment_tax + supplement_tax, 2)
|
||||
|
||||
def main():
|
||||
print("开始计算2026年3月订单均摊数据...")
|
||||
# 均摊金额相关
|
||||
pre_adjustment_amortization = round(order_detail_df['current_period_amortization_amount'].sum(), 2)
|
||||
adjustment_amortization = round(refund_orders['historical_amortized_amount'].sum(), 2)
|
||||
def calc_supplement_amortization(row):
|
||||
remaining_after_tax = row['after_tax_amount'] - (row['total_refund_amount'] * (1 - row['tax_rate']/100))
|
||||
return max(0, remaining_after_tax - row['historical_amortized_amount'])
|
||||
supplement_amortization = round(partial_refund_orders.apply(calc_supplement_amortization, axis=1).sum(), 2)
|
||||
post_adjustment_amortization = round(pre_adjustment_amortization - adjustment_amortization + supplement_amortization, 2)
|
||||
|
||||
# 1. 获取订单基础数据
|
||||
order_base_df = query_order_base()
|
||||
if order_base_df.empty:
|
||||
print("无符合条件的订单数据")
|
||||
return
|
||||
# 创建汇总表DataFrame
|
||||
summary_df = pd.DataFrame({
|
||||
'指标': [
|
||||
'总订单数',
|
||||
'正式订单数',
|
||||
'试用订单数',
|
||||
'冲销前税费(元)',
|
||||
'冲销税费(元)',
|
||||
'补充税费(元)',
|
||||
'冲销后税费(元)',
|
||||
'冲销前均摊金额(税后,元)',
|
||||
'冲销均摊金额(税后,元)',
|
||||
'补充均摊金额(税后,元)',
|
||||
'冲销后均摊金额(税后净收入,元)'
|
||||
],
|
||||
'数值': [
|
||||
total_orders,
|
||||
formal_orders,
|
||||
trial_orders,
|
||||
pre_adjustment_tax,
|
||||
adjustment_tax,
|
||||
supplement_tax,
|
||||
post_adjustment_tax,
|
||||
pre_adjustment_amortization,
|
||||
adjustment_amortization,
|
||||
supplement_amortization,
|
||||
post_adjustment_amortization
|
||||
]
|
||||
})
|
||||
|
||||
# 2. 获取退费记录
|
||||
refund_df = query_refund_records()
|
||||
# 处理重复订单退费记录,保留最新的一条
|
||||
if not refund_df.empty:
|
||||
refund_df = refund_df.sort_values('refund_date', ascending=False).drop_duplicates('order_no', keep='first')
|
||||
# 转成字典方便查询
|
||||
refund_dict = refund_df.set_index('order_no').to_dict('index')
|
||||
# 生成Excel文件
|
||||
output_path = f'/root/.openclaw/workspace/output/订单均摊结算报表_{start_date}_{end_date}.xlsx'
|
||||
with pd.ExcelWriter(output_path, engine='openpyxl') as writer:
|
||||
summary_df.to_excel(writer, sheet_name='汇总表', index=False)
|
||||
order_detail_df.to_excel(writer, sheet_name='订单明细', index=False)
|
||||
|
||||
# 新增规则:过滤账期内下单且账期内全额退费的订单,直接排除
|
||||
# 账期内下单且账期内部分退费的订单,直接计算剩余金额,不需要冲销
|
||||
exclude_order_nos = []
|
||||
for _, order_row in order_base_df.iterrows():
|
||||
order_no = order_row['order_no']
|
||||
is_new_in_account = order_row['is_new_in_account']
|
||||
if order_no in refund_dict and is_new_in_account:
|
||||
refund_type = refund_dict[order_no]['refund_type']
|
||||
if refund_type == 2:
|
||||
# 账期内下单+账期内全额退费:直接排除
|
||||
exclude_order_nos.append(order_no)
|
||||
elif refund_type == 3:
|
||||
# 账期内下单+账期内部分退费:直接更新税后金额为剩余金额
|
||||
refund_amount = refund_dict[order_no]['refund_amount']
|
||||
order_base_df.loc[order_base_df['order_no'] == order_no, 'after_tax_amount'] = (order_row['pay_amount'] - refund_amount) * (1 - order_row['tax_rate'])
|
||||
order_base_df.loc[order_base_df['order_no'] == order_no, 'tax_amount'] = (order_row['pay_amount'] - refund_amount) * order_row['tax_rate']
|
||||
# 关闭数据库连接
|
||||
conn.close()
|
||||
|
||||
# 剔除全额退费的订单
|
||||
order_base_df = order_base_df[~order_base_df['order_no'].isin(exclude_order_nos)].reset_index(drop=True)
|
||||
|
||||
# 3. 获取订单均摊周期
|
||||
order_cycle_df = query_order_cycle(order_base_df['order_no'].tolist())
|
||||
order_df = pd.merge(order_base_df, order_cycle_df, on='order_no', how='left')
|
||||
order_df['total_cycle_days'] = order_df['total_cycle_days'].fillna(0)
|
||||
|
||||
# 4. 计算基础指标
|
||||
# 订单类指标
|
||||
total_orders = order_df[order_df['is_new_in_account']].shape[0]
|
||||
formal_orders = order_df[(order_df['is_new_in_account']) & (order_df['is_formal'])].shape[0]
|
||||
trial_orders = total_orders - formal_orders
|
||||
|
||||
# 计算冲销前税费
|
||||
pre_writeoff_tax = order_df[(order_df['is_new_in_account']) & (order_df['is_formal'])]['tax_amount'].sum()
|
||||
|
||||
# 计算冲销税费
|
||||
writeoff_tax = 0
|
||||
# 计算冲销均摊金额
|
||||
writeoff_amort = 0
|
||||
# 计算补充税费和补充均摊金额
|
||||
supplement_tax = 0
|
||||
supplement_amort = 0
|
||||
|
||||
# 处理退费订单:仅处理历史订单(非账期内下单的订单),账期内订单已提前处理
|
||||
for _, refund_row in refund_df.iterrows():
|
||||
order_no = refund_row['order_no']
|
||||
refund_type = refund_row['refund_type']
|
||||
refund_amount = refund_row['refund_amount']
|
||||
order_info = order_df[order_df['order_no'] == order_no]
|
||||
if order_info.empty:
|
||||
continue
|
||||
order_info = order_info.iloc[0]
|
||||
# 跳过账期内下单的订单,已提前处理
|
||||
if order_info['is_new_in_account']:
|
||||
continue
|
||||
|
||||
# 累加冲销税费
|
||||
writeoff_tax += order_info['pay_amount'] * order_info['tax_rate']
|
||||
|
||||
# 计算该订单历史均摊金额(2025-09-01至2026-02-28的均摊)
|
||||
amort_start = order_info['amortization_start_date']
|
||||
total_cycle = order_info['total_cycle_days']
|
||||
history_amort_days = calculate_amortization_days(amort_start, total_cycle, datetime(2025,9,1).date(), datetime(2026,2,28).date())
|
||||
daily_amort = order_info['after_tax_amount'] / total_cycle if total_cycle > 0 else 0
|
||||
history_amort_amount = history_amort_days * daily_amort
|
||||
writeoff_amort += history_amort_amount
|
||||
|
||||
# 部分退费处理
|
||||
if refund_type == 3:
|
||||
remaining_amount = order_info['pay_amount'] - refund_amount
|
||||
remaining_after_tax = remaining_amount * (1 - order_info['tax_rate'])
|
||||
# 补充税费
|
||||
supplement_tax += remaining_amount * order_info['tax_rate']
|
||||
# 补充均摊:从转正日到账期最后一日的均摊,应用最后一天补差规则
|
||||
days = calculate_amortization_days(amort_start, total_cycle, amort_start, ACCOUNT_END)
|
||||
daily_amort_partial = remaining_after_tax / total_cycle if total_cycle > 0 else 0
|
||||
if total_cycle > 0 and days >= total_cycle:
|
||||
supplement_amort += round(remaining_after_tax, 2)
|
||||
else:
|
||||
supplement_amort += round(days * daily_amort_partial, 2)
|
||||
|
||||
# 计算冲销后税费
|
||||
after_writeoff_tax = pre_writeoff_tax - writeoff_tax + supplement_tax
|
||||
|
||||
# 计算冲销前均摊金额
|
||||
pre_writeoff_amort = 0
|
||||
# 历史未退费订单的账期均摊(排除历史退费订单)
|
||||
refund_order_nos = list(refund_dict.keys())
|
||||
for _, order_row in order_df[~order_df['order_no'].isin(refund_order_nos)].iterrows():
|
||||
if not order_row['is_formal'] or order_row['total_cycle_days'] <= 0:
|
||||
continue
|
||||
amort_start = order_row['amortization_start_date']
|
||||
days = calculate_amortization_days(amort_start, order_row['total_cycle_days'], ACCOUNT_START, ACCOUNT_END)
|
||||
daily_amort = order_row['after_tax_amount'] / order_row['total_cycle_days']
|
||||
pre_writeoff_amort += days * daily_amort
|
||||
# 账期内正式订单的账期均摊
|
||||
for _, order_row in order_df[order_df['is_new_in_account'] & order_df['is_formal']].iterrows():
|
||||
if order_row['total_cycle_days'] <= 0:
|
||||
continue
|
||||
amort_start = order_row['amortization_start_date']
|
||||
days = calculate_amortization_days(amort_start, order_row['total_cycle_days'], ACCOUNT_START, ACCOUNT_END)
|
||||
daily_amort = order_row['after_tax_amount'] / order_row['total_cycle_days']
|
||||
pre_writeoff_amort += days * daily_amort
|
||||
|
||||
# 计算冲销后均摊金额
|
||||
after_writeoff_amort = pre_writeoff_amort - writeoff_amort + supplement_amort
|
||||
|
||||
# 5. 生成汇总表
|
||||
summary_df = pd.DataFrame([{
|
||||
'指标': '订单数',
|
||||
'数值': total_orders
|
||||
}, {
|
||||
'指标': '正式订单数',
|
||||
'数值': formal_orders
|
||||
}, {
|
||||
'指标': '试用订单数',
|
||||
'数值': trial_orders
|
||||
}, {
|
||||
'指标': '冲销前税费(元)',
|
||||
'数值': round(pre_writeoff_tax, 2)
|
||||
}, {
|
||||
'指标': '冲销税费(元)',
|
||||
'数值': round(writeoff_tax, 2)
|
||||
}, {
|
||||
'指标': '补充税费(元)',
|
||||
'数值': round(supplement_tax, 2)
|
||||
}, {
|
||||
'指标': '冲销后税费(元)',
|
||||
'数值': round(after_writeoff_tax, 2)
|
||||
}, {
|
||||
'指标': '冲销前均摊金额(税后,元)',
|
||||
'数值': round(pre_writeoff_amort, 2)
|
||||
}, {
|
||||
'指标': '冲销均摊金额(税后,元)',
|
||||
'数值': round(writeoff_amort, 2)
|
||||
}, {
|
||||
'指标': '补充均摊金额(税后,元)',
|
||||
'数值': round(supplement_amort, 2)
|
||||
}, {
|
||||
'指标': '冲销后均摊金额(税后净收入,元)',
|
||||
'数值': round(after_writeoff_amort, 2)
|
||||
}])
|
||||
|
||||
# 6. 生成订单明细表
|
||||
order_detail_list = []
|
||||
for _, order_row in order_df.iterrows():
|
||||
# 计算历史均摊金额(2025-09-01至2026-02-28)
|
||||
amort_start = order_row['amortization_start_date']
|
||||
total_cycle = order_row['total_cycle_days']
|
||||
history_days = calculate_amortization_days(amort_start, total_cycle, datetime(2025,9,1).date(), datetime(2026,2,28).date())
|
||||
daily_amort = order_row['after_tax_amount'] / total_cycle if total_cycle > 0 else 0
|
||||
# 周期最后一天补差规则
|
||||
if total_cycle > 0 and history_days >= total_cycle:
|
||||
history_amort = round(order_row['after_tax_amount'], 2)
|
||||
else:
|
||||
history_amort = round(history_days * daily_amort, 2)
|
||||
|
||||
# 计算账期均摊金额
|
||||
account_days = calculate_amortization_days(amort_start, total_cycle, ACCOUNT_START, ACCOUNT_END)
|
||||
total_used_days = history_days + account_days
|
||||
# 周期最后一天补差规则
|
||||
if total_cycle > 0 and total_used_days >= total_cycle:
|
||||
# 如果累计天数超过总周期,账期均摊=总金额-历史均摊
|
||||
account_amort = round(order_row['after_tax_amount'] - history_amort, 2)
|
||||
if account_amort < 0:
|
||||
account_amort = 0
|
||||
else:
|
||||
account_amort = round(account_days * daily_amort, 2)
|
||||
|
||||
# 未确认收入
|
||||
total_amort = history_amort + account_amort
|
||||
unconfirmed_income = round(order_row['after_tax_amount'] - total_amort, 2)
|
||||
if unconfirmed_income < 0:
|
||||
unconfirmed_income = 0
|
||||
|
||||
# 剩余周期
|
||||
used_days = history_days + account_days
|
||||
remaining_cycle = max(0, total_cycle - used_days)
|
||||
|
||||
# 新增字段:下单时间
|
||||
create_time = order_row['created_at'].strftime('%Y-%m-%d')
|
||||
|
||||
# 新增字段:退费时间
|
||||
refund_time = ""
|
||||
refund_type = None
|
||||
if order_row['order_no'] in refund_dict:
|
||||
refund_time = refund_dict[order_row['order_no']]['refund_date'].strftime('%Y-%m-%d')
|
||||
refund_type = refund_dict[order_row['order_no']]['refund_type']
|
||||
|
||||
# 新增字段:订单分类
|
||||
order_category = ""
|
||||
if refund_type is not None:
|
||||
if refund_type == 2:
|
||||
order_category = "历史新增全额退费订单"
|
||||
elif refund_type == 3:
|
||||
order_category = "历史新增部分退费订单"
|
||||
else:
|
||||
if order_row['is_new_in_account']:
|
||||
if order_row['is_formal']:
|
||||
order_category = "账期新增正式订单"
|
||||
else:
|
||||
order_category = "账期新增试用订单"
|
||||
else:
|
||||
order_category = "历史正式订单"
|
||||
|
||||
order_detail_list.append({
|
||||
'订单号': order_row['order_no'],
|
||||
'下单时间': create_time,
|
||||
'退费时间': refund_time,
|
||||
'订单分类': order_category,
|
||||
'订单金额(元)': round(order_row['pay_amount'], 2),
|
||||
'税率': f"{order_row['tax_rate'] * 100}%",
|
||||
'税额(元)': round(order_row['tax_amount'], 2),
|
||||
'税后金额(元)': round(order_row['after_tax_amount'], 2),
|
||||
'总均摊周期(天)': int(total_cycle),
|
||||
'已均摊天数(天)': used_days,
|
||||
'历史均摊金额(元)': history_amort,
|
||||
'账期均摊金额(元)': account_amort,
|
||||
'未确认收入(元)': unconfirmed_income,
|
||||
'剩余周期(天)': remaining_cycle
|
||||
})
|
||||
detail_df = pd.DataFrame(order_detail_list)
|
||||
|
||||
# 7. 写入Excel
|
||||
output_path = f"/root/.openclaw/workspace/output/订单均摊结算报表_{ACCOUNT_START}_{ACCOUNT_END}.xlsx"
|
||||
with pd.ExcelWriter(output_path) as writer:
|
||||
summary_df.to_excel(writer, sheet_name='汇总表', index=False)
|
||||
detail_df.to_excel(writer, sheet_name='订单明细', index=False)
|
||||
|
||||
print(f"报表生成完成,保存路径:{output_path}")
|
||||
print("\n📊 账期2026-03-01至2026-03-31均摊结算结果:")
|
||||
print(f"总订单数:{total_orders}单")
|
||||
print(f"正式订单数:{formal_orders}单")
|
||||
print(f"试用订单数:{trial_orders}单")
|
||||
print(f"冲销后税费:{round(after_writeoff_tax, 2)}元")
|
||||
print(f"冲销后均摊金额(税后净收入):{round(after_writeoff_amort, 2)}元")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
print(f"报表已生成:{output_path}")
|
||||
print(f"📊 账期{start_date}至{end_date}均摊结算结果:")
|
||||
print(f"总订单数:{total_orders}单")
|
||||
print(f"正式订单数:{formal_orders}单")
|
||||
print(f"试用订单数:{trial_orders}单")
|
||||
print(f"冲销后税费:{post_adjustment_tax}元")
|
||||
print(f"冲销后均摊金额(税后净收入):{post_adjustment_amortization}元")
|
||||
|
||||
@ -16,6 +16,38 @@
|
||||
3. 表关联规则:
|
||||
- bi_vala_order.trade_no ↔ bi_refund_order.trade_no 关联
|
||||
- bi_vala_order.out_trade_no ↔ bi_refund_order.out_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/
|
||||
├── summary.sql # 汇总查询SQL模板
|
||||
└── detail.sql # 订单明细查询SQL模板
|
||||
```
|
||||
|
||||
## 操作流程
|
||||
### 步骤1:确认核心参数
|
||||
执行前必须向用户确认以下参数:
|
||||
@ -65,26 +97,46 @@
|
||||
账期内总收入 = 账期内所有正常订单的日均摊金额总和 + 账期内所有退费冲销金额总和
|
||||
## 核心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:获取所有符合条件的订单基础信息
|
||||
WITH order_base AS (
|
||||
order_base AS (
|
||||
SELECT
|
||||
o.id AS order_id,
|
||||
o.order_no,
|
||||
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.status AS order_status,
|
||||
o.order_status, -- 修正:字段名是order_status不是status
|
||||
a.id AS account_id,
|
||||
o.key_from,
|
||||
o.sale_channel
|
||||
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.status IN (3,4)
|
||||
AND o.order_status IN (3,4) -- 修正:字段名是order_status不是status
|
||||
AND o.pay_amount_int >= 1000
|
||||
AND a.status = 1
|
||||
),
|
||||
@ -97,20 +149,243 @@ order_cycle AS (
|
||||
WHERE status != -1
|
||||
GROUP BY order_no
|
||||
),
|
||||
-- 步骤3:获取账期内的退费记录
|
||||
-- 步骤3:获取账期内的退费记录(仅用于计算当前账期的冲销金额,不影响订单退费状态标记)
|
||||
refund_records AS (
|
||||
SELECT
|
||||
r.out_trade_no AS order_no,
|
||||
CAST(r.refund_amount AS NUMERIC) / 100 AS refund_amount,
|
||||
r.refund_amount_int / 100 AS refund_amount, -- 修正:使用分为单位的refund_amount_int,避免字符串转换问题,除以100得到元
|
||||
r.refund_type,
|
||||
DATE(r.updated_at) AS refund_date
|
||||
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
|
||||
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:判断订单在账期内的状态分类,并计算各类均摊金额
|
||||
order_amortization AS (
|
||||
SELECT
|
||||
owc.*,
|
||||
-- 是否为账期内新增订单
|
||||
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,
|
||||
|
||||
-- ===== 正常均摊金额计算(未退费 或 账期前部分退费的订单)=====
|
||||
-- 均摊有效起始日(取转正日和账期起始日的较大值)
|
||||
GREATEST(owc.amortization_start_date, '${账期起始日}'::date) AS eff_start,
|
||||
-- 均摊有效结束日(取均摊结束日和账期结束日的较小值)
|
||||
LEAST(owc.amortization_end_date, '${账期结束日}'::date) AS eff_end
|
||||
|
||||
FROM order_with_cycle owc
|
||||
WHERE owc.total_cycle_days > 0 -- 排除无均摊周期的异常订单
|
||||
),
|
||||
|
||||
-- 步骤6:计算每个订单在账期内的均摊天数和金额
|
||||
order_period_amort AS (
|
||||
SELECT
|
||||
oa.*,
|
||||
-- 账期内有效均摊天数
|
||||
CASE WHEN oa.eff_end >= oa.eff_start
|
||||
THEN (oa.eff_end - oa.eff_start + 1)
|
||||
ELSE 0
|
||||
END AS period_amort_days,
|
||||
|
||||
-- 该订单从转正日到账期结束日的总已均摊天数(用于计算历史累计)
|
||||
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 remaining_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 remaining_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 remaining_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 remaining_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`
|
||||
|
||||
107
skills/vala-order-amortization-stat/run.py
Executable file
107
skills/vala-order-amortization-stat/run.py
Executable file
@ -0,0 +1,107 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
import argparse
|
||||
import json
|
||||
import psycopg2
|
||||
import pandas as pd
|
||||
|
||||
def main():
|
||||
# Parse command line arguments
|
||||
parser = argparse.ArgumentParser(description='订单均摊结算统计脚本')
|
||||
parser.add_argument('--start', type=str, help='账期起始日(YYYY-MM-DD)')
|
||||
parser.add_argument('--end', type=str, help='账期结束日(YYYY-MM-DD)')
|
||||
parser.add_argument('pos_start', nargs='?', type=str, help='账期起始日(位置参数)')
|
||||
parser.add_argument('pos_end', nargs='?', type=str, help='账期结束日(位置参数)')
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get start and end dates
|
||||
start_date = args.start or args.pos_start
|
||||
end_date = args.end or args.pos_end
|
||||
|
||||
if not start_date or not end_date:
|
||||
print("错误:请提供账期起始日和结束日", file=sys.stderr)
|
||||
print("用法1:python3 run.py --start 2026-03-01 --end 2026-03-31", file=sys.stderr)
|
||||
print("用法2:python3 run.py 2026-03-01 2026-03-31", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Read PostgreSQL password from secrets.env
|
||||
secrets_path = '/root/.openclaw/workspace/secrets.env'
|
||||
if not os.path.exists(secrets_path):
|
||||
print(f"错误:secrets.env文件不存在 {secrets_path}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(secrets_path, 'r', encoding='utf-8') as f:
|
||||
secrets_content = f.read()
|
||||
|
||||
pg_password_match = re.search(r"PG_ONLINE_PASSWORD='(.*)'", secrets_content)
|
||||
if not pg_password_match:
|
||||
print("错误:未找到PG_ONLINE_PASSWORD配置", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
pg_password = pg_password_match.group(1)
|
||||
|
||||
# Read SQL templates
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
summary_sql_path = os.path.join(script_dir, 'sql', 'summary.sql')
|
||||
detail_sql_path = os.path.join(script_dir, 'sql', 'detail.sql')
|
||||
|
||||
if not os.path.exists(summary_sql_path) or not os.path.exists(detail_sql_path):
|
||||
print("错误:SQL模板文件不存在", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
with open(summary_sql_path, 'r', encoding='utf-8') as f:
|
||||
summary_sql_template = f.read()
|
||||
with open(detail_sql_path, 'r', encoding='utf-8') as f:
|
||||
detail_sql_template = f.read()
|
||||
|
||||
# Replace placeholders
|
||||
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)
|
||||
|
||||
# Connect to PostgreSQL
|
||||
try:
|
||||
conn = psycopg2.connect(
|
||||
host="bj-postgres-16pob4sg.sql.tencentcdb.com",
|
||||
port=28591,
|
||||
user="ai_member",
|
||||
password=pg_password,
|
||||
database="vala_bi"
|
||||
)
|
||||
except Exception as e:
|
||||
print(f"数据库连接失败:{str(e)}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Execute queries
|
||||
try:
|
||||
summary_df = pd.read_sql(summary_sql, conn)
|
||||
detail_df = pd.read_sql(detail_sql, conn)
|
||||
except Exception as e:
|
||||
print(f"SQL执行失败:{str(e)}", file=sys.stderr)
|
||||
conn.close()
|
||||
sys.exit(1)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
# Write to Excel
|
||||
output_dir = '/root/.openclaw/workspace/output'
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
output_file = os.path.join(output_dir, f'订单均摊结算报表_{start_date}_{end_date}.xlsx')
|
||||
|
||||
try:
|
||||
with pd.ExcelWriter(output_file, engine='openpyxl') as writer:
|
||||
summary_df.to_excel(writer, sheet_name='汇总表', index=False)
|
||||
detail_df.to_excel(writer, sheet_name='订单明细', index=False)
|
||||
except Exception as e:
|
||||
print(f"Excel生成失败:{str(e)}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Print summary result as JSON
|
||||
result = summary_df.to_dict(orient='records')[0]
|
||||
print(json.dumps(result, ensure_ascii=False, indent=2))
|
||||
print(f"\n报表已保存到:{output_file}", file=sys.stderr)
|
||||
sys.exit(0)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
216
skills/vala-order-amortization-stat/sql/detail.sql
Normal file
216
skills/vala-order-amortization-stat/sql/detail.sql
Normal file
@ -0,0 +1,216 @@
|
||||
WITH
|
||||
-- 步骤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(o.created_at) + INTERVAL '7 days' 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
|
||||
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
|
||||
),
|
||||
-- 步骤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 '{period_start}' AND '{period_end}'
|
||||
)
|
||||
,
|
||||
-- 步骤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) * INTERVAL '1 day' - INTERVAL '1 day' 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:判断订单在账期内的状态分类,并计算各类均摊金额
|
||||
order_amortization AS (
|
||||
SELECT
|
||||
owc.*,
|
||||
-- 是否为账期内新增订单
|
||||
CASE WHEN DATE(owc.amortization_start_date) <= '{period_end}'
|
||||
AND owc.amortization_start_date >= '{period_start}'::date - INTERVAL '6 days'
|
||||
THEN 1 ELSE 0 END AS is_new_in_period,
|
||||
-- 账期内新增且已转正
|
||||
CASE WHEN DATE(owc.amortization_start_date) >= '{period_start}'
|
||||
AND DATE(owc.amortization_start_date) <= '{period_end}'
|
||||
THEN 1
|
||||
WHEN owc.amortization_start_date < '{period_start}'
|
||||
THEN 1
|
||||
ELSE 0
|
||||
END AS is_formal,
|
||||
|
||||
-- ===== 正常均摊金额计算(未退费 或 账期前部分退费的订单)=====
|
||||
-- 均摊有效起始日(取转正日和账期起始日的较大值)
|
||||
GREATEST(owc.amortization_start_date, '{period_start}'::date) AS eff_start,
|
||||
-- 均摊有效结束日(取均摊结束日和账期结束日的较小值)
|
||||
LEAST(owc.amortization_end_date, '{period_end}'::date) AS eff_end
|
||||
|
||||
FROM order_with_cycle owc
|
||||
WHERE owc.total_cycle_days > 0 -- 排除无均摊周期的异常订单
|
||||
),
|
||||
|
||||
-- 步骤6:计算每个订单在账期内的均摊天数和金额
|
||||
order_period_amort AS (
|
||||
SELECT
|
||||
oa.*,
|
||||
-- 账期内有效均摊天数
|
||||
CASE WHEN oa.eff_end >= oa.eff_start
|
||||
THEN EXTRACT(DAY FROM (oa.eff_end - oa.eff_start))::int + 1
|
||||
ELSE 0
|
||||
END AS period_amort_days,
|
||||
|
||||
-- 该订单从转正日到账期结束日的总已均摊天数(用于计算历史累计)
|
||||
CASE WHEN '{period_end}'::date >= oa.amortization_start_date
|
||||
THEN EXTRACT(DAY FROM (LEAST('{period_end}'::date, oa.amortization_end_date) - oa.amortization_start_date))::int + 1
|
||||
ELSE 0
|
||||
END AS total_amorted_days_to_period_end,
|
||||
|
||||
-- 该订单从转正日到账期起始日前一天的历史已均摊天数
|
||||
CASE WHEN '{period_start}'::date > oa.amortization_start_date
|
||||
THEN EXTRACT(DAY FROM (LEAST('{period_start}'::date - 1, oa.amortization_end_date) - oa.amortization_start_date))::int + 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 < '{period_start}'::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 < '{period_start}'::date
|
||||
THEN 1 ELSE 0
|
||||
END AS is_pre_period_partial_refund,
|
||||
|
||||
-- 判断是否为账期内退费
|
||||
CASE WHEN opa.has_refund = 1
|
||||
AND opa.latest_refund_date >= '{period_start}'::date
|
||||
AND opa.latest_refund_date <= '{period_end}'::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 remaining_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 remaining_tax_amount
|
||||
|
||||
FROM order_period_amort opa
|
||||
)
|
||||
SELECT
|
||||
order_no AS "订单号",
|
||||
ROUND(pay_amount::numeric, 2) AS "订单金额",
|
||||
ROUND(tax_rate::numeric, 2) AS "税率",
|
||||
ROUND(tax_amount::numeric, 2) AS "税额",
|
||||
ROUND(after_tax_amount::numeric, 2) AS "税后金额",
|
||||
total_cycle_days AS "总均摊周期",
|
||||
period_amort_days AS "账期内均摊天数",
|
||||
ROUND((historical_amorted_days * daily_amort_amount)::numeric, 2) AS "历史均摊金额",
|
||||
ROUND(
|
||||
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 remaining_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 "账期均摊金额",
|
||||
ROUND(
|
||||
(CASE
|
||||
WHEN is_pre_period_partial_refund = 1 OR (is_current_period_refund = 1 AND is_full_refund = 0)
|
||||
THEN remaining_after_tax_amount
|
||||
ELSE after_tax_amount
|
||||
END - (historical_amorted_days * daily_amort_amount))::numeric, 2
|
||||
) AS "未确认收入",
|
||||
(total_cycle_days - total_amorted_days_to_period_end) AS "剩余周期"
|
||||
FROM order_final_amort
|
||||
WHERE is_pre_period_full_refund = 0;
|
||||
288
skills/vala-order-amortization-stat/sql/summary.sql
Normal file
288
skills/vala-order-amortization-stat/sql/summary.sql
Normal file
@ -0,0 +1,288 @@
|
||||
WITH
|
||||
-- 步骤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(o.created_at) + INTERVAL '7 days' 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
|
||||
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
|
||||
),
|
||||
-- 步骤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 '{period_start}' AND '{period_end}'
|
||||
)
|
||||
,
|
||||
-- 步骤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) * INTERVAL '1 day' - INTERVAL '1 day' 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:判断订单在账期内的状态分类,并计算各类均摊金额
|
||||
order_amortization AS (
|
||||
SELECT
|
||||
owc.*,
|
||||
-- 是否为账期内新增订单
|
||||
CASE WHEN DATE(owc.amortization_start_date) <= '{period_end}'
|
||||
AND owc.amortization_start_date >= '{period_start}'::date - INTERVAL '6 days'
|
||||
THEN 1 ELSE 0 END AS is_new_in_period,
|
||||
-- 账期内新增且已转正
|
||||
CASE WHEN DATE(owc.amortization_start_date) >= '{period_start}'
|
||||
AND DATE(owc.amortization_start_date) <= '{period_end}'
|
||||
THEN 1
|
||||
WHEN owc.amortization_start_date < '{period_start}'
|
||||
THEN 1
|
||||
ELSE 0
|
||||
END AS is_formal,
|
||||
|
||||
-- ===== 正常均摊金额计算(未退费 或 账期前部分退费的订单)=====
|
||||
-- 均摊有效起始日(取转正日和账期起始日的较大值)
|
||||
GREATEST(owc.amortization_start_date, '{period_start}'::date) AS eff_start,
|
||||
-- 均摊有效结束日(取均摊结束日和账期结束日的较小值)
|
||||
LEAST(owc.amortization_end_date, '{period_end}'::date) AS eff_end
|
||||
|
||||
FROM order_with_cycle owc
|
||||
WHERE owc.total_cycle_days > 0 -- 排除无均摊周期的异常订单
|
||||
),
|
||||
|
||||
-- 步骤6:计算每个订单在账期内的均摊天数和金额
|
||||
order_period_amort AS (
|
||||
SELECT
|
||||
oa.*,
|
||||
-- 账期内有效均摊天数
|
||||
CASE WHEN oa.eff_end >= oa.eff_start
|
||||
THEN EXTRACT(DAY FROM (oa.eff_end - oa.eff_start))::int + 1
|
||||
ELSE 0
|
||||
END AS period_amort_days,
|
||||
|
||||
-- 该订单从转正日到账期结束日的总已均摊天数(用于计算历史累计)
|
||||
CASE WHEN '{period_end}'::date >= oa.amortization_start_date
|
||||
THEN EXTRACT(DAY FROM (LEAST('{period_end}'::date, oa.amortization_end_date) - oa.amortization_start_date))::int + 1
|
||||
ELSE 0
|
||||
END AS total_amorted_days_to_period_end,
|
||||
|
||||
-- 该订单从转正日到账期起始日前一天的历史已均摊天数
|
||||
CASE WHEN '{period_start}'::date > oa.amortization_start_date
|
||||
THEN EXTRACT(DAY FROM (LEAST('{period_start}'::date - 1, oa.amortization_end_date) - oa.amortization_start_date))::int + 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 < '{period_start}'::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 < '{period_start}'::date
|
||||
THEN 1 ELSE 0
|
||||
END AS is_pre_period_partial_refund,
|
||||
|
||||
-- 判断是否为账期内退费
|
||||
CASE WHEN opa.has_refund = 1
|
||||
AND opa.latest_refund_date >= '{period_start}'::date
|
||||
AND opa.latest_refund_date <= '{period_end}'::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 remaining_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 remaining_tax_amount
|
||||
|
||||
FROM order_period_amort opa
|
||||
),
|
||||
|
||||
-- 步骤8:最终汇总计算
|
||||
summary_calc AS (
|
||||
SELECT
|
||||
-- ===== 账期内新增订单统计 =====
|
||||
COUNT(CASE WHEN DATE(amortization_start_date - INTERVAL '7 days') >= '{period_start}'::date
|
||||
AND DATE(amortization_start_date - INTERVAL '7 days') <= '{period_end}'::date
|
||||
THEN 1 END) AS total_new_orders,
|
||||
|
||||
COUNT(CASE WHEN DATE(amortization_start_date - INTERVAL '7 days') >= '{period_start}'::date
|
||||
AND DATE(amortization_start_date - INTERVAL '7 days') <= '{period_end}'::date
|
||||
AND amortization_start_date <= '{period_end}'::date
|
||||
THEN 1 END) AS formal_orders,
|
||||
|
||||
COUNT(CASE WHEN DATE(amortization_start_date - INTERVAL '7 days') >= '{period_start}'::date
|
||||
AND DATE(amortization_start_date - INTERVAL '7 days') <= '{period_end}'::date
|
||||
AND amortization_start_date > '{period_end}'::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 remaining_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 remaining_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;
|
||||
Loading…
Reference in New Issue
Block a user