关于 consumer-goods-promotion-bo-api-deploy
面向 Salesforce 管理员和开发者的技能,用于在 Consumer Goods Cloud 中启用 Promotion BO API,并生成、部署和接入自定义工作流步骤。通过端到端 REST 验证检查部署结果。
主要功能
- 01检查组织权限与包命名空间
- 02预览并应用 BO API 初始数据
- 03生成并部署 Apex 工作流步骤
- 04将步骤注册到选定工作流
- 05通过 REST 接口验证流程
适用场景
- 01为 Salesforce 组织启用 Promotion BO API
- 02向创建、更新或复制流程添加自定义步骤
- 03验证 Promotion API 的导入与状态接口
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill consumer-goods-promotion-bo-api-deploy安装后在 Claude Code 中输入/consumer-goods-promotion-bo-api-deploy确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/set-comment-value/interview-answers.jsonREADME.mdSetCommentValue.clsSetCommentValue.cls-meta.xmlassets/set-comment-value/payloads/copy.jsoncreate.jsonupdate.jsonreferences/conventions-and-payload-rules.mdgenerate-and-wire.mdpreflight-and-seed-queries.mdreference-example-set-comment-value.mdsmoke-and-verify.md详细介绍
部署 Promotion BO API
功能说明
在 Salesforce 组织中启用 Consumer Goods Cloud TPM Promotion Business Object (BO) API,询问用户要添加到促销流水线中的一个自定义工作流步骤,生成并部署相应的 Apex 类,将其注册到 BO API 实体,并接入客户选定的 {create, update, copy} 工作流子集,最后通过以下三个 REST 端点端到端验证整个流程:
POST /services/apexrest/<prefix>/promotions/initializePOST /services/apexrest/<prefix>/promotions/ingestGET /services/apexrest/<prefix>/promotions/status?importId=...
此流程用于无界面交付:指定一个组织,回答访谈问题,技能即可安装并验证此步骤。Promotion BO API 随 CGCloud 托管包一同提供(已发布版本为 cgcloud,开发/测试版为 cgcloud_dev)。所有 Apex 标识符、对象/字段 API 名称和 REST URL 均在运行时根据检测到的包前缀生成。
derive 不在此处的范围内(后续技能会介绍)。写入和冒烟测试阶段的详细操作手册位于 references/ 下:
references/preflight-and-seed-queries.md— 第 1/2/3 阶段的 SOQL(TPM 权限、权限集门控、框架状态验证、静态资源检索步骤、批处理链轮询)。references/conventions-and-payload-rules.md— 销售组织分区、优先根据架构推导契约,以及九条跨工作流载荷规则 R1–R9。references/generate-and-wire.md— 第 5a 阶段(生成并部署类)和第 5b 阶段(注册并接入步骤),含完整的register-step.apex。references/smoke-and-verify.md— 第 6 阶段(契约推导、载荷生成、create/update/copy 流程及验证)。references/reference-example-set-comment-value.md— 随附的SetCommentValue预设(资源位于assets/set-comment-value/)。
请先收集输入信息
开始前先询问,不要猜测。
- 组织别名 — 目标组织的
sf别名。必填。 - 销售组织 — 必填的预设参数,默认值为
0001。必须为 4 个字符且全部大写(与TPMSetupData.validateSalesOrg保持一致)。所有后续查找(促销模板、战术模板、锚定客户、产品筛选条件、SKU)都限定在此销售组织范围内——请参阅references/conventions-and-payload-rules.md(“Sales-org partitioning”)。 - 是否同意植入种子数据? — 第 3a 阶段会获取打包的 BO API 种子数据,并在插入 8 个对象中的约 232 行之前征求确认。供 CI 调用方使用时可传入
--yes。 - 是否试运行? —
--dry-run会运行第 1、2、3a 预览阶段、第 5 阶段访谈,以及仅验证的部署。不写入任何内容。
待了解框架状态后,再通过第 5 阶段的访谈收集定制内容。
先查找命名空间
前缀取决于已安装的构建版本——请检测,不要猜测。已发布包 → cgcloud;开发/测试版 → cgcloud_dev;通过源代码部署 → 无前缀。
sf data query --target-org <alias> \
--query "SELECT NamespacePrefix FROM ApexClass WHERE Name = 'TPMSetupData'" --json
读取 records[0].NamespacePrefix,并设置下文各处都会用到的三个变量:
PREFIX_DOT— Apex 引用:cgcloud./cgcloud_dev./ 空。PREFIX_UNDER— 对象/字段 API 名称:cgcloud__/cgcloud_dev__/ 空。URL_NS— REST 路径:/cgcloud//cgcloud_dev/ 空。
每次 Apex 调用均采用 ${PREFIX_DOT}ClassName.method(...);每个 SOQL 对象/字段均采用 ${PREFIX_UNDER}Object__c;每个 REST URL 均采用 /services/apexrest${URL_NS}/promotions/<endpoint>。在运行时替换。
阶段 1 — 预检
运行以下命令,并在首次失败时停止。
sf --version;node --version(仅在运行随附脚本时)。另外,在会话开始时执行一次export SF_SKIP_NEW_VERSION_CHECK=true,避免过时 CLI 的更新横幅输出到 stdout,导致下游任何sf … --json | jq …管道出错(替代的sed去除方式请参阅references/smoke-and-verify.md中的“解析注意事项”)。sf org display --target-org <alias> --json— 确认可访问;捕获instanceUrl+ org id(仅用于显示/日志记录)。REST 调用通过sf api request rest使用 CLI 中存储的会话 — 此技能绝不提取访问令牌。- 在本地验证销售组织:长度为 4、大写、非空(与
TPMSetupData.validateSalesOrg一致)。快速失败。 - TPM 授权(Permission Set License) — 当前用户的
CGCRetailAndTPMMgmtPslPSL 分配。SOQL + 警告/停止路由见references/preflight-and-seed-queries.md第 4 步。 - 当前用户权限集(TPM 管理员角色) — 当前用户必须持有
TPM_Master_Data_Admin(或与%TPM%Master Data Admin%匹配的克隆);若缺失,请提供sf org assign permset --name TPM_Master_Data_Admin --target-org <alias>,并等待完成。SOQL 见references/preflight-and-seed-queries.md第 5 步。确认具有管理员访问权限后方可继续。 - 命名空间发现(见上文)。
- 当前用户的
${PREFIX_UNDER}Sales_Org__c必须等于<salesOrg>。 空值会抛出SOU0003;不匹配会显示为“未找到”(创建/更新)或TransformationError(复制)。SOQL 见references/preflight-and-seed-queries.md第 7 步。空值或!= '<salesOrg>'→ 停止;仅修复用户记录。参见references/conventions-and-payload-rules.mdR6;类别访问权限将在阶段 6 根据 R9 进行预检。
阶段 2 — 验证 BO API 框架状态
只读;用于判断是否需要运行阶段 3。运行
references/preflight-and-seed-queries.md
(“阶段 2 — 验证 BO API 框架状态”)中的三条按销售组织范围筛选的 SOQL 查询:
工作流行、步骤行数、连接行数 — 均限定为 <salesOrg>。
- 工作流行数为零 → 此销售组织为全新状态,阶段 3 将进行播种。
- 状态完整(4 条工作流行
create/update/copy/derive;步骤行 + 连接行均 >0) → 阶段 3 对此销售组织不执行任何操作(仍需运行以确保幂等;3a 会显示新增数为零)。 - 状态不完整 → 错误;停止并报告缺少哪些行。由人工先检查。
阶段 3a — 预览默认 BO API 播种数据(获取 + 确认)
工作流/步骤/连接/实体/输入结构以 CSV 文件形式随附于打包的
TPMSetupData 静态资源中;TPMSetupData.setupBOApi 会读取并
执行 upsert。运行前,向用户展示将要写入的内容。
- 找到并检索
TPMSetupData静态资源到专用的setup-data/子项目(使只读托管资源不会进入阶段 5a 的部署树./.promotion-bo-api-deploy/force-app),然后确认每个目标 sObject 均存在。SOQL + 完整检索步骤 + 8 个 CSV 文件清单 + describe 循环见references/preflight-and-seed-queries.md(“阶段 3a — 检索打包的播种数据”)。任何步骤失败 → 停止并显示打印出的差异/错误。 - 显示每个对象的行数(8 个对象合计约 232 行;“基于
Unique_Key__c执行 upsert — 幂等”)+0_BO_API_Workflow__c.csv和0_BO_API_Workflow_Step__c.csv的前 3 行,并替换其中的{{NS}}/{{SALES_ORG}}。 - 等待确认,除非指定
--yes。“no”/空白 → 正常中止,并指出已检索的 BOApi/ 目录。 - 指定
--dry-run时 → 在此处停止;不要运行 3b。
阶段 3b — 应用默认种子数据
仅在确认 3a 后执行。
PHASE3_START=$(date -u +%FT%TZ)。- 通过匿名 Apex 运行 metadata-wizard setup:
${PREFIX_DOT}TPMSetupData.setupMetadataWizard('<salesOrg>');→sf apex run --target-org <alias> --file ./.promotion-bo-api-deploy/setup-metadata-wizard.apex。 - 等待批处理链完成。
GenericDemoSetupDataBatch会通过finish()自行链接后续批处理,因此按类轮询,并使用连续两次结果为零的中断模式 + 最终的NumberOfErrors/ExtendedStatus检查。SOQL 查询见references/preflight-and-seed-queries.md(“阶段 3b — 轮询批处理链”)。任何Failed/Aborted/NumberOfErrors > 0→ 停止。 - 对
${PREFIX_DOT}TPMSetupData.setupBOApi('<salesOrg>');重复步骤 2–3。
阶段 4 — 验证 BO API 框架已启用
重新运行阶段 2 的查询。预期有 4 条 BO_API_Workflow__c 记录(create/update/copy/derive,Unique_Key__c 唯一),≥46 条 BO_API_Workflow_Step__c 记录,≥54 条 BO_API_Workflow_Workflow_Step__c 关联记录。数量不足 → 停止,并输出缺少的内容。只要有组件失败,就绝不要声称成功。
阶段 5 — 访谈:用户想自定义什么?
框架现已启用。自定义内容由用户提供 — 不要跳过访谈,也不要自行编造答案。打印:
The Promotion BO API framework is installed. To add a custom step, tell me:
- Which BO API entity does it target? (e.g. Promotion/Tactic — I'll list registered entities)
- Which workflows should it fire in? (create, update, copy)
- What does it read from the ingest input?
- What does it write on the SObject or elsewhere?
- Any preconditions or side effects?
Say "use the SetCommentValue reference" for a worked example.
5.1 — 需要收集的答案
| 字段 | 必填 | 说明 |
|---|---|---|
stepName | 是 | PascalCase Apex 类名 + BO API Workflow Step Name。在 ApexClass 中必须唯一。 |
entity | 是 | BO API 实体 Name — 必须与从 ${PREFIX_UNDER}BO_API_Entity__c 实时查询到的某条记录匹配(共享,不按销售组织分区)。常见记录包括 Promotion、Tactic、ProductFilter、ManualInputs、CustomState。查询组织 — 不要假设列表固定。 |
workflows | 是 | {create, update, copy} 的非空子集。derive 不在范围内。 |
actionName | 是 | 类在 call(String action, ...) 中接收的符号操作字符串。约定为 stepName 的 lowerCamelCase 形式。 |
inputPaths | 是(≥1) | 要从 currentInput 读取的 JSON 路径,例如 ["Comment"]。 |
inputPathType | 是 | 输入路径的 JSON 类型 — String、Array、Boolean、Number、Object。此值决定每条 SIS 记录的 RecordType;值错误会在摄取时导致 TransformationError: Expected <Type>。如果类型不同 → 按类型分组,每组运行一次阶段 5b。 |
inputPathRequired | 可选 | ingest payload 中是否必须包含 inputPaths。默认为 false。此值决定 BO_API_Step_Input_Structure__c.Required__c(平台默认值为 true — skill 始终显式写入)。参见 references/generate-and-wire.md。 |
outputWrites | 是(≥1) | {field, source} 对;field 是不带命名空间前缀的目标 API 名称(skill 会添加 ${PREFIX_UNDER}),source 引用 inputPaths 的值 / 字面量 / 计算表达式。 |
description | 是 | 一句话描述步骤 → ${PREFIX_UNDER}Description__c + 类文档字符串。 |
preconditions | 可选 | 写入前的自由文本条件。 |
sortAfter | 可选 | 要排在其后的已命名打包步骤;默认值为 max(Sort__c) + 10。 |
5.2 — 访谈流程
- 预设快捷方式。
--preset set-comment-value(或“使用 SetCommentValue 参考”)会从references/reference-example-set-comment-value.md(assets/set-comment-value/interview-answers.json)加载答案,并跳过交互式访谈。--interview-file <path>会加载与 5.1 表格对应的 JSON(优先于--preset)。 - 逐一提问(或一次性提出一组问题);在每个必填字段都有值之前,不要继续。
- 用户指定实体后,显示该实体可写字段:
任何sf sobject describe --target-org <alias> --sobject '${PREFIX_UNDER}<Entity>__c' --json \ | jq -r '.fields[] | select(.updateable == true and .createable == true) | .name'outputWrites字段都必须出现在这里;否则停止并要求用户换一个字段(FLS 的范围限定为调用方)。 - 确认计划(概述 Class/Entity/Workflows/Reads/Writes/Sort/Description →“部署并接线?[y/N]”)。如果
workflows包含copy,请预先说明两个仅适用于 copy 的前提,避免运行时才发现:(a) 每个子项的输入值来自源 promotion——outputWrites仍会执行,但<inputPaths>的值会被忽略(只有Slogan这类顶层字段会传递);(b) 正在运行的User.Sales_Org__c必须与源模板的Sales_Org__c相同(在 Phase 1 第 7 步预检,并在每次 Phase 6 导入前重新检查;如果流程中途发生偏差,copy会失败并报错TransformationError: The User and the Promotion Template must have the same Sales org——请修复用户记录,不要在流程中途修改;参见references/conventions-and-payload-rules.mdR6)。输入n/留空 → 将答案保存到./.promotion-bo-api-deploy/interview.json并退出。输入y→ 执行 Phase 5a。
Phases 5a / 5b — 生成并部署、注册并接线
操作手册见 references/generate-and-wire.md。简而言之:检查类名以确保幂等性;确认目标字段可写;清理 ./.promotion-bo-api-deploy/force-app/main/default/classes/ 中任何过时的 .cls/.cls-meta.xml(遗留文件会导致 MDAPI 整批回滚且不显示错误);根据访谈内容生成与命名空间无关的 Callable——类和 call 方法都必须为 global(打包的 CallableManager 通过 Type.forName 跨越命名空间边界进行解析;在托管安装中,public 会导致运行时失败——参见操作手册);显示并确认;运行 sf project deploy start。然后,在一个 anon-Apex 事务中,upsert 一条 BO_API_Workflow_Step__c(Classname__c 中填写不带命名空间的类名)、为每个选定的工作流 upsert 一条 BO_API_Workflow_Workflow_Step__c 关联记录(不包含 derive),并 upsert BO_API_Step_Input_Structure__c 记录(根据 inputPathType 设置 RecordType,根据 inputPathRequired 显式设置 Required__c)。两个 upsert 都以 Unique_Key__c 为键(幂等)。
Phase 6 — 通过 REST 端点进行冒烟测试
操作手册见 references/smoke-and-verify.md。根据 BO_API_Step_Input_Structure__c(以架构为准)推导每个调用工作流的负载契约,应用 references/conventions-and-payload-rules.md 中的规则 R1–R9,在销售组织内解析所有引用,生成 ./out/smoke/{create,update,copy}.json,然后为每个选定的工作流运行 initialize → ingest → poll status。通过直接查询 BO_API_Transaction_Log__c 进行验证(任何行的值 != 'Calculated' → 失败),并断言 outputWrites 字段与 ./out/smoke/expected.json 相符。只有在访谈中包含 update 和 copy 时,才运行相应流程。
Phase 7 — 报告
简要状态:组织、销售组织、dry-run 标志;命名空间前缀;seed 行数(Phases 2 和 4 前后);访谈摘要;部署结果 + workflow-step id + junction ids;每个调用工作流的 smoke 导入 ids——全部状态为 Calculated 且断言相符,或准确列出失败项(SOQL、expected、actual);手动后续事项(无问题时填写 none)。
规则
- 只要任何属于本次 smoke 导入 ID 的
BO_API_Transaction_Log__c行满足Status__c != 'Calculated',就绝不能宣称成功。Calculated是最终成功状态(R2);选项列表中不存在Processed。 - 只要任何
outputWrites断言失败,或自阶段开始以来仍有任何GenericDemoSetupDataBatchAsyncApexJob正在运行或已失败,就绝不能宣称成功。 - 预检或验证检查一旦失败,立即停止;报告确切的失败原因。
- 绝不能对命名空间前缀进行硬编码——所有 Apex/SOQL/REST 引用都必须由第 1 阶段的检测结果拼接而成。
- 绝不能臆造 Apex 方法签名——使用已打包的
TPMSetupData/BO_API_*对象以及正式发布的 REST 端点所声明的内容。 - 已植入的 BO API 元数据属于打包内容——在调用
TPMSetupData.setupBOApi之前,先预览并征得用户确认。 - 绝不能臆造访谈答案。若没有用户输入,也没有
--preset/--interview-file,则停止并打印问题。 - 绝不能接入访谈中未包含的工作流。
create/update/copy均需显式选择;derive始终排除。 - 绝不能根据硬编码模板拼接 smoke 负载——按工作流从
BO_API_Step_Input_Structure__c推导可接受的路径,并先根据该契约验证用户输入。 - 绝不能在没有销售组织筛选条件的情况下解析引用值。属于其他销售组织的记录对本次调用无效。
- 绝不能生成可见性为
public的Callable步骤类——类和call方法都必须声明为global。public仅适用于同一命名空间的开发组织;在托管安装中会失败并报错Type "<stepName>" does not exist;事务日志会显示通用的Error。请参阅references/generate-and-wire.md。 - 绝不能在多个步骤部署间重复使用
./.promotion-bo-api-deploy/force-app/main/default/classes/——先删除上一个步骤的.cls/.cls-meta.xml文件。残留文件会触发 MDAPI 整批静默回滚,而 CLI 仍会报告"created": true。 - 插入
BO_API_Step_Input_Structure__c行时,绝不能不显式设置Required__c。平台默认值为true;该技能的默认值为false,只有设置inputPathRequired时才会启用。省略该字段会导致所有可选路径被静默设为必填。 - 未确认运行用户的
${PREFIX_UNDER}Sales_Org__c等于<salesOrg>之前,绝不能越过第 1 阶段——值为空会抛出SOU0003;不匹配会显示“未找到”(create/update)或TransformationError(copy)。仅在用户记录上修复。请参阅references/conventions-and-payload-rules.mdR6。 - 绝不能摄取这样的负载:其
ProductFilter.Criteria.<level>引用了运行用户没有Product_Category_Share__cAccess_Level__c='ReadWrite'权限的类别——请按照references/conventions-and-payload-rules.mdR9 在第 6 阶段预检。不要静默插入共享行。