客户预收款、收款业绩、在线支付与合同订单状态逻辑

梳理日期:2026-06-18
项目路径:D:\devonline
重点代码:OrderPaymentControllerReceivablesControllerReceivablesServiceContractControllerContractInfoService

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_moneyreceivables_status = 2,否则为 1

3. 客户预收款流程

入口:POST /order_payment/generate_order_usecustomer
代码:app/Http/Controllers/Dingtalk/OrderPaymentController.php::generateOrderUseCustomer

3.1 生成预收款二维码

  1. 校验客户、当前用户、预收款城市 ysk_dd_dept_id
  2. 非 admin 只能给自己关联城市生成预收款。
  3. 作废该客户尚未支付、未失效、未绑定合同的旧收款码。
  4. 选择收款资质:
    • 优先沿用该客户、该预收款城市最近一次已支付订单的资质。
    • 找不到时,按预收款城市随机/权重取可用资质。
  5. 调用椰子支付 get_payment_qrcode 生成二维码。
  6. 写入 crm_order_payment
    • order_status = 0
    • order_cus_con_status = 0
    • is_ysk = 1
    • contract_no = ''
    • ysk_dd_dept_id = 预收款城市

3.2 预收款支付成功回调

入口:/api/dingtalk/pay/notify_call_back
代码:notifyCallBack -> updateOrderPaymentStatus

支付回调后:

  1. crm_payment_log
  2. 通过 Redis key pay_callback_processed:{outTradeNo} 做短时幂等。
  3. 查询订单并更新:
    • order_status = 1
    • pay_time = 当前时间
    • pay_shanghu_jydh/trade_no = 支付平台 tradeNo
    • order_cus_con_status = 1,仅预收款订单设置
    • 收款账户、商户、三方流水等字段
  4. 更新商户月收款统计。
  5. 通知收款人。
  6. crm_online_receives_jilu 对应草稿状态置为 2

代码现状注意:notifyCallBack 里当前强制 $order_info['order_status'] = 0$params['status'] = 2,所以逻辑会按“未处理的成功回调”继续走。这个是当前代码行为,排查回调幂等/失败回调时要特别留意。

4. 预收款起合同流程

入口:合同创建/更新
代码:

  • app/Http/Controllers/Dingtalk/ContractController.php::create
  • app/Services/ContractInfoService.php::doCreateContract

4.1 合同创建时绑定预收款

前端把选中的预收款订单 ID 放到 bind_ysk_ids

创建合同前校验:

  1. 若选择了多个预收款订单,必须来自同一收款商户资质:按 sign_info_id 去重,数量不能大于 1。
  2. bind_ysk_ids 是数组时转成逗号字符串写入合同。

合同创建成功后:

  1. 合同保存 bind_ysk_ids
  2. 更新这些订单:
    • order_cus_con_status = 2
    • contract_id = 新合同 ID
    • contract_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 = 1
    • contract_no = ''
    • contract_id = 0
    • receivables_id = 0

已经进入业绩绑定/审批的订单不在合同作废处强行处理,由业绩审批或业绩作废逻辑处理。

5. 合同收款码在线支付流程

入口:

  • 钉钉:POST /payorder/generate_order_contract
  • Android:同名控制器入口

代码:OrderPaymentController::generateOrderContract

5.1 生成合同收款码

适用:已有合同,直接基于合同收款。

主要逻辑:

  1. 校验合同编号、收款信息、分业绩信息。
  2. 合同必须存在。
  3. 取合同签约资质的 payyzcode,找不到时按城市可用资质兜底。
  4. 作废该合同下未支付、未失效的旧订单。
  5. 金额校验:
    • 该合同已存在的待审批/已通过业绩金额不能超过合同总额。
    • 该合同已支付订单金额 + 本次收款金额不能超过合同总额。
    • 已有业绩金额 + 本次收款金额不能超过合同总额。
  6. 调用椰子支付生成二维码。
  7. 写入 crm_order_payment
    • order_status = 0
    • order_cus_con_status = 3
    • is_ysk = 2
    • contract_no/contract_id = 当前合同
    • ysk_dd_dept_id = 合同城市
  8. 写入 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::create
  • ReceivablesService::create

6.1 可选订单列表

代码:BanzhuFun::getCustomerNoUseOverOrder

按客户查已支付订单:

  • customer_id = 当前客户
  • order_status = 1
  • created_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 创建业绩时的校验

  1. 合同编号不能为空,合同必须存在。
  2. 合同状态必须是 CUSTOMER_SIGNED = 4,否则不能添加业绩。
  3. 合同总额限制:
    • 统计该合同 status in (1,2,5) 的业绩金额。
    • 加上本次业绩金额后不能超过合同 total_money
  4. 分业绩时:
    • 使用 receivables_total_money 作为本次总业绩。
    • 分业绩目标城市不能是兼职组 1001161116
    • 分给个人时不能分给自己。
  5. 使用在线支付/预收款订单时:
    • bind_order_ids 对应订单必须已支付。
    • 订单可用余额之和必须覆盖本次业绩金额。
    • 预收款城市 ysk_dd_dept_id 必须与合同 dd_dept_id 一致。

6.3 创建业绩后的状态写入

不分业绩:

  1. 创建一条 crm_receivables
    • status = 5 WAIT_MANAGER
    • type = 8 时写入 trade_noonline_pay_bankbind_order_ids
  2. 若绑定了订单:
    • 更新 crm_order_payment
      • receivables_id = 当前业绩 ID
      • order_cus_con_status = 4
      • contract_no/contract_id = 当前合同
      • is_guanlian_receives = 1
    • 合并订单 ID 到合同 bind_ysk_ids
    • 按可用余额拆分写入 crm_payorder_contract_receivable
      • status = 1 WAIT_MANAGER
      • receivables_money = 本次占用该订单金额

分业绩:

  1. 创建主业绩,金额为 receivables_total_money - share_money
  2. 创建从业绩,金额为 share_moneyshare_master_id = 主业绩 ID
  3. 主/从业绩都初始为 WAIT_MANAGER = 5
  4. 绑定订单时,会按主业绩金额和从业绩金额分别写入 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 -> 3is_guanlian_receives = 0receivables_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 = receivablesreceivables_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 且有关联订单的业绩:

  1. 如果合同已作废 contract.status = 5
    • 订单恢复到 order_cus_con_status = 1
    • 清空 contract_no/contract_id
  2. 如果合同未作废:
    • 订单恢复到 order_cus_con_status = 3
  3. is_guanlian_receives = 0
  4. 对应 crm_payorder_contract_receivable.status = 4 CANCEL
  5. 分业绩会连带处理主/从业绩的占用明细。

9. 整体流程图

Created with Raphaël 2.2.0
通过
驳回
通过
驳回
通过
驳回
客户
生成预收款二维码
generateOrderUseCustomer
crm_order_payment
order_status=0
order_cus_con_status=0
is_ysk=1
支付平台回调 notifyCallBack
预收款支付成功
order_status=1
order_cus_con_status=1
创建合同时选择 bind_ysk_ids
合同保存预收款 ID
订单 order_cus_con_status=2
合同审批
订单 order_cus_con_status=3
合同待签/已审批
订单 order_cus_con_status=6
清空合同关联
客户签署完成
contract.status=4
添加业绩选择在线支付/预收款订单
校验合同已签、合同金额、订单余额、城市一致
创建 crm_receivables
status=5 待负责人审批
订单 order_cus_con_status=4
写占用明细 status=1
负责人审批
业绩 status=1
待财务审批
业绩 status=3
订单恢复 3
占用 status=3
财务/CRM2最终审批
业绩 status=2
订单 status=5
占用 status=2
合同累计回款
业绩 status=3
订单恢复 3
占用 status=3
合同直接收款码
generateOrderContract
订单 order_status=0
order_cus_con_status=3
is_ysk=2
写临时业绩草稿

10. 订单状态机

生成预收款二维码
预收款支付成功
起合同绑定预收款
合同审批通过
合同审批驳回
重新用于合同
添加业绩绑定订单
业绩最终通过
业绩负责人/财务驳回
合同作废且未绑定业绩
合同收款码生成
is_ysk=2
0 初始未付款
1 只付款
2 绑定合同审批中
3 合同通过未绑定业绩
4 绑定业绩中
5 绑定业绩成功
6 合同审核驳回

11. 业绩审批状态图

创建业绩
负责人通过
负责人驳回
释放订单占用
财务/CRM2通过
订单=5 合同累计回款
财务/CRM2驳回
订单恢复3
作废
作废
作废后释放或调整订单
5 待负责人审批
1 待财务审批
2 已通过
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. 代码现状注意点

  1. type == 8 是在线支付/预收款绑定业绩的关键判断,但常量里没有显式定义该枚举。
  2. 支付回调当前强制按成功回调处理:$order_info['order_status'] = 0$params['status'] = 2
  3. BanzhuFun::getCustomerNoUseOverOrder 只返回 created_at >= 2026-02-13 21:36:32 的订单,历史订单不会出现在通用可用列表里。
  4. 传统本地审批里,分业绩财务通过时,主业绩占用明细会置为 PASS;从业绩占用明细在部分路径下可能没有同步置为 PASS,CRM2 回调路径则会遍历主/从业绩一起处理。
  5. 合同签署完成只改合同状态为 CUSTOMER_SIGNED,订单状态不随签署动作变化。
作者:wangxuqian  创建时间:2026-02-05 10:44
最后编辑:wangxuqian  更新时间:2026-07-24 15:55