Files
uniprop-manual/prop-acc/scenarios/prepaid/refund-full-resident-moveout.md

191 lines
7.3 KiB
Markdown
Raw Normal View History

2026-05-25 23:27:56 +08:00
---
title: prop-acc · prepaid · 场景 - 业户搬走全额退余
aliases:
- 业户搬走退预存款
- 全额退预存款
- refund-full-resident-moveout
- 场景-业户搬走退预存款
tags:
- 场景
- prop-acc
- 预存款
- 退款
audience:
- 业户
- 业务人员
status: 已发布
sub_feature: prepaid
last_review: 2026-05-25
code_version: 2026-05-22
---
# 场景:业户搬走全额退余
业户**搬离社区**(卖房 / 退租 / 不再使用本物业),要把预存款账户余额全额退回。退款后**不自动关账**(prepaid 特性),业务人员**主动**走 [[close-resident-moveout|关账]] 流程。
## 典型情境
> [!example] 真实情境
> 刘先生把 12-3-501 房子卖了,下周搬走。他在预存款账户里还有 ¥3,200(本月物业费扣完之后的余),要全额退回。
## 业户视角
### 第 1 步:告知物业搬走
- 跟物业管家说"我下周搬走,把预存款里的钱退给我"
- 提供退款渠道(银行卡 / 微信 / 支付宝)
### 第 2 步:等退款
- 业务人员核对账户余额
- 操作退款(走线下 + 系统)
### 第 3 步:收到红字收据 + 退款到账
- 红字收据"预付款退款 ¥-3,200"
- 银行 / 微信收到 ¥3,200
### 第 4 步:账户被关
- 后续物业再发账单(若有)→ 不会自动从这个账户扣(已 Closed)
- 业户在小程序"我的预存款" 显示 "🔒 已关闭"
## 业务人员视角
### 第 1 步:核实业户搬走情况
- 房屋已过户(看 community_user_profile 状态)
- 业户已结清其他费用(无未付账单)
- 业户提供退款渠道
> [!warning] 注意未付账单
> 如果业户还有未付账单(物业费 / 水电费等),**先抵扣再退余**:
> - 业户余额 ¥3,200,有未付账单 ¥800 → 先 [[consume-monthly-property-bill|抵 800]],余 ¥2,400 → 再退 ¥2,400
> - 不要直接退全部 → 否则未付账单仍挂业户身上,变成"搬走后还欠物业钱",催收困难
### 第 2 步:打开账户做退款
后台 → 预存款 → 找到刘先生账户(Active,balance=3200)→ 进 `ViewPrepaidAccount` → 点 `RefundAction`(标签"退款")。
> [!warning] 按钮可见性
> `RefundAction` 守护:`canOperate() && balance > 0` + Policy `->authorize('refund')`。Frozen / Closed / 零余额账户灰化。
Modal 表单:
| 字段 | 填什么 |
|---|---|
| **退款金额** | ¥3,200(默认带入当前余额)|
| **退款渠道(PaymentChannel)** | 选业户指定回款方式 |
| **退款备注** | 必填,如 "业户搬离,12-3-501 已过户,退预存款全额" |
### 第 3 步:提交
系统调 `RefundFromPrepaidAccountAction`,事务内:
1. 校验 `canOperate()`(Active only)
2. 校验金额 ≤ 当前余额
3.`CollectionOrder`(`type=Prepaid`,`actual_amount=-3200` 红字,`Completed`)
4.`PrepaidAccount::refund(3200, ...)`:
- 模型层再校验 `canOperate()`(三层防御之模型层兜底)
-`PrepaidTransaction`(`type=refund`,`amount=3200`,`balance_before=3200`,`balance_after=0`,关联红字 CO)
- 更新 `balance=0`
5. **不自动关账**(prepaid 特性,与 deposit 不同 —— 见 [[account-state-machine]] "零余额不自动关账" 段)
6. 触发 `CollectionOrderCompleted` → Listener 建红字 Receipt"预付款退款 ¥-3,200"
### 第 4 步:走线下退款
- 银行转账:导出回款指令 → 银行办理
- 微信:在物业微信号上做退款
- 支付宝:同上
### 第 5 步:主动关账([[close-resident-moveout]])
退完余额后**账户仍是 Active 状态**,需手动走 `CloseAccountAction` 关掉。这是 prepaid 与 deposit 的关键差异。
详见 [[close-resident-moveout]]。
### 第 6 步:把红字收据给业户
后台找 Receipt → 微信 / 邮件发刘先生。
## 系统流程
```mermaid
sequenceDiagram
participant 业户
participant 财务
participant Filament
participant RefundFromPrepaidAccountAction
participant 数据库
participant 监听器
Note over 业户,财务: 业户搬走,余额 3200
财务->>财务: 核实无未付账单(若有,先 consume)
财务->>Filament: ViewPrepaidAccount → RefundAction(3200)
Filament->>RefundFromPrepaidAccountAction: handle(account, 3200, channel)
RefundFromPrepaidAccountAction->>RefundFromPrepaidAccountAction: canOperate()? Active=true
RefundFromPrepaidAccountAction->>RefundFromPrepaidAccountAction: 3200 ≤ 3200 yes
RefundFromPrepaidAccountAction->>数据库: 开启事务
RefundFromPrepaidAccountAction->>数据库: 1. 建 CO(Prepaid, -3200 红字, Completed)
RefundFromPrepaidAccountAction->>数据库: 2. account.refund(3200) → balance 3200→0
RefundFromPrepaidAccountAction->>数据库: 3. balance=0, **status 仍 Active**
RefundFromPrepaidAccountAction->>监听器: 4. 触发 CollectionOrderCompleted
监听器->>数据库: 5. 建 Receipt("预付款退款 ¥-3,200")
RefundFromPrepaidAccountAction->>数据库: 提交事务
Filament-->>财务: 成功(注:account 仍 Active,需手动关账)
Note over 财务: 接着走关账
财务->>Filament: CloseAccountAction → status=Closed
财务-->>业户: 银行/微信退 3200 + 红字收据
```
## 与 deposit 退款的关键差异
| 维度 | deposit 退款(refund-full-no-damage) | prepaid 退款(本场景) |
|---|---|---|
| 余额清零后状态 | **自动 Closed** | **仍 Active**(可继续充值)|
| 关账操作 | 不需要 | **需手动 CloseAccountAction** |
| 业务背景 | 装修结束等业务节点 | 业户搬走等长期事件 |
| 是否常见 | 高频(每户装修都做) | 低频(业户搬走才做)|
## 常见问题
> [!question] 为什么 prepaid 不自动关账?
> 详见 [[account-state-machine]] "零余额不自动关账" 段。简言之:预存款账户**一户一账**,频繁开关无意义,业户随时可能继续充值。
> [!question] 退完不关账户有什么风险?
> 几乎无风险:
> - 业户搬走后再无消费/充值动作 → 账户保持 0 余额 Active
> - 但**长期闲置 Active 账户**会出现在审计扫描里([[audit-low-balance-and-overdue]] 类似),业务上不专业
> - 推荐**退完立即关**(走 [[close-resident-moveout]]),清爽
> [!question] 业户搬走后又租回来或买回来,关了账户怎么办?
> 当前**一户一账约束阻塞**(unique 不允许重开)。详见 [[one-account-per-resident]] "已知设计 gap"。业务上目前用:
> - 业户重新现金 / 微信付账单(不用预存款)
> - 联系运维特殊处理(罕见)
> [!question] 退款渠道与充值渠道不同可以吗?
> 可以,看 [[../deposit/refund-with-payment-channel-switch]] 介绍的换渠道逻辑(deposit 模块,逻辑相同)。
> [!question] 业户失联但要退预存款怎么办?
> 几个选项:
> - **暂留 Active**:不操作,等业户出现(余额对业户仍可用)
> - **freeze 账户**:走 [[freeze-suspected-fraud|风控冻结]] 流程(reason 改"业户失联待联系")
> - **不可走 ForceClose retain**(prepaid 没有 ForceClose,与 deposit 不同)
> - 长期失联(>2 年)走业务流程(类似 deposit 的 retain,但 prepaid 当前没有内建机制,需运维介入)
## 异常分支
- 退一部分余下继续用 → [[refund-partial-after-consume]]
- 账户 Frozen 想退 → 先 [[unfreeze-after-verification|解冻]] 再退(prepaid 没有 ForceClose)
- 关账步骤 → [[close-resident-moveout]]
## 相关文档
- [[refund-partial-after-consume]]
- [[close-resident-moveout]]
- [[account-state-machine]]
- [[transaction-types]]
- [[consume-monthly-property-bill]]