--- title: prop-acc · prepaid · 场景 - 部分消费后退余(不自动关账) aliases: - 部分退预存款 - 退一部分留账户 - refund-partial-after-consume - 场景-预存款部分退余 tags: - 场景 - prop-acc - 预存款 - 退款 audience: - 业户 - 业务人员 status: 已发布 sub_feature: prepaid last_review: 2026-05-25 code_version: 2026-05-22 --- # 场景:部分消费后退余(不自动关账) 业户**部分使用**预存款后,想**退余下一部分**(不全退,继续保留账户)。退完仍 Active,业户可后续继续充值复用。本场景突出 prepaid 与 deposit 的**关键差异**:零余额不自动关账,部分余额更不会。 ## 典型情境 > [!example] 真实情境 > 陈先生 3 个月前充了 ¥5,000 预存款,期间扣了 ¥2,400(3 个月物业费),余额 ¥2,600。他最近现金流紧张,想**先退 ¥1,500 应急**,留 ¥1,100 在账户继续扣物业费。 ## 业户视角 ### 第 1 步:跟物业说要退一部分 - "我想从预存款退 ¥1,500,留点继续用" - 提供退款渠道 ### 第 2 步:等退款 - 业务人员核实余额、操作 ### 第 3 步:收到红字收据 + 退款 - 红字收据"预付款退款 ¥-1,500" - 银行 / 微信收到 ¥1,500 ### 第 4 步:账户保持 Active - 小程序"我的预存款"显示余额 ¥1,100 - 仍可继续抵账单 - 后续可继续充值 > [!info] 与 deposit 的核心差异 > deposit 退完余额到 0 会自动 Closed。prepaid **退完无论余额多少都不自动关**,业户随时可继续用。 ## 业务人员视角 ### 第 1 步:打开账户 后台 → 预存款 → 陈先生账户(Active,balance=2600)→ 进 `ViewPrepaidAccount`。 ### 第 2 步:`RefundAction` Modal | 字段 | 填什么 | |---|---| | **退款金额** | **¥1,500**(不是全额,**手动改**)| | 退款渠道 | 微信 / 银行 | | 备注 | 选填,如 "业户申请部分退款" | > [!warning] 易错点 > Modal 默认带入**当前余额全额**(¥2,600)。**必须手动改为 ¥1,500**,否则就成了全退。 ### 第 3 步:提交 系统调 `RefundFromPrepaidAccountAction`,事务内: 1. 校验 `canOperate()` 2. 校验金额 ≤ 余额(1500 ≤ 2600 ✓) 3. 建 `CollectionOrder`(`type=Prepaid`,`actual=-1500` 红字,`Completed`) 4. 调 `account.refund(1500)`: - 加 `PrepaidTransaction`(type=refund, 2600→1100,关联 CO) - 更新 balance=1100 5. **不关账**(余额非 0) 6. 触发监听器 → Receipt"预付款退款 ¥-1,500" ### 第 4 步:走线下退款 + 给收据 银行 / 微信退 ¥1,500;红字收据交业户。**账户保持 Active,余额 ¥1,100**。 ### 第 5 步:告知业户 "已退 ¥1,500,账户还有 ¥1,100 可继续抵账单"。 ## 系统流程 ```mermaid sequenceDiagram participant 业户 participant 财务 participant Filament participant 数据库 Note over 业户: 余额 2600,要退 1500 业户->>财务: 退 1500 财务->>Filament: ViewPrepaidAccount → RefundAction(modal, **改成 1500**) Filament->>数据库: RefundFromPrepaidAccountAction 数据库->>数据库: 建 CO(-1500 红字) + PrepaidTransaction(refund, 2600→1100) 数据库->>数据库: balance=1100(**不关账**) 数据库->>监听器: 触发监听器 → Receipt("预付款退款 ¥-1,500") 财务-->>业户: 微信退 1500 + 红字收据 + 告知余额 1100 ``` ## 流水台账(累计) | 流水 | type | amount | balance_before | balance_after | 备注 | |---|---|---|---|---|---| | 1 | deposit | 5000 | 0 | 5000 | 3 个月前首次充值 | | 2 | consume | 800 | 5000 | 4200 | 第 1 月物业费 | | 3 | consume | 800 | 4200 | 3400 | 第 2 月物业费 | | 4 | consume | 800 | 3400 | 2600 | 第 3 月物业费 | | **5** | **refund** | **1500** | **2600** | **1100** | **本场景** | 账户余额 ¥1,100,**仍 Active**,后续可继续抵账单或充值。 ## 与 deposit 退款的差异 | 维度 | deposit 部分退款(refund-partial-after-forfeit) | prepaid 部分退款(本场景) | |---|---|---| | 退款产生的 CO 类型 | type=Deposit, -N 红字 | type=Prepaid, -N 红字 | | 退完余额 0 | **自动 Closed** | **仍 Active** | | 退完余额非 0 | 仍 Active(deposit 也允许部分退后继续动)| 仍 Active | | 业务背景 | 押金扣罚 + 退余 | 业户应急需现金,部分提取 | ## 常见问题 > [!question] 退完余额变 0,会自动关账吗? > **不会**。即使全退到 0,prepaid 仍保持 Active。详见 [[account-state-machine]]"零余额不自动关账"段 + [[close-with-zero-balance-decision]]。 > [!question] 业务人员退多了(改余额时手抖)? > 系统会校验 `amount ≤ balance` 守护拦截。例如余额 2600,改成 3000 提交 → 抛错 "amount exceeds balance"。**预防**:Modal 提交前再三确认数字。 > [!question] 业户想退完了又改主意,要充回去? > 当然可以。直接走 [[deposit-additional-topup|追加充值]] 把钱充回账户即可。账户一直 Active 没动过。 > [!question] 业户拿到红字收据后困惑"我没买东西啊为什么收据是负数"? > 解释: > - 红字 = 钱从物业流出 / 回到您手里 > - 这不是消费收据,是退款收据 > - 详见 [[../deposit/red-receipt-design]](deposit 模块的概念,prepaid 复用相同设计) > [!question] 业户问"我现在余额 1100,还能扣账单吗?" > 当然能。账户 Active,余额 > 0,正常用。物业费下月扣完后余额会变 ¥300(假设 ¥800 月费)。 ## 异常分支 - 全额退 → [[refund-full-resident-moveout]] - Frozen 状态退 → 先 [[unfreeze-after-verification]] 再退 - 退完想关账 → 走 [[close-with-zero-balance-decision]] - 业户搬走全退 + 关账 → [[refund-full-resident-moveout]] + [[close-resident-moveout]] ## 相关文档 - [[refund-full-resident-moveout]] - [[account-state-machine]] - [[close-with-zero-balance-decision]] - [[transaction-types]] - [[../deposit/red-receipt-design]]