客户预收款、收款业绩、在线支付与合同订单状态逻辑
梳理日期:2026-06-18
项目路径:D:\devonline
重点代码:OrderPaymentController、ReceivablesController、ReceivablesService、ContractController、ContractInfoService
1. 核心概念
1.1 表与模型
| 表/模型 | 作用 | 关键字段 |
|---|---|---|
crm_order_payment / OrderPayment |
在线支付订单、预收款订单、合同收款码订单 | order_status, order_cus_con_status, is_ysk, contract_no, contract_id, receivables_id, bind_ysk_ids, ysk_dd_dept_id, pay_shanghu_jydh |
crm_receivables / Receivables |
真实业绩/收款记录 | status, type, contract_no, receivables_money, is_share, share_master_id, bind_order_ids, trade_no, online_pay_bank |
crm_payorder_contract_receivable / PayOrderContractReceivable |
预收款订单金额与业绩之间的占用/核销明细 | payorder_id, receivables_id, receivables_money, status |
crm_contract / Contract |
合同 | status, receivables_status, receivables_money, bind_ysk_ids, total_money |
crm_online_receives_jilu / OnlineReceivesJiLu |
在线收款码生成时保存的临时业绩草稿 | order_pay_id, status, receivables_money, is_share |
1.2 收款方式
来源:app/Http/BaseConstant.php
| 值 | 含义 |
|---|---|
1 |
微信 |
2 |
支付宝 |
3 |
现金 |
4 |
网银转账 |
5 |
银行汇款 |
6 |
对公扫码 |
7 |
线上 |
8 |
代码里作为“在线支付/预收款订单绑定业绩”的特殊类型使用 |
注意:BaseConstant::PAY_METHODS 只定义到 ONLINE = 7,但收款创建逻辑大量判断 type == 8,代表“使用在线支付订单/预收款订单添加业绩”。
2. 状态字典
2.1 支付订单付款状态:crm_order_payment.order_status
| 值 | 含义 | 代码表现 |
|---|---|---|
0 |
未支付 | 生成收款码时默认值 |
1 |
已支付 | 支付回调 notifyCallBack -> updateOrderPaymentStatus 设置 |
辅助字段:is_invalid = 0 正常,1 作废/失效。生成新二维码前,旧的未支付订单会调用取消二维码并置为失效。
2.2 订单-客户-合同-业绩状态:crm_order_payment.order_cus_con_status
数据库注释最终版本:
| 值 | 含义 | 典型变化 |
|---|---|---|
0 |
初始未付款 | 新建预收款订单时 |
1 |
只付款,尚未绑定合同 | 预收款支付成功后 |
2 |
已绑定合同,合同审批中 | 使用预收款起合同时 |
3 |
合同通过,未绑定业绩 | 合同审批通过后;或业绩驳回后恢复 |
4 |
绑定业绩中 | 添加业绩并选择该订单后 |
5 |
绑定业绩成功 | 业绩财务审批/CRM2 最终通过后 |
6 |
合同审核驳回 | 合同审批驳回后,清空合同关联 |
2.3 是否预收款:crm_order_payment.is_ysk
| 值 | 含义 |
|---|---|
1 |
预收款订单。客户先付款,不一定已有合同 |
2 |
非预收款合同收款订单。基于已签合同直接生成收款码 |
2.4 业绩状态:crm_receivables.status
| 值 | 常量 | 含义 |
|---|---|---|
1 |
WAIT_APPROVAL |
待财务审批 |
2 |
PASS_APPROVAL |
已通过 |
3 |
NO_APPROVAL |
已驳回 |
4 |
IS_REFUND |
已退款 |
5 |
WAIT_MANAGER |
待负责人审批 |
6 |
FREEZE |
作废后冻结 |
主线状态:5 待负责人审批 -> 1 待财务审批 -> 2 已通过,任一审批环节拒绝则到 3 已驳回。
2.5 预收款占用明细状态:crm_payorder_contract_receivable.status
| 值 | 常量 | 含义 | 是否占用可用金额 |
|---|---|---|---|
1 |
WAIT_MANAGER |
已提交/待处理 | 是 |
2 |
PASS |
业绩审批通过 | 是 |
3 |
NOT_PASS |
审批未通过 | 否 |
4 |
CANCEL |
已作废 | 否 |
可用金额计算统一按:order_money - SUM(status in (1,2) 的 receivables_money)。
2.6 合同状态:crm_contract.status
| 值 | 常量 | 含义 |
|---|---|---|
1 |
WAIT_APPROVAL |
待审批 |
2 |
PASS_APPROVAL |
审批通过 |
3 |
NO_APPROVAL |
审批拒绝 |
4 |
CUSTOMER_SIGNED |
客户签约成功 |
5 |
CONTRACT_CANCEL |
合同废除 |
6 |
CONTRACT_TIMEOUT |
合同签约超时 |
7 |
CONTRACT_EXPIRED |
合同过期 |
2.7 合同回款状态:crm_contract.receivables_status
| 值 | 常量 | 含义 |
|---|---|---|
0 |
NO_RECEIVABLES |
未回款 |
1 |
SOME_RECEIVABLES |
回款未完成 |
2 |
OK_RECEIVABLES |
已回款 |
3 |
REFUNDS |
有退款 |
业绩最终通过时,合同 receivables_money += 本次通过业绩金额;若累计达到 total_money,receivables_status = 2,否则为 1。
3. 客户预收款流程
入口:POST /order_payment/generate_order_usecustomer
代码:app/Http/Controllers/Dingtalk/OrderPaymentController.php::generateOrderUseCustomer
3.1 生成预收款二维码
- 校验客户、当前用户、预收款城市
ysk_dd_dept_id。 - 非 admin 只能给自己关联城市生成预收款。
- 作废该客户尚未支付、未失效、未绑定合同的旧收款码。
- 选择收款资质:
- 优先沿用该客户、该预收款城市最近一次已支付订单的资质。
- 找不到时,按预收款城市随机/权重取可用资质。
- 调用椰子支付
get_payment_qrcode生成二维码。 - 写入
crm_order_payment:order_status = 0order_cus_con_status = 0is_ysk = 1contract_no = ''ysk_dd_dept_id = 预收款城市
3.2 预收款支付成功回调
入口:/api/dingtalk/pay/notify_call_back
代码:notifyCallBack -> updateOrderPaymentStatus
支付回调后:
- 写
crm_payment_log。 - 通过 Redis key
pay_callback_processed:{outTradeNo}做短时幂等。 - 查询订单并更新:
order_status = 1pay_time = 当前时间pay_shanghu_jydh/trade_no = 支付平台 tradeNoorder_cus_con_status = 1,仅预收款订单设置- 收款账户、商户、三方流水等字段
- 更新商户月收款统计。
- 通知收款人。
- 将
crm_online_receives_jilu对应草稿状态置为2。
代码现状注意:notifyCallBack 里当前强制 $order_info['order_status'] = 0、$params['status'] = 2,所以逻辑会按“未处理的成功回调”继续走。这个是当前代码行为,排查回调幂等/失败回调时要特别留意。
4. 预收款起合同流程
入口:合同创建/更新
代码:
app/Http/Controllers/Dingtalk/ContractController.php::createapp/Services/ContractInfoService.php::doCreateContract
4.1 合同创建时绑定预收款
前端把选中的预收款订单 ID 放到 bind_ysk_ids。
创建合同前校验:
- 若选择了多个预收款订单,必须来自同一收款商户资质:按
sign_info_id去重,数量不能大于 1。 bind_ysk_ids是数组时转成逗号字符串写入合同。
合同创建成功后:
- 合同保存
bind_ysk_ids。 - 更新这些订单:
order_cus_con_status = 2contract_id = 新合同 IDcontract_no = 新合同编号
4.2 合同审批对预收款订单的影响
入口:/contract/approval
代码:ContractController::approvalContract / ContractInfoService
| 合同审批结果 | 合同状态 | 预收款订单变化 |
|---|---|---|
| 通过 | contract.status = 2 |
order_cus_con_status: 2 -> 3 |
| 驳回 | contract.status = 3 |
order_cus_con_status: 2 -> 6,清空 contract_no/contract_id |
合同签署完成后,合同状态变为 4 CUSTOMER_SIGNED。代码中 completeSign 只改合同状态和签署信息,不额外改订单状态。业绩添加时会校验合同必须是 CUSTOMER_SIGNED。
4.3 合同作废对预收款订单的影响
合同作废审批通过后,未绑定业绩的订单会恢复可用:
- 查
contract_no = 当前合同号且order_cus_con_status = 3的订单。 - 更新为:
order_cus_con_status = 1contract_no = ''contract_id = 0receivables_id = 0
已经进入业绩绑定/审批的订单不在合同作废处强行处理,由业绩审批或业绩作废逻辑处理。
5. 合同收款码在线支付流程
入口:
- 钉钉:
POST /payorder/generate_order_contract - Android:同名控制器入口
代码:OrderPaymentController::generateOrderContract
5.1 生成合同收款码
适用:已有合同,直接基于合同收款。
主要逻辑:
- 校验合同编号、收款信息、分业绩信息。
- 合同必须存在。
- 取合同签约资质的
payyzcode,找不到时按城市可用资质兜底。 - 作废该合同下未支付、未失效的旧订单。
- 金额校验:
- 该合同已存在的待审批/已通过业绩金额不能超过合同总额。
- 该合同已支付订单金额 + 本次收款金额不能超过合同总额。
- 已有业绩金额 + 本次收款金额不能超过合同总额。
- 调用椰子支付生成二维码。
- 写入
crm_order_payment:order_status = 0order_cus_con_status = 3is_ysk = 2contract_no/contract_id = 当前合同ysk_dd_dept_id = 合同城市
- 写入
crm_online_receives_jilu临时业绩草稿。
5.2 支付成功后
回调仍走 notifyCallBack -> updateOrderPaymentStatus。
非预收款订单只更新付款成功字段,不把 order_cus_con_status 改为 1;因此合同在线收款订单通常保持生成时的 3,表示“合同通过/可用于绑定业绩”。
代码注释显示:2025-12-03 后,支付回调不再自动生成真实业绩,只更新订单支付成功和临时草稿状态。真实业绩仍需要走“添加业绩”。
6. 添加业绩使用在线支付/预收款
入口:
- 钉钉:
POST /receivables/create - Android:
POST /receivables/create
代码:
ReceivablesController::createReceivablesService::create
6.1 可选订单列表
代码:BanzhuFun::getCustomerNoUseOverOrder
按客户查已支付订单:
customer_id = 当前客户order_status = 1created_at >= 2026-02-13 21:36:32- 计算:
used_money = SUM(pcr.status IN (1,2) 的 receivables_money)available_money = order_money - used_money
- 只返回
available_money > 0的订单。
6.2 创建业绩时的校验
- 合同编号不能为空,合同必须存在。
- 合同状态必须是
CUSTOMER_SIGNED = 4,否则不能添加业绩。 - 合同总额限制:
- 统计该合同
status in (1,2,5)的业绩金额。 - 加上本次业绩金额后不能超过合同
total_money。
- 统计该合同
- 分业绩时:
- 使用
receivables_total_money作为本次总业绩。 - 分业绩目标城市不能是兼职组
1001161116。 - 分给个人时不能分给自己。
- 使用
- 使用在线支付/预收款订单时:
bind_order_ids对应订单必须已支付。- 订单可用余额之和必须覆盖本次业绩金额。
- 预收款城市
ysk_dd_dept_id必须与合同dd_dept_id一致。
6.3 创建业绩后的状态写入
不分业绩:
- 创建一条
crm_receivables:status = 5 WAIT_MANAGERtype = 8时写入trade_no、online_pay_bank、bind_order_ids
- 若绑定了订单:
- 更新
crm_order_payment:receivables_id = 当前业绩 IDorder_cus_con_status = 4contract_no/contract_id = 当前合同is_guanlian_receives = 1
- 合并订单 ID 到合同
bind_ysk_ids - 按可用余额拆分写入
crm_payorder_contract_receivable:status = 1 WAIT_MANAGERreceivables_money = 本次占用该订单金额
- 更新
分业绩:
- 创建主业绩,金额为
receivables_total_money - share_money。 - 创建从业绩,金额为
share_money,share_master_id = 主业绩 ID。 - 主/从业绩都初始为
WAIT_MANAGER = 5。 - 绑定订单时,会按主业绩金额和从业绩金额分别写入
crm_payorder_contract_receivable占用明细。
7. 业绩审批与订单状态变化
7.1 传统本地审批
入口:/receivables/approval
代码:ReceivablesController::approvalReceivables / ReceivablesService
负责人环节:
| 当前业绩状态 | 操作 | 业绩状态变化 | 订单/占用变化 |
|---|---|---|---|
5 WAIT_MANAGER |
通过 | 5 -> 1 WAIT_APPROVAL |
订单仍保持 4,占用仍保持 1 |
5 WAIT_MANAGER |
驳回 | 5 -> 3 NO_APPROVAL |
订单 4 -> 3,is_guanlian_receives = 0,receivables_id = 0;占用 1 -> 3 NOT_PASS |
财务环节:
| 当前业绩状态 | 操作 | 业绩状态变化 | 订单/合同/占用变化 |
|---|---|---|---|
1 WAIT_APPROVAL |
通过 | 1 -> 2 PASS_APPROVAL |
订单 4 -> 5;占用 1 -> 2 PASS;合同累计 receivables_money 增加并更新 receivables_status |
1 WAIT_APPROVAL |
驳回 | 1 -> 3 NO_APPROVAL |
订单 4 -> 3,释放关联;占用 1 -> 3 NOT_PASS |
7.2 CRM2 审批回调
入口:POST /dingtalk/receivables/approval_callback
代码:ReceivablesController::crm2ApprovalCallback
CRM2 回调使用 callback_params.biz_type = receivables 和 receivables_id/biz_id 定位本地业绩。
| CRM2 回调 | 本地目标状态 | 本地副作用 |
|---|---|---|
| 负责人通过进入财务节点 | WAIT_APPROVAL = 1 |
主/从业绩一起改为待财务审批 |
最终通过 instance.status = 1 |
PASS_APPROVAL = 2 |
主/从业绩通过;占用明细改 PASS;在线支付订单 4 -> 5;合同累计回款 |
驳回 instance.status = -1 |
NO_APPROVAL = 3 |
主/从业绩驳回;占用明细改 NOT_PASS;在线支付订单恢复 3 |
CRM2 回调用数据库事务和 lockForUpdate 保护,且如果业绩已经是目标状态,会直接返回,避免重复累计合同回款。
8. 作废/删除业绩对订单的影响
代码:ReceivablesController::delete
当作废 type = 8 且有关联订单的业绩:
- 如果合同已作废
contract.status = 5:- 订单恢复到
order_cus_con_status = 1 - 清空
contract_no/contract_id
- 订单恢复到
- 如果合同未作废:
- 订单恢复到
order_cus_con_status = 3
- 订单恢复到
is_guanlian_receives = 0- 对应
crm_payorder_contract_receivable.status = 4 CANCEL - 分业绩会连带处理主/从业绩的占用明细。
9. 整体流程图
10. 订单状态机
is_ysk=2
11. 业绩审批状态图
释放订单占用
订单=5 合同累计回款
订单恢复3
12. 关键代码索引
| 主题 | 文件 |
|---|---|
| 常量定义 | app/Http/BaseConstant.php |
| 生成预收款、合同收款码、支付回调 | app/Http/Controllers/Dingtalk/OrderPaymentController.php |
| 钉钉端业绩创建/审批/删除/CRM2 回调 | app/Http/Controllers/Dingtalk/ReceivablesController.php |
| Android 端业绩创建/审批公共服务 | app/Services/ReceivablesService.php |
| 合同创建、审批、签署、作废 | app/Http/Controllers/Dingtalk/ContractController.php |
| 合同服务版创建/审批逻辑 | app/Services/ContractInfoService.php |
| 可用预收款订单查询 | app/Http/CommonFuncs/BanzhuFun.php |
| 预收款占用明细模型 | app/Models/Crm/PayOrderContractReceivable.php |
13. 代码现状注意点
type == 8是在线支付/预收款绑定业绩的关键判断,但常量里没有显式定义该枚举。- 支付回调当前强制按成功回调处理:
$order_info['order_status'] = 0、$params['status'] = 2。 BanzhuFun::getCustomerNoUseOverOrder只返回created_at >= 2026-02-13 21:36:32的订单,历史订单不会出现在通用可用列表里。- 传统本地审批里,分业绩财务通过时,主业绩占用明细会置为
PASS;从业绩占用明细在部分路径下可能没有同步置为PASS,CRM2 回调路径则会遍历主/从业绩一起处理。 - 合同签署完成只改合同状态为
CUSTOMER_SIGNED,订单状态不随签署动作变化。
最后编辑:wangxuqian 更新时间:2026-07-24 15:55