Files
uniprop-manual/prop-acc/concepts/billing/periodic-bill-generation.md
2026-05-26 00:48:12 +08:00

8.7 KiB
Raw Permalink Blame History

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 · 周期账单生成机制
周期账单生成
月度物业费生成
GeneratePeriodicBillsAction
BillingMergeStrategy
合单策略
概念
prop-acc
账单
业务流程
业务人员
财务
架构师
已发布 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 日前付清。

业户可选:

业务人员视角

月初执行

每月 1-3 日,业务人员王主管:

  1. 打开 BillsListPage → 顶部 "生成周期账单"
  2. 选费用类型(物业费 / 停车费 / 等)+ 期次 + 策略 + 备注
  3. 提交 → 系统扫描 + 生成 → 报告"已生成 N 张账单,跳过 M 张"
  4. 抽样核对几张 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)。业务流程上业务人员通常先做完抄表 + 计量账单生成 → 再触发周期账单生成。

相关文档