8.7 KiB
title, aliases, tags, audience, status, sub_feature, last_review, code_version
| title | aliases | tags | audience | status | sub_feature | last_review | code_version | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| prop-acc · billing · 周期账单生成机制 |
|
|
|
已发布 | billing | 2026-05-26 | 2026-05-22 |
周期账单生成机制
物业费、停车费、电视费等按周期(月 / 季 / 年)固定收的账单,通过 GeneratePeriodicBillsAction 批量生成。配合 BillingMergeStrategy 枚举决定"同业户多个费用类型是否合并到一张账单"。
业务场景
每月 1 日,物业为本社区 300 户业主生成本月物业费账单(每户 ¥800 起步,按房屋面积浮动)。系统应:
- 自动算每户的金额(按 RatePlan + 房屋面积)
- 批量建 Bill(300 张)
- 各张 Bill 期次清晰(billing_period 5/1 - 5/31)
- 不重复生成(本月已生成的不再生成)
- 业务人员收到结果报告
批量生成流程
flowchart TD
A[业务人员触发 GeneratePeriodicBillsAction] --> B[Modal 输入参数]
B --> C[参数:期次 / 费用类型 / 社区范围 / 合并策略]
C --> D[扫描候选业户清单]
D --> E[每户:计算应付金额]
E --> F{已有本期 Bill?}
F -->|否| G[建新 Bill]
F -->|是,根据策略| H{合并策略}
H -->|MERGE 合单| I[追加到既有 Bill]
H -->|SKIP 跳过| J[不动]
H -->|REPLACE 替换| K[作废旧 Bill + 建新]
G --> L[报告完成]
I --> L
J --> L
K --> L
GeneratePeriodicBillsAction 参数
业务人员触发(Bills List 页 → 顶部 "批量生成周期账单" 按钮):
| 参数 | 说明 |
|---|---|
| 期次(billing_period) | 如 "2026 年 5 月",决定 start / end |
| 费用类型(fee_type_id) | 物业费 / 停车费 / 有线电视费 / ... |
| 社区范围(community_id) | 单社区 / 全平台 |
| 业户范围 | 单个业户 / 全社区业户 / 自定义清单 |
| 合并策略(merge_strategy) | MERGE / SKIP / REPLACE |
| 备注 | 选填 |
提交后系统扫描候选业户清单,逐户计算 + 建账。
BillingMergeStrategy 枚举
[!info] 实际枚举值看
packages/prop-acc/src/Enums/BillingMergeStrategy.php可能值(推测):
值 含义 SkipExisting同业户同期次已有 Bill → 跳过(不重复生成) Merge追加到既有 Bill(同业户同期次的不同费用类型合一张) Replace作废旧 Bill + 建新(罕见,数据修复用) 默认策略通常是
SkipExisting(最安全)。
三种策略对照
策略 1:SkipExisting(默认,推荐)
业务人员 5 月 1 日点击"生成 5 月物业费"。系统:
- 张阿姨已经有 5 月物业费 Bill → 跳过(避免重复)
- 陈先生没有 5 月物业费 Bill → 新建
适用:正常月度操作,业务人员不确定是否之前已经生成过,跳过最安全。
策略 2:Merge(合单)
业务人员同时为业户生成"5 月物业费 + 5 月电视费"。系统:
- 找到张阿姨已有 5 月物业费 Bill(¥800)
- 追加电视费 ¥40 到同一张 Bill(amount: 800 + 40 = 840)
- 业户收到一张账单 ¥840(明细两项)
适用:多种费用类型一起发(减少业户收到的账单数,体验好)。
[!warning] Merge 的局限 合并后单一 Bill 的金额 = 各费用类型总和,但关联的 sourceable 怎么处理?Bill 表 sourceable 字段是 1:1。当前实现可能:
- 不挂 sourceable(
sourceable_type=null)- 或挂主费用类型的 source
具体看代码。Merge 策略实施细节复杂,生产环境谨慎用。
策略 3:Replace(替换)
业务人员发现 5 月物业费金额算错了(RatePlan 改过),要全部重算:
- 找到张阿姨已有的 5 月物业费 Bill
- 作废(VoidBillAction,状态翻 Void)
- 重新生成新 Bill(按新 RatePlan)
适用:数据修复场景(罕见,需高权限 + 必填原因)。
[!warning] Replace 的风险 已付的 Bill 作废后,业户已经付的钱怎么办?
- Bill 状态翻 Void
- 已付的 CollectionOrderBill 关联保留(审计需要)
- 但业户的钱 = 物业账面多收了
- 必须走退款(建红字 CollectionOrder)
Replace 策略只对 Unpaid Bill 使用相对安全;Paid Bill 慎用,需配套退款流程。
金额计算
周期账单金额怎么算?取决于 RatePlan 配置:
| 算法 | 说明 |
|---|---|
| 固定金额 | 每户每月固定 ¥800(简单) |
| 按面积 | 房屋面积 × 单价(例如物业费 ¥3 / m² / 月) |
| 按车位 | 每个停车位固定金额(停车费) |
| 按设备 | 每台空调 ¥X / 月(中央空调费) |
| 阶梯 | 类似计量账单的阶梯(罕见) |
具体看 RatePlan 的配置 + 业务计算逻辑。可能在 PeriodicBillGenerationService(若有)实现,或在 Action 内联。
生成的 Bill 字段
每张生成的 Bill:
| 字段 | 值 |
|---|---|
bill_no |
自动编号(B-202605-501-001 或类似) |
community_id |
所属社区 |
asset_id |
业户房屋 |
resident_id |
业户 |
fee_type_id |
费用类型 |
bill_type |
Periodic(枚举) |
amount |
算出的金额 |
paid_amount |
0(刚生成,未付) |
status |
Unpaid |
billing_period_start |
期次开始(2026-05-01) |
billing_period_end |
期次结束(2026-05-31) |
due_at |
到期日(通常本期末 + 宽限期,如 2026-06-15) |
sourceable_* |
null(周期账单通常无 source) |
业户视角
业户每月初(或月底,看物业策略)收到推送:
张阿姨您好,您的 2026 年 5 月物业费 ¥800 已生成。请于 6 月 15 日前付清。
业户可选:
- 现金 / 微信付 → 走 collect-payment-single
- 预存款抵 → 走 [collect-via-prepaid-auto]
- 与其他账单一起付 → 走 collect-payment-batch
- 暂时不付 → 到期日后变逾期,走 exception-overdue-bills 催收
业务人员视角
月初执行
每月 1-3 日,业务人员王主管:
- 打开 BillsListPage → 顶部 "生成周期账单"
- 选费用类型(物业费 / 停车费 / 等)+ 期次 + 策略 + 备注
- 提交 → 系统扫描 + 生成 → 报告"已生成 N 张账单,跳过 M 张"
- 抽样核对几张 Bill 的金额 + 业户
异常处置
| 异常 | 处置 |
|---|---|
| 某户 RatePlan 配置缺失 | 跳过该户 + 在报告里标记 → 单独建 RatePlan + 单独生成 |
| 某户已有同期 Bill(被跳过) | 看是否需要 Merge / Replace |
| 全部失败(系统错) | 联系运维查日志 |
自动化的可能
issue.md Q6 未列入"待补",但业务上可能需要:
- Scheduled job 月初自动跑 — 不需要业务人员手动触发
- 跟 prepaid 自动抵扣 job 串联 — 账单生成 → 立即触发 ../prepaid/auto-deduction-design → 业户感受"无感扣账单"
当前仍是手动触发。
常见问题
[!question] 同业户同期次同费用类型能有两张 Bill 吗? 业务上不应该(违反业务规则:一户一月一物业费)。系统层面看是否有 unique 约束:
UNIQUE INDEX (community_id, resident_id, fee_type_id, billing_period_start)若有 → 数据库层挡住重复;若无 → 全靠 Action 的 SkipExisting 策略判断,理论上可能因并发产生重复。
[!question] 业户搬走中途,本月物业费要不要全收? 看物业策略 + 合同约定:
- 全月收(简单,业户合同到月底)
- 按天分摊(按搬走前的天数算)
- 不收(罕见)
系统当前简版通常是全月收。按天分摊需要业务层算法支持。
[!question] 周期账单生成后才发现 RatePlan 配错怎么办? 走 Replace 策略只对 Unpaid 安全。如果已付:
- 看错的金额方向:
- 收多了 → 退差额(走 void-paid-bill 类似流程)
- 收少了 → 补开账单(新建一张 ¥差额 的临时账单)
[!question] 批量生成多久?300 户 5 秒? 单户 ~50-100ms(查 RatePlan + 查业户 + 建 Bill + 日志)。300 户顺序执行 ~15-30 秒。可优化为并发(若业务量大)。
[!question] 周期账单和计量账单可以一起生成吗? 不在同一个 Action(周期是
GeneratePeriodicBillsAction,计量是 ../meter/bill-generation-pipeline)。业务流程上业务人员通常先做完抄表 + 计量账单生成 → 再触发周期账单生成。