# CMA 模块操作手册:资金收付款计划预警(cash-plan-alerts) > 用途:每开发/验证完一个 CMA 模块,产出/更新本手册。防重复修改(先查手册再动手)、防重复验证(照清单执行)。 > 更新触发:每次该模块 bug 修复/功能变更 → 同步更新本手册 ⑤修改记录。 ## ① 模块定位 - **模块名**:资金收付款计划预警(cash_plan 预警引擎) - **解决什么问题**:把 cash_plans(收付款计划)里「到期未收款」的计划变成预警中心的待办(kpi_alerts.alert_type='cash_plan'),另有「未来30天资金缺口」预测预警(alert_type='forecast')。 - **所属流程环节**:资金管理 → 现金预测 / 预警中心 → 处置 - **主要角色**:finance(财务,处置预警)、ceo(查看) - **生成入口**:`POST /api/cma/admin/alerts/check`(手动/admin)、cron、`/api/cma/cash/*` 相关端点 ## ② 操作步骤 - 财务:预警中心 `/alerts` 查看「到期未收款」pending 预警 → 处理(联系客户/创建改善计划)→ 置 resolved。 - 运维/Bot:`POST /api/cma/admin/alerts/check`(**需 Bearer token**);可带 `?entity_id=N` 只跑单个账套。 - 余额快照批次口径开关(环境变量,改后需重启后端): - `CMA_CASH_SNAPSHOT_MODE` = `skip`(默认,批次行不生成逐户预警)| `aggregate`(按批次聚合 1 条)| `per_customer`(旧行为,逐户) - `CMA_CASH_SNAPSHOT_SOURCES` = 逗号分隔的 source 名单(覆盖内置名单) ## ③ 验证路径 - **自动脚本**:`backend/scripts/verify_cashplan_alert_engine_fix.py`(43 项验收电池) - 在**生产库副本**(mysqldump → `cma_cashvfy`)上跑,零生产写入;唯一生产访问是只读计数;跑完自动删副本。 - 覆盖:KPI 归属(433/434)/兜底 ORDER BY 确定性/entity_id 归属/去重刷新/快照批次三档开关/端点 401 与 entity 过滤。 - **期望输出**:`汇总: 43/43 PASS` + `[cleanup] cma_cashvfy 已删除`,退出码 0。 - **已测基线**:2026-09-13 → 43/43 PASS(副本);生产端点实测 `?entity_id=2` 200 且 `new_alerts=0`。 - **手工复核 SQL**(只读): ```sql -- 博海 pending 红色"到期未收款"应为 0 SELECT COUNT(*) FROM cma.kpi_alerts WHERE entity_id=2 AND alert_type='cash_plan' AND status='pending'; -- 新预警归属:应收应挂 433(BH_AR_BALANCE),现金应挂 434(BH_CASH_BALANCE) SELECT kpi_id, entity_id, COUNT(*) FROM cma.kpi_alerts WHERE alert_type='cash_plan' GROUP BY kpi_id, entity_id; ``` ## ④ 已知边界 / 坑 - **KPI 选择顺序**:候选标准码(酣客口径 AR_001/F_AR_DAYS/C_AR_BALANCE…)→ **实体前缀后缀码**(`_AR_BALANCE` / `_CASH_BALANCE` / `_AP_BALANCE`,命中博海 BH_*)→ 兜底(finance 维度,`ORDER BY id`)。**任何兜底查询都必须带 ORDER BY**,否则同一条件会出现两种答案(实测 `idx_entity`→318 / `uk_entity_kpi`→437)。 - **entity_id 必须显式写**:`KPIAlert(entity_id=...)`。该列 default=1,漏传会把博海的预警挂到酣客工作台(2026-09-10 实测 589/590)。 - **去重键 = suggestion.plan_id**(不是 alert_message,后者含动态"已逾期X天")。命中后必须**刷新**文本/等级/天数/归属,不能 `continue`。 - **余额快照批次**(source ∈ bohai_ar_20260630 / bohai_ap_20260630 / bohai_prepay_20260630 / receivables_migrate):plan_date 是**导入基准日**(期末日),不是到期日 → 逐户生成就会永远 overdue(伪逾期)。默认 `skip` 不逐户生成。 - **skip 模式不接管存量快照预警**:历史遗留的 pending 快照预警需财务手工置 resolved(引擎不会自动改它们的状态)。 - **⚠️ 易踩点**: - `LAST_INSERT_ID()` 是**连接级**的——用「新连接」的 raw 查询取不到刚插入的自增 id(验证脚本曾因此把 plan_id 记成 0)。 - 金额单位是**万元**:cash_plans.amount 注释=万元,导入票据/台账的「元」值必须先 ÷10000,否则预警文本放大 10000 倍。 - FastAPI `HTTPBearer(auto_error=True)` 对「无 Authorization 头」返回 **403**;本端点用 `require_auth_401` 显式返回 **401**(验收要求)。 - **历史 bug**:2026-08-29 博海 216 条伪"到期未收款"洪水(元当万元 + 基准日当到期日 + 兜底挂 BH_ADM_EXPENSE 437 + 错挂 entity 1)→ 2026-09-13 修复(本手册⑤)。 ## ⑤ 修改记录 | 日期 | 修改人 | 改动 | commit | 影响 | |------|--------|------|--------|------| | 2026-09-13 | wecom-fullstack | 手册创建;F1 显式写 entity_id(引擎2处+deviation_engine/alert_generator/deviation_push/bot_bridge/bot_bridge_v2);F2 候选码加实体前缀后缀 + 兜底 ORDER BY;F3 去重命中改为刷新文本/等级/天数/归属;F4 余额快照批次开关(默认 skip,另备 aggregate/per_customer);F5 admin 端点补鉴权(无 token 401)+ 可选 entity_id | (待填) | 博海不再重建伪逾期预警;预警 KPI 归属 433/434;不再跨账套错挂 | ## ⑥ 关联 - **依赖表**:`cash_plans`、`kpi_alerts`、`kpi_definitions`、`kpi_values`、`system_config` - **依赖接口**:`POST /api/cma/admin/alerts/check[?entity_id=N]`、`GET /api/cma/cash/*` - **关联模块**:预警中心(alerts)、预警规则(alert-rules)、资金预测(cash-flow) - **代码**:`backend/app/utils/cash_forecast_engine.py::check_cash_alerts`、`backend/app/main.py::admin_check_alerts` ---