diff --git a/.vala_skill_hashes b/.vala_skill_hashes index 2b28020..626213d 100644 --- a/.vala_skill_hashes +++ b/.vala_skill_hashes @@ -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 0f55a0c8d2d8bde498ac9781daa6f18cd4e4618d87f42263fe70e991a9980bea +vala-order-amortization-stat 4f1facb78e3adb16af09cfab1578d3273bcaad1b4c1b409b9de2a97d0c63409b diff --git a/skills/vala-order-amortization-stat/SKILL.md b/skills/vala-order-amortization-stat/SKILL.md index 887e44d..b0b9154 100644 --- a/skills/vala-order-amortization-stat/SKILL.md +++ b/skills/vala-order-amortization-stat/SKILL.md @@ -1,6 +1,6 @@ # SKILL.md - vala-order-amortization-stat 订单均摊结算统计技能 ## 技能描述 -用于统计指定账期内的订单均摊收入、退费冲销,按天计算均摊金额,支持部分退费场景的剩余金额和周期自动计算。 +用于统计指定账期内的订单均摊收入、退费冲销,按天计算均摊金额,支持部分退费场景的退后订单金额和周期自动计算。 ## 触发场景 当用户提到以下关键词组合时激活本技能: 1. 订单均摊、按天均摊、收入均摊 @@ -66,7 +66,7 @@ vala-order-amortization-stat/ 3. 账期前退费订单处理规则: - 退费完成时间在账期起始日之前的订单,不会纳入本账期的冲销逻辑: - 全额退费订单:冲销动作已在退费发生的对应账期执行完毕,本账期完全不统计该订单的任何数据 - - 部分退费订单:冲销动作、剩余金额/剩余周期调整已在退费发生的对应账期完成,本账期仅按调整后的剩余金额计算均摊,不产生额外冲销金额 + - 部分退费订单:冲销动作、退后订单金额/剩余周期调整已在退费发生的对应账期完成,本账期仅按调整后的退后订单金额计算均摊,不产生额外冲销金额 ### 步骤3:核心计算逻辑 #### 3.1 周期计算 - 每个订单的总均摊周期 = bi_vala_seasonal_ticket中同一order_no下status != -1的不同id数量 × 90天 @@ -76,13 +76,14 @@ vala-order-amortization-stat/ - 税率规则: - 订单下单时间在2026年5月1日之前:税率1% - 订单下单时间在2026年5月1日及之后:税率6% - - 订单税后金额 = (bi_vala_order.pay_amount_int / 100) × (1 - 税率)(单位:元) - - 订单税费金额 = (bi_vala_order.pay_amount_int / 100) × 税率(单位:元) + - 订单税后金额 = (bi_vala_order.pay_amount_int / 100) / (1 + 税率)(单位:元) + - 订单税费金额 = (bi_vala_order.pay_amount_int / 100) / (1 + 税率) × 税率(单位:元) + - **计税方式**:价外税,即订单金额为含税价,需先剥离税额再计算税后金额 - **均摊规则更新**:所有均摊计算均基于税后金额进行,含税金额和税费单独统计 - 日均摊金额(税后)= 订单税后金额 / 总均摊周期 - 🔹 周期最后一天补差规则:订单均摊周期的最后一天,均摊金额不按日均摊计算,采用补差方式确保总额完全匹配: - 正常订单:最后一天均摊金额 = 订单税后总金额 - 前(总均摊周期-1)天累计已均摊金额 - - 部分退费订单:最后一天均摊金额 = 订单剩余税后金额 - 剩余均摊周期前(剩余天数-1)天累计已均摊金额 + - 部分退费订单:最后一天均摊金额 = 退后订单税后金额 - 剩余均摊周期前(剩余天数-1)天累计已均摊金额 避免浮点精度导致的金额尾差 #### 3.2 退费场景计算 所有退费冲销金额均为税后金额,税费同步对应冲销 @@ -91,7 +92,7 @@ vala-order-amortization-stat/ - 历史退费订单冲销税费金额:上述历史均摊金额对应的税费(统一显示为负数,用于冲销) 2. **全额退费(bi_refund_order.refund_type = 2)**:无需额外计算补充均摊,仅执行上述通用冲销逻辑 3. **部分退费(bi_refund_order.refund_type = 3)**:在通用冲销逻辑基础上,额外计算: - - 历史部分退费订单补充均摊金额:部分退费后剩余的待均摊税后金额,在本账期内产生的均摊收入 + - 历史部分退费订单补充均摊金额:部分退费后的退后订单待均摊税后金额,在本账期内产生的均摊收入 - 历史部分退费订单补充税费金额:上述补充均摊金额对应的税费 #### 3.3 账期内收入计算 账期内总收入 = 账期内所有正常订单的日均摊金额总和 + 账期内所有退费冲销金额总和 @@ -249,7 +250,7 @@ order_final_amort AS ( 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 @@ -262,17 +263,17 @@ order_final_amort AS ( 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, + 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 remaining_tax_amount + END AS refunded_tax_amount FROM order_period_amort opa ), @@ -299,9 +300,9 @@ summary_calc AS ( ROUND(SUM( CASE WHEN is_pre_period_full_refund = 1 THEN 0 -- 账期前全额退费,不统计 - 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 - THEN remaining_after_tax_amount / total_cycle_days * period_amort_days + 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 -- 正常未退费订单 @@ -322,7 +323,7 @@ summary_calc AS ( 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 + 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 @@ -347,7 +348,7 @@ summary_calc AS ( END )::numeric, 2) AS writeoff_tax, - -- ===== 补充均摊金额(税后):账期内部分退费订单,退费后剩余金额的均摊 ===== + -- ===== 补充均摊金额(税后):账期内部分退费订单,退后订单金额的均摊 ===== ROUND(SUM( CASE WHEN is_current_period_refund = 1 AND is_full_refund = 0 @@ -405,7 +406,7 @@ FROM summary_calc; 3. **试用订单数**:账期内新增的订单中,截止账期结束日仍处于7天试用期内(下单日+7天 > 账期结束日)的订单数量,试用订单未开始均摊,不参与金额计算 4. **冲销前税费**:账期内所有正式订单的含税订单总金额 × 各订单对应税率 的总和 5. **冲销税费**:本账期内发生退费的所有订单的含税订单总金额 × 各订单对应税率 的总和(正数展示,计算时扣除) -6. **补充税费**:本账期内发生部分退费的订单,退费后剩余的含税订单金额 × 各订单对应税率 的总和,其中:部分退费订单剩余含税金额 = 原订单含税金额 - 退费含税金额 +6. **补充税费**:本账期内发生部分退费的订单,退后订单含税金额 × 各订单对应税率 的总和,其中:部分退费订单退后含税金额 = 原订单含税金额 - 退费含税金额 7. **冲销后税费**:账期内最终确认的总税费,计算公式:`冲销后税费 = 冲销前税费 - 冲销税费 + 补充税费` 8. **冲销前均摊金额(税后)**:历史未退费订单 + 账期内正式订单 在本账期内产生的税后均摊收入总和 9. **冲销均摊金额(税后)**:本账期内发生退费的所有订单,需要冲销的历史均摊金额总和(正数展示,计算时扣除) diff --git a/skills/vala-order-amortization-stat/sql/detail.sql b/skills/vala-order-amortization-stat/sql/detail.sql index 942f6db..15d9f99 100644 --- a/skills/vala-order-amortization-stat/sql/detail.sql +++ b/skills/vala-order-amortization-stat/sql/detail.sql @@ -1,12 +1,12 @@ WITH --- 步骤0:获取所有历史退费记录(无时间范围,用于标记订单是否为退费订单,不受当前账期限制) +-- 步骤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) + 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 -- 最后一次退费完成时间 + MAX(DATE(updated_at)) AS latest_refund_date FROM bi_refund_order WHERE status = 3 GROUP BY out_trade_no @@ -15,34 +15,33 @@ all_refund_records AS ( 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.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) * 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, + (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, -- 修正:字段名是order_status不是status + 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, -- 累计退费金额 - COALESCE(ar.is_full_refund, 0) AS is_full_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.is_full_refund, 0) AS is_full_refund, ar.refund_type, - ar.latest_refund_date -- 退费完成日期(解决历史退费订单退费日期为空的问题) + 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 -- 关联全量退费记录,不限制时间 + 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.order_status IN (3,4) AND o.pay_amount_int >= 1000 AND a.status = 1 ), --- 步骤2:计算每个订单的总均摊周期 +-- 步骤2:计算每个订单的当前均摊周期(退费后)和原始均摊周期(退费前) order_cycle AS ( SELECT out_trade_no AS order_no, @@ -51,166 +50,253 @@ order_cycle AS ( WHERE status != -1 GROUP BY out_trade_no ), --- 步骤3:获取账期内的退费记录(仅用于计算当前账期的冲销金额,不影响订单退费状态标记) -refund_records AS ( +order_cycle_original 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:合并订单基础信息与均摊周期 + 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, - -- 均摊结束日 = 转正日 + 总周期天数 - 1 + 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, - -- 日均摊金额(税后) - CASE WHEN COALESCE(oc.total_cycle_days, 0) > 0 + -- 原始均摊结束日(退费前) + 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 + -- 退后日税费均摊 + 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 ), - --- 步骤5:判断订单在账期内的状态分类,并计算各类均摊金额 -order_amortization AS ( +-- 步骤4:判断订单场景分类 +order_classified 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 + -- 均摊有效结束日 + 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 + -- 判断是否为账期前全额退费(场景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, - -- 判断是否为账期前部分退费(本账期按剩余金额均摊) - CASE WHEN opa.has_refund = 1 AND opa.is_full_refund = 0 - AND opa.latest_refund_date < '{period_start}'::date + -- 判断是否为账期前部分退费(场景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 opa.has_refund = 1 - AND opa.latest_refund_date >= '{period_start}'::date - AND opa.latest_refund_date <= '{period_end}'::date + 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 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 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 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 + -- 退后订单税后金额 + 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, - FROM order_period_amort opa + -- 退后订单税费 + 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 order_no AS "订单号", + order_date AS "下单日期", + latest_refund_date AS "退费日期", + CASE + WHEN is_pre_period_full_refund = 1 THEN '账期前全额退费' + WHEN is_pre_period_partial_refund = 1 THEN '账期前部分退费' + WHEN is_current_period_refund = 1 AND is_full_refund = 1 AND is_ordered_in_period = 1 THEN '账期内下单全额退费' + WHEN is_current_period_refund = 1 AND is_full_refund = 1 AND is_ordered_in_period = 0 THEN '历史下单账期内全额退费' + 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 = 0 THEN '历史下单账期内部分退费' + WHEN DATE(amortization_start_date) > '{period_end}'::date THEN '试用期中' + ELSE '正常订单' + END 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(tax_rate::numeric, 4) AS "税率", + ROUND(tax_amount::numeric, 2) AS "原订单税额", + ROUND(after_tax_amount::numeric, 2) AS "原订单税后金额", + original_cycle_days AS "原始均摊周期", + total_cycle_days AS "退后均摊周期", + + -- 历史均摊金额(基于原始日均摊) + ROUND((historical_amorted_days * original_daily_amort_amount)::numeric, 2) AS "历史均摊金额", + + -- 冲销历史均摊(场景B/E:历史下单+账期内退费) + ROUND( + CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0 AND historical_amorted_days > 0 + THEN -(historical_amorted_days * original_daily_amort_amount) + ELSE 0 + END::numeric, 2 + ) AS "冲销历史均摊", + + -- 冲销原税款(场景B/E:历史下单+账期内退费) + ROUND( + CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0 + THEN -tax_amount + ELSE 0 + END::numeric, 2 + ) AS "冲销原税款", + + -- 冲销未确认收入(场景B/E:历史下单+账期内退费) + ROUND( + CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0 + THEN -(after_tax_amount - historical_amorted_days * original_daily_amort_amount) + ELSE 0 + END::numeric, 2 + ) AS "冲销未确认收入", + + -- 账期均摊金额 ROUND( CASE + -- 场景F:账期前全额退费,不统计 WHEN is_pre_period_full_refund = 1 THEN 0 - WHEN is_pre_period_partial_refund = 1 THEN + -- 场景D:账期内下单+全额退费,无均摊 + 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 + -- 场景A:账期内下单+部分退费,按退后订单金额/退后周期从转正日均摊 + 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 - THEN remaining_after_tax_amount / total_cycle_days * period_amort_days + THEN daily_amort_amount * period_amort_days ELSE 0 END - WHEN is_current_period_refund = 0 AND period_amort_days > 0 THEN + -- 场景B:历史下单+账期内部分退费,按退后订单金额/退后周期从转正日重新均摊 + 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 + THEN daily_amort_amount * refunded_amort_days_to_period_end + ELSE 0 + END + -- 场景C:账期前部分退费,按退后订单金额/退后周期正常均摊 + WHEN is_pre_period_partial_refund = 1 THEN + CASE WHEN total_cycle_days > 0 AND period_amort_days > 0 + THEN daily_amort_amount * period_amort_days + ELSE 0 + END + -- 正常订单 + WHEN 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 + THEN after_tax_amount - original_daily_amort_amount * (original_cycle_days - 1) + original_daily_amort_amount * (period_amort_days - 1) + ELSE original_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 + CASE + -- 账期内下单 + 已转正 + 部分退费 → 退后订单税额 + WHEN is_ordered_in_period = 1 + AND DATE(amortization_start_date) <= '{period_end}'::date + AND is_current_period_refund = 1 AND is_full_refund = 0 + THEN refunded_tax_amount + -- 账期内下单 + 已转正 + 未全额退费(含未退费)→ 原订单税额 + WHEN is_ordered_in_period = 1 + AND DATE(amortization_start_date) <= '{period_end}'::date + AND (has_refund = 0 OR (is_current_period_refund = 0 AND is_full_refund = 0)) + THEN tax_amount + ELSE 0 + END::numeric, 2 + ) AS "账期税额", + + -- 未确认收入 + ROUND( + CASE + WHEN is_pre_period_full_refund = 1 THEN 0 + WHEN is_current_period_refund = 1 AND is_full_refund = 1 THEN 0 + WHEN is_current_period_refund = 1 AND is_full_refund = 0 THEN + refunded_after_tax_amount - daily_amort_amount * refunded_amort_days_to_period_end + WHEN is_pre_period_partial_refund = 1 THEN + refunded_after_tax_amount - daily_amort_amount * total_amorted_days_to_period_end + ELSE + after_tax_amount - original_daily_amort_amount * total_amorted_days_to_period_end + END::numeric, 2 ) AS "未确认收入", - (total_cycle_days - total_amorted_days_to_period_end) AS "剩余周期" -FROM order_final_amort + + -- 剩余周期 + CASE + WHEN is_current_period_refund = 1 AND is_full_refund = 1 THEN 0 + ELSE (total_cycle_days - total_amorted_days_to_period_end) + END AS "剩余周期" + +FROM order_with_days WHERE is_pre_period_full_refund = 0; diff --git a/skills/vala-order-amortization-stat/sql/summary.sql b/skills/vala-order-amortization-stat/sql/summary.sql index bcc7708..940a57e 100644 --- a/skills/vala-order-amortization-stat/sql/summary.sql +++ b/skills/vala-order-amortization-stat/sql/summary.sql @@ -1,12 +1,12 @@ WITH --- 步骤0:获取所有历史退费记录(无时间范围,用于标记订单是否为退费订单,不受当前账期限制) +-- 步骤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) + 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 -- 最后一次退费完成时间 + MAX(DATE(updated_at)) AS latest_refund_date FROM bi_refund_order WHERE status = 3 GROUP BY out_trade_no @@ -15,274 +15,202 @@ all_refund_records AS ( 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.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) * 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, + (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, -- 修正:字段名是order_status不是status + 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, -- 累计退费金额 - COALESCE(ar.is_full_refund, 0) AS is_full_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.is_full_refund, 0) AS is_full_refund, ar.refund_type, - ar.latest_refund_date -- 退费完成日期(解决历史退费订单退费日期为空的问题) + 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 -- 关联全量退费记录,不限制时间 + 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.order_status IN (3,4) AND o.pay_amount_int >= 1000 AND a.status = 1 ), --- 步骤2:计算每个订单的总均摊周期 +-- 步骤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 + 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_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, - -- 均摊结束日 = 转正日 + 总周期天数 - 1 + 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, - -- 日均摊金额(税后) - CASE WHEN COALESCE(oc.total_cycle_days, 0) > 0 - THEN ob.after_tax_amount / oc.total_cycle_days - ELSE 0 + 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 - THEN ob.tax_amount / oc.total_cycle_days - ELSE 0 + 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 ), - --- 步骤5:判断订单在账期内的状态分类,并计算各类均摊金额 -order_amortization AS ( +-- 步骤4:分类 +order_classified 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 - + LEAST(owc.amortization_end_date, '{period_end}'::date) AS eff_end, + 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 -- 排除无均摊周期的异常订单 + WHERE owc.total_cycle_days > 0 OR (owc.has_refund = 1 AND owc.is_full_refund = 1 AND owc.original_cycle_days > 0) ), - --- 步骤6:计算每个订单在账期内的均摊天数和金额 -order_period_amort AS ( +-- 步骤5:计算天数 +order_with_days 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 + 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 >= oa.amortization_start_date - THEN EXTRACT(DAY FROM (LEAST('{period_end}'::date, oa.amortization_end_date) - oa.amortization_start_date))::int + 1 - ELSE 0 + 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 > 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 + 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 ), - --- 步骤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:最终汇总计算 +-- 步骤6:汇总 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 order_date >= '{period_start}'::date AND order_date <= '{period_end}'::date THEN 1 END) AS total_new_orders, + 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 formal_orders, + 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 trial_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, - - -- ===== 冲销前均摊金额(税后):所有非"账期前全额退费"订单在账期内的均摊 ===== + -- 冲销历史均摊(场景B/E:历史下单+账期内退费) 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 + CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0 AND historical_amorted_days > 0 + THEN historical_amorted_days * original_daily_amort_amount ELSE 0 END )::numeric, 2) AS writeoff_amort, - -- ===== 冲销税费 ===== + -- 冲销原税款(场景B/E:历史下单+账期内退费) ROUND(SUM( - CASE WHEN is_current_period_refund = 1 AND historical_amorted_days > 0 - THEN daily_tax_amount * historical_amorted_days + CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0 + THEN tax_amount ELSE 0 END )::numeric, 2) AS writeoff_tax, - -- ===== 补充均摊金额(税后):账期内部分退费订单,退费后剩余金额的均摊 ===== + -- 冲销未确认收入(场景B/E:历史下单+账期内退费) 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 + CASE WHEN is_current_period_refund = 1 AND is_ordered_in_period = 0 + THEN after_tax_amount - historical_amorted_days * original_daily_amort_amount ELSE 0 END - )::numeric, 2) AS supplement_amort, + )::numeric, 2) AS writeoff_unconfirmed, - -- ===== 补充税费 ===== + -- 账期均摊金额 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 + CASE + WHEN is_pre_period_full_refund = 1 THEN 0 + WHEN is_current_period_refund = 1 AND is_full_refund = 1 THEN 0 + -- 场景A:账期内下单+部分退费 + 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 + THEN daily_amort_amount * period_amort_days ELSE 0 END + -- 场景B:历史下单+账期内部分退费,从转正日重新均摊 + 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 + THEN daily_amort_amount * refunded_amort_days_to_period_end ELSE 0 END + -- 场景C:账期前部分退费 + WHEN is_pre_period_partial_refund = 1 THEN + CASE WHEN total_cycle_days > 0 AND period_amort_days > 0 + THEN daily_amort_amount * period_amort_days ELSE 0 END + -- 正常订单 + WHEN period_amort_days > 0 THEN + CASE + WHEN eff_end = amortization_end_date + THEN after_tax_amount - original_daily_amort_amount * (original_cycle_days - 1) + original_daily_amort_amount * (period_amort_days - 1) + ELSE original_daily_amort_amount * period_amort_days + END ELSE 0 END - )::numeric, 2) AS supplement_tax + )::numeric, 2) AS period_amort, - FROM order_final_amort + -- 账期税费(仅账期内下单且已转正的非全额退费订单) + ROUND(SUM( + CASE + -- 账期内下单 + 已转正 + 部分退费 → 退后订单税额 + WHEN is_ordered_in_period = 1 + AND amortization_start_date <= '{period_end}'::date + AND is_current_period_refund = 1 AND is_full_refund = 0 + THEN refunded_tax_amount + -- 账期内下单 + 已转正 + 未全额退费(含未退费) → 原订单税额 + WHEN is_ordered_in_period = 1 + AND amortization_start_date <= '{period_end}'::date + AND (has_refund = 0 OR (is_current_period_refund = 0 AND is_full_refund = 0)) + THEN tax_amount + ELSE 0 + END + )::numeric, 2) AS period_tax + + FROM order_with_days + WHERE is_pre_period_full_refund = 0 ) 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 "冲销后均摊金额" + writeoff_amort AS "冲销历史均摊", + writeoff_tax AS "冲销原税款", + writeoff_unconfirmed AS "冲销未确认收入", + period_amort AS "账期均摊金额", + period_tax AS "账期税费", + ROUND((period_amort - writeoff_amort)::numeric, 2) AS "冲销后均摊金额", + ROUND((period_tax - writeoff_tax)::numeric, 2) AS "冲销后税费" FROM summary_calc;