Files
uniprop-manual/prop-acc/scenarios/billing/collect-payment-batch.md
2026-05-26 01:03:15 +08:00

258 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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]]