vault backup: 2026-05-26 00:23:07
This commit is contained in:
246
prop-acc/scenarios/meter/generate-bill-tiered-pricing.md
Normal file
246
prop-acc/scenarios/meter/generate-bill-tiered-pricing.md
Normal file
@@ -0,0 +1,246 @@
|
||||
---
|
||||
title: prop-acc · meter · 场景 - 阶梯水电价生成账单(progressive 累进)
|
||||
aliases:
|
||||
- 阶梯计价账单
|
||||
- tiered pricing 算例
|
||||
- generate-bill-tiered-pricing
|
||||
- 场景-阶梯计价生成账单
|
||||
tags:
|
||||
- 场景
|
||||
- prop-acc
|
||||
- 计量表
|
||||
- 账单生成
|
||||
audience:
|
||||
- 业务人员
|
||||
- 财务
|
||||
- 业户
|
||||
status: 已发布
|
||||
sub_feature: meter
|
||||
last_review: 2026-05-26
|
||||
code_version: 2026-05-22
|
||||
---
|
||||
|
||||
# 场景:阶梯水电价生成账单(progressive 累进)
|
||||
|
||||
业户**用量超过低阶梯**进入更高阶梯时,系统按 **progressive 累进** 算账单(每段用量按各自单价,**不是**整月按最高阶梯)。本场景完整演示算例。
|
||||
|
||||
## 典型情境
|
||||
|
||||
> [!example] 真实情境
|
||||
> 张阿姨 5 月用水 35 吨(平时 12-15 吨,本月浇花 + 装修),嘉禾花园的水费阶梯如下:
|
||||
>
|
||||
> | 阶梯 | 月用水(吨)| 单价(元/吨)|
|
||||
> |---|---|---|
|
||||
> | 第一阶梯 | 0-20 | 3.0 |
|
||||
> | 第二阶梯 | 21-30 | 4.5 |
|
||||
> | 第三阶梯 | 30+ | 6.0 |
|
||||
>
|
||||
> 张阿姨 5 月用 35 吨,本月水费应是多少?
|
||||
|
||||
**Progressive 累进算法**(本系统采用):
|
||||
|
||||
```
|
||||
段 1(0-20 吨):20 × 3.0 = 60 元
|
||||
段 2(21-30 吨):10 × 4.5 = 45 元
|
||||
段 3(31-35 吨): 5 × 6.0 = 30 元
|
||||
合计: 135 元 ✅
|
||||
```
|
||||
|
||||
**Full-tier 简陋实现**(错!本系统未采用):
|
||||
|
||||
```
|
||||
35 吨 × 6.0(整月按最高阶梯) = 210 元 ❌
|
||||
```
|
||||
|
||||
差额 ¥75。简陋实现对业户极不公平,**是市场上劣质系统的常见 bug**。本系统 `MeterBillCalculator::calculateTiered()` 实现的是 progressive,业户收到的账单准确。
|
||||
|
||||
## 系统流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 抄表完成
|
||||
participant Action[GenerateBillsFromMeterReadingsAction]
|
||||
participant Service[MeterBillGenerationService]
|
||||
participant Calc[MeterBillCalculator]
|
||||
participant 数据库
|
||||
|
||||
抄表完成->>Action: handle([5月 reading,consumption=35])
|
||||
Action->>Service: generateBillForReading(reading)
|
||||
Service->>数据库: 查 meter.fee_type=水费 → 查 RatePlan + RateTier(3 个 tier)
|
||||
Service->>Calc: calculate(consumption=35, ratePlan, min, max)
|
||||
Calc->>Calc: calculateTiered(35, [tier1, tier2, tier3])
|
||||
Note over Calc: 段 1:min(35,20)-0=20 → 20*3=60
|
||||
Note over Calc: 段 2:min(35,30)-20=10 → 10*4.5=45
|
||||
Note over Calc: 段 3:35-30=5 → 5*6=30
|
||||
Note over Calc: 总和 135
|
||||
Calc-->>Service: 135
|
||||
Service->>Service: clamp(135, min, max) = 135
|
||||
Service->>数据库: 建 Bill(amount=135, sourceable=reading)
|
||||
Service->>数据库: 更新 reading.bill_id
|
||||
Service-->>Action: ok
|
||||
```
|
||||
|
||||
## `calculateTiered()` 算法(伪代码)
|
||||
|
||||
```php
|
||||
function calculateTiered(float $consumption, Collection $tiers): float
|
||||
{
|
||||
$amount = 0.0;
|
||||
$remaining = $consumption;
|
||||
|
||||
foreach ($tiers as $tier) {
|
||||
if ($remaining <= 0) break;
|
||||
|
||||
$tierRange = $tier->upper ? $tier->upper - $tier->lower : INF;
|
||||
$consumedInTier = min($remaining, $tierRange);
|
||||
|
||||
$amount += $consumedInTier * $tier->unit_price;
|
||||
$remaining -= $consumedInTier;
|
||||
}
|
||||
|
||||
return $amount;
|
||||
}
|
||||
```
|
||||
|
||||
详见 [[multiplier-and-tiered-pricing|倍率与阶梯计价]] 概念。
|
||||
|
||||
## 业户视角
|
||||
|
||||
### 您收到的账单(简化版)
|
||||
|
||||
```
|
||||
5 月水费账单
|
||||
|
||||
用水量:35 吨
|
||||
|
||||
明细:
|
||||
0-20 吨段:20 × 3.0 = 60 元
|
||||
21-30 吨段:10 × 4.5 = 45 元
|
||||
31+ 吨段: 5 × 6.0 = 30 元
|
||||
合计:135 元
|
||||
|
||||
应付:¥135.00
|
||||
```
|
||||
|
||||
> [!info] 账单明细的展示
|
||||
> 当前 Bill / Receipt 是否展示阶梯明细取决于模板设计。**强烈推荐展示**:
|
||||
>
|
||||
> - 业户能看清"为什么收 135 不是 105"
|
||||
> - 政策合规(国家阶梯水电价要求公开透明)
|
||||
> - 减少业户疑问
|
||||
|
||||
### 用得越多越贵的教育意义
|
||||
|
||||
阶梯计价**鼓励节约**:
|
||||
|
||||
| 用量 | 总水费 | 平均单价 |
|
||||
|---|---|---|
|
||||
| 15 吨(低用)| 45 | 3.0 |
|
||||
| 35 吨(本场景)| 135 | 3.86 |
|
||||
| 60 吨(浪费)| 270 | 4.5 |
|
||||
|
||||
业户**用越多平均单价越高**,符合"超额消费多付费"的政策导向。
|
||||
|
||||
## 业务人员视角
|
||||
|
||||
### 配置阶梯
|
||||
|
||||
阶梯定义在 `RatePlan` + `RateTier`(不在 meter 子模块,通常运营 / 财务总监配):
|
||||
|
||||
- 后台 → 费率管理 → 选水费 → 编辑 RatePlan
|
||||
- 加 RateTier:`tier=1, lower=0, upper=20, unit_price=3.0`
|
||||
- 加 RateTier:`tier=2, lower=20, upper=30, unit_price=4.5`
|
||||
- 加 RateTier:`tier=3, lower=30, upper=null, unit_price=6.0`(upper=null 表示无上限)
|
||||
|
||||
> [!warning] 阶梯配置要严谨
|
||||
> 配置错的常见症状:
|
||||
> - 段不连续(`tier1 upper=20`, `tier2 lower=22`)→ 21 吨用量无法分配
|
||||
> - 段重叠(`tier1 upper=20`, `tier2 lower=18`)→ 18-20 吨段算两次
|
||||
> - 缺最高段(没有 `tier_max`)→ 超过最高阶梯的用量无单价
|
||||
>
|
||||
> 业务人员配置完后**用极端值算例**验证(0 / 1 / 20 / 21 / 30 / 31 / 100 / 1000 吨各算一遍看是否合理)。
|
||||
|
||||
### 月度账单生成
|
||||
|
||||
抄表完成 → 业务人员触发 `GenerateBillsFromMeterReadingsAction`(或自动)→ 系统调 `MeterBillCalculator` → 每张表算金额 → 建 Bill。
|
||||
|
||||
### 异常处理
|
||||
|
||||
阶梯计价的常见异常:
|
||||
|
||||
| 异常 | 处置 |
|
||||
|---|---|
|
||||
| 业户用量极高(> 100 吨) | [[exception-high-consumption|高用量预警]] → 排查是否漏水 / 设备故障 |
|
||||
| 业户用量极低(0 吨)| `min_amount` 兜底,详见 [[generate-bill-min-max-cap]] |
|
||||
| 业户用量倒走(reading 错)| 走 [[exception-readings-locked-after-bill|修正流程]] |
|
||||
|
||||
## 财务视角
|
||||
|
||||
### 账面会计
|
||||
|
||||
阶梯账单的**总金额**仍归"水费收入"科目。无需按阶梯拆分入账。
|
||||
|
||||
阶梯只影响**业户感知**(单价不同)和**业户行为引导**(鼓励节约),不影响会计核算。
|
||||
|
||||
### 报表统计
|
||||
|
||||
业务可能想看"本月各阶梯段用量分布"(政策报告 / 节约成效),需要单独的报表 SQL:
|
||||
|
||||
```sql
|
||||
-- 本月各阶梯段用量分布(简化版,真实算法更复杂)
|
||||
SELECT
|
||||
SUM(LEAST(consumption, 20)) AS tier1_volume,
|
||||
SUM(GREATEST(LEAST(consumption, 30) - 20, 0)) AS tier2_volume,
|
||||
SUM(GREATEST(consumption - 30, 0)) AS tier3_volume
|
||||
FROM acc_meter_readings
|
||||
WHERE meter_id IN (SELECT id FROM acc_meters WHERE community_id=? AND fee_type_id=水费)
|
||||
AND read_at BETWEEN '2026-05-01' AND '2026-05-31';
|
||||
```
|
||||
|
||||
## 常见问题
|
||||
|
||||
> [!question] 阶梯按月 / 按年?
|
||||
> 看物业政策:
|
||||
> - **按月**(常见):每月 reset,从段 1 开始算
|
||||
> - **按年**(部分地区):全年累计,跨段更慢
|
||||
>
|
||||
> 当前系统**按月**(单次抄表 = 单段计价)。按年的话需要不同算法(累加去年 12 月以来的用量,再分阶梯)。
|
||||
|
||||
> [!question] 不同物业 / 不同社区可以有不同阶梯吗?
|
||||
> 可以。`RatePlan` 按 `community_id` + `fee_type_id` 隔离。每个社区独立配置。
|
||||
|
||||
> [!question] 阶梯改了,历史 Bill 怎么办?
|
||||
> 历史 Bill 不变(`Bill.amount` 是当时算出的,不动态查 RatePlan)。新 Bill 按新阶梯算。
|
||||
>
|
||||
> 这是正确做法(已发账单不应因配置变化追溯改金额)。
|
||||
|
||||
> [!question] 业户对算法有疑问怎么解释?
|
||||
> 给业户看明细(段 1 + 段 2 + 段 3)+ 阶梯单价表。绝大多数业户看懂后接受。
|
||||
|
||||
> [!question] progressive 算法的边界用量(如 20 吨整)算哪段?
|
||||
> 看实现细节:
|
||||
> - `consumption=20`:段 1 全部(20 × 3 = 60),段 2 / 3 不进入
|
||||
> - `consumption=20.01`:段 1(20 × 3 = 60)+ 段 2(0.01 × 4.5 ≈ 0.045)
|
||||
> - 边界是 inclusive 还是 exclusive 看 RateTier 配置(`lower`/`upper` 字段语义)
|
||||
|
||||
> [!question] 阶梯计价对工业表(multiplier > 1)的影响?
|
||||
> 倍率 + 阶梯叠加:`consumption = (current - previous) × multiplier`,然后这个 consumption 走阶梯。例如三相工业表 multiplier=10:
|
||||
>
|
||||
> - 物理表头读数差 28 度 → consumption = 280 度
|
||||
> - 280 度走阶梯计算
|
||||
>
|
||||
> 详见 [[generate-bill-with-multiplier]]。
|
||||
|
||||
## 异常分支
|
||||
|
||||
- 工业表倍率参与 → [[generate-bill-with-multiplier]]
|
||||
- 异常用量触发 min/max → [[generate-bill-min-max-cap]]
|
||||
- 用量异常高(漏水)→ [[exception-high-consumption]]
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [[multiplier-and-tiered-pricing]]
|
||||
- [[bill-generation-pipeline]]
|
||||
- [[meter-vs-meter-reading]]
|
||||
- [[generate-bill-with-multiplier]]
|
||||
- [[generate-bill-min-max-cap]]
|
||||
Reference in New Issue
Block a user