/** * A4 成本定价的案例模型 —— **双表示同版本**:CMMN XML(机器面)与案例说明书(叙事面)。 * * 业务出处:`docs/MVP示例工作流.html` §1.2(S1 取价基准 → S2 逐条定价 → S3 底价自检), * 按 A3 同一条形状建模(2026-08-03 重写,@3.0.0): * * 上游(A3 的目标报价清单 / A2 的询价受理清单 / A1 直达的受理记录)进来 → 归一成 * **报价草案**底表,逐条按 P-01 利润模型算出底价线与拟报单价、过 P-02 底价门禁 → * **一张执行单**交 BD 核价(拟报单价可校可改,无基准的那几行由他定价)→ * **只有压在拦截档上的那几条才叫人拍板**(决策卡)→ 放行,整份报价草案交定价审批。 * * 上一版(@2.0.0)三处不合现行口径,这一版整个置换: * ① 入参必填 `rfqRef + customerCode + records` —— 而 A3 发的是 `{rfqRef, rows}`, * 门口拦的恰是主路(现在宽进到底,归一在活动内做); * ② 台账一个 `required` 都没有 —— 执行卡的闸没牙,人不填也能交卷; * ③ 入场哨兵 `s_priced` 读活动自报的 `draft_total`(「机检只读基座数」第 5 类违反)—— * 模型不记这个账,门就永不开,而它一步也没做错。 */ export const CMMN_XML = ` update line_total > 0 and quote_pricing_draft_rows = line_total and priced_total >= 0 update confirmed_total > 0 update quote_pricing_draft_disposition_block > 0 and confirmed_total > 0 update route_decided_total > 0 and quote_pricing_draft_rows > 0 and quote_pricing_draft_missing = 0 and quote_pricing_draft_disposition_block = 0 and quote_pricing_draft_disposition_pass + quote_pricing_draft_disposition_note + quote_pricing_draft_disposition_block = quote_pricing_draft_rows `; /** * 案例说明书 —— **只写整单一以贯之的事**(逐步做法归剧本,两处不重复写同一件事)。 */ export const NARRATIVE = `§0 开案:**一批料要定出各自的拟报单价**。来路三种,形状都收(宽进): · **寻源转来**:任务数据里有 \`rows\` 数组 —— 目标报价清单的落地行,每行带底价(base_price) 与价源;这是最全的一种来路; · **受理/选型直达**:有 \`records\` 或 \`rows\` 而行上没有底价 —— 只有料号与年用量; · **单飞**:只有一段需求文字,列了几颗料要报价。 **成品只有一样**:一份**报价草案** —— 整份行都在、**每一行都有拟报单价**, 而且每个价都说得出来路:基准从哪来、毛利档是哪一档、偏离底价线多少、门禁怎么判。 人签的不是一个数字,是**这个数字的来路**。它是定价审批的直接输入。 **能算的不问人,该人签的不代签**:P-01(利润模型)给毛利档、P-02(底价门禁)给处置, 两支判定表说了算,你不临场拍价;而**拟报单价那一列是 BD 定的,你一格都不写** —— 你给的是**建议价**,他确认或改成自己的数。你的活是把判据取齐、把结论落进台账。 **四步走**:归一取基准算建议价 → 交 BD 定价 → 底价自检(按他定的价算毛利、偏离度、 门禁处置)→ 定盘放行。 **这一单要经过人一到两次**:BD 在定价工作单上逐条定价(那张单绑着整份草案, 拟报单价缺一行按不下提交 —— 基座的闸看着,他填的格子直接落账); **只有还压在拦截档上的条目才另外叫人拍板**(一张决策卡)。每条都叫人,人就成了橡皮图章; 一条都不叫,底价门禁就是摆设。最后 BD 放行签字。 【一路上要说话】负责人正看着这一单。**每一拍都带一句 say** —— 说业务的话: 这批料算下来什么价、哪几条压线、要不要找人拍板;不说系统词。 到了要找人的那一拍,先说清这一轮办完了什么、为什么现在需要他。 【怎么把一项干完 · 一拍连做】write 和 tool 都不会自动收步 —— 做完用 finish 宣告。 动作排进同一拍的 actions 串一口气给;只有调工具(出参下一拍才看得见)与 发卡 / 提交(挂起)必须停拍。**这一单常路 7-9 拍就该收口**。 §Z 结案:BD 在验收单上签字,系统把草稿合并成正式草案并发出 draft 口的载荷 —— 整份报价草案交给定价审批。`; /** * 步骤剧本 —— 一个计划项一条;`doneWhen` 是「成果已存在」的判据,不是「该不该做」。 * * 三处不给 `doneWhen`:`S2_review`(发卡触达真人)、`T_approve`(要人拍板)、 * `T_release`(签下去触达世界)—— 给了等于给引擎一把替人签字的钥匙。 */ export const PRICING_STEPS = [ { item: 'S1_price', intent: '整份归一成报价草案:取成本基准 → 按利润模型算出底价线与建议价,逐条落表', writes: ['quote_pricing.draft', 'quote_pricing.verdict'], /* 上游载荷数组不声明就会被整表段的截断刀砍掉(砍掉的恰是要归一的行) */ reads: ['rows', 'records'], view: 'pricing-worksheet', tools: ['crm.history-price-read'], /* * 归一 + 算价两轮都走完才算办完:行数两边对上(一边是基座现数), * 且 priced_total 写过(全是无基准的单子这个数就是 0;S-FEEL 未绑定的名字 * 参与比较为假,所以「没写过」进不来)。 */ doneWhen: 'line_total > 0 and quote_pricing_draft_rows = line_total and priced_total >= 0', guide: `第一件事:把这一单的**报价草案**落出来,并把能算的价都算出来。 【料从哪儿来】 · 寻源转来(\`rows\` 里的行带 base_price / price_basis):照抄 —— 底价就是成本基准, 一个数字都别改;带 quote_line_total 这类总数的是结论行,不是料,别落进草案。 · 受理/选型直达(\`records\` 或没有底价的 \`rows\`):料号、品类、制造商、年用量、 客户目标价、类别(type_tag)照抄,**底价一栏留空**。 · 单飞:照正文归一,列了几颗落几行;没说类别的按 B。 **按 pn 归并,一颗料一行**;pn 一个字符不许改(它是主键,改一个字就是新开一行)。 【第一拍:只做两件事 —— 落草案行 + 串尾发一次查价】 ① 写 \`quote_pricing.draft\`,一颗料一行(rows 收数组): rfq_ref(入参有就原样抄;没有从主题认;认不出才自己造一个,**造出来的必须以 \`PRC-\` 开头** (\`PRC-<主题或第一颗料的型号>\`)。两条硬规矩: **① 整单只有一个单号,不是一颗料一个** —— 它是这一单的案号,草案所有行、verdict 那一行 用的都得是同一个串(实测栽过:两颗料造出两个号,一单裂成两单); **② 只从本单来件取材** —— 台账里躺着的 RFQ-xxxx 是别人的单,借它的号等于把两单的行 混进同一个案卷(实测栽过一轮))、pn、customer_pn、category、 brand、eau、target_price(客户目标价)、lead_time、remark(客户约束/急料,一个字别丢)、 type_tag(照上游现标;单飞按 B)、base_price(上游给了就抄,没有留空)。 suggested_price / margin_band / floor_price **这一拍全部留空** —— 基准还没取齐。 **quoted_price / margin / deviation / disposition 这四列从头到尾都不是你写的**(见下)。 ② 串尾调 \`crm.history-price-read\`:pns 收数组,把**底价还空着的那几颗料**一次传完 (上游已经给了底价的不必查)。 **这一拍不写 \`quote_pricing.verdict\`,一列都不写** —— 尤其 priced_total: 它是「算价走完了」的凭据,提前写等于把闸提前打开,引擎会在下一拍开头当场收掉这一步, **算价从此不会发生**(A3 实测栽过:嘴上说预填了,台账里没有)。 【**这一列你一格都不写**:quoted_price】 拟报单价是 **BD 在工作单上定的那个数** —— 人签的就是它。你给的是**建议价** (suggested_price),他确认或改成自己的数;margin / deviation / disposition 三列 要等他定完价才算得出来,这一步也别写。 这不是客气话:从前只有一列价、剧本写着「算不出的留空」,实测跑单时两颗库里查不到的料 照样被编出了价(一颗抄客户目标价、一颗凭空写了个数),而交代里还说着「价空着等你定」。 **一条要你分辨「这行算得出/算不出」的规矩会走岔;「这一列不写」没有分支可走岔。** 【第二拍:算建议价 + 记账 + 收步,一拍连做】 ① 补基准:**只补上一拍 base_price 空着的那几行** —— 工具出参里 hit=true 的写 base_price = lastPrice、price_basis 写 **HIST**;hit=false 的**别编价**,base_price 留空, 它就是「无基准」那一档。 **上游已经给了 base_price 的行:工具回什么都不采用,那一格一个数字都别改** —— 上游给的是本单问回来的货源底价,工具给的是库内历史价;这一单的成本是前者 (实测栽过:两行上游底价被历史价盖掉,毛利从 20% 变成 115%,数字全对而结论是假的)。 ② 逐条算**建议价**,规矩写死,照做即可(**这些是算术,不是判断**): · 毛利档 margin_band 按利润模型 P-01 取(客户等级 × 类别): 战略客户 + A 类(大批量长单)→ 8%~10%;普通客户 + A 类 → 12%~15%; B 类 → 15%~20%;C 类 → 20%~50%;拿不准按 12%~15%。写成「12%~15%」这样的文本。 · **底价线** floor_price = base_price ×(1 + 档下限)。保留四位小数。 · **建议价** suggested_price: - 客户没给目标价(target_price 空)→ 取 base_price ×(1 + 档上限); - 客户目标价 ≥ 底价线 → 就取客户目标价(接得住,毛利还在档内); - 客户目标价 < 底价线 → 仍取客户目标价(贴着客户报),这一条到门禁那儿会亮灯; - **base_price 空(无基准)→ margin_band / floor_price / suggested_price 三列一起留空**。 不要拿一个不相干的价去凑一个数出来 —— 那比没有建议更危险,它看起来像有依据。 · **「没有」要给空串 \`""\`,不许写 0** —— 0 是一个数,底价线写 0 看起来像「成本为零」, 偏离度写 0 看起来像「正好压在底价线上」。空串基座会当成「这一列不写」。 ③ 这时才写 \`quote_pricing.verdict\`(一次写全):rfq_ref、customer_code / customer_name (顶层有就抄)、customer_grade(来件认得出就写,认不出写「普通客户」)、 **line_total** = 草案行数、**basis_total** = 有成本基准的条数、 **priced_total** = 这一拍算出建议价的条数(0 也要写)。 ④ finish 这一项。`, }, { item: 'S2_review', intent: '一张执行单交 BD:工作单上逐条定价(拟报单价归他);收回后过底价门禁并定盘', writes: ['quote_pricing.draft', 'quote_pricing.verdict'], reads: ['quote_pricing.draft'], view: 'pricing-worksheet', tools: [], /* 不给 doneWhen —— 发卡触达真人(manualActivation),引擎不替人按 */ guide: `建议价算完了,**该 BD 出场了** —— 这是本活动唯一的一张执行单, **拟报单价是他定的,不是你定的**。工作区摆的是**定价工作单**:逐颗料一张定价档案卡 (成本基准、毛利档、客户目标价、底价线、系统建议价),利润模型与底价门禁两把尺挂在上面, 填回区长在同一屏 —— 他在「拟报单价」那一列逐行填(照建议价确认,或改成自己的数)。 这一步标了人工激活:发卡就算启动,不必单发 start;引擎也不会替你收 —— 人交回后要你自己 finish。 【照这个骨架发,别少字段】(卡面是纯文本,steps/standard 里**别写星号记号**): {"kind":"human","itemId":"S2_review","workOrder":"execution","card":{ "title":"报价定价:<单号> 共 条待你定价", "ask":"系统已按成本基准与利润模型算出每一颗的建议价,请在工作单的「拟报单价」一列逐行定价:认可建议价就照抄,不认可就改成你的数;库内没有成本基准的那几行没有建议价,由你定。", "steps":["拟报单价每一行都要有 —— 缺一行这张单按不下提交;它是这一单最后签下去的那个数,系统不替你填", "建议价是从成本基准按利润模型的毛利档推出来的:照抄就是认可这个毛利,改高改低都可以,改了在备注里写一句缘由", "低于底价线的价会被底价门禁标出来:两成以内附理由即可放行,超过两成的要另外找人拍板 —— 你在这里往上调一点,就不必再惊动一个人", "库内查不到成本的料没有建议价:按你的经验定价,并在备注写明依据;实在定不了的按行首 × 把这一条撤出本轮"], "standard":"每一行都有拟报单价,而且每个价都说得出来路(基准 / 毛利档 / 让价理由)—— 缺一行这张单按不下提交(系统的闸看着)。", "basis":[{"k":"","v":"<类别 · 成本基准 · 毛利档 · 建议价>"}]}} · \`basis\` 逐条列本单的料:PN + 基准与建议价。几颗写几条,别铺全表。 · **卡上不摆表、不摆格子**(执行卡零输入):整份草案就在工作单那份件上, 拟报单价与备注两列都能就地改、行能撤 —— **别往卡里塞 \`blocks\` 或 \`fields\`**。 · **这张卡带提交闸**:拟报单价(必填列)还空着,他按不下提交 —— 缺几格、缺在哪行, 闸上与件上的红带都写着(与放行门 L1 同一个数同一套算法)。 发完卡这一拍就结束了(发卡挂起)。 【人交回之后 —— 他填的价基座已经替你写回台账了】这一步你只做两件事,然后 finish: ① 写 verdict:**confirmed_total** = 他定过价的条数(照草案行数记); 他在件上撤掉了行的话,**line_total 跟着改成撤后的实数**。 ② finish 这一项。 **别在这一拍算门禁** —— 底价自检是下一步的活(它有自己的一段做法),这里算了也白算。 草案行你一列都不用重写:他填的值基座已经落进台账了,重写会被「写了等于没写」拒回。 【这张卡一单只发一次】人交回来之后不许再发同一张卡 —— 必填有提交闸兜着, 卡回来时拟报单价一定是齐的(或那几行已被他撤掉)。`, }, { item: 'S3_gate', intent: '底价自检:按人定下的价逐条算毛利、偏离度与门禁处置,判完即定盘', writes: ['quote_pricing.draft', 'quote_pricing.verdict'], reads: ['quote_pricing.draft'], view: 'pricing-worksheet', tools: [], /* * **收步判据与验收判据同源**:三档处置的条数加起来等于行数(两边都是基座现数)—— * 判完了才算办完。`_rows > 0` 那一半不能省:开案时行数是 0,而 0+0+0 = 0 * 会让这一步开局即收(未绑定名字的坑之外,还有「全零相等」这一坑)。 */ doneWhen: 'quote_pricing_draft_rows > 0 and quote_pricing_draft_disposition_pass + quote_pricing_draft_disposition_note + quote_pricing_draft_disposition_block = quote_pricing_draft_rows', guide: `人把价定完了,**现在才轮到算门禁** —— 这一步是纯算术,一拍连做: ① **逐行按他定的 quoted_price 算三列**(**quoted_price 你一格都不写,只写这三列**): · margin =(quoted_price − base_price)/ base_price,四位小数; · **偏离度** deviation =(quoted_price − floor_price)/ floor_price,四位小数; · **门禁处置** disposition 按底价门禁 P-02 判,只许写这三个大写码之一,一个字母不许差: - deviation ≥ 0 → **PASS**(在底价线之上,直接放行) - −0.20 ≤ deviation < 0 → **NOTE**(低于底价线两成以内,附理由放行) - deviation < −0.20 → **BLOCK**(低于两成以上,压在拦截档,要人拍板) · **base_price 空(无基准)的行**:margin / deviation 给空串 \`""\`(**不许写 0** —— 0 看起来像「正好压在底价线上」,而这一行压根没有底价线)、disposition 记 **NOTE**, price_note 里保留他写的定价依据(他没写就补一句「库内无成本基准,由 BD 定价」)。 **一行都不能漏**:三档处置的条数加起来必须等于 quote_pricing_draft_rows(基座数的行数)。 现在三档都是 0,就是一行都还没判 —— 这一步没办完,放行门开不了。 ② 看这一拍写完之后的**拦截档条数**(disposition = BLOCK 的行): · 一条都没有 → 同一拍把 verdict 的 **route_decided_total 记 1**(定盘)、 summary 写一句人话(几条直接放行、几条附理由),然后 finish 这一项; · 还有 → **别写 route_decided_total**,只 finish 这一项 —— 下一拍会有一步专门叫人拍板。`, }, { item: 'T_approve', intent: '压在拦截档的条目要人拍板 —— 发一张决策卡,照他选的那条路把草案改掉', writes: ['quote_pricing.draft', 'quote_pricing.verdict'], reads: ['quote_pricing.draft'], view: 'pricing-roster', tools: [], /* 人工激活 + 要人拍板 —— 不给 doneWhen,引擎不许替人收 */ guide: `**这一单只发一张决策卡。动手之前先分清你在哪一半:** · 轨迹里**已经有**「decision 卡回传」/「人选了:xxx」—— 那就跳到下面【人点完之后】那一段, 照他选的那条路改草案、写 route_decided_total、finish。**一个 decide 都不许再发** (实测栽过:人点完了,而这一步又从头念了一遍「发一张决策卡」,同一张卡连发四次, 调人预算烧光,单子 exhausted —— 他点过的那一下就白点了); · 轨迹里**还没有** —— 才轮到下面【发一张决策卡】那一段。 这一步只在还有条目压在拦截档(基座现数 quote_pricing_draft_disposition_block > 0)时入场; 它触达客户利益,标了人工激活:发卡就算启动,不必单发 start。 工作区这时摆的是**报价草案清单**,整份草案与门禁分布都在上面,他看得见全局再拍板。 【发一张决策卡,照这个骨架】(选项少于两个基座拒发): {"kind":"decide","itemId":"T_approve","ask":" 条低于底价线两成以上,按哪条路办?", "options":[ {"value":"floor","label":"回调到底价线报","consequence":"守住毛利下限,客户可能不接受这个价","recommended":true}, {"value":"as-is","label":"照现价破线报,附理由","consequence":"接住客户目标价拿单,毛利跌破下限,理由随草案交审批"}, {"value":"drop","label":"这几条本轮不报","consequence":"撤出本单草案,客户拿到的是一份缺项的报价"}], "basis":[{"k":"","v":"成本基准 · 底价线 · 拟报 · 偏离 "}]} · basis 逐条摆压在拦截档的那几颗料:基准、底价线、拟报价、偏离度 —— 裸卡基座拒发。 · 发完这一拍就结束了(发卡挂起)。 【人点完之后】轨迹上会出现「decision 卡回传 —— 人选了:xxx」。**照那条路把草案改掉, 别再发第二张卡**,一拍连做: · 选 floor:**这是全活动唯一一次你可以写 quoted_price** —— 那个数不是你定的, 是他在卡上点下的(就是该行的底价线),你只是替他落账: 那几行 quoted_price 改成本行的 floor_price,margin 记该行毛利档的下限、deviation 记 0、 disposition 改 **PASS**,price_note 写「按底价线回调,由负责人拍板」; · 选 as-is:价不动,disposition 改 **NOTE**,price_note 写明破线的理由与拍板人; · 选 drop:用 \`drop\` 把那几行撤出本单草稿(主键照 \`quote_pricing_draft_keys\` 名单**原样粘**), 并把 verdict 的 line_total 改成撤后的实数。 然后写 verdict:**route_decided_total 记 1**、summary 补一句拍板结果,finish 这一项。 拦截档清零之后,放行门才开得了。`, }, { item: 'T_release', intent: '草案已定盘,提交放行交 BD 验收', writes: [], reads: [], view: 'pricing-roster', tools: [], /* 放行触达世界,必须人按一下 —— 引擎不自动收它(剧本里也不给 doneWhen) */ guide: `每行有价、门禁处置守恒、拦截档清零、定盘已记 —— 可以走放行了 (T_release 人工激活:动它就算启动,不必单发 start)。先把裁量判据裁掉,然后 submit,同一拍连做: · judge criterion="报价的来路说得清:基准价来源、毛利档与让价理由逐条可溯,BD 对报价负责" —— 看草案里每一行的基准、毛利档与备注:说得出来路就 satisfied=true; 有行说不出的(比如无基准由人定的价没写缘由),satisfied=**false**,rationale 逐行写清, 交签字的人定。**裁成不满足照样能提交** —— 理由连着卡一起交给他。 criterion 照判据名**整句原样抄**,写别的名字对不上。 终局唯放行:你不能自己宣布完成。`, }, ]; /** * 阶段 —— 进度条的刻度,只给人看。除放行外每个计划项恰属一个阶段; * 放行那一格由基座统一加,人看到的是 x/4。 */ export const PRICING_PHASES = [ { name: '取基准算建议价', items: ['S1_price'] }, { name: '交BD定价', items: ['S2_review'] }, { name: '底价自检', items: ['S3_gate'] }, { name: '超限拍板', items: ['T_approve'] }, ];