259 lines
7.5 KiB
Markdown
259 lines
7.5 KiB
Markdown
---
|
|
title: prop-acc · billing · 场景 - 单张账单收款
|
|
aliases:
|
|
- 单张收款
|
|
- 收款
|
|
- CollectPaymentAction
|
|
- collect-payment-single
|
|
- 场景-单张账单收款
|
|
tags:
|
|
- 场景
|
|
- prop-acc
|
|
- 账单
|
|
- 收款
|
|
audience:
|
|
- 业户
|
|
- 业务人员
|
|
status: 已发布
|
|
sub_feature: billing
|
|
last_review: 2026-05-26
|
|
code_version: 2026-05-22
|
|
---
|
|
|
|
# 场景:单张账单收款
|
|
|
|
业户**单张账单付款**(物业费 / 水费 / 电费的某一张),业务人员后台触发 `CollectPaymentAction`。最基础高频的收款场景。
|
|
|
|
## 典型情境
|
|
|
|
> [!example] 真实情境
|
|
> 张阿姨 5 月物业费 ¥800 账单已生成,她下午到物业前台:
|
|
>
|
|
> - "我交 5 月物业费"
|
|
> - 业务人员小李打开张阿姨账户 → 找到 5 月物业费账单 → 收款
|
|
|
|
## 业户视角
|
|
|
|
### 第 1 步:到前台 / 小程序
|
|
|
|
带:
|
|
|
|
- 钱(现金 / 微信 / POS 卡)
|
|
- 房号 / 姓名(身份证)
|
|
|
|
### 第 2 步:告诉业务人员要付哪张账单
|
|
|
|
> "我交 5 月物业费"
|
|
|
|
业务人员从系统找到对应账单。
|
|
|
|
### 第 3 步:确认金额 + 付款方式
|
|
|
|
业务人员告诉张阿姨:
|
|
|
|
> "您 5 月物业费 ¥800,请选付款方式"
|
|
|
|
| 付款方式 | 操作 |
|
|
|---|---|
|
|
| 现金 | 给钱 → 找零 |
|
|
| 微信扫码 | 业务人员出示物业收款码 → 业户扫 |
|
|
| POS 刷卡 | 业户给银行卡 → POS 机刷 |
|
|
| 银行转账 | 业户给凭证(线下转账已到账)|
|
|
|
|
### 第 4 步:拿收据
|
|
|
|
- 纸质:**当场打印**
|
|
- 电子:发到业户微信 / 邮箱
|
|
|
|
> [!success] 完成
|
|
> 账单状态从 Unpaid → Paid。业户带收据离开,5 分钟内搞定。
|
|
|
|
## 业务人员视角
|
|
|
|
### 第 1 步:找账单
|
|
|
|
后台 → 账单 → 列表 → 按业户姓名 / 房号 / 期次过滤 → 找到张阿姨的 5 月物业费(Unpaid,¥800)→ 进 `ViewBill`。
|
|
|
|
或者:
|
|
|
|
- 后台 → 业户 → 找到张阿姨 → "她的账单"标签 → 看 Unpaid 列表 → 选
|
|
|
|
### 第 2 步:点击 `CollectPaymentAction`(标签"收款")
|
|
|
|
右上角状态管理组。
|
|
|
|
> [!warning] 按钮可见性
|
|
> `CollectPaymentAction` 守护:`canBePaid()`(Unpaid / Partial)+ `->authorize('collect')`。Paid / Void / Suspended / Processing 灰化。
|
|
|
|
Modal 表单:
|
|
|
|
| 字段 | 填什么 |
|
|
|---|---|
|
|
| **收款金额** | ¥800(默认全额,可改为部分)|
|
|
| **支付方式(`payment_channel_id`)** | 现金 / 微信 / POS / 银行转账 |
|
|
| **收款银行账户(`bank_account_id`)** | 微信/POS/转账选对应银行账户;现金可空 |
|
|
| **收款备注** | 选填,如"业户现场付款" |
|
|
|
|
### 第 3 步:提交
|
|
|
|
系统在**一个事务**内:
|
|
|
|
1. 校验 Bill 可付(`canBePaid` = Unpaid / Partial)
|
|
2. 校验金额 ≤ Bill 剩余应付(`amount - paid_amount`)
|
|
3. 建 `CollectionOrder`(`type=Bill`,`actual_amount=+800`,`status=Completed`,`payment_channel`,`meta.fund_source=external`)
|
|
4. 建 `CollectionOrderBill`(`bill_id`,`collection_order_id`,`allocated_amount=800`)
|
|
5. 更新 `Bill.paid_amount += 800`
|
|
6. 更新 `Bill.status`:若 paid_amount = amount → `Paid`;若 < amount → `Partial`
|
|
7. 触发 `CollectionOrderCompleted` 事件
|
|
8. Listener 自动建 `Receipt` + `ReceiptItem`(文案"物业费 ¥800")
|
|
9. 写 activitylog(可选,具体看实现)
|
|
|
|
### 第 4 步:给业户收据
|
|
|
|
后台找到新生成 Receipt → 打印 / 微信发。
|
|
|
|
## 系统流程
|
|
|
|
```mermaid
|
|
sequenceDiagram
|
|
participant 业户
|
|
participant 业务[业务人员]
|
|
participant Filament
|
|
participant Action[CollectPaymentAction]
|
|
participant DB
|
|
participant Listener
|
|
|
|
业户->>业务: 付物业费 800
|
|
业务->>Filament: ViewBill → CollectPaymentAction(modal)
|
|
Filament->>Action: handle(bill, 800, channel, bank)
|
|
Action->>Action: canBePaid()? Unpaid=true
|
|
Action->>Action: 800 ≤ remaining(800)? yes
|
|
|
|
Action->>DB: 开启事务
|
|
Action->>DB: 1. 建 CollectionOrder(type=Bill, +800, Completed)
|
|
Action->>DB: 2. 建 CollectionOrderBill(allocated=800)
|
|
Action->>DB: 3. Bill.paid_amount=800
|
|
Action->>DB: 4. Bill.status=Paid(800=800)
|
|
Action->>Listener: 5. 触发 CollectionOrderCompleted
|
|
Listener->>DB: 6. 建 Receipt("物业费 ¥800")
|
|
Action->>DB: 提交事务
|
|
|
|
Filament-->>业务: 成功通知
|
|
业务-->>业户: 收据
|
|
```
|
|
|
|
## 部分付场景(business 上常见)
|
|
|
|
业户只想付 ¥300(全额 ¥800):
|
|
|
|
```
|
|
Modal 表单:
|
|
- 收款金额:300(手动改)
|
|
- 支付方式:现金
|
|
- 备注:"暂时只能付一部分"
|
|
```
|
|
|
|
提交后:
|
|
|
|
- Bill.paid_amount = 300
|
|
- Bill.status: Unpaid → **Partial**
|
|
- 建 CollectionOrder(+300)+ CollectionOrderBill(allocated=300)
|
|
|
|
业户后续补付 ¥500 → 同样走 `CollectPaymentAction` → 第 2 笔 CollectionOrderBill → Bill 收齐 → Paid。
|
|
|
|
详见 [[exception-partial-payment]]。
|
|
|
|
## 数据示例(完整流水)
|
|
|
|
业户付 ¥800 后:
|
|
|
|
### Bill 表
|
|
|
|
| 字段 | 值 |
|
|
|---|---|
|
|
| `id` | 12345 |
|
|
| `amount` | 800 |
|
|
| `paid_amount` | 800 |
|
|
| `status` | Paid |
|
|
|
|
### CollectionOrderBill 表
|
|
|
|
| 字段 | 值 |
|
|
|---|---|
|
|
| `bill_id` | 12345 |
|
|
| `collection_order_id` | 67890 |
|
|
| `allocated_amount` | 800 |
|
|
|
|
### CollectionOrder 表
|
|
|
|
| 字段 | 值 |
|
|
|---|---|
|
|
| `id` | 67890 |
|
|
| `collection_type` | Bill |
|
|
| `actual_amount` | +800 |
|
|
| `payment_channel_id` | 微信 |
|
|
| `status` | Completed |
|
|
| `meta.fund_source` | external |
|
|
|
|
### Receipt 表
|
|
|
|
| 字段 | 值 |
|
|
|---|---|
|
|
| `collection_order_id` | 67890 |
|
|
| `amount` | +800 |
|
|
| line_items | [{ 物业费(5月), 800 }] |
|
|
|
|
## 常见问题
|
|
|
|
> [!question] 业户付的钱与账单金额不一致(多付 / 少付)?
|
|
> | 情况 | 处置 |
|
|
> |---|---|
|
|
> | 少付 | 走部分付 Partial 状态;后续补付 |
|
|
> | **多付** | **不应该**(Modal 守护 `amount ≤ remaining`)。如果业务上业户给的钱多,**找零给业户**(系统层面只收账单的金额) |
|
|
|
|
> [!question] 业户付错账单(本想付物业费,付到电费)?
|
|
> 业务人员可:
|
|
> - 立即作废这笔 CollectionOrder(走 [[void-paid-bill]] 类似流程)+ 重新对正确账单收款
|
|
> - 或者**留这笔不动**(资金确实到账)+ 业务人员手工调整 CollectionOrderBill 的 allocated_amount(危险,会破坏审计)
|
|
>
|
|
> **推荐第一种**(走作废 + 重收)。
|
|
|
|
> [!question] Frozen Bill 能收款吗?
|
|
> 不能。`canBePaid()` 只允许 Unpaid / Partial。Suspended 状态需先 [[resume-bill|恢复]]。
|
|
|
|
> [!question] 已付的 Bill 能再收款吗?
|
|
> 不能。`canBePaid()` Paid 时返 false。理论上 Bill 已经付清。
|
|
|
|
> [!question] 业户预存款够付,业务人员怎么操作?
|
|
> 看自动 / 手动:
|
|
> - 自动(待补的 [[../prepaid/auto-deduction-design|预存款自动抵扣 job]]):业务人员不操作,系统自动
|
|
> - 手动:业务人员在 `ViewBill` → 走 `CollectPaymentAction` 选"预存款抵扣"(或专用 Action)→ 详见 [[collect-via-prepaid-auto]]
|
|
|
|
> [!question] 收款时 PaymentChannel 写错(选了微信实际是现金)?
|
|
> CollectionOrder 一经创建**通常不可改**字段。错了:
|
|
> - 走作废(详见 [[void-paid-bill]])+ 重新建
|
|
> - 或 tinker 改字段(运维 + 留审计)
|
|
>
|
|
> 预防:Modal 提交前再三确认。
|
|
|
|
> [!question] activitylog 记什么?
|
|
> 详见 [[smart-bulk-delete-design]] activitylog 设计。CollectPayment 通常也记一条 activitylog:event=collected,properties 含 amount / payment_channel / receipt_id。
|
|
|
|
## 异常分支
|
|
|
|
- 部分付场景 → [[exception-partial-payment]]
|
|
- 批量付(多张账单一起付)→ [[collect-payment-batch]]
|
|
- 预存款抵 → [[collect-via-prepaid-auto]]
|
|
- 收款错了想撤 → [[void-paid-bill]]
|
|
- Bill 挂起中无法收 → [[resume-bill]]
|
|
- 逾期账单催收 → [[exception-overdue-bills]]
|
|
|
|
## 相关文档
|
|
|
|
- [[bill-six-state-machine]]
|
|
- [[bill-vs-collection-order]]
|
|
- [[exception-partial-payment]]
|
|
- [[collect-payment-batch]]
|
|
- [[collect-via-prepaid-auto]]
|
|
- [[../adhoc/collection-order-and-receipt]]
|