vault backup: 2026-05-25 23:22:55

This commit is contained in:
Willie
2026-05-25 23:22:55 +08:00
parent ee7abbfc45
commit f407569771
5 changed files with 741 additions and 16 deletions

View File

@@ -0,0 +1,195 @@
---
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]]