销售觉得多,不是字段总数多,是四件事叠在一起
报备 12 项必填里 5 项能由省份、项目类型、属性细分推出来,却全让人填; 组织数据 6 格是 SAP 口径,销售不知道该选什么; 客户开发产品明细 3 遍录同一批产品,甲方、终端客户、合作伙伴各存两处; 而必填口径网页 12、飞书卡 16、闸门 12,三处各说各话。 方案只改谁填、何时填、能不能算出来,查重四要素、商机必挂经销商、三方会签、业绩比例审批一条不删。
四个症结,各在代码哪一行
证据全部来自 server/ + web/,不是印象| 症结 | 现状 | 证据 |
|---|---|---|
| 能算出来的还让人填 | 报备 12 项必填里 5 项可由省份 + 项目类型 + 属性细分推导,却全算必填 | OpportunityCreate.tsx:121 ORG_DERIVED oppty-intake.js:432 deriveOrg |
| 别人的信息让销售填 | 组织数据 6 格是 SAP 口径;客户开发 AT1 三个「是否」是主管审批要判断的事 | tub-meta.js:389-403 customer-dev.js:213-215 |
| 同一件事填两三遍 | 产品明细录三遍;甲方 / 终端客户 / 合作伙伴各存两处;采购总量三屏各露一次;拜访记录复制三份 | customer-dev.js:1517 · db.js:1663 visits.js:649-665 |
| 必填口径三处各说各话 | 网页 12 项、飞书卡 16 项、闸门 12 行;▲ 里有的建表就带默认值,闸门永远绿 | fields.tsx:23-30 · tub-meta.js:377 opportunities.js:576 · db.js:1690-1693 |
再叠一层:客户档案里还留着 SaaS 时代的付费状态、ARR、点亮产品这类立邦没有的概念,一级行业字典跟库里数据对不上。销售看到一堆不认识的格子,即使不必填,也会读成「这系统不是给我的」。
商机 · 报备与 ▲ 补填
节点定义 tub-meta.js:16-30 · 闸门 opportunities.js#gateCheck:569-6861.1 报备表单逐字段
| 字段 | 列 | 现状 | 方案 | 说明 |
|---|---|---|---|---|
| 商机描述 | name | * 手填 | 保留 * | 语音 / 地址 + 品类已能拼草稿,保留人核对oppty-intake.js:944-951 |
| 项目地址 | project_address | * 手填 + 查重 | 保留 * | 查重四要素之首,唯一刻意不自动填的字段 |
| 省 / 市 / 区县 | province city district | * 三格手填 | 地址联动 | 选中地址候选已同步四格;无候选时从地址文本解析;改只读可展开改bot-oppty.js:490-497 |
| 项目类型 | project_type | * 无默认 | 保留 * 一次点击 | 做成描述下方「新建 / 旧改」二选一;描述含「旧改 / 改造 / 翻新」时预选 |
| 项目属性 | project_attr | * 手填 | 细分反推 | 一对一反推已实现,改只读tub-meta.js:651-659 SUB_TO_ATTR |
| 项目属性细分 | project_attr_sub | * 手填 | 保留 * | 决定事业部与地区,真正的输入 |
| 客户操作模式 | customer_mode | * 且 ▲ | 保留 * 删 ▲ | 代码自己登记了这条重复tub-meta.js:321-322 |
| 所属事业部 | bu_id | * 手填 | 推导 | derive-org 已按类型 × 属性推;不一致只提示tub-meta.js:589 |
| 所在大区 / 所在地区 | project_region_id area_id | * 手填 | 省份→大区→地区 | oppty-intake.js:432 · tub-meta.js:627/669/634 |
| 项目建筑面积 | building_area | * | 保留 * | 金额与重点项目分级的源头 |
| 产品分类 | product_categories | * 多选 | 保留 * | |
| 产品 / 数量 | 飞书卡独有 | 飞书 * | 下线 | 报备只到分类;型号数量在产品推荐明细 M04bot-oppty.js:415-418 |
| 甲方 / 终端客户 | party_a_id end_customer_id | 网页可选、飞书 * | 标前引导前必填 | 报备时 AI 候选一键采纳;本来就是跟踪节点「梳理人脉」的内容POST /suggest-end-customer |
| 项目所在国家 | country | ▲ 默认中国 | 默认 删 ▲ | 有默认值的 ▲ 是假闸 |
| 商机来源 | source_code | ▲ | 默认 + 入口自动 | 默认「销售自建」;从客户开发 / 线索 / 经销商入口报的自动写;不再单独闸 |
| 合作伙伴 | partner_id | ▲ | 保留 ▲ | 制度 §5.2 商机必挂经销商 |
| 业绩比例四格 | *_ratio | ▲ 四格,建表默认 100/0/0/100 | 非跨区不展示 跨区只填一格 | 跨区由系统判;只填「负责员工比例」,其余联动 = 100 − x;现状闸门靠 allDefault 猜「有没有人动过」tub-meta.js:415-437 |
| 销售组织 / 分销渠道 / 产品线 / 销售部 | 4 格 | ▲ 手选 | 从报备人档案带出 | 不让销售猜 SAP 编码;缺主数据由 CRM 专员补档案。org_sales_dept_id 后端已 = actor.dept_idopportunities.js:1535 · employee_org |
| 落地销售部 | land_sales_dept_id | 界面标 ▲,清单里没有 | 默认 = 组织销售部 | 跨区才可改opportunities.js:1537 |
| 把握度 | win_rate | ▲ 默认 10 | 节点加权默认 可改,不闸 | 手填 win_rate 与 NODES[].winRate 是同一概念两个数tub-meta.js:14-15 |
| 开工 / 预计签约日期 | start_date close_date | ▲ | 保留 ▲ | 漏斗与预测要用 |
| 项目竣工日期 | complete_date | ▲ 重点项目 | 可选 重点项目改签约前 | 标前引导时多数项目答不出竣工日 |
| 客户项目编码 / 是否政企 | 可选 | 折叠「更多」 |
1.2 必填口径收成一份
| 现状 | 方案 |
|---|---|
| REPORT_REQUIRED 12 + REPORT_REQUIRED_BIZ 4 + 前端镜像 REQUIRED 12 + 闸门循环 12 行tub-meta.js:324-337 / 358-363 · fields.tsx:23-30 · opportunities.js:576 | 一份 REPORT_REQUIRED(7 项手填)+ 一份 REPORT_DERIVED(5 项推导),前端从 /api/meta 拉,不再镜像;飞书卡 buildFormFields 同源 |
| CONFIRM_GATE_INCLUDES_BIZ=false,甲方全空能过确认tub-meta.js:561 | 甲方 / 终端客户挪到 PRE_BID 闸门(与人脉、竞品同一道),开关删掉 |
| fields.tsx 的 FIELD_LABEL / SPLIT_RATIOS、nodes.ts 的 SIGNED_NODES 各是后端的第二份拷贝 | 全部改读 meta |
1.3 子表:一处填一次
| 项 | 现状 | 方案 |
|---|---|---|
| 甲方 / 终端客户 / 合作伙伴 | 主表三列 + opportunity_party 一行,终端客户还第三次冗余成 account_iddb.js:1663 · opportunities.js:1602-1604 | 相关方表是唯一事实源;主表三列改为保存钩子回写的只读缓存,WRITABLE 摘掉,界面只在「相关方」一处编辑 |
| 金额 / 涂装面积 | 由明细汇总回写,却仍在 WRITABLE,任何 PUT 改了下次保存明细就盖掉opportunities.js:1454-1455 · oppty-subs.js:517-518 | 出白名单,只读;无明细时允许「预估金额」一格,标 amount_source='ESTIMATE' |
| 面积 6 列 | building_area / coat_area / 四个分项,取数三条来路opportunities.js:3186-3194 | 销售只填建筑面积;涂装面积 = 明细汇总;四个分项留给解决方案在方案阶段填 |
| 产品推荐明细 | M01 / M04 / 涂装面积,金额后端算 | 保留;M01 由 M04 反推,一对多的才问 |
| 竞争对手 | 名称必填 + 3 格 | 保留;「体系」改 M04 下拉 |
| 中标通知书任务 | 是否持有凭证 * + 附件 | 「是否持有凭证」由附件存在推导,删这一格 |
| 销售预测 10 列 | 手填 | 首版由预估总金额 ÷ 工期月数按月摊出,人改 |
1.4 商机主表死列
核对后下线,不再进任何表单与白名单:lost_reason competitor lost_remark(已被 win_lose_reason 取代)、gross_margin(白名单已撤)、need_solution(有读无写)、house_ratio house_id、social_info、lead_no、initial_oppty_id initial_oppty_desc、related_oppty_no、wbs_code(恒等于 oppty_no,改读侧别名)。
终端客户开发 · AT1–AT6
闸门 customer-dev.js#cdStageWork:191-320 · 可写白名单 :583-6072.1 AT1 立项
| 字段 | 列 | 现状 | 方案 | 说明 |
|---|---|---|---|---|
| 项目信息及目标是否清晰 | at1_goal_clear | * 手选 | 下线 | 改为 AT1 主管审批卡上的勾选,属于审批意见 |
| 团队成员及分工是否确定 | at1_team_ready | * 手选 | 下线 | 由 cd_member 是否含在职「主管」推导,闸门已有 needDuty('主管')customer-dev.js:233 |
| 开发路径是否可实现目标 | at1_path_feasible | * 手选 | 下线 | 同上,进主管审批卡 |
| 本次招采区域 | bid_regions | * | 保留 * | |
| 是否集采 / 集采周期 | is_group_buy group_buy_* | * / 条件 | 保留 | 已条件化 |
| 最近招采时间 | last_bid_date | * | 可选 | 历史信息,立项时常不知道 |
| 客户类型 / 期望招采模式 / 合作模式 | customer_type expect_bid_mode coop_mode | * | 保留 * | |
| 项目落地方式 | landing_mode | * | 移到 AT2 | 引导策划阶段才定 |
| 回款方式 / 基材环境 | payment_mode substrate_env | 可选 | 可选 | |
| 客户采购总量 | purchase_total | * | 保留 * | 两个市占率的分母 |
| 耗量 / 预算附件标记 | usage_attached budget_attached | 已推导 | 保留 | |
| 需求明细 | cd_product AT1 | ≥1 行 | 保留 改跨阶段一张表 | 见 2.4 |
| 项目团队「主管」 | cd_member | 手选 | 从 KA 上级带出 | 一键确认;缺上级时才手选 |
| 人员组织编号 | org_code | 表头有、永远 NULL | 列删掉 | CustomerDevelopmentDetail.tsx:1547 / 1554 |
2.2 AT2 需求引导
| 字段 | 现状 | 方案 |
|---|---|---|
| 资质审核 / 目标销售总额 | * | 保留 * |
| 客户采购总量 | AT2 / AT3 各一个只读副本 | 只在页头显示一次 |
| 引导目标策划表 | 三行 × 目标 + 策略 = 6 必填 | 至少一行 三个策划项不一定每项都有动作 |
| 项目落地方式 | 在 AT1 | 从 AT1 移来 * |
| AT2 明细 | 带出不落库,要再点保存customer-dev.js:1537 | 带出行直接落库 只补 M04 与涂装面积 |
| 底部四页签 | 需求里有、未实现 | 不做 复用关联商机上的人脉与竞品 |
2.3 AT3 招投标
| 字段 | 现状 | 方案 |
|---|---|---|
| 是否中标 / 实际招采模式 / 中标时间 | * | 保留 * |
| 签约总额 / 总市占率 | 已推导 | 保留 |
| 支持度 ×2 / 价格利润 / 标书符合度 + 说明 | * 5 项主观评分 | 折叠成「投标复盘」 退一步:只在未中标时必填 |
| 三方会签 | 财务 / 法务 / 信用各三格 | 保留 不是销售填 |
| AT3 明细 10 列 | 重录 | 续接,只补投标金额 / 签约额 / 覆盖范围;行级 is_won 删掉customer-dev.js:279-280 |
| bid_amount / np_qty | 填了没下游 | 列保留,表格默认隐藏 |
2.4 产品明细:三张改一张
现状 cd_product 按 phase 存三套行,前端三套列。方案是一行产品贯穿 AT1 到 AT3,列按阶段解锁:
| 阶段 | 解锁的列 |
|---|---|
| AT1 | M01 产品市场分类、客户预计采购量 |
| AT2 | + M04 系列、预计涂装面积、立邦预计销售量、执行标准、客户合作品牌 |
| AT3 | + 投标总金额、立邦签约额、中标价区间 / 单位、覆盖范围 / 省市 |
存储上 phase 保留为「该行最早出现的阶段」,汇总 SQL 改为按列非空而非按 phase。carryOverLines、carryMissingLines 与 test-cd-carryover.mjs 整组退役。
2.5 AT4–AT6 与死项
- AT4 唯一动作「关联商机」保留;商机相关方有「终端客户」且该客户有在途开发时自动关联,不再要 KA 回来手点。
- AT6 满意度 UI 补星号:闸门必填但没标(CustomerDevelopmentDetail.tsx:1465)。
- 死项:DUTIES 里的「交付」无任何用途;ATT_FLAG_COL 三份重复映射;CD_TASK_FIELDS 里永不触发的「预计总市占率」;cd_landing_push 三格正文与 AT3 价格区间双存。
客户档案 · 联系人
accounts.js · contacts.js · AccountDetail.tsx · OpptyContacts.tsx3.1 客户档案新建:18 格 → 4 格
| 保留必填 | 名称、客户身份(终端 / 合作伙伴)、终端客户类型、客户经理 KA |
|---|---|
| 折叠「更多」 | 所属集团、客户等级、一级行业、客户分类 1/2、细分市场、省 / 市、备注 |
| 从界面摘掉 | 付费状态、ARR、点亮产品、套件首次点亮、CSM、售前、渠道经理、配额状态 / 认领日期(全站没有调 /claim 的前端)、二至四级行业。列保留在表里,只从立邦界面与 WRITABLE 摘掉 |
| 词表修正 | 一级行业字典换立邦词表(现状是「信息技术 / 金融 / 教育」,库里数据是「房地产 / 电子科技」);客户来源 ACCOUNT_SOURCE 与线索页写死的 SOURCES 合成一套 |
| 列表与页头 | 「未付费」灰标从列表第二列与页头首个 Tag 撤掉;ARR 列撤掉Accounts.tsx:52,69 · AccountDetail.tsx:430 |
| 顺手修 | PUT /api/accounts/:id 漏了 ka_owner_id 员工存在校验accounts.js:273-275 |
3.2 联系人:两套表单合一
同一张 contact 表两组画像列互不覆盖:客户 360 填 role_tag / willingness / depth 与手选 KDM,商机人脉填身份类别 / 决策角色 / 关系程度。立邦真正在用的是后者。
- 统一成商机人脉那套 7 格:姓名 *、单位、手机、部门、身份类别、决策角色、关系程度;客户 360 的新建联系人改调同一组件。
- role_tag(选项写死在 JSX、不走字典)、willingness、depth 从界面摘掉;is_kdm 统一由 decision_role = 决策人 推导。
- 唯一新增的一格:直属上级 boss_id,可选。列与防串户校验都在(contacts.js:69-73),没有控件导致决策链图画不出汇报关系。
跟进 · 拜访 · 任务
拜访打卡只有 1 格必填,是全系统最轻的表单,其他表单应向它看齐4.1 跟进记录:4 必填 → 1
| 字段 | 现状 | 方案 |
|---|---|---|
| 沟通内容 | * | 唯一必填 |
| 跟进日期 | * 默认今天 | 默认今天 折叠 |
| 跟进方式 | * 前端标必填、后端不校验 | 按入口推断 拜访 = 线下、妙记 = 视频会议、群聊 = IM |
| 一句话进展 | * 仅前端 | LLM 从内容抽一句 可改;空则取前 60 字 |
| 转成待办区块 | 默认勾选,5 格 | 默认折叠 下一步计划非空时展开预填 |
FU_SOURCES 白名单补上 PARTNER 与 AI_CHAT(followups.js:69),列表加来源筛选,让管理者看得见「手写 vs 机器」的比例。
4.2 拜访打卡与任务:只修死角
- weather 列前后端都没控件,下线。
- 一段现场记录复制三份(visit_checkin.note → follow_up.content → activity_task.strategy),补记要三处一起改:后两处改为引用 visit_id 读取,不复制正文。
- 「项目坐标」手填格改为从商机 project_lat / lng 带出,报备时地址候选已有坐标。
- 任务的 remind_at 无控件(db.js:420 自认「本期只落库」),从 EDITABLE 摘掉;提醒走机器人巡检规则。
横切:三条规则
- 有默认值的字段不当必填。国家、把握度、四比例、来源,凡是已带默认值的,闸门不再检查。检查一个永远有值的格子只会制造假绿灯。
- 推导得出的字段不进白名单。amount、coat_area、at2_share_rate、at3_sign_total、附件标记、主表三个相关方 id、is_kdm、has_bid_cert,全部只有一个写入口。客户开发侧已这么做(customer-dev.js:592-597),商机侧补齐。
- 表单只显示这个角色这个节点要填的格子。报备页不显示 ▲;跟踪页只显示简介 / 人脉 / 竞品 / 甲方终端;标前引导前的补填页只显示 3 + 1 项。工作台「去补」深链直达那一屏。
分批落地
每批发版前跑 npm test 全量,加两条新回归:三处必填清单同源断言;推导字段经 PUT 写不进去| 批 | 内容 | 改动面 | 工作量 |
|---|---|---|---|
| 第一批 只改配置 | ▲ 清单瘦到 3 + 1;删 customer_mode 双标;国家 / 来源 / 把握度默认;比例非跨区免填;组织 4 格从 employee_org 带出;网页 / 飞书 / 闸门必填收成一份;客户档案 9 个 SaaS 列摘掉;跟进抽屉默认值与待办折叠 | tub-meta.js · fields.tsx · bot-oppty.js#buildFormFields · Accounts.tsx / AccountDetail.tsx · FollowUpDrawer.tsx | 2 人日 |
| 第二批 推导与一处填一次 | 报备页「系统已推断」折叠区;主表三个相关方 id 改只读缓存;amount / coat_area 出白名单;AT1 三问下线 + 主管带出;落地方式移 AT2;AT3 五项折叠;联系人表单合一 + boss_id;拜访三份正文改引用 | OpportunityCreate.tsx · opportunities.js#WRITABLE · customer-dev.js#cdStageWork · CustomerDevelopmentDetail.tsx · AccountDetail.tsx · visits.js | 4 人日 |
| 第三批 动表结构 | cd_product 跨阶段一张表(含迁移与 syncCdDerived 改写);语音报备 7 格;parse-intake 的甲方 / 合作伙伴 / 联系人直接落相关方与人脉草稿而非只提示;网页 ADOPT_KEYS 放开采用 amount;AT4 关联商机自动化 | customer-dev.js · oppty-intake.js · bot-oppty.js · OpportunityCreate.tsx:160 | 4 人日 |
怎么判断有没有效
先取基线,再改,改完同口径复测仓库里新加了一个只读盘点脚本,逐表逐列数「真填了的行数」,另给跟进来源与写操作入口分布。线上 ssh 到 /opt/crm-agent2 原样跑,库以只读方式打开。本地库只有种子数据,基线要在线上取。
node server/scripts/field-fill-rate.mjs
| 指标 | 取自 | 目标 |
|---|---|---|
| 一条商机报备时销售实际敲过的格数 | audit_log create 明细 vs 推导列 | 12 → 7 |
| 报备后停在 DRAFT(缺项草稿)的占比 | opportunity.save_status | 飞书卡缺项草稿减半 |
| 提交 / 推进被 409 打回的次数 | audit_log · notify_log *_MISSING | 降 |
| 报备到商机确认的中位时长 | oppty_node_log | 降 |
| 跟进记录中机器来源占比 | follow_up.source | MANUAL 降到一半以下 |
| 客户开发三屏明细行数一致率 | cd_product 按 cd_id 比对 | 合表后恒 100% |
不动的,以及为什么
- 项目地址不自动填。查重核心,OpportunityCreate.tsx:929-931 的理由成立。
- 合作伙伴 ▲。制度 §5.2。
- 三方会签、业绩比例审批链、AT1/AT2/AT3 审批。制度。
- 拜访「现场记录」必填。没有正文的打卡没有意义。
- account.arr 等列不从表里物理删除。schema 与沪硅版语义共用,只从立邦界面与白名单摘掉。
- custom_field 不加新字段。先有消费者,再谈可配。