From 548216446017730e76fa16301a2f9e4fa3389e4a Mon Sep 17 00:00:00 2001 From: Willie Date: Mon, 25 May 2026 22:22:37 +0800 Subject: [PATCH] vault backup: 2026-05-25 22:22:37 --- .obsidian/workspace.json | 32 +-- .../deposit/deposit-additional-topup.md | 147 ++++++++++++++ .../deposit/deposit-first-time-renovation.md | 165 ++++++++++++++++ .../deposit/deposit-on-behalf-by-company.md | 163 ++++++++++++++++ .../deposit/refund-full-no-damage.md | 182 ++++++++++++++++++ 5 files changed, 673 insertions(+), 16 deletions(-) create mode 100644 prop-acc/scenarios/deposit/deposit-additional-topup.md create mode 100644 prop-acc/scenarios/deposit/deposit-first-time-renovation.md create mode 100644 prop-acc/scenarios/deposit/deposit-on-behalf-by-company.md create mode 100644 prop-acc/scenarios/deposit/refund-full-no-damage.md diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json index e94f5c7..eae0ecc 100644 --- a/.obsidian/workspace.json +++ b/.obsidian/workspace.json @@ -13,12 +13,12 @@ "state": { "type": "markdown", "state": { - "file": "prop-acc/concepts/adhoc/collection-order-and-receipt.md", + "file": "prop-acc/maps/deposit-knowledge-map.md", "mode": "source", "source": false }, "icon": "lucide-file", - "title": "collection-order-and-receipt" + "title": "deposit-knowledge-map" } } ] @@ -94,7 +94,7 @@ "state": { "type": "backlink", "state": { - "file": "prop-acc/concepts/adhoc/collection-order-and-receipt.md", + "file": "prop-acc/maps/deposit-knowledge-map.md", "collapseAll": false, "extraContext": false, "sortOrder": "alphabetical", @@ -104,7 +104,7 @@ "unlinkedCollapsed": true }, "icon": "links-coming-in", - "title": "Backlinks for collection-order-and-receipt" + "title": "Backlinks for deposit-knowledge-map" } }, { @@ -113,12 +113,12 @@ "state": { "type": "outgoing-link", "state": { - "file": "prop-acc/concepts/adhoc/collection-order-and-receipt.md", + "file": "prop-acc/maps/deposit-knowledge-map.md", "linksCollapsed": false, "unlinkedCollapsed": true }, "icon": "links-going-out", - "title": "Outgoing links from collection-order-and-receipt" + "title": "Outgoing links from deposit-knowledge-map" } }, { @@ -156,13 +156,13 @@ "state": { "type": "outline", "state": { - "file": "prop-acc/concepts/adhoc/collection-order-and-receipt.md", + "file": "prop-acc/maps/deposit-knowledge-map.md", "followCursor": false, "showSearch": false, "searchQuery": "" }, "icon": "lucide-list", - "title": "Outline of collection-order-and-receipt" + "title": "Outline of deposit-knowledge-map" } }, { @@ -196,6 +196,13 @@ }, "active": "b06ed69835363258", "lastOpenFiles": [ + "prop-acc/scenarios/deposit/refund-full-no-damage.md", + "prop-acc/index.md", + "prop-acc/scenarios/deposit/deposit-additional-topup.md", + "prop-acc/concepts/adhoc/collection-order-and-receipt.md", + "prop-acc/scenarios/deposit/deposit-on-behalf-by-company.md", + "prop-acc/scenarios/deposit/deposit-first-time-renovation.md", + "prop-acc/scenarios/deposit", "prop-acc/maps/deposit-knowledge-map.md", "prop-acc/concepts/deposit/deposit-vs-adhoc-vs-prepaid.md", "prop-acc/concepts/deposit/red-receipt-design.md", @@ -205,7 +212,6 @@ "prop-acc/concepts/deposit/deposit-account-vs-transaction.md", "prop-acc/concepts/deposit", "prop-acc/maps/adhoc-knowledge-map.md", - "prop-acc/index.md", "prop-acc/scenarios/adhoc/cancel-resident-withdrawal.md", "prop-acc/scenarios/adhoc/flow-a-counter-buy-ic-card.md", "概念-CollectionOrder与Receipt.md", @@ -218,11 +224,6 @@ "业户.md", "prop-acc/scenarios/adhoc/config-add-item.md", "prop-acc/scenarios/adhoc/config-delist-item-handle-pending.md", - "prop-acc/scenarios/adhoc/cross-channel-supplementary-payment.md", - "prop-acc/scenarios/adhoc/auto-void-timeout.md", - "prop-acc/scenarios/adhoc/receipt-reprint-lost.md", - "prop-acc/scenarios/adhoc/receipt-onsite-print.md", - "prop-acc/scenarios/adhoc/receipt-miniapp-pdf-download.md", "prop-acc/scenarios/adhoc", "prop-acc/concepts/adhoc", "resident-portal/scenarios", @@ -230,7 +231,6 @@ "resident-portal/procedures", "resident-portal/maps", "resident-portal/glossary", - "resident-portal/features", - "resident-portal/faq" + "resident-portal/features" ] } \ No newline at end of file diff --git a/prop-acc/scenarios/deposit/deposit-additional-topup.md b/prop-acc/scenarios/deposit/deposit-additional-topup.md new file mode 100644 index 0000000..cd291eb --- /dev/null +++ b/prop-acc/scenarios/deposit/deposit-additional-topup.md @@ -0,0 +1,147 @@ +--- +title: prop-acc · deposit · 场景 - 已有账户追加缴款 +aliases: + - 追加缴款 + - 押金账户追加 + - deposit-additional-topup + - 场景-押金追加缴款 +tags: + - 场景 + - prop-acc + - 保证金 + - 缴纳 +audience: + - 业户 + - 业务人员 +status: 已发布 +sub_feature: deposit +last_review: 2026-05-25 +code_version: 2026-05-22 +--- + +# 场景:已有账户后续追加缴款 + +业户**已经有一个 Active 状态的押金账户**,现在要追加缴款(扩大装修范围 / 押金标准提升 / 分期补齐)。比首次开户简单 —— 不重建账户,只加流水。 + +## 典型情境 + +> [!example] 真实情境 +> 张阿姨家原来交了 ¥3,000 装修保证金。装修过程中她决定多动一面承重墙,物业要求她**补 ¥2,000 押金**(高风险装修加押)。第二天她到前台补缴。 + +## 业户视角 + +### 第 1 步:到前台 / 小程序 + +带身份证(部分物业要求)到前台,告诉职员: + +> "我要给装修押金账户补 ¥2,000。" + +### 第 2 步:付款 + +同 [[deposit-first-time-renovation|首次缴款]]:现金 / 微信 / POS / 银行转账。 + +### 第 3 步:拿收据 + +新收据"装修保证金缴纳 ¥2,000"。 + +> [!info] 您账户里现在有多少? +> 旧 ¥3,000 + 新 ¥2,000 = **¥5,000**。下次退押时按这个总数算。 + +## 业务人员视角 + +> [!info] 与首次缴款的关键差异 +> **不开新账户,在既有账户上加缴款流水**。如果误开新账户,会出现两个并行账户,退款时一笔退不清,业户会困惑。 + +### 第 1 步:找到既有账户 + +后台 → 保证金 → 账户列表 → 按业户姓名 / 房号 / 缴款人姓名搜索 → 找到 Active 账户。 + +### 第 2 步:进入账户详情(`ViewDepositAccount`) + +详情页右上角的状态管理组里点 **`DepositAction`** 按钮(标签"追加缴款")。 + +> [!warning] 按钮可能灰化 +> 仅当账户 `status=Active` 时可点。Frozen / Closed 状态下灰化。详见 [[account-state-machine]] 的 `canDeposit()` 守护。 + +### 第 3 步:Modal 表单 + +| 字段 | 填什么 | +|---|---| +| 缴款金额 | ¥2,000 | +| 支付方式 | 现金 / 微信 / POS / 银行转账 | +| 收款银行账户 | 微信/POS/转账选对应银行 | +| 备注 | 选填,如 "动承重墙加押" | + +### 第 4 步:提交 + +系统调用业务层 `DepositIntoAccountAction`,在事务内: + +1. 校验账户 `canDeposit()`(Active only) +2. 建 `CollectionOrder`(`actual_amount=+2000`,`status=Completed`) +3. 在既有账户上加 `DepositTransaction`(`type=deposit`,`amount=2000`,`balance_before=3000`,`balance_after=5000`,关联 CO) +4. 更新 `DepositAccount.balance` = 5000 +5. 触发 `CollectionOrderCompleted` → Listener 自动建 Receipt + +### 第 5 步:给收据 + +后台找到新生成的 Receipt(只对应这一笔追加),交给业户。 + +## 系统流程 + +```mermaid +sequenceDiagram + participant 业户 + participant 前台 + participant Filament + participant DepositIntoAccountAction + participant 数据库 + participant 监听器 + + 业户->>前台: 给押金账户加 2000 + 前台->>Filament: ViewDepositAccount → DepositAction(modal) + Filament->>DepositIntoAccountAction: handle(account, 2000, paymentChannel) + DepositIntoAccountAction->>DepositIntoAccountAction: canDeposit() ? (Active=true) + DepositIntoAccountAction->>数据库: 开启事务 + DepositIntoAccountAction->>数据库: 1. 建 CollectionOrder (+2000, Completed) + DepositIntoAccountAction->>数据库: 2. 建 DepositTransaction (deposit, 3000→5000) + DepositIntoAccountAction->>数据库: 3. 更新 DepositAccount.balance=5000 + DepositIntoAccountAction->>监听器: 4. 触发 CollectionOrderCompleted + 监听器->>数据库: 5. 建 Receipt (装修保证金缴纳 ¥2,000) + DepositIntoAccountAction->>数据库: 提交事务 + Filament-->>前台: 成功通知 + 刷新余额显示 + 前台->>业户: 给新收据 +``` + +> [!info] 一处代码,两处入口 +> `DepositIntoAccountAction` 是**两处调用方共用**的业务 Action: +> - 本场景(追加缴款):从 `ViewDepositAccount` → `DepositAction` 触发 +> - [[deposit-first-time-renovation|首次开户]]:从 `CreateDepositAccount` 表单触发 +> +> 未来若小程序在线缴款,也复用同一份。逻辑只写一遍。 + +## 常见问题 + +> [!question] Frozen 账户能追加缴款吗? +> **不能**。`canDeposit()` 只允许 `Active`。Frozen 状态下按钮灰化,直接调 Action 会抛 `RuntimeException`。 +> +> 这条规则比直觉更严:看上去"多存钱不亏",但实际风险是纠纷期间装修公司继续往受冻结账户灌钱,资金被困、责任更复杂。详见 [[account-state-machine]] "关键守护" 段。 + +> [!question] Closed 账户呢? +> 同样**不能**。Closed 是永久终态,新业务请[[deposit-first-time-renovation|开新账户]]。 + +> [!question] 多次追加缴款,余额怎么算? +> 每笔 `DepositTransaction` 都有 `balance_before` 和 `balance_after`,账户 `balance` 字段也实时更新。任何时刻业户问"我现在有多少押金",看 `DepositAccount.balance` 即可。审计可重算 `SUM(deposit) - SUM(refund) - SUM(forfeiture)` 校验自洽。 + +> [!question] 追加缴款的收据是单独一张还是合并到原收据? +> **单独一张**。每笔 `DepositTransaction` 对应一张独立的 `Receipt`。这与"原单上挂退款标记"的反模式相反,详见 [[red-receipt-design]] "为什么不在原 CollectionOrder 上挂退款标记" 段。 + +> [!question] 业户能在小程序自助追加缴款吗? +> 待补。当前 Action 假设手工操作(前台触发)。未来加小程序在线支付时,前端调同一个 `DepositIntoAccountAction`,只需多加一个支付回调入口。 + +## 相关文档 + +- [[deposit-first-time-renovation]] +- [[account-state-machine]] +- [[deposit-account-vs-transaction]] +- [[transaction-types]] +- [[exception-deposit-on-frozen]] diff --git a/prop-acc/scenarios/deposit/deposit-first-time-renovation.md b/prop-acc/scenarios/deposit/deposit-first-time-renovation.md new file mode 100644 index 0000000..d31b244 --- /dev/null +++ b/prop-acc/scenarios/deposit/deposit-first-time-renovation.md @@ -0,0 +1,165 @@ +--- +title: prop-acc · deposit · 场景 - 装修业户首次缴 5000 保证金 +aliases: + - 首次缴装修保证金 + - deposit-first-time-renovation + - 场景-装修业户首次缴保证金 +tags: + - 场景 + - prop-acc + - 保证金 + - 缴纳 +audience: + - 业户 + - 业务人员 +status: 已发布 +sub_feature: deposit +last_review: 2026-05-25 +code_version: 2026-05-22 +--- + +# 场景:装修业户首次缴 5,000 保证金 + +最常见的保证金场景。业户准备装修,**第一次**给物业交装修押金,系统**当场建账户 + 收钱 + 出收据**。 + +## 典型情境 + +> [!example] 真实情境 +> 张阿姨,72 岁,儿子要装修她家(12-3-501)。装修前一天她带着身份证、房产证、装修施工合同来物业前台,要交 ¥5,000 装修保证金。 + +## 业户视角(您要做什么) + +### 第 1 步:到前台带齐材料 + +- 身份证(或户口本) +- 房产证(或租赁合同 + 业主授权书) +- 装修施工合同(物业要存档,确认装修方信息) + +### 第 2 步:告诉职员 + +"我家要装修了,来交装修保证金,房号 12-3-501。" + +### 第 3 步:付款 + +职员告诉您**多少钱**(常见 ¥3,000-10,000,各小区不同),您可选: + +- **现金** +- **微信扫码** +- **POS 刷卡** +- **银行转账**(需打印银行凭证) + +### 第 4 步:拿收据 + +- 纸质收据:**当场打印** +- 电子收据:发到您的微信或邮箱 + +> [!success] 完成 +> 整个过程 10 分钟内搞定。收据写"装修保证金缴纳 ¥5,000",妥善保管,装修结束时退押凭这个。 + +## 业务人员视角(物业职员怎么操作) + +> [!info] 关键差异 +> 这是**首次**开户 + 缴款。之后追加缴款走 [[deposit-additional-topup]],流程更简短。 + +### 第 1 步:打开后台 + +登录 Filament 后台 → 保证金 → **新建账户**(`CreateDepositAccount` 页面)。 + +### 第 2 步:填表单(账户信息) + +| 字段 | 填什么 | +|---|---| +| **缴款人类型(`payer_type`)** | 选 `Owner`(业主)/ `Tenant`(租户)/ `Contractor`(承包商)等;详见 [[payer-types]] | +| **缴款人姓名(`payer_name`)** | "张某某" | +| **缴款人联系方式(`payer_contact`)** | 张阿姨手机号 | +| **业户档案(`community_user_profile_id`)** | 通过房号 12-3-501 找到张阿姨绑定的业户档案(Owner / Tenant 类型必填) | +| **房屋单元(`asset_id`)** | 12-3-501 房屋资产 | +| **押金种类(`fee_type_id`)** | "装修保证金"(本物业项目预先配置的 FeeType) | +| **备注(`remark`)** | 选填,如 "2026 年春季装修,施工方:王装修有限公司" | + +### 第 3 步:填首次缴款信息 + +`CreateDepositAccount` 页面下半部分有"首次缴款"段,**和开户一起一气呵成**: + +| 字段 | 填什么 | +|---|---| +| **缴款金额** | ¥5,000 | +| **支付方式(PaymentChannel)** | 现金 / 微信 / POS / 银行转账 | +| **收款银行账户** | 微信/POS/转账选对应银行;现金可空 | +| **缴款备注** | 选填,如 "首次开户缴款" | + +### 第 4 步:提交 + +点击"创建"。系统在一个数据库事务内完成: + +1. 建 `DepositAccount`(`status=Active`,`balance=5000`) +2. 建 `CollectionOrder`(`actual_amount=+5000`,`status=Completed`) +3. 建 `DepositTransaction`(`type=deposit`,`amount=5000`,`balance_before=0`,`balance_after=5000`,关联 CO) +4. 触发 `CollectionOrderCompleted` 事件 +5. Listener 自动建 `Receipt` + `ReceiptItem`,文案"装修保证金缴纳 ¥5,000" + +### 第 5 步:打印 / 发收据 + +- 后台收据列表找到刚生成的 Receipt → 点击"打印"或"发送" +- 把纸质收据交给张阿姨,或直接微信发她 + +## 系统流程 + +```mermaid +sequenceDiagram + participant 业户 + participant 前台 + participant Filament + participant 数据库 + participant 监听器 + + 业户->>前台: 我要交装修保证金 5000(现金) + 前台->>Filament: CreateDepositAccount 填表 + 提交 + Filament->>数据库: 开启事务 + Filament->>数据库: 1. 建 DepositAccount (Active, balance=5000) + Filament->>数据库: 2. 建 CollectionOrder (actual=+5000, Completed) + Filament->>数据库: 3. 建 DepositTransaction (deposit, balance 0→5000) + Filament->>监听器: 4. 触发 CollectionOrderCompleted + 监听器->>数据库: 5. 建 Receipt + ReceiptItem ("装修保证金缴纳 ¥5,000") + Filament->>数据库: 提交事务 + Filament-->>前台: 成功通知 + 显示新账户 + 前台->>业户: 给收据 +``` + +> [!info] 一气呵成 +> 5 个数据库写入在同一事务里。任何一步失败,整笔回滚 —— 不会出现"账户建了但钱没记""收据生成了但流水没建"的半成品状态。 + +## 常见问题 + +> [!question] 业户没带身份证 / 房产证可以办吗? +> 各物业政策不同,系统不强制证件检查。属于物业内部审核流程。 + +> [!question] 装修方是装修公司不是业户,该怎么开? +> 走 [[deposit-on-behalf-by-company]] 流程:`payer_type=Company`,不绑业户档案,只记公司名 + 联系人。 + +> [!question] 业户没带够现金可以拆分付款吗? +> 单笔 `CollectionOrder` 只支持一种支付方式。可以**先开账户缴一部分**,过几天 [[deposit-additional-topup|追加缴款]] 补齐。 + +> [!question] 录错金额(把 ¥5,000 录成 ¥50,000)怎么办? +> 不要试图改流水,流水是不可变的。正确做法:建一笔 `Refund` 退还多收的差额,业户拿到一张红字收据。详见 [[transaction-types]] "真实修正场景示例" 段。 + +> [!question] 这个账户绑了张阿姨,但实际付钱的是她儿子,可以吗? +> 系统只看 `payer_*` 字段。如果想严格反映"儿子代付",可在 `payer_name` 写儿子名 + `community_user_profile_id` 仍绑张阿姨;退款时收据上文案会按 `payer_name` 显示。或者继续用张阿姨名,备注里说明。 + +> [!question] 同一业户能开多个押金账户吗? +> 可以,只要 `(community_id, fee_type_id, payer_type, asset_id)` 组合不同。例如张阿姨可以同时有"装修保证金"和"入驻押金"两个账户,各自独立。 + +## 异常分支 + +- 录错金额想撤回 → [[refund-partial-after-forfeit]] 或 [[refund-full-no-damage]] +- 装修过程出问题(损坏 / 违约)→ [[forfeit-damage-public-area]] / [[forfeit-violation-no-permit]] +- 业户和物业有纠纷暂时冻结 → [[freeze-during-dispute]] +- 已开账户后续追加 → [[deposit-additional-topup]] + +## 相关文档 + +- [[deposit-account-vs-transaction]] +- [[account-state-machine]] +- [[payer-types]] +- [[refund-full-no-damage]] +- [[deposit-additional-topup]] diff --git a/prop-acc/scenarios/deposit/deposit-on-behalf-by-company.md b/prop-acc/scenarios/deposit/deposit-on-behalf-by-company.md new file mode 100644 index 0000000..be9bebe --- /dev/null +++ b/prop-acc/scenarios/deposit/deposit-on-behalf-by-company.md @@ -0,0 +1,163 @@ +--- +title: prop-acc · deposit · 场景 - 装修公司代业户缴纳 +aliases: + - 装修公司代缴 + - 三方账户开户 + - deposit-on-behalf-by-company + - 场景-装修公司代缴 +tags: + - 场景 + - prop-acc + - 保证金 + - 缴纳 + - 三方账户 +audience: + - 业户 + - 业务人员 +status: 已发布 +sub_feature: deposit +last_review: 2026-05-25 +code_version: 2026-05-22 +--- + +# 场景:装修公司代业户缴纳 + +装修公司本月承接小区**多户业主**装修,公司账户**一次性垫付**多张保证金。每户单独开账户,缴款人记公司而非业主。 + +## 典型情境 + +> [!example] 真实情境 +> 王老板的"王装修有限公司"本月在小区承接 **3 户业主**(12-3-501 / 12-3-502 / 12-3-503)的装修。三户业主都让王老板帮忙付保证金,王老板直接刷公司对公账户一笔转 ¥15,000 到物业。物业要为这 3 户分别开 3 个押金账户。 + +## 业户视角(装修公司王老板) + +### 第 1 步:到前台带齐材料 + +- 营业执照(公司主体证明) +- 三户业主的装修施工合同(每户一份) +- 三户业主的授权书(允许王装修代缴押金) +- 三户房产证复印件 +- 公司对公账户银行卡(用于退款时回款) + +### 第 2 步:告诉职员 + +> "我帮 501、502、503 三户业主交装修保证金,每户 ¥5,000,从我公司账户一笔转 ¥15,000 过来。" + +### 第 3 步:对公转账 + +把 ¥15,000 转到物业指定收款账户(转账备注写"3 户装修押金代缴")。 + +### 第 4 步:拿 3 张收据 + +物业为每户单独建账户、单独出收据。您拿到: + +- 收据 1:"装修保证金缴纳 ¥5,000(12-3-501,业主李某某)" +- 收据 2:"装修保证金缴纳 ¥5,000(12-3-502,业主陈某某)" +- 收据 3:"装修保证金缴纳 ¥5,000(12-3-503,业主刘某某)" + +每张收据"缴款人"写**王装修有限公司**(您),但"关联业户"写对应业主。 + +> [!warning] 退款回哪 +> 退款时**退到您公司对公账户**(因为缴款人是您),不是退给业主。三户结清后您拿到 3 张红字退款收据,自己再跟业主结清。 + +## 业务人员视角(物业职员) + +> [!info] 关键差异 +> 与 [[deposit-first-time-renovation|业主自缴]]的最大不同: +> - `payer_type = Company`(不是 Owner) +> - **不绑业户档案**(`community_user_profile_id = NULL`) +> - 用 `payer_name` + `payer_contact` 记公司名 + 联系人 +> - 但**仍绑业户档案到 `community_user_profile_id` 是允许的** —— 若你想精确表达"代缴的对象是哪位业主",可以填业主的 profile id,系统不强制三方账户为空 + +### 第 1 步:为每户开账户(3 个) + +每户单独打开 `CreateDepositAccount`,填: + +| 字段 | 12-3-501 | 12-3-502 | 12-3-503 | +|---|---|---|---| +| `payer_type` | `Company` | `Company` | `Company` | +| `payer_name` | "王装修有限公司" | "王装修有限公司" | "王装修有限公司" | +| `payer_contact` | 王老板手机 | 王老板手机 | 王老板手机 | +| `community_user_profile_id` | 业主李某某(可选,推荐填) | 业主陈某某(可选) | 业主刘某某(可选) | +| `asset_id` | 12-3-501 房屋 | 12-3-502 房屋 | 12-3-503 房屋 | +| `fee_type_id` | 装修保证金 | 装修保证金 | 装修保证金 | +| 首次缴款金额 | ¥5,000 | ¥5,000 | ¥5,000 | +| 支付方式 | 银行转账 | 银行转账 | 银行转账 | +| 收款银行账户 | 物业指定收款账户 | 同上 | 同上 | +| 备注 | "王装修代缴" | "王装修代缴" | "王装修代缴" | + +提交 3 次,生成 3 个独立的 `DepositAccount` + 3 张 Receipt。 + +### 第 2 步:核对对公转账记录 + +物业会计核对银行流水:王装修一笔转入 ¥15,000 == 3 张 Receipt 合计 ¥15,000 ✅。 + +### 第 3 步:把 3 张收据交给王老板 + +> [!tip] 不要把收据交给业主 +> 业主交的不是这笔钱,收据上"缴款人"是公司。给业主反而会让退款时找钱方向混乱。 + +## 系统视角:为什么不能一个账户装多户 + +> [!warning] 反模式 +> 看起来"王装修一个账户记 ¥15,000 余额"很简洁,但**会出大问题**: +> +> 1. **退款时谁的钱是谁的算不清** —— 501 装修完先退,要从 ¥15,000 里退 ¥5,000,剩 ¥10,000 是哪两户的? +> 2. **每户的扣罚责任无法独立** —— 502 砸了公共墙,要从 502 的 ¥5,000 里扣,但账户里钱已经混了 +> 3. **业主侧无法查询自己的押金状态** —— 业主在小程序看"我的押金",看不到自己 ¥5,000,只看到一个公司账户 +> +> 所以即使是"一笔转账",**也必须开 N 个独立账户**,每个对应一户。系统设计的"账户 + 流水"模式天然支持这种细颗粒度,详见 [[deposit-account-vs-transaction]]。 + +## 系统流程 + +```mermaid +sequenceDiagram + participant 王装修 + participant 前台 + participant Filament + participant 数据库 + participant 监听器 + + 王装修->>前台: 我帮 3 户业主交押金,公司对公转 15000 + Note over 前台: 核对转账已到账 + + loop 3 次(每户一次) + 前台->>Filament: CreateDepositAccount (payer=Company, asset=N号房, amount=5000) + Filament->>数据库: 建 DepositAccount + CO (+5000) + DepositTransaction + Filament->>监听器: 触发 CollectionOrderCompleted + 监听器->>数据库: 建 Receipt ("装修保证金缴纳 ¥5,000") + end + + 前台->>王装修: 给 3 张收据 +``` + +## 常见问题 + +> [!question] 装修公司账户和业主账户在退款时差异? +> 业务上差异最大的是"退给谁": +> - Owner / Tenant 账户:退给业主本人(通常原路退,看缴款渠道) +> - Company / Contractor 账户:退给公司 / 承包商(需手工指定回款渠道) +> +> 详见 [[refund-with-payment-channel-switch]]。 + +> [!question] 装修公司倒闭了,押金怎么办? +> 走 [[force-close-retain]] 强制关账并保留资金,等待法律程序确定归属。 + +> [!question] 装修公司转包给装修队,业主想押金挂装修队怎么办? +> 重新开账户,`payer_type=Contractor`,`payer_name=` 装修队负责人。原公司账户走 [[refund-full-no-damage]] 退款给装修公司,装修队再缴一笔新账户。**不要试图改账户的 payer 字段** —— 缴款人变更等于换主体,审计无法追溯。 + +> [!question] 三户分账户开比一户一户开慢,有批量功能吗? +> 当前没有批量。3 户开 3 次就行,每次 ~3 分钟。如果业务上常发(装修公司频繁代缴 10+ 户)再加批量入口。 + +## 异常分支 + +- 装修公司不还业主对应押金 → 业务层面问题,系统不解决(账面退款已退给公司,后续是公司与业主民事关系) +- 业主提出"装修公司不给退我的押金,物业能直接退给我吗" → 不能。账面缴款人是公司,退款只能退公司。如有纠纷走 [[freeze-during-dispute]] 冻结 + 调解 + +## 相关文档 + +- [[deposit-first-time-renovation]] +- [[payer-types]] +- [[deposit-account-vs-transaction]] +- [[refund-with-payment-channel-switch]] +- [[force-close-retain]] diff --git a/prop-acc/scenarios/deposit/refund-full-no-damage.md b/prop-acc/scenarios/deposit/refund-full-no-damage.md new file mode 100644 index 0000000..0d493d7 --- /dev/null +++ b/prop-acc/scenarios/deposit/refund-full-no-damage.md @@ -0,0 +1,182 @@ +--- +title: prop-acc · deposit · 场景 - 装修完无损全额退还 +aliases: + - 全额退还押金 + - 无损装修退押 + - refund-full-no-damage + - 场景-押金全额退还 +tags: + - 场景 + - prop-acc + - 保证金 + - 退款 +audience: + - 业户 + - 业务人员 +status: 已发布 +sub_feature: deposit +last_review: 2026-05-25 +code_version: 2026-05-22 +--- + +# 场景:装修完无损全额退还 + +最常见、最理想的退押场景。业户装修完物业验收**无损坏无违约**,押金原额退还,账户自动关闭。 + +## 典型情境 + +> [!example] 真实情境 +> 张阿姨家装修完了(花了 6 周),物业例行验收公共走道、电梯、楼栋出入口,**没有任何损坏**。张阿姨当初交了 ¥5,000 装修保证金,现在物业要全额退给她。 + +## 业户视角 + +### 第 1 步:装修完通知物业验收 + +- 微信通知物业管家 / 直接到前台说"我家装修完了,来验收一下退押金" +- 通常物业 2-3 个工作日内派人到现场 + +### 第 2 步:验收无损,等退款 + +- 验收员检查公共区域、电梯、楼道、出入口 +- 无损 → 出验收报告"无损,准予退押" +- 物业财务凭报告办理退款 + +### 第 3 步:收到退款 + +退款方式取决于您当初**怎么交的押金**: + +| 当初缴款方式 | 退款方式 | +|---|---| +| 微信扫码 | 退微信 / 银行卡 | +| 现金 | 通常退到您银行卡(物业转账)| +| 银行转账 | 原账户回款 | +| POS 刷卡 | 原卡原路退 | + +> [!info] 一两个工作日内到账 +> 退款不是即时,物业财务批量处理(通常隔日)。 + +### 第 4 步:收到红字收据 + +您会收到一张**红字收据**: +> "装修保证金退还 ¥-5,000" + +- 金额前的负号表示"退款" +- 这是中国会计的"红字凭证"标准写法 +- 妥善保管,可作税务凭证 + +> [!success] 完成 +> 账户自动关闭,不会再有变动。短信/微信通知:"您的押金账户已结清,余额 0"。 + +## 业务人员视角(物业财务) + +### 第 1 步:核对验收报告 + +确认装修完验收通过、无损坏、无未结违约。 + +### 第 2 步:打开账户 + +后台 → 保证金 → 账户列表 → 找到张阿姨的账户(Active,balance=5000)→ 点击进入 `ViewDepositAccount`。 + +### 第 3 步:点击 `RefundAction`(标签"退款") + +右上角状态管理组里。Modal 表单: + +| 字段 | 填什么 | +|---|---| +| **退款金额** | ¥5,000(默认带入当前余额,可改) | +| **退款渠道(PaymentChannel)** | 选张阿姨指定的回款方式(微信 / 银行转账 / POS) | +| **退款备注** | 选填,如 "装修验收无损,全额退还" | + +> [!warning] 按钮可见性 +> `RefundAction` 加了 `->authorize('refund')` Policy 守护:`update` 权限 + `canWithdraw()` 状态(Active only)。Frozen / Closed 账户该按钮灰化。 + +### 第 4 步:提交 + +系统调 `RefundFromDepositAccountAction`,事务内: + +1. 校验账户 `canWithdraw()`(Active only) +2. 校验金额 ≤ 当前余额 +3. 建 `CollectionOrder`(`actual_amount=-5000` 红字,`status=Completed`) +4. 加 `DepositTransaction`(`type=refund`,`amount=5000`,`balance_before=5000`,`balance_after=0`,关联红字 CO) +5. 更新 `DepositAccount.balance=0` +6. **余额清零自动关账** → 账户 `status=Closed` +7. 触发 `CollectionOrderCompleted` → Listener 建红字 Receipt"装修保证金退还 ¥-5,000" + +### 第 5 步:走线下退款流程 + +- 微信 / POS 退款:在对应支付网关后台操作 +- 银行转账:导出回款指令交银行 +- 现金退款(罕见):张阿姨到前台领现金 + +### 第 6 步:把红字收据交给张阿姨 + +后台找到红字 Receipt → 打印 / 发微信。 + +## 系统流程 + +```mermaid +sequenceDiagram + participant 业户 + participant 财务 + participant Filament + participant RefundFromDepositAccountAction + participant 数据库 + participant 监听器 + + 业户->>财务: 装修完了,验收无损,退押金 + 财务->>Filament: ViewDepositAccount → RefundAction(modal) + Filament->>RefundFromDepositAccountAction: handle(account, 5000, channel) + RefundFromDepositAccountAction->>RefundFromDepositAccountAction: canWithdraw()? (Active=true) + RefundFromDepositAccountAction->>RefundFromDepositAccountAction: amount ≤ balance? (5000≤5000) + RefundFromDepositAccountAction->>数据库: 开启事务 + RefundFromDepositAccountAction->>数据库: 1. 建 CollectionOrder (-5000 红字, Completed) + RefundFromDepositAccountAction->>数据库: 2. 建 DepositTransaction (refund, 5000→0) + RefundFromDepositAccountAction->>数据库: 3. balance=0,自动 status=Closed + RefundFromDepositAccountAction->>监听器: 4. 触发 CollectionOrderCompleted + 监听器->>数据库: 5. 建 Receipt (装修保证金退还 ¥-5,000 红字) + RefundFromDepositAccountAction->>数据库: 提交事务 + Filament-->>财务: 成功通知 + 财务-->>业户: 银行/微信退款 + 红字收据 +``` + +> [!info] 自动关账 +> 余额清零时,系统自动把账户翻成 `Closed`,不需要再单独走一次 [[close-after-zero-balance|关账]] 操作。 + +## 常见问题 + +> [!question] 业户当初付现金,退款必须退现金吗? +> 不强制。退款时选 `PaymentChannel` 可以与缴款时不同。常见做法是退到业户银行卡,详见 [[refund-with-payment-channel-switch]]。 + +> [!question] 退一半就行可以吗? +> 可以,Modal 表单的"退款金额"字段可以改成任意 ≤ 余额的数字。剩余款还在账户里,账户保持 Active。后续可继续退或扣或关。 + +> [!question] 退款后红字收据上的"-"是什么意思? +> 中国会计的"红字凭证"。负数表示资金方向是"从物业流出 / 回到业户",与缴款时的"+"对称。详见 [[red-receipt-design]]。 + +> [!question] 业户拿到红字收据后觉得"扣了我的钱"? +> 给业户解释: +> - "-5,000" 不是"扣 5,000",是"退还 5,000" +> - 黑字(无负号)是物业收到的钱,红字(有负号)是物业退给您的钱 +> - 这是会计标准做法 + +> [!question] 退款失败(银行退回 / 微信渠道异常)怎么办? +> 当前系统**不处理这种回调**。物业财务需要在线下沟通业户,重新指定退款渠道,然后: +> - 如果账户已关:开新账户(不推荐)或 tinker 改流水(运维介入) +> - 推荐:**业务流程上避免** —— 退款前与业户确认渠道有效 + +> [!question] 已关账户能反悔吗? +> 不能。`canBeReopened()` 永远 false。详见 [[account-state-machine]] "为什么不允许 Reopen" 段。如果业户反悔继续装修要再交押金,**开新账户**。 + +## 异常分支 + +- 有损坏 → 先扣 [[forfeit-damage-public-area]],再走 [[refund-partial-after-forfeit]] +- 退款渠道与缴款不同 → [[refund-with-payment-channel-switch]] +- 退款时账户 Frozen → 走 [[force-close-refund]] 强制关并退 + +## 相关文档 + +- [[deposit-account-vs-transaction]] +- [[account-state-machine]] +- [[red-receipt-design]] +- [[refund-partial-after-forfeit]] +- [[close-after-zero-balance]]