vault backup: 2026-05-26 01:03:15
This commit is contained in:
257
prop-acc/scenarios/billing/collect-payment-batch.md
Normal file
257
prop-acc/scenarios/billing/collect-payment-batch.md
Normal file
@@ -0,0 +1,257 @@
|
||||
---
|
||||
title: prop-acc · billing · 场景 - 同业户多账单批量收款
|
||||
aliases:
|
||||
- 批量收款
|
||||
- BatchCollectPaymentAction
|
||||
- 一次付多张
|
||||
- collect-payment-batch
|
||||
- 场景-同业户批量收款
|
||||
tags:
|
||||
- 场景
|
||||
- prop-acc
|
||||
- 账单
|
||||
- 收款
|
||||
audience:
|
||||
- 业户
|
||||
- 业务人员
|
||||
status: 已发布
|
||||
sub_feature: billing
|
||||
last_review: 2026-05-26
|
||||
code_version: 2026-05-22
|
||||
---
|
||||
|
||||
# 场景:同业户多账单批量收款
|
||||
|
||||
业户**本月有多张账单**(物业费 + 水电气费 + 其他),想**一次性付清**。业务人员走 `BatchCollectPaymentAction`,**一笔 CollectionOrder 关联多张 Bill**(走 CollectionOrderBill 多对多)。
|
||||
|
||||
## 典型情境
|
||||
|
||||
> [!example] 真实情境
|
||||
> 张阿姨本月有 4 张账单:
|
||||
>
|
||||
> | 账单 | 金额 |
|
||||
> |---|---|
|
||||
> | 5 月物业费 | ¥800 |
|
||||
> | 5 月水费 | ¥54 |
|
||||
> | 5 月电费 | ¥168 |
|
||||
> | 5 月燃气 | ¥30 |
|
||||
> | **合计** | **¥1,052** |
|
||||
>
|
||||
> 张阿姨到前台:"4 张账单我一次性付清,微信扫码"。业务人员**1 笔操作**完成 4 张账单收款。
|
||||
|
||||
## 业户视角
|
||||
|
||||
### 第 1 步:告诉业务人员要付哪些
|
||||
|
||||
> "我把本月 4 张账单都付了,微信扫码"
|
||||
|
||||
### 第 2 步:确认金额
|
||||
|
||||
业务人员说:"4 张账单合计 ¥1,052,微信扫这个码"。
|
||||
|
||||
### 第 3 步:微信付
|
||||
|
||||
业户扫码 → 输密码 / 指纹 → 付 ¥1,052。
|
||||
|
||||
### 第 4 步:拿收据
|
||||
|
||||
可能是:
|
||||
|
||||
- 一张 Receipt 含 4 行明细(物业费 ¥800 / 水费 ¥54 / ...)
|
||||
- 或 4 张独立 Receipt(每张账单一张)
|
||||
|
||||
具体看实现。**业户体验上前者更好**(一张收据看全部)。
|
||||
|
||||
## 业务人员视角
|
||||
|
||||
### 第 1 步:找业户
|
||||
|
||||
后台 → 业户 → 找到张阿姨 → "她的账单"标签 → 看到 4 张 Unpaid。
|
||||
|
||||
或:后台 → 账单 → 列表 → 过滤业户=张阿姨 + 状态=Unpaid。
|
||||
|
||||
### 第 2 步:选中多张账单
|
||||
|
||||
Table 上勾选 4 张账单 → 顶部 **"批量收款"** 按钮(`BatchCollectPaymentAction`)。
|
||||
|
||||
或:后台 → 业户视图 → "批量收款"(若 UI 支持单业户聚合)。
|
||||
|
||||
### 第 3 步:Modal 表单
|
||||
|
||||
| 字段 | 填什么 |
|
||||
|---|---|
|
||||
| **选中账单数** | 4 张(显示)|
|
||||
| **合计金额** | ¥1,052(自动算)|
|
||||
| **支付方式** | 微信 |
|
||||
| **收款银行账户** | 物业微信账户 |
|
||||
| **备注** | 选填,如 "业户本月全套缴" |
|
||||
|
||||
### 第 4 步:提交
|
||||
|
||||
系统在**一个事务**内:
|
||||
|
||||
1. 校验每张 Bill 可付(`canBePaid()`)
|
||||
2. 建 **1 个 CollectionOrder**(`type=Bill`,`actual_amount=+1052`,`status=Completed`)
|
||||
3. 建 **4 个 CollectionOrderBill**(每张账单一个,各自 `allocated_amount` = 该账单金额)
|
||||
4. 4 张 Bill 各自 `paid_amount = amount`,`status = Paid`
|
||||
5. 触发 `CollectionOrderCompleted` 事件
|
||||
6. Listener 建 Receipt(可能含 4 个 line_items)
|
||||
|
||||
### 第 5 步:给收据
|
||||
|
||||
后台找到 Receipt → 打印 / 微信发。
|
||||
|
||||
## 系统流程
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant 业户
|
||||
participant 业务
|
||||
participant Filament
|
||||
participant Action[BatchCollectPaymentAction]
|
||||
participant DB
|
||||
|
||||
业务->>Filament: BillsList → 选 4 张 → 批量收款
|
||||
Filament->>Action: handle(bills, channel, bank)
|
||||
Action->>Action: 每张 Bill 校验 canBePaid
|
||||
|
||||
Action->>DB: 开启事务
|
||||
Action->>DB: 1. 建 CollectionOrder(+1052, Completed)
|
||||
|
||||
loop 每张 Bill
|
||||
Action->>DB: 2. 建 CollectionOrderBill(allocated=bill.amount)
|
||||
Action->>DB: 3. Bill.paid_amount = amount, status=Paid
|
||||
end
|
||||
|
||||
Action->>Listener: 4. 触发 CollectionOrderCompleted
|
||||
Listener->>DB: 5. 建 Receipt(line_items × 4)
|
||||
Action->>DB: 提交事务
|
||||
|
||||
Filament-->>业务: 成功通知
|
||||
业务-->>业户: 收据(含 4 项明细)
|
||||
```
|
||||
|
||||
## 数据示例
|
||||
|
||||
收款后:
|
||||
|
||||
### CollectionOrder(1 条)
|
||||
|
||||
```
|
||||
id: 67890
|
||||
collection_type: Bill
|
||||
actual_amount: +1052
|
||||
payment_channel: 微信
|
||||
status: Completed
|
||||
meta.fund_source: external
|
||||
```
|
||||
|
||||
### CollectionOrderBill(4 条)
|
||||
|
||||
| collection_order_id | bill_id | allocated_amount |
|
||||
|---|---|---|
|
||||
| 67890 | 物业费 Bill | 800 |
|
||||
| 67890 | 水费 Bill | 54 |
|
||||
| 67890 | 电费 Bill | 168 |
|
||||
| 67890 | 燃气 Bill | 30 |
|
||||
|
||||
### Bill(4 条更新)
|
||||
|
||||
每张 paid_amount = amount,status = Paid。
|
||||
|
||||
### Receipt(1 条,4 行明细)
|
||||
|
||||
```
|
||||
collection_order_id: 67890
|
||||
amount: +1052
|
||||
line_items: [
|
||||
{ 物业费(5月), 800 },
|
||||
{ 水费(5月), 54 },
|
||||
{ 电费(5月), 168 },
|
||||
{ 燃气(5月), 30 },
|
||||
]
|
||||
```
|
||||
|
||||
## 与单张收款的对比
|
||||
|
||||
| 维度 | [[collect-payment-single|单张]] | **批量(本场景)** |
|
||||
|---|---|---|
|
||||
| Modal 选账单 | 1 张(从 ViewBill 进入)| **多张**(Table 勾选)|
|
||||
| CollectionOrder | 1 个 | 1 个(共用)|
|
||||
| CollectionOrderBill | 1 个 | **N 个**(每张一个)|
|
||||
| 业务人员操作 | 单笔 | 一笔 |
|
||||
| 业户体验 | 一张一张付(慢)| 一次付清(快)|
|
||||
|
||||
**批量收款的好处**:业户体验更好(一次付),业务人员工作量更小。**唯一前提**:业户愿意一次付清。
|
||||
|
||||
## 不同支付方式的分摊
|
||||
|
||||
业户支付的钱**自动按账单原金额比例分摊** 到各 Bill。不需要业务人员手动分配。
|
||||
|
||||
例:¥1,052 微信付 → 4 个 CollectionOrderBill 各自 allocated_amount = 该账单 amount(全额分配)。
|
||||
|
||||
### 如果业户付不够(部分批量付)
|
||||
|
||||
业户只想付 ¥600(不够 ¥1,052)→ 业务人员有几种选择:
|
||||
|
||||
| 策略 | 操作 |
|
||||
|---|---|
|
||||
| **优先付物业费**(默认?)| ¥600 全部分配给物业费 Bill(部分付)|
|
||||
| **按比例分摊** | 600 × 800/1052 = 456 给物业费,600 × 54/1052 = 31 给水费, ... |
|
||||
| **业务人员手动决定** | 给业务人员选哪张账单付多少 |
|
||||
|
||||
当前实现的具体策略看代码。**业务上建议优先付到期早的**(避免逾期)。
|
||||
|
||||
> [!info] 批量收款的"部分付"复杂度
|
||||
> 上述场景比单张部分付更复杂。当前 `BatchCollectPaymentAction` 可能**只支持全额批量**(若金额够付所有选中账单 → 全付;不够 → 走单张部分付逐张操作)。看实现。
|
||||
|
||||
## 业务人员视角:Modal 的预检查
|
||||
|
||||
类似 [[smart-bulk-delete-design|智能批删]] 的预检查思路:
|
||||
|
||||
- 选中 4 张账单 → Modal 显示"总计 ¥1,052"
|
||||
- 选中包含已 Paid 的账单 → Modal 显示"4 选 + 1 已付跳过"
|
||||
- 选中包含 Suspended → Modal 提示 "该账单挂起,无法收款"
|
||||
|
||||
> [!info] 当前实现的成熟度
|
||||
> `BatchCollectPaymentAction` 的智能 Modal 程度看实现。可能比 `BulkDeleteBillsAction` 简单(批删有 issue.md Q6 详细设计,批收款未单独描述)。
|
||||
|
||||
## 常见问题
|
||||
|
||||
> [!question] 选中跨业户的多张账单能批量收款吗?
|
||||
> 业务上**不应该**(不同业户付的钱不能混)。系统层面应**校验同业户**,否则拒绝。
|
||||
>
|
||||
> 例外:**家属代付**场景(儿子来付父母账单)→ 业务上算同业户的钱。
|
||||
|
||||
> [!question] 批量收款失败一半怎么办?
|
||||
> 事务内**全成功或全失败**。任一 Bill 校验失败(例如某张已 Paid)→ 整笔回滚 → 业户的钱不被收。
|
||||
>
|
||||
> 实施上可能优化为"部分成功"(只失败的跳过),但破坏事务原子性,通常不推荐。
|
||||
|
||||
> [!question] 业户支付的钱比账单合计**多**?
|
||||
> Modal 守护应限制金额 ≤ 合计。如果业务上业户故意多给:
|
||||
>
|
||||
> - 找零给业户(系统层面只收账单的金额)
|
||||
> - **或转入业户预存款账户**(若有此自动逻辑)
|
||||
|
||||
> [!question] 不同期次的账单能一起付吗?
|
||||
> 可以(账单状态都是 Unpaid 即可)。例如付 4 月物业费 + 5 月物业费 + 5 月水电气。
|
||||
|
||||
> [!question] 批量收款的 activitylog 怎么记?
|
||||
> 一条 CollectionOrder 的 activitylog(event=created)+ 每张 Bill 状态变化的 log。可在 SQL 反查 affected_bill_ids。
|
||||
|
||||
## 异常分支
|
||||
|
||||
- 单张付 → [[collect-payment-single]]
|
||||
- 部分付 → [[exception-partial-payment]]
|
||||
- 预存款抵 → [[collect-via-prepaid-auto]]
|
||||
- 业户全付不起,挑某张付 → 走 [[collect-payment-single]] 逐张
|
||||
- 收错了想撤 → [[void-paid-bill]](已付作废 + 退款)
|
||||
|
||||
## 相关文档
|
||||
|
||||
- [[bill-vs-collection-order]]
|
||||
- [[collect-payment-single]]
|
||||
- [[collect-via-prepaid-auto]]
|
||||
- [[exception-partial-payment]]
|
||||
- [[../prepaid/auto-deduction-design]]
|
||||
Reference in New Issue
Block a user