--- title: prop-acc · prepaid · 场景 - 手动抵扣月度物业费 aliases: - 抵扣物业费 - 预存款抵账单 - consume-monthly-property-bill - 场景-预存款抵月物业费 tags: - 场景 - prop-acc - 预存款 - 消费 audience: - 业户 - 业务人员 status: 已发布 sub_feature: prepaid last_review: 2026-05-25 code_version: 2026-05-22 --- # 场景:手动抵扣月度物业费 预存款最高频操作 —— 月底物业费账单出来后,业务人员后台**手动触发** `ConsumeAction`,从业户的预存款余额扣对应金额、Bill 状态翻 Paid。**未来批量自动 job 落地后,这条路径变成"运维个例兜底"**(详见 [[auto-deduction-design]])。 ## 典型情境 > [!example] 真实情境 > 张阿姨的 12-3-501 房 5 月物业费账单 ¥800 已出账。张阿姨预存款账户余额 ¥4,200。物业财务王主管月初批量为 100+ 户业户做物业费抵扣,张阿姨是其中一户。 ## 业户视角 ### 您会感受到什么 - 5 月底账单出来后,几天内收到推送: > "您的 5 月物业费 ¥800 已自动从预存款扣减,余额 ¥3,400" - 收到收据:"物业费 ¥800(5 月)" - 小程序"我的预存款"显示新流水:`-800.00 抵扣 物业费(5月)` - 小程序"我的账单"显示该账单 ✅ 已付 ### 您要做什么 什么都不用做。看看就行 —— - 如果余额够,账单自动归零,无感 - 如果余额不够,会收到"余额不足"提示,需要您手动充值或现金/微信付 > [!info] 与现金付的差异 > 业户拿到的收据**长一样**(都是"物业费 ¥800"),只是结算来源不同。详见 [[consume-via-bill-collection-type]]。 ## 业务人员视角 ### 第 1 步:确认账单已生成 后台 → 账单(Bill)模块 → 5 月物业费账单批量 → 状态 Unpaid → 列表里有张阿姨的账单。 ### 第 2 步:打开张阿姨的预存款账户 后台 → 预存款 → 账户列表 → 按业户姓名搜 → 找到 Active 账户(balance=4200)→ 进 `ViewPrepaidAccount`。 ### 第 3 步:点击 `ConsumeAction`(标签"消费抵扣") > [!warning] 按钮可见性 > `ConsumeAction` 守护:`canOperate()`(Active only)+ `balance > 0` + Policy `->authorize('consume')`。Frozen / Closed / 零余额账户灰化。 Modal 表单: | 字段 | 填什么 | |---|---| | **关联账单(Bill)** | 选业户的未付账单(下拉显示该业户 community 内 status=unpaid 的账单)| | **抵扣金额** | 自动带入账单金额(可改,部分抵扣场景)| | **备注** | 选填,如 "5 月物业费手动抵扣" | ### 第 4 步:提交 系统调 `ConsumeFromPrepaidAccountAction`,事务内: 1. 校验 `canOperate()`(Active only) 2. 校验跨社区(Bill 与 Account 必须同 community) 3. 校验余额(≥ 抵扣金额) 4. 建 `CollectionOrder`(`type=Bill`,`actual=+800`,`meta.fund_source=prepaid`,`Completed`) 5. 建 `CollectionOrderBill` 关联 CO 与 Bill 6. 调 `PrepaidAccount::consume($bill, $amount)`: - 加 `PrepaidTransaction`(`type=consume`,`amount=800`,`balance_before=4200`,`balance_after=3400`,`related_bill_id=...`,关联 CO) - 更新 `balance=3400` 7. 调 `Bill::recordPayment($amount)`: - 更新 `Bill.status=Paid` 8. 触发 `CollectionOrderCompleted` → Listener 建 Receipt(走 Bill 渠道,文案"物业费 ¥800") ### 第 5 步:给收据 / 通知 后台找到新建 Receipt → 发业户(微信 / 邮件)。 ## 系统流程 ```mermaid sequenceDiagram participant 业户 participant 财务 participant Filament participant ConsumeAction participant PrepaidAccount participant Bill participant 数据库 participant 监听器 Note over 业户,财务: 5 月物业费账单已出,张阿姨 balance=4200 财务->>Filament: ViewPrepaidAccount → ConsumeAction(选 Bill, 800) Filament->>ConsumeAction: handle(account, bill, 800) ConsumeAction->>PrepaidAccount: canOperate() ? Active=true ConsumeAction->>PrepaidAccount: community_id match Bill? yes ConsumeAction->>PrepaidAccount: balance >= 800? 4200≥800 yes ConsumeAction->>数据库: 开启事务 ConsumeAction->>数据库: 1. 建 CO(type=Bill, +800, meta.fund_source=prepaid) ConsumeAction->>数据库: 2. 建 CollectionOrderBill 关联 ConsumeAction->>PrepaidAccount: 3. consume(bill, 800) PrepaidAccount->>数据库: 建 PrepaidTransaction(consume, 4200→3400, related_bill_id) PrepaidAccount->>数据库: 更新 balance=3400 ConsumeAction->>Bill: 4. recordPayment(800) Bill->>数据库: status=Paid ConsumeAction->>监听器: 5. 触发 CollectionOrderCompleted 监听器->>数据库: 建 Receipt("物业费 ¥800") ConsumeAction->>数据库: 提交事务 Filament-->>财务: 成功 财务-->>业户: 推送 + 收据 ``` ## 流水台账(本场景在累计流水中) | 流水 | type | amount | balance_before | balance_after | related_bill_id | 备注 | |---|---|---|---|---|---|---| | 1 | deposit | 5000 | 0 | 5000 | — | 首次充值 | | **2** | **consume** | **800** | **5000** | **4200** | **Bill #5月物业费** | **本场景** | | (后续 4 月 5 月各 800 ...) | 5 月账单进入 Paid 状态,张阿姨账户余额变 ¥3,400。 ## 部分抵扣的特殊情况 若业户余额**不够全付**(例如余额 ¥500,账单 ¥800): | 选项 | 当前实现 | |---|---| | 抵扣 ¥500,账单剩 ¥300 待付 | 看 `Bill::recordPayment()` 是否支持部分支付 | | 全部跳过(不抵)| 等业户充值或其他方式付 | **当前推荐**:跳过,告知业户"余额不足,请充值或选其他方式付"。部分抵扣需 Bill 模块配合。 ## 常见问题 > [!question] Modal 表单里"关联账单"下拉如何过滤? > 系统只显示: > - 与本账户同社区(community_id 一致) > - 业户本人(resident_id 一致) > - 状态 Unpaid > - 按 due_at 升序(最早到期的先,引导业务人员优先抵) > > 多个账单 → [[consume-multiple-bills-priority|按优先级抵扣]] > [!question] 抵扣后业户问"我用预存款付的为啥收据写'物业费'?" > 这是**有意设计**(详见 [[consume-via-bill-collection-type]]):业户感知一致,不管怎么付,收据都长一样。如果业户想知道"是用预存款付的",可在小程序"我的账单"看到付款方式 = "预存款抵扣"。 > [!question] 抵扣失败如何排查? > 看后台 / 日志的错误信息: > - "账户冻结" → 解冻 > - "跨社区不允许" → 业务人员选错账单 / 账户 > - "余额不足" → 业户先充值 > - "账单已 Paid" → 不要重复抵扣 > [!question] 月底 100+ 户挨个 Modal 抵扣太慢了吧? > 是的,这就是**月初批量自动抵扣 job** 的存在意义(详见 [[auto-deduction-design]])。**job 实现前**业务人员必须挨个手动。 > [!question] 已抵扣的账单想撤回怎么办? > 不可变流水设计。如果抵错(例如抵了别人的账单): > - 走退款 [[refund-partial-after-consume]] —— 但这退的是预存款余额,不是"撤销抵扣" > - 撤销账单需 Bill 模块支持 reverse,不在本场景 > - 实际:**预防胜于补救**,Modal 表单提交前再三确认 Bill ID ## 异常分支 - 余额不够 → 业户先充 [[deposit-additional-topup]] 再来抵 - 账户 Frozen → 先 [[unfreeze-after-verification]] - 多张账单一起抵 → [[consume-multiple-bills-priority]] - 计量类账单 → [[consume-meter-bill]] - 月初批量(未来)→ [[consume-batch-auto-monthly]] ## 相关文档 - [[transaction-types]] - [[consume-via-bill-collection-type]] - [[account-state-machine]] - [[consume-multiple-bills-priority]] - [[consume-batch-auto-monthly]] - [[auto-deduction-design]]