--- title: prop-acc · billing · 场景 - 挂起账单(业户失联/纠纷) aliases: - 挂起账单 - SuspendBillAction - 暂停收款 - suspend-bill - 场景-挂起账单 tags: - 场景 - prop-acc - 账单 - 调整 audience: - 业务人员 - 财务 status: 已发布 sub_feature: billing last_review: 2026-05-26 code_version: 2026-05-22 --- # 场景:挂起账单(业户失联 / 纠纷) 业户**与物业有纠纷**或**长期失联**,该账单暂时**不应被收款 / 不应被催收**,但又不能直接作废(纠纷可能解决,业户可能出现)。走 `SuspendBillAction` 把账单挂起,状态 Unpaid → Suspended,后续可 [[resume-bill|恢复]]。 ## 典型情境 > [!example] 真实情境(一):业户失联 > 王先生(15-7-203)三个月没缴物业费(累计 3 张账单 ¥2,400 Unpaid),物业多次联系电话不通、上门无人。物业认为业户可能搬走 / 失联 / 出国,**先把这 3 张账单挂起**,避免: > > - 月度报表"应收账款"虚高(挂着收不回来) > - 催收资源浪费(联系不上的还反复发短信) > - 业户突然回来时账单仍在(不会变作废) > [!example] 真实情境(二):纠纷期间 > 陈先生认为 5 月物业费 ¥800 不合理(物业服务质量纠纷),拒绝付。物业 / 业主委员会调解中,**挂起该账单**,等调解结果: > - 调解物业胜诉 → [[resume-bill|恢复]] → 业户付 > - 调解业户胜诉 → [[void-paid-bill|作废]] → 不收 > - 调解妥协 → 走拆账单 / 重新算金额 ## 业务人员视角 ### 第 1 步:确认挂起场景 | 场景 | 是否走挂起 | |---|---| | 业户失联 1-3 个月 | ✅ 挂起(等业户出现) | | 业户失联 >6 个月 | 可考虑作废(看物业政策)| | 业户纠纷中 | ✅ 挂起 | | 业户拒不付 | 不挂起,走逾期催收([[exception-overdue-bills]]) | | 业户真的搬走永久不再来 | 走作废 / 法律手段 | ### 第 2 步:打开账单 后台 → 账单 → 找到 Unpaid Bill → 进 `ViewBill`。 ### 第 3 步:点击 `SuspendBillAction`(标签"挂起") > [!warning] 按钮可见性 > 守护:`bill.status === Unpaid || Partial` + Policy `->authorize('suspend')`。Paid / Void / Suspended 状态灰化。 Modal 表单: | 字段 | 填什么 | |---|---| | **挂起原因(reason)** | **必填且详细**,如 "业户失联 3 个月,电话不通 + 上门无人 + 微信无回应" | ### 第 4 步:提交 `SuspendBillAction` 业务层逻辑: ```php class SuspendBillAction { public function handle(Bill $bill, string $reason, User $user): void { if (! in_array($bill->status, [BillStatus::Unpaid, BillStatus::Partial])) { throw new RuntimeException("账单状态不可挂起"); } $bill->update([ 'status' => BillStatus::Suspended, 'meta' => array_merge($bill->meta ?? [], [ 'suspend_reason' => $reason, 'suspended_at' => now(), 'suspended_by' => $user->id, ]), ]); activity() ->performedOn($bill) ->causedBy($user) ->withProperties([ 'reason' => $reason, 'from_status' => $bill->getOriginal('status'), 'to_status' => BillStatus::Suspended->value, 'bill_no' => $bill->bill_no, ]) ->event('suspended') ->log('账单已挂起'); } } ``` ### 第 5 步:通知业户(可选) - 失联场景:不通知(联系不上,无意义) - 纠纷场景:通知"您的账单已挂起,等调解结果" ## 系统流程 ```mermaid sequenceDiagram participant 业务 participant Filament participant Action[SuspendBillAction] participant DB participant Activity 业务->>Filament: ViewBill → SuspendBillAction(modal) Filament->>Action: handle(bill, reason, user) Action->>Action: 校验 status Unpaid/Partial Action->>DB: 开启事务 Action->>DB: 1. bill.status=Suspended Action->>DB: 2. bill.meta.suspend_reason / suspended_at / suspended_by Action->>Activity: 3. log(event=suspended) Action->>DB: 提交 Filament-->>业务: 成功 ``` ## 挂起后的能力对照 | 操作 | Unpaid / Partial | **Suspended** | |---|---|---| | `CollectPaymentAction`(收款)| ✅ | ❌(`canBePaid=false`)| | `CollectPaymentAction`(预存款抵)| ✅ | ❌ | | `SuspendBillAction`(再挂起)| ✅ | ❌(已是 Suspended)| | `ResumeBillAction`(恢复)| ❌ | ✅ | | `VoidBillAction`(作废)| ✅(canBeVoided=true)| ✅ | | 看账单 / 看历史 | ✅ | ✅(只读)| | 出现在 `OverdueBillsListWidget` | ✅(若到期)| ❌(挂起不算逾期)| | 出现在月度账单清单 | ✅ | ✅(标 Suspended)| > [!info] Suspended 与 Overdue 的关系 > 挂起的账单**不算逾期**(catch up 不会出现在催收清单)。但**期次仍在历史**(归属本月报表)。 > > 这是**有意设计**:挂起的目的是停掉催收 + 暂时退出收款流程,但不让账单凭空消失。 ## 业户视角 ### 失联场景(无感) 业户失联本来就联系不上,业务方决定挂起 → 业户**完全不知道**。等业户出现时: - 业户来电话 / 上门 - 业务人员说"您有挂起的账单 ¥XXX,我现在帮您恢复 + 收款" - 走 [[resume-bill|恢复]] → 收款 ### 纠纷场景 业户**可能收到通知**(看物业政策): > 陈先生您好,您的 5 月物业费账单已挂起(暂停收款),等纠纷调解结果。预计 X 月 Y 日前出结果。 业户: - 知情 + 等待 - 期间不会被催收 - 调解后走 [[resume-bill|恢复]] 或 [[void-paid-bill|作废]] ## 与"作废"的对比 | 维度 | **挂起(Suspended,本场景)** | [[void-paid-bill|作废 Void]] | |---|---|---| | 是否可恢复 | ✅ 走 [[resume-bill]]| ❌ 终态 | | 业务场景 | 临时暂停(纠纷 / 失联)| 永久消除 | | 报表归属 | 仍在本期(标 Suspended)| 标 Void(可过滤) | | 后续是否能收款 | 恢复后能 | 不能(已 Void)| **简单判断**:**不确定后续怎么办 → 挂起**;**确定不再收 → 作废**。 ## 长期 Suspended 的处理 挂起后**长期没恢复 / 没作废**的账单,业务上需要定期 review: | 挂起时长 | 推荐处置 | |---|---| | < 1 月 | 正常等待 | | 1-3 月 | 评估业户情况(再次联系)| | 3-6 月 | 决定:恢复(若有进展)/ 作废(若无希望)| | > 6 月 | 通常作废(或走法律手段)| 可加 audit 场景:**"长期挂起账单清单"**(类似 [[../deposit/audit-long-pending-accounts]])。当前 issue.md 未明确实施,可作为未来扩展。 ## 常见问题 > [!question] 挂起原因填得详细到什么程度? > **越详细越好**。审计要求 + 业户事后查询 + 业务复盘都需要。推荐结构: > > - 触发事件(纠纷 / 失联 / 其他) > - 已采取的联系措施(电话 / 上门 / 微信) > - 业务上的预期(等调解 / 等业户回来 / 等司法) > [!question] 挂起的账单已经有部分付(Partial)? > 看实施。SuspendBillAction 可能允许(从 Partial → Suspended)。已付的部分**不退**(只是暂停后续收款)。 > [!question] 同一业户多张挂起,能批量挂吗? > 当前**无批量挂起 UI**。逐张走 SuspendBillAction,效率低但可控。 > > 可加 `BulkSuspendBillsAction`(类似批删的设计),业务方提需求时实施。 > [!question] 挂起影响 prepaid 自动抵扣 job? > 是。job 应**跳过 Suspended 状态**的账单。设计上看 [[../prepaid/auto-deduction-design]]。 > [!question] activitylog 怎么查挂起记录? > ```sql > SELECT * FROM activity_log > WHERE event = 'suspended' > AND created_at BETWEEN ? AND ? > ORDER BY created_at DESC; > ``` > > 详见 [[audit-activitylog-trace]]。 ## 异常分支 - 业户出现/纠纷解决 → [[resume-bill]] - 确定不再收 → [[void-paid-bill]] - 业务上误挂起 → [[resume-bill]] 撤回(reason 改"误操作") - 长期挂起无解 → 作废 / 法律手段(待业务方明确) ## 相关文档 - [[bill-six-state-machine]] - [[resume-bill]] - [[void-paid-bill]] - [[exception-overdue-bills]] - [[audit-activitylog-trace]] - [[../deposit/freeze-during-dispute]](类似的"暂停"模式对比)