/** * A2 替代选型的案例模型 —— **双表示同版本**:CMMN XML(机器面)与案例说明书(叙事面)。 * * 业务出处:`docs/MVP示例工作流.html` §1.5,但按 **MVP 简化口径**建模 * (2026-08-02 用户定:A2 是复杂活动,MVP 期间不展开大规模业务建模): * * A1 带着询价受理记录转进来 → 挑出 C 类 → **一张执行单**让 FAE 在系统外完成选型、 * 把替代料填回来 → 判替代料在库里行情稳不稳:稳转 A、不稳转 B → * 询价受理记录就此转换成**询价受理清单**,传给下游(有 B 类去寻源,全 A 直通定价)。 * * SOP 里初选核险 / 圈选替代 / 对比评估那三段的**实质工作在 FAE 脑子里与系统外** * (撒网、读规格书、参数对比 —— 本系统没有那些数据源);系统的本分是归一、组卡、 * 判稳、留痕。所以图上只有四项:归一 → 交人填回 → 判稳定标 → 放行。 * * ── 建模纪律(照 A1 的账)────────────────────────────────────────────── * 图上有几步就是几项,不多建一件;触达世界的项 manualActivation 且不给 doneWhen; * 哨兵必须有 ifPart 且读基座数、认「办完了没有」;案卷项 id 就是任务视图键名。 * * 上一版(@2.0.0)只有「派单 + 放行」两项,归一与判稳整个不存在 —— * C 类怎么来的、去向旗从哪儿出,全靠模型临场发挥;E5 那条边(needSourcing) * 因为台账里没有承载列,**从来没有成立过**。这一版把这两步补成真的。 */ export const CMMN_XML = ` update alt_line_total > 0 and quote_alternative_list_rows = alt_line_total update filled_total >= 0 update route_decided_total > 0 and quote_alternative_list_rows > 0 and quote_alternative_list_type_tag_c = 0 and quote_alternative_list_type_tag_a + quote_alternative_list_type_tag_b = quote_alternative_list_rows `; /** * 案例说明书 —— **只写整单一以贯之的事**(逐步做法归剧本,两处不重复写同一件事)。 */ export const NARRATIVE = `§0 开案:**一份带 C 类料的询价受理记录要过替代选型**。来路有两种,形状都收(宽进): · **流内**:上游受理活动放行后带着它的落地事实进来 —— 顶层有单号 / 客户名 / 三类条数等 摊平字段,\`records\` 数组里是上游全部落地行**混在一起**(带年用量与制造商的是受理记录行、 带分级依据与历史天数的是分类表行、带各种总数的是结论行 —— 按 PN 归并自辨,别当成三张表); · **单飞**:没有上游,只有一段需求(型号或芯片类型 + 参数 + 场景)—— 照来件归一, 没给的照实留空。 **这个活动是复杂活动的 MVP 简化形**:替代选型的实质工作(查规格、撒网、参数对比) 由 FAE 在**系统外**完成,系统只负责三件事 —— 把清单归一好、把执行单组好交给他、 把他填回的替代料**判稳定标**(在库行情稳转 A,不稳转 B)。 **成品只有一样**:一份**询价受理清单** —— 整份行都在、C 类已全部转成 A 或 B、 去向旗立了(还有 B 类就去寻源补价,全 A 直通定价)。判稳的尺与三分类 P-03 的 A/B 两行同一把:**历史价 ≤ 90 天且行情稳 → A,否则 → B**。 FAE 的定案不只是一个型号:**替代等级(P2P 直换 / FUNC 功能兼容 / OTHER 须验证)、 参数差异、风险与验证建议一并落表** —— 那是他的专业产出,逐颗可溯。 **人环两次,不多不少**:FAE 交回**一张选型执行单**(工作单里一颗待选型芯片一页, 右上角翻页逐颗定案,必填缺着按不下提交)、放行时 FAE 签字。 【一路上要说话】负责人正看着这一单。**每一拍都带一句 say** —— 说业务的话: 哪几颗料要替代、替代料查出来什么行情、这单接下来往哪走; 不说「归一化」「写草稿 N 行」这类系统词。到了要找人的那一拍,先说清这一轮办完了什么、 为什么现在需要他。别每拍一个腔调复读。 【怎么把一项干完 · 一拍连做】一个计划项常要两三个动作才交得出成果。write 和 tool 都**不会**自动把那一项收掉 —— 都做完了,用 finish 宣告完成。动作排进同一拍的 actions 串里一口气给,别拆拍。只有两处必须停拍:调工具要看出参(tool 放串尾), 发卡 / 提交会挂起(只能是串尾)。**常路 6-9 拍就该收口** —— 选型只有一张卡,颗数多不多不加拍。 §Z 结案:FAE 在验收单上签字,系统把草稿合并成正式清单并发出 roster 口的载荷 —— 清单整份交给下游,下游按自己的口径归一需要的数据。`; /** * 步骤剧本 —— 一个计划项一条;`doneWhen` 是「成果已存在」的判据,不是「该不该做」。 */ export const ALTERNATIVE_STEPS = [ { item: 'S1_normalize', intent: '把上游受理记录(或单飞来件)整份归一成选型底表,C 类行标待选型', writes: ['quote_alternative.list', 'quote_alternative.verdict'], /* * `records` 是上游整包落地行 —— 不声明的话它会被整表段的截断刀砍掉 * (那一段按字符数截,上游一单的落地行远超刀口,砍掉的恰是 C 类行)。 * 声明了的键**置前且不参与截断**。单飞起单没有这个键,声明也无副作用。 */ reads: ['records'], /* 归一与 FAE 选型期间摆**选型工作单**;判稳转正后才换收口那张清单 */ view: 'alt-worksheet', tools: [], /* * 归一完才算办完:两边对上 —— alt_line_total 自报、rows 基座现数。 * **不能写 `records != null`**:S-FEEL 未绑定名字 `!= null` 为 true,开局即收(A1 踩过)。 */ doneWhen: 'alt_line_total > 0 and quote_alternative_list_rows = alt_line_total', guide: `第一件事:把这一单的**选型底表**落出来 —— 整份承继,不是只有 C 类。 工作区这一步摆的是**替代选型工作单**(逐颗料的选型档案与填回区),你落的行就是它的血肉。 【料从哪儿来】 · 流内(任务数据里有 \`records\` 数组):那是上游全部落地行**混在一起**的一份 —— 同一颗料会出现两三次:带 eau/brand/target_price 的是**受理记录行**(以它为准), 带 basis/history_days/market_stable 的是分类表行(它的 type_tag 与受理记录一致), 带 line_total/type_a_total 这类总数的是结论行(不是料,别落进清单)。 **按 pn 归并**:一颗料归一成清单里的一行,列取受理记录行的值; type_tag 照抄成本清单的 **orig_type(原标)与 type_tag(现标)两列同一个值**。 · 单飞(没有 \`records\`):照入参与正文归一 —— 有型号落型号;只给芯片类型与参数、 没有具体 PN 的,\`pn\` 写「类型-场景」需求代号(如「运放-车规-01」), 这类需求本来就是来选型的,orig_type 与 type_tag 都写 C。 【单号 rfq_ref】入参有 \`rfqRef\` 就**原样照抄**;没有就从主题/正文里认(RFQ-xxxx); 认不出才造一个稳定案号「ALT-<型号或类型>」—— 它是本活动所有台账行的主键, 造完这一单从头用到尾,**不许中途换写法**(换一个写法就是新开一行)。 造号**只从本单来件取材,不许从台账里已有的别单单号里挑** —— 正式表里躺着的 单号是别人的单,借它的号等于把两单的行混进同一个案卷。 【逐列怎么落】写 \`quote_alternative.list\`,一颗料一行,一拍写完(rows 收数组): pn(**一个字符不许改**)、customer_pn、category、brand、eau、target_price、lead_time、 **remark(客户备注:预授权替代、认证要求、禁用约束 —— 选型的第一手依据,一个字别丢)** 照上游值抄,没有的留空字符串(**绝不写 0 顶空**);orig_type / type_tag 照抄原标 (**只写 A、B、C 三个大写字母之一**,写别的基座数不出来); C 类行把「为什么是 C」写进 basis(从分类表行的 basis 抄,或客户点名替代的原话); alt_pn / alt_brand / alt_grade / alt_diff / alt_risk / alt_verify / alt_note **全部留空** —— 那是 FAE 的定案,你不许编。 【同一拍记账】写 \`quote_alternative.verdict\`(一单一行): rfq_ref、customer_code / customer_name(顶层摊平字段里有就抄,没有留空)、 **alt_line_total** = 清单行数、**c_total** = C 类条数。 写完 finish 这一项 —— 引擎按「自报条数 = 基座行数」收步,少拆多写都过不去。`, }, { item: 'S2_fill', intent: '一张选型执行单交 FAE:工作单一颗一页,定案(型号/品牌/等级)逐页填回', writes: ['quote_alternative.verdict'], reads: ['quote_alternative.list'], view: 'alt-worksheet', tools: [], /* * 不给 doneWhen —— 发卡触达真人(manualActivation),引擎不替人按; * 「FAE 选完填回来了」也判不出来:它是人环,不是台账里能自己变出来的数。 */ guide: `底表归一好了,**该 FAE 出场了**。规矩(2026-08-02 用户定): **一张执行单,工作单里一颗待选型芯片一页** —— 他在右上角翻页逐颗定案, 定案三格(型号/品牌/等级)是每页的必填,缺着按不下提交(系统的闸看着)。 【怎么发】照这个骨架发一张卡(它标了人工激活:发卡就算启动,不必单发 start; 引擎也不会替你收 —— 人交回后要你自己 finish)。卡面是纯文本, steps/standard 里**别写星号记号**: {"kind":"human","itemId":"S2_fill","workOrder":"execution","card":{ "title":"替代选型:<单号> 待选型 颗", "ask":"这几颗料要替代:请照工作单逐页的选型档案在系统外完成选型,把定案填进各页右栏的「替代定案」(型号/品牌/等级是必填,缺着按不下提交),填完保存,回这张卡提交;确实找不到替代的,在那一页的选型备注写明缘由后交回。", "steps":["工作单一颗一页,右上角翻页;每页「替代定案」栏里给出替代料型号与品牌,替代等级点三档之一:P2P(同封装同引脚可直换)/ FUNC(功能兼容,需外围电路或 layout 调整)/ OTHER(部分兼容,须打样验证)", "P2P 必须逐项核对过五样:封装尺寸、引脚定义、电气参数、温度等级、认证 —— 以原厂官网规格书为准,同封装不等于同引脚;核对过哪几项写进参数差异项", "FUNC 或 OTHER 的,参数差异项与差异风险写清差在哪、要动什么;验证建议写明要不要打样、验什么", "客户备注里已预授权的替代优先采信;点名禁用的品牌不得作候选;确实找不到替代:在那一页的选型备注写明缘由(那也是一个答案);填完保存,回这张卡提交"], "standard":"每一颗:型号、品牌、等级都有且来源可溯(三格必填,闸看着);或备注写明替代未果的缘由。交回后判稳定标由系统接着办。", "basis":[{"k":"<待选型 PN 之一>","v":"<为什么是 C + 客户约束(预授权/认证/禁用)>"}]}} · \`basis\` 逐颗列待选型的料(几颗写几条);标题带单号与颗数。 · **卡上不摆格子**(填写全在工作区):定案填在工作单各页的「替代定案」栏里, 不往卡里塞 \`fields\` 或 \`blocks\`。 发完卡挂起,这一拍到此为止。**这张卡一单只发一次** —— 必填有提交闸兜着, 卡回来时每颗要么三格齐、要么备注写明未果。 【人交回之后】**他的定案已经在台账里了** —— 他在工作单上按「保存」那一刻, 基座就把那几格落进了草稿层(该由人亲手落下的,不经你的手)。 **不许再把那几格 write 一遍** —— 值相同的写入被「写了等于没写」拒(实测)。 这一拍只做两件事,然后 finish: ① 清点 alt_pn 有值的颗数写 **filled_total** 进 verdict(全部未果就是 0,照实写); ② finish 这一项,**这一拍到此为止** —— 判稳、转标、提交都不是这一拍的活: 转标还没做,submit 排进这一串必被「判据没过」拒回(实测白挨一次)。 **这一拍也不许调 crm.history-price-read** —— 查行情是判稳那一步(下一步)开头的活; 实测提前查过一次:查完没处落,连着四拍把同一行原样重写、四次被 「写了等于没写」拒回,白烧四拍才想起该 finish。`, }, { item: 'S3_reclass', intent: '替代料批量查历史行情:稳转 A、不稳转 B;清单转正,立去向旗', writes: ['quote_alternative.list', 'quote_alternative.verdict'], reads: ['quote_alternative.list'], view: 'alt-roster', tools: ['crm.history-price-read'], /* * **收步判据与验收判据同源**:C 类清零(基座按值现数)才算这一步办完 —— * 收步比验收松,松出来的那一截就是返工(A1 的账)。 * route_decided_total 是本步显式写下的记账数(布尔旗分不清「没写」与「写了 false」)。 */ doneWhen: 'quote_alternative_list_type_tag_c = 0 and route_decided_total > 0', guide: `FAE 填回来了,现在把清单**转正**:每颗替代料判稳、定标,清单从「受理记录」 变成「询价受理清单」。 【第一拍:查行情】把所有原标 C 行的 **\`alt_pn\` 那一列的值**(替代料的料号)收进 一个数组,调 \`crm.history-price-read\` 一次传完(参数 pns 收数组)。 **判稳的行情只有这一条来路 —— 这一拍亲自查 alt_pn。**两条近路都不许走: · **不许传原 PN**(pn 列是被替代的旧料,查它查到的是旧料的行情); · **不许拿上游带进来的数顶替**:入参里若躺着 history_days / market_stable / lifecycle,那是 A1 当时查**原 PN** 得的账 —— 跟这颗**替代料**稳不稳没有关系。 实测抄过一次近路:上游写着 9999,模型没调工具就把替代料判成 B, 而那颗替代料库里明明有 43 天的稳定历史,整单去向就此走岔。 调完这一拍就结束(工具出参要下一拍才看得见)。 【第二拍:定标 + 记账 + 收步,一拍连做】 ① **规则写死,不留裁量**(与三分类 P-03 的 A/B 两行同一把尺): historyDays ≤ 90 且 marketStable = true → **type_tag 转 A**;否则 → **转 B**。 工具回的 lifecycle 若是 EOL/缺货/要求替代 —— 那说明替代料选错了,**不转标**, 当成替代未果处置(见③)。 ② 逐行写回 \`quote_alternative.list\`(按 rfq_ref + pn 对行,主键一个字符不许改): type_tag(A 或 B,**照规则写,不许臆断**)、history_days(工具出参照抄)、 basis(一句依据:「替代料 历史价 天·行情稳 → 转 A」这样)。 **先对号再用**:工具出参每行带着查询的 pn —— 那个 pn 必须等于这一行的 alt_pn, 对不上就是第一拍传错了列,回去重查,别把别人的行情安到这颗替代料头上。 orig_type **不动** —— 原标是留痕,转的是现标。 ③ **替代料留空(FAE 判未果)的 C 行**: · 选型备注写了缘由 → 用 \`drop\` 把那一行撤出本单草稿(主键照 \`_keys\` 名单**原样粘**), 把撤了哪几颗、为什么写进 verdict.skipped_note,并把 alt_line_total 改成撤后的行数 (清单条数跟着改对,别让「条数 5 而表里 4 行」把归一那步的判据顶翻); · 备注也空着 → 发一张**决策卡(decide)**问负责人:「这颗料替代未果,剔出本轮 还是退回整单」两个选项摆出来,人点哪个照哪个办。**只问这一次** (补全卡已退役 —— 二选一是处置,本就该用决策卡)。 ④ 写 verdict:converted_a_total / converted_b_total(转成 A/B 各几条,照②的实数)、 **need_sourcing**(清单里还有 B 类 → true,全 A → false;照基座读数 quote_alternative_list_type_tag_b 的正负写,别凭记忆)、**route_decided_total 记 1**、 summary 一句人话(几颗替代、转成什么、往哪走)。 ⑤ finish 这一项,**这一拍到此为止**。引擎按「C 类清零 + 去向定过」收步 —— 有一颗 C 没处置,这一步收不掉,出路只有三条:转标(填了替代料)、 剔除(未果且有说明)、问人(未果且没说明)。 **别把 submit 排进这一串**:放行那一步要先把 L3 裁量裁过才轮得到 submit, 跳过它会被当场拒回(实测白挨过一次)。 **去向不用你选,也不用人选**:契约给两个出口各写了判据(还有没有 B 类), 放行那一刻基座按序求值自己定;need_sourcing 那枚旗是给流程包的边读的, 两处读的都是同一个事实 —— 你只负责把标转对。`, }, { item: 'T_release', view: 'alt-roster', intent: '清单已转正,提交放行交 FAE 验收', writes: [], reads: [], tools: [], /* 放行触达世界,必须人按一下 —— 引擎不自动收它(剧本里也不给 doneWhen) */ guide: `清单整份转正、去向已定 —— 可以走放行了(T_release 人工激活: 动它就算启动,不必单发 start)。先把裁量判据裁掉,然后 submit,同一拍连做: · judge criterion="替代料来源可溯且选型说明已留:以原厂官网规格书或客户预授权为准,每颗替代写明凭什么" —— 看清单里每颗替代料的 basis 与选型备注:都写了依据就 satisfied=true; 有替代料说不出来源的,satisfied=**false**,rationale 逐颗写清哪几颗没依据, 交签字的人定。**裁成不满足照样能提交** —— 理由连着卡一起交给他,那正是这条判据的用处。 criterion 照判据名**整句原样抄**,写别的名字对不上。 **去向不用你裁**:两个出口各写了判据,放行那一刻基座按序求值、首个命中即出。 终局唯放行:你不能自己宣布完成。`, }, ]; /** * 阶段 —— 进度条的刻度,只给人看。除放行外每个计划项恰属一个阶段; * 放行那一格由基座统一加(CLOSING_PHASE),人看到的是 x/4。 */ export const ALTERNATIVE_PHASES = [ { name: '归一清单', items: ['S1_normalize'] }, { name: '选型填回', items: ['S2_fill'] }, { name: '判稳定标', items: ['S3_reclass'] }, ];