5.4 KiB
title, aliases, tags, audience, status, sub_feature, last_review, code_version
| title | aliases | tags | audience | status | sub_feature | last_review | code_version | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| prop-acc · deposit · 红字凭证设计 |
|
|
|
已发布 | deposit | 2026-05-25 | 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 档 |
双重表达,信息冗余 |
资金流示意
flowchart TD
subgraph "缴款(蓝字)"
A1[业户缴 5000] --> A2[DepositTransaction<br/>type=deposit, amount=5000]
A1 --> A3[CollectionOrder<br/>actual=+5000]
A3 --> A4[Receipt<br/>amount=+5000<br/>「装修保证金缴纳 ¥5,000」]
end
subgraph "退款(红字)"
B1[业户退 3000] --> B2[DepositTransaction<br/>type=refund, amount=3000]
B1 --> B3[CollectionOrder<br/>actual=-3000 红字]
B3 --> B4[Receipt<br/>amount=-3000 红字<br/>「装修保证金退还 ¥-3,000」]
end
subgraph "扣罚(红字)"
C1[物业扣 2000] --> C2[DepositTransaction<br/>type=forfeiture, amount=2000]
C1 --> C3[CollectionOrder<br/>actual=-2000 红字]
C3 --> C4[Receipt<br/>amount=-2000 红字<br/>「装修保证金扣罚 ¥-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(从原单视角的退款入口) |
已知重叠,未来要统一为单一退款流程 |