Files
2026-05-26 01:08:16 +08:00

8.0 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 · 场景 - 挂起账单(业户失联/纠纷)
挂起账单
SuspendBillAction
暂停收款
suspend-bill
场景-挂起账单
场景
prop-acc
账单
调整
业务人员
财务
已发布 billing 2026-05-26 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 业务层逻辑:

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 步:通知业户(可选)

  • 失联场景:不通知(联系不上,无意义)
  • 纠纷场景:通知"您的账单已挂起,等调解结果"

系统流程

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 日前出结果。

业户:

与"作废"的对比

| 维度 | 挂起(Suspended,本场景) | void-paid-bill | |---|---|---| | 是否可恢复 | 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 怎么查挂起记录?

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 改"误操作")
  • 长期挂起无解 → 作废 / 法律手段(待业务方明确)

相关文档