diff --git a/.obsidian/workspace.json b/.obsidian/workspace.json
index 93ea41b..4518254 100644
--- a/.obsidian/workspace.json
+++ b/.obsidian/workspace.json
@@ -196,6 +196,12 @@
},
"active": "b06ed69835363258",
"lastOpenFiles": [
+ "prop-acc/concepts/deposit/red-receipt-design.md",
+ "prop-acc/concepts/deposit/transaction-types.md",
+ "prop-acc/concepts/deposit/payer-types.md",
+ "prop-acc/concepts/deposit/account-state-machine.md",
+ "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",
@@ -217,11 +223,6 @@
"prop-acc/scenarios/adhoc/receipt-miniapp-pdf-download.md",
"prop-acc/scenarios/adhoc/exception-paid-but-cannot-deliver.md",
"prop-acc/scenarios/adhoc/exception-payment-split-failure.md",
- "prop-acc/scenarios/adhoc/exception-wechat-callback-delay.md",
- "prop-acc/scenarios/adhoc-audit-ic-card-stock-reconciliation.md",
- "prop-acc/scenarios/adhoc/exception-cross-community-pending.md",
- "prop-acc/scenarios/adhoc/exception-duplicate-order.md",
- "prop-acc/scenarios/adhoc/void-after-payment.md",
"prop-acc/scenarios/adhoc",
"prop-acc/concepts/adhoc",
"resident-portal/scenarios",
@@ -230,7 +231,6 @@
"resident-portal/maps",
"resident-portal/glossary",
"resident-portal/features",
- "resident-portal/faq",
- "resident-portal/decisions"
+ "resident-portal/faq"
]
}
\ No newline at end of file
diff --git a/prop-acc/concepts/deposit/account-state-machine.md b/prop-acc/concepts/deposit/account-state-machine.md
new file mode 100644
index 0000000..8d5108b
--- /dev/null
+++ b/prop-acc/concepts/deposit/account-state-machine.md
@@ -0,0 +1,133 @@
+---
+title: prop-acc · deposit · 押金账户状态机
+aliases:
+ - 押金账户状态机
+ - DepositAccount 状态机
+ - Active / Frozen / Closed
+tags:
+ - 概念
+ - prop-acc
+ - 保证金
+ - 状态机
+audience:
+ - 业户
+ - 业务人员
+status: 已发布
+sub_feature: deposit
+last_review: 2026-05-25
+code_version: 2026-05-22
+---
+
+# 押金账户状态机
+
+押金账户三种状态:**Active(在押)** / **Frozen(冻结)** / **Closed(已结清)**。
+
+> [!warning] 重要原则
+> 一旦 Closed,永远 Closed。**不允许重开**。新业务一律开新账户。理由见本文末"为什么不允许 reopen"。
+
+## 三状态速查
+
+| 状态 | 中文 | 何时进入 | 能做什么 |
+|---|---|---|---|
+| `Active` | 在押 | 新账户首次缴款后 | 缴款 / 退款 / 扣罚 / 冻结 / 关账(余额 0) |
+| `Frozen` | 冻结 | 发生纠纷、内审等 | 看流水(只读)/ 解冻 / 强制关账 |
+| `Closed` | 已结清 | 余额清零正常关账 OR ForceClose | 看流水(只读),没有任何可写操作 |
+
+## 状态机图
+
+```mermaid
+stateDiagram-v2
+ [*] --> Active : 开户 + 首次缴款
+ Active --> Active : 追加缴款 / 部分退款 / 扣罚
+ Active --> Frozen : freeze() 纠纷/审计
+ Frozen --> Active : unfreeze() 调解完成
+ Active --> Closed : close() 余额=0
+ Frozen --> Closed : forceClose() 强制结账
+ Closed --> [*]
+
+ note right of Frozen
+ 冻结期间禁止任何资金进出
+ canDeposit = false
+ canWithdraw = false
+ end note
+
+ note right of Closed
+ 永久终态
+ canBeReopened = false
+ 新业务请开新账户
+ end note
+```
+
+## 守护方法(代码层)
+
+`DepositAccount` 模型上有一组 `can*()` 守护方法,**所有写入 Action 必须先调用**:
+
+| 方法 | 返回 true 的状态 | 用途 |
+|---|---|---|
+| `canDeposit()` | Active **only** | DepositAction 准入 |
+| `canWithdraw()` | Active **only** | RefundAction / ForfeitureAction 准入 |
+| `canBeFreezed()` | Active | FreezeAction 准入 |
+| `canBeUnfreezed()` | Frozen | UnfreezeAction 准入 |
+| `canBeClosed()` | balance=0 且 ≠Closed | CloseAction 准入 |
+| `canBeReopened()` | **永远 false** | 占位,刻意禁止 |
+| `canOperate()` | Active | 复合判断:既不在 Frozen 也不在 Closed |
+| `hasBalance()` | balance>0 | 配合判定能否 Close / 是否需 ForceClose |
+| `isAvailable()` | Active | UI 显示"可用"或灰化 |
+
+> [!info] 关键守护:Frozen 不允许任何资金动作
+> `canDeposit()` 与 `canWithdraw()` **都只允许 Active**,Frozen 一律拒绝。这条规则比直觉更严:
+>
+> - 原本曾允许 `[Active, Frozen]` 都能缴款(看着"反正多存钱不亏")
+> - 但与"冻结 = 暂停所有交易"的语义矛盾
+> - 真实风险:纠纷期间装修公司继续往受冻结账户灌钱 → 资金被困、责任更复杂
+>
+> 现在两个方向严格一致:Frozen = 完全冻结,只能解冻或 ForceClose。
+
+## 业务人员视角
+
+后台账户列表的"状态"列对应这三个值。
+
+- 看到 `Active`:绿色,可点开操作
+- 看到 `Frozen`:橙色,所有按钮变灰,只剩 `Unfreeze` / `ForceClose`
+- 看到 `Closed`:灰色,完全只读,只能看流水
+
+## 业户视角
+
+业户**通常感受不到状态机**,只感受到结果:
+
+- 余额能正常用 → Active
+- 申请退款被拒,前台告知"账户冻结中,等纠纷处理完才能动" → Frozen
+- 账户被关 → 收到一张红字收据 + 短信告知"您的押金账户已结清"
+
+## 为什么不允许 Reopen
+
+`canBeReopened()` 永远返回 `false`,是**刻意的设计**。
+
+| 假设允许 reopen | 风险 |
+|---|---|
+| Closed → Active 又开放 | 流水台账"已结清"的语义被破坏,审计难追责 |
+| 业务方:"业户搬回来了就 reopen 老账户" | 鼓励混账;两次业务关系应有清晰边界 |
+| 系统级:"误操作 close 了能反悔" | close 已经守护"余额 0",误操作不会丢钱;新业务开新账户即可 |
+
+替代做法:业户搬回来续约,**开新账户**。旧账户保留作为历史台账,与"曾经的业务关系结束"语义一致。
+
+## 异常路径:Frozen + 有余额 + 想关账?
+
+矛盾:
+
+- `Frozen` 不允许 `withdraw`(包括退款 / 扣罚)
+- `canBeClosed()` 要求余额=0
+- 又不能 unfreeze 直接关
+
+这种困境通过 **ForceClose** 解决,见 [[force-close-refund]] / [[force-close-forfeit]] / [[force-close-retain]] 三种 disposition。
+
+ForceClose 是**唯一**能合法从 Frozen 直接到 Closed 的路径,通过专门的 `DepositAccountPolicy::forceClose()` 守护(`update` 权限 + `isFrozen() && hasBalance()`)。
+
+## 相关文档
+
+- [[deposit-account-vs-transaction]]
+- [[transaction-types]]
+- [[freeze-during-dispute]]
+- [[unfreeze-after-mediation]]
+- [[close-after-zero-balance]]
+- [[force-close-refund]]
diff --git a/prop-acc/concepts/deposit/deposit-account-vs-transaction.md b/prop-acc/concepts/deposit/deposit-account-vs-transaction.md
new file mode 100644
index 0000000..bde5c75
--- /dev/null
+++ b/prop-acc/concepts/deposit/deposit-account-vs-transaction.md
@@ -0,0 +1,125 @@
+---
+title: prop-acc · deposit · 押金账户与押金流水
+aliases:
+ - 押金账户与押金流水
+ - DepositAccount 与 DepositTransaction
+ - 押金的双对象模式
+tags:
+ - 概念
+ - prop-acc
+ - 保证金
+ - 核心概念
+audience:
+ - 业户
+ - 业务人员
+status: 已发布
+sub_feature: deposit
+last_review: 2026-05-25
+code_version: 2026-05-22
+---
+
+# 押金账户与押金流水
+
+保证金模块底层是**两个对象**配合的:**账户**(`DepositAccount`)只记**当前状态**,**流水**(`DepositTransaction`)记**每一笔变动**。
+
+## 为什么要两个对象
+
+> [!info] 类比:银行存折
+> - **账户** = 存折封面那一行"当前余额 ¥5,000"
+> - **流水** = 翻开存折每一页:几号存了多少、几号取了多少、每笔的"前余额→后余额"
+
+如果只有账户没有流水,查到余额是 ¥3,000 你不知道**钱从哪里来的、被扣了什么**。审计、业户对账、纠纷复盘全都失据。
+
+如果只有流水没有账户,每次想知道当前余额都要重新累加全部历史,慢且容易脏。
+
+所以两个都要:**账户给你"现在长什么样"**,**流水给你"如何变成现在这样"**。
+
+## 字段速查
+
+### DepositAccount(账户)
+
+| 字段 | 含义 |
+|---|---|
+| `id` | 账户 ID |
+| `community_id` | 所属物业项目 |
+| `fee_type_id` | 押金种类(装修保证金 / 入驻押金 / ...) |
+| `payer_type` | 缴款人类型(详见 [[payer-types]]) |
+| `payer_name` | 缴款人姓名(冗余存,免关联 user 表) |
+| `payer_contact` | 缴款人联系方式 |
+| `community_user_profile_id` | 业户档案 ID(若缴款人是平台业户) |
+| `asset_id` | 关联房屋单元(若与具体房屋相关) |
+| **`balance`** | **当前余额(单一事实来源)** |
+| `status` | 账户状态(详见 [[account-state-machine]]) |
+| `opened_at` | 开户时间 |
+| `meta` | JSON 扩展字段(`force_closed_*` 等审计标记) |
+
+### DepositTransaction(流水)
+
+| 字段 | 含义 |
+|---|---|
+| `id` | 流水 ID |
+| `deposit_account_id` | 归属账户 |
+| `type` | 流水类型(详见 [[transaction-types]]) |
+| `amount` | 本笔金额(正数;退款/扣罚也是正数,方向由 type 表达) |
+| `balance_before` | 本笔之前账户余额 |
+| `balance_after` | 本笔之后账户余额 |
+| `related_collection_order_id` | 关联收款单(deposit / refund / forfeiture 都关联) |
+| `memo` | 备注 |
+| `operated_by` | 操作员 ID |
+| 创建后**不可变** | 一旦生成就只读,任何"撤销"都建新流水反向冲 |
+
+## 两者的契约
+
+**账户.balance 必须等于流水按时间累加的净值**。
+
+```php
+$account->verifyBalance(); // bool,看是否一致
+$account->getBalanceDifference(); // float,差额(0 才对)
+$account->calculateBalanceFromTransactions(); // 现场重算
+```
+
+这是审计的核心校验。日常运行中两者必须一致;若出现不一致只可能是 bug 或人为绕过(已通过 [[account-state-machine]] 守护和 Policy 双重防御)。
+
+## 与一次性收费的 CollectionOrder + Receipt 关系
+
+保证金的每笔流水**也会建一张 CollectionOrder 和 Receipt**(详见 [[collection-order-and-receipt]]),退款 / 扣罚走红字 CollectionOrder(详见 [[red-receipt-design]])。
+
+```mermaid
+flowchart LR
+ A[业户缴款 5000] --> B[DepositTransaction
type=deposit, amount=5000]
+ A --> C[CollectionOrder
actual=5000]
+ C --> D[Receipt
amount=5000]
+ B -.关联.-> C
+
+ E[退款 5000] --> F[DepositTransaction
type=refund, amount=5000]
+ E --> G[CollectionOrder
actual=-5000 红字]
+ G --> H[Receipt
amount=-5000 红字]
+ F -.关联.-> G
+```
+
+为什么不只用 `DepositTransaction`?因为业户要拿到一张可下载、可打印的**收据凭证** —— 那是 `Receipt` 的职责。`DepositTransaction` 是内部台账,`Receipt` 是对外凭证。
+
+## 业户视角(您看到什么)
+
+业户**通常不直接接触账户/流水概念**。您看到的是:
+
+- 物业前台告诉您"您还有 ¥5,000 装修保证金在账"
+- 装修完了物业给您**一张红字收据**:"装修保证金退还 ¥-5,000"
+- 微信小程序"我的账户"里能查到余额和历次变动
+
+底下的两个对象都是后台的事。
+
+## 业务人员视角
+
+后台 → 保证金 → 账户列表,你看到的每行就是一个 `DepositAccount`。点开"查看"进入 `ViewDepositAccount`,右侧的"流水"标签就是该账户的 `DepositTransaction` 列表(只读、按时间倒序)。
+
+所有写入操作(`DepositAction` / `RefundAction` / `ForfeitureAction` 等)都**同时写账户余额 + 流水**,事务内完成。任何只写一边的代码都是 bug。
+
+## 相关文档
+
+- [[account-state-machine]]
+- [[transaction-types]]
+- [[payer-types]]
+- [[red-receipt-design]]
+- [[collection-order-and-receipt]]
+- [[deposit-vs-adhoc-vs-prepaid]]
diff --git a/prop-acc/concepts/deposit/payer-types.md b/prop-acc/concepts/deposit/payer-types.md
new file mode 100644
index 0000000..6c68770
--- /dev/null
+++ b/prop-acc/concepts/deposit/payer-types.md
@@ -0,0 +1,102 @@
+---
+title: prop-acc · deposit · 缴款人类型
+aliases:
+ - 押金缴款人类型
+ - DepositPayerType
+ - 谁交的押金
+tags:
+ - 概念
+ - prop-acc
+ - 保证金
+ - 业务字典
+audience:
+ - 业户
+ - 业务人员
+status: 已发布
+sub_feature: deposit
+last_review: 2026-05-25
+code_version: 2026-05-22
+---
+
+# 缴款人类型(DepositPayerType)
+
+押金账户必须记录**谁交的钱**,因为退款时要原路退回。系统支持 6 种缴款人,比常见物业系统的"业主/租户"二分细得多。
+
+## 6 种类型速查
+
+| 枚举 | 中文 | 典型场景 | 是否绑业户档案 |
+|---|---|---|---|
+| `Owner` | 业主 | 自住业主交装修押金 | 通常绑(`community_user_profile_id`) |
+| `Tenant` | 租户 | 出租房租客装修需要押金 | 通常绑(若租户已注册) |
+| `Contractor` | 装修承包商 | 自然人装修工头交押金 | 通常**不绑**(临时关系) |
+| `Company` | 装修公司 | 法人装修公司交批量押金 | **不绑**(法人非业户) |
+| `Supplier` | 供应商 | 进场施工的供货方押金(罕见) | 不绑 |
+| `Other` | 其他 | 兜底 | 视情况 |
+
+## 为什么不只用"业主/租户"二分
+
+常见物业系统只有"业主"或"租户"两类,会带来这些问题:
+
+> [!warning] 真实坑
+> - 装修公司代多个业户交押金,挂在哪个业户名下都不对
+> - 工头是自然人但不是业户,系统逼着你建假业户
+> - 供应商进场施工要交押金,业户表里根本没他
+
+本系统拆得更细:
+
+- **Owner / Tenant** 是平台业户(`community_user_profile_id` 必有)
+- **Contractor / Company / Supplier / Other** 是"三方"(`community_user_profile_id` 可空,靠 `payer_name` + `payer_contact` 记)
+
+## 业户账户 vs 三方账户
+
+代码层有两个辅助方法:
+
+```php
+$account->isOwnerAccount(); // payer_type == Owner
+$account->isThirdPartyAccount(); // payer_type ∈ {Contractor, Company, Supplier, Other}
+```
+
+差异:
+
+| 维度 | 业户账户 | 三方账户 |
+|---|---|---|
+| `community_user_profile_id` | 必填 | 可空 |
+| 业户能否在小程序查询 | 能(通过业户登录) | 不能(无业户账号) |
+| 退款方式 | 通常原路退回业户绑定支付方式 | 需手工指定退款渠道 |
+| 找人的难度 | 通过业户档案找 | 靠 `payer_contact` 联系 |
+
+## 业户视角(您是哪种)
+
+> [!example] 张阿姨(业主)
+> 自住业主张阿姨家请人装修,自己交了 ¥5,000 押金。
+> → `payer_type = Owner`,账户绑张阿姨的业户档案。退款直接退到她微信。
+
+> [!example] 王装修(装修公司)
+> 王老板的装修公司本月承接小区 3 户业主的装修,公司账户一次性垫付 3 张押金 ¥15,000。
+> → 3 个独立账户,每个 `payer_type = Company`,`payer_name = "王装修有限公司"`。
+> 退款时退到公司对公账户。每户业主对自己那 ¥5,000 知情但不直接拿到。
+
+> [!example] 李工头(承包商)
+> 自然人装修工头,无公司主体,自己出名字交押金。
+> → `payer_type = Contractor`,`payer_name = "李某某"`。退款退给个人。
+
+## 业务人员视角
+
+后台开账户时**第一个必填字段**就是 `payer_type`,选完才决定后续字段是否显示业户档案选择器。
+
+> [!tip] 选错会怎样?
+> `payer_type` 只影响 UI 显示和退款找人的便利性,**不影响**账户能不能用、能不能退。万一开账户时选错,可以联系运维通过 tinker 改字段;不影响资金流水。
+
+## 与其他模块的关系
+
+- **业户(Resident)**:Owner / Tenant 类型必填 `community_user_profile_id`,关联 [[业户]] 跨域概念
+- **房屋单元(Housing Unit)**:可选 `asset_id`,关联 [[房屋单元]] 跨域概念 —— 装修押金通常与具体房屋关联;入驻押金可不绑
+- **费用类型(FeeType)**:必填 `fee_type_id`,区分"装修保证金 / 入驻押金 / 设备押金 / ..."
+
+## 相关文档
+
+- [[deposit-account-vs-transaction]]
+- [[deposit-first-time-renovation]]
+- [[deposit-on-behalf-by-company]]
+- [[业户]]
+- [[房屋单元]]
diff --git a/prop-acc/concepts/deposit/red-receipt-design.md b/prop-acc/concepts/deposit/red-receipt-design.md
new file mode 100644
index 0000000..3903f1d
--- /dev/null
+++ b/prop-acc/concepts/deposit/red-receipt-design.md
@@ -0,0 +1,130 @@
+---
+title: prop-acc · deposit · 红字凭证设计
+aliases:
+ - 红字凭证设计
+ - 退款扣罚的金额正负
+ - 红字 CollectionOrder
+tags:
+ - 概念
+ - prop-acc
+ - 保证金
+ - 架构决策
+audience:
+ - 业务人员
+ - 架构师
+status: 已发布
+sub_feature: deposit
+last_review: 2026-05-25
+code_version: 2026-05-22
+---
+
+# 红字凭证设计
+
+押金的退款和扣罚**也走 `CollectionOrder` + `Receipt` 链路**,通过**金额正负**表达方向 —— 这就是"红字凭证"。本文说明为什么这么设计,以及它对凭证文案、报表、未来扩展的影响。
+
+## 核心结论(一句话)
+
+> [!tip] 本系统采用「金额正负」而非「新增枚举 case」表达退款/扣罚方向。
+> 缴款 `actual_amount = +5000`,退款 `actual_amount = -3000`,扣罚 `actual_amount = -2000`。
+> `Receipt.amount` 同样可负。
+
+## 设计决策表
+
+| 维度 | 金额正负(已采用) | 加 `CollectionType` 枚举 case |
+|---|---|---|
+| **Schema 改动** | 零迁移 — `actual_amount` 是 `decimal` 无 CHECK 约束 | 改 enum + 改 listener match 分支 |
+| **报表聚合** | `SUM(actual_amount)` 一行算净值(收入 - 退款) | 需 `GROUP BY type` 再相减 |
+| **中国会计实践** | ✅「红字凭证」就是负数,会计师熟悉 | 不通用,需另解释 |
+| **未来扩展** | 同模式可直接复用(账单退款 / 预存款退款) | 枚举持续膨胀,每加一个业务加一个 case |
+| **类型分类细粒度** | `DepositTransaction.type` 已有 3 档 | 双重表达,信息冗余 |
+
+## 资金流示意
+
+```mermaid
+flowchart TD
+ subgraph "缴款(蓝字)"
+ A1[业户缴 5000] --> A2[DepositTransaction
type=deposit, amount=5000]
+ A1 --> A3[CollectionOrder
actual=+5000]
+ A3 --> A4[Receipt
amount=+5000
「装修保证金缴纳 ¥5,000」]
+ end
+
+ subgraph "退款(红字)"
+ B1[业户退 3000] --> B2[DepositTransaction
type=refund, amount=3000]
+ B1 --> B3[CollectionOrder
actual=-3000 红字]
+ B3 --> B4[Receipt
amount=-3000 红字
「装修保证金退还 ¥-3,000」]
+ end
+
+ subgraph "扣罚(红字)"
+ C1[物业扣 2000] --> C2[DepositTransaction
type=forfeiture, amount=2000]
+ C1 --> C3[CollectionOrder
actual=-2000 红字]
+ C3 --> C4[Receipt
amount=-2000 红字
「装修保证金扣罚 ¥-2,000」]
+ end
+```
+
+## 凭证文案如何区分退款/扣罚
+
+`Receipt.amount` 都是负数,但**文案不同**,业户一眼能看明白方向。
+
+| `DepositTransaction.type` | Receipt 文案 | 业户感受 |
+|---|---|---|
+| `refund` | 装修保证金退还 ¥-3,000 | 拿回自己的钱 |
+| `forfeiture` | 装修保证金扣罚 ¥-2,000 | 钱被扣了 |
+
+文案由 Listener `generateDepositReceiptItems` 按 `DepositTransaction.type` 选词,自动注入 Receipt 的 line items。
+
+> [!info] 未来增强:PDF 红色字样
+> Receipt PDF 模板对负数金额**当前是普通样式**,优先级排期低。可加红字效果(中国习惯)等业务方反馈再上。
+
+## CollectionStatus 仍用 `Completed` 而不是 `Disbursed`
+
+退款的 CollectionOrder 同样进入 `Completed` 状态,**不新增**"Disbursed(已支出)"枚举值。
+
+理由:
+
+- `Completed` 语义 = **"事务完成"**,不局限"收到钱"
+- 不加新 case 减少枚举膨胀,符合"最简单实用"
+- 报表里"已完成的 CO" `SUM(actual_amount)` 就是净值,无需关心方向
+
+`refund_status` 字段也**保持 `None`**,因为退款的 CollectionOrder 是**独立的红字单**,不是"原单的退款标记"。这与传统电商"在原订单上挂退款"模式不同 —— 那种模式适合一次性商品交易,不适合押金的"账户 + 流水"长期账本。
+
+## 为什么不在原 CollectionOrder 上挂退款标记?
+
+替代方案:在缴款单上加 `refund_amount` 和 `refund_status`,退款时改这两个字段。
+
+为什么我们没选:
+
+| 问题 | 后果 |
+|---|---|
+| 原单状态会被频繁更新 | 缴款时建立的"已完成快照"被破坏,审计追溯困难 |
+| 多次部分退款怎么办 | 原单要记一个数组?变成事实上的子流水,等于在错位置实现 |
+| 跨期对账 | 4 月缴的 5000 在 6 月退 3000 → 4 月的报表数据会变,跨期数字不稳定 |
+| 与新业务复用 | 账单 / 预存款的退款不能复用此模式,要各自重复实现 |
+
+红字独立单解决以上全部问题:每张单是不可变快照,跨期数字不变,各业务模块通过相同的"红字 CO + Receipt"模式表达退款。
+
+## 与一次性收费(adhoc)的对比
+
+| 维度 | adhoc 一次性收费 | deposit 保证金 |
+|---|---|---|
+| 主对象 | `AdHocEvent` | `DepositAccount` |
+| 流水/凭证 | `CollectionOrder` + `Receipt`(单笔) | `DepositTransaction` + 关联 `CollectionOrder` + `Receipt`(长期账本) |
+| 退款方式 | 走 [[adhoc-void-after-payment]] 作废 + 退款 | **红字 CollectionOrder**(独立单)|
+| 是否进入收入 | ✅ 一次性收入 | ❌ 负债科目(代管款) |
+
+详见 [[deposit-vs-adhoc-vs-prepaid]]。
+
+## 待补 / 已知限制
+
+| 项 | 状态 |
+|---|---|
+| Receipt PDF 红色字体样式 | 待补,优先级低 |
+| 小程序在线申请退款(走支付网关 webhook) | 待补,等业务有需求再加 |
+| `CollectionOrders/Actions/RecordRefundAction`(从原单视角的退款入口) | **已知重叠**,未来要统一为单一退款流程 |
+
+## 相关文档
+
+- [[deposit-account-vs-transaction]]
+- [[transaction-types]]
+- [[collection-order-and-receipt]]
+- [[refund-full-no-damage]]
+- [[forfeit-damage-public-area]]
diff --git a/prop-acc/concepts/deposit/transaction-types.md b/prop-acc/concepts/deposit/transaction-types.md
new file mode 100644
index 0000000..25d2fe3
--- /dev/null
+++ b/prop-acc/concepts/deposit/transaction-types.md
@@ -0,0 +1,121 @@
+---
+title: prop-acc · deposit · 押金流水类型
+aliases:
+ - 押金流水类型
+ - DepositTransactionType
+ - 三种流水
+tags:
+ - 概念
+ - prop-acc
+ - 保证金
+ - 业务字典
+audience:
+ - 业户
+ - 业务人员
+status: 已发布
+sub_feature: deposit
+last_review: 2026-05-25
+code_version: 2026-05-22
+---
+
+# 押金流水类型(DepositTransactionType)
+
+押金账户的所有资金变动**只有 3 种合法流水**:**deposit(存入)** / **refund(退款)** / **forfeiture(扣罚)**。
+
+> [!warning] 第 4 种 `adjustment(调整)`在代码里仍然存在,但**已弃用**。本文末解释为什么。
+
+## 3 种合法流水速查
+
+| 类型 | 中文 | 余额方向 | 触发 Action | 是否建 CollectionOrder | 凭证 Receipt 金额 |
+|---|---|---|---|---|---|
+| `Deposit` | 存入 | + 增加 | [[deposit-first-time-renovation]] / [[deposit-additional-topup]] 等 | ✅ 正数 | 正数(蓝字) |
+| `Refund` | 退款 | − 减少 | [[refund-full-no-damage]] / [[refund-partial-after-forfeit]] | ✅ 负数(红字) | 负数(红字) |
+| `Forfeiture` | 扣罚 | − 减少 | [[forfeit-damage-public-area]] / [[forfeit-violation-no-permit]] | ✅ 负数(红字) | 负数(红字) |
+
+详见 [[red-receipt-design]] 关于"红字凭证"的设计。
+
+## amount 字段一律为正数
+
+不要被"退款扣罚"的方向误导:
+
+- `DepositTransaction.amount` **永远是正数**
+- 方向由 `type` 表达,不由 amount 符号
+- 关联的 `CollectionOrder.actual_amount` 才是负数(红字凭证)
+
+举例(业户首次缴 ¥5,000,后退 ¥3,000,再扣 ¥2,000):
+
+| 流水 ID | type | amount | balance_before | balance_after |
+|---|---|---|---|---|
+| 1 | `Deposit` | 5000 | 0 | 5000 |
+| 2 | `Refund` | 3000 | 5000 | 2000 |
+| 3 | `Forfeiture` | 2000 | 2000 | 0 |
+
+最后账户余额 0,可走 [[close-after-zero-balance]] 关账。
+
+## 三种流水的语义边界
+
+> [!info] Refund vs Forfeiture
+> 都是从账户里减钱,业户都拿到红字凭证。差别在**业务定性**:
+>
+> | 维度 | Refund(退款) | Forfeiture(扣罚) |
+> |---|---|---|
+> | 业户是否应得 | ✅ 是,本来该退回 | ❌ 不,作为违约/损坏赔偿 |
+> | 是否需举证 | 否 | 需(损坏证据、违约事实) |
+> | 凭证表述 | "装修保证金退还 ¥-X" | "装修保证金扣罚 ¥-X" |
+> | 业户感知 | 拿回自己的钱 | 钱被扣了 |
+
+凭证文案由 Listener `generateDepositReceiptItems` 按 `type` 选词,业户拿到 PDF 一眼能看明白方向(详见 [[red-receipt-design]])。
+
+## 为什么 `Adjustment`(调整)弃用
+
+代码里 `DepositTransactionType::Adjustment` 仍存在,但**对应的 Action 已删除**,UI 完全禁用。
+
+### 设计取舍(摘自 issue.md Q3)
+
+| 维度 | 保留 adjustment 的代价 | 不保留的解法 |
+|---|---|---|
+| **审计风险** | 余额修正给前台开后门,任何余额都能"改对" | 任何错误用 deposit + refund/forfeiture 组合补正,**留下完整修正记录** |
+| **责任追溯** | adjustment 没有方向/凭证,事后查不清当时改了什么 | 每笔补正都是合规流水 + 对应凭证 |
+| **业户感知** | 业户不知道余额为什么突然变了 | 业户拿到对应凭证,知道每分钱的来去 |
+
+### 真实修正场景示例
+
+"业户王先生缴款时被收银员录错,把 ¥5,000 录成 ¥50,000(多录 10 倍)。"
+
+**错误做法**(adjustment):
+- 直接把账户余额改成 ¥5,000
+
+**正确做法**:
+- 建一笔 `Refund` ¥45,000(对应红字 CollectionOrder)
+- 备注"录错金额修正"
+- 业户拿到一张红字收据 "装修保证金退还 ¥-45,000",事后审计完全可追
+
+这样虽然多一笔流水,但**每一分钱都有凭证**。
+
+## 业户视角
+
+业户在小程序"我的押金账户"里能看到流水列表,每行就是一笔 `DepositTransaction`:
+
+```
+2026-03-01 +5,000.00 装修保证金缴纳
+2026-04-15 -3,000.00 装修保证金退还
+2026-04-15 -2,000.00 装修保证金扣罚(公共走道墙面损坏)
+ 余额 0.00 ✅ 已结清
+```
+
+## 业务人员视角
+
+后台 → 保证金 → 账户详情 → 流水标签。
+
+- 点击账户上方的"缴纳 / 退款 / 扣罚"按钮分别触发 `DepositAction` / `RefundAction` / `ForfeitureAction`
+- 三个按钮的可见性由 [[account-state-machine]] 守护:Frozen 状态下都灰化
+- 不存在"修改流水""删除流水"按钮 —— 任何错误走补充流水
+
+## 相关文档
+
+- [[deposit-account-vs-transaction]]
+- [[account-state-machine]]
+- [[red-receipt-design]]
+- [[deposit-first-time-renovation]]
+- [[refund-full-no-damage]]
+- [[forfeit-damage-public-area]]