--- 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]]