consumer-goods-promotion-bo-api-deploy について
Salesforce 管理者と開発者向けに、Consumer Goods Cloud の Promotion BO API を有効化し、カスタムワークフローステップを追加するスキルです。Apex の生成とデプロイ、登録、対象ワークフローへの接続を行い、REST エンドポイントで検証します。
主な機能
- 01組織の権限とパッケージ名前空間の確認
- 02BO API 初期データのプレビューと適用
- 03Apex ワークフローステップの生成とデプロイ
- 04選択したワークフローへのステップ登録
- 05REST エンドポイントによるフロー検証
活用シーン
- 01Salesforce 組織で Promotion BO API を有効化
- 02作成、更新、コピーのワークフローに手順を追加
- 03Promotion 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 を有効にし、追加するカスタム Workflow Step を 1 つユーザーにヒアリングして、Apex クラスを生成・デプロイし、BO API エンティティに登録します。その後、顧客が選択した {create, update, copy} ワークフローの一部に組み込み、次の 3 つの REST エンドポイントを通じて、最初から最後までの処理を検証します。
POST /services/apexrest/<prefix>/promotions/initializePOST /services/apexrest/<prefix>/promotions/ingestGET /services/apexrest/<prefix>/promotions/status?importId=...
ヘッドレスデリバリーを想定しています。組織を指定してヒアリングに回答すると、このスキルがステップをインストールして検証します。Promotion BO API は CGCloud のマネージドパッケージ(リリース版は cgcloud、dev/beta 版は 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— 販売組織によるパーティション分割、スキーマを基にした契約の導出、ワークフロー間で適用される 9 つのペイロード規則 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、dev/beta 版なら cgcloud_dev、ソースからデプロイした場合はプレフィックスなしです。
sf data query --target-org <alias> \
--query "SELECT NamespacePrefix FROM ApexClass WHERE Name = 'TPMSetupData'" --json
records[0].NamespacePrefix を読み取り、以下で使用する 3 つの変数に割り当てます。
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 …パイプを壊さないようにします(別の方法として、references/smoke-and-verify.mdの「parse caveat」にあるsedによる除去も参照してください)。sf org display --target-org <alias> --json— 接続可能であることを確認し、instanceUrlと組織 ID を取得します(表示/ログ出力のみ)。REST 呼び出しには、CLI に保存されたセッションを使うsf api request restを使用します。このスキルがアクセストークンを取り出すことはありません。- Sales 組織をローカルで検証します。4 文字、大文字、空白以外であることを確認します(
TPMSetupData.validateSalesOrgと同じ検証です)。失敗したら即座に停止します。 - TPM の権限(権限セットライセンス) — 実行ユーザーに
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.mdの R6 を参照してください。カテゴリへのアクセスは、R9 に従ってフェーズ 6 で事前確認します。
フェーズ 2 — BO API フレームワークの状態を確認
読み取り専用です。フェーズ 3 の実行が必要かどうかを確認します。references/preflight-and-seed-queries.md の「Phase 2 — Verify BO API framework state」にある、Sales 組織を対象とする 3 つの SOQL クエリを実行します。ワークフロー行、ステップ行数、ジャンクション行数のすべてを <salesOrg> で絞り込みます。
- ワークフロー行が 0 件 → この Sales 組織にはまだデータがなく、フェーズ 3 でシードします。
- 状態が揃っている(ワークフロー行が 4 件
create/update/copy/derive、ステップ行とジャンクション行がそれぞれ 1 件以上)→ この Sales 組織ではフェーズ 3 は何も変更しません(冪等性を保つため、引き続き実行します。3a では新規追加が 0 件と表示されます)。 - 状態が不完全 → エラー。停止し、不足している行を報告します。まず担当者が確認してください。
フェーズ 3a — デフォルトの BO API シードをプレビュー(取得 + 確認)
ワークフロー/ステップ/ジャンクション/エンティティ/入力構造は、パッケージ内の TPMSetupData 静的リソースに CSV として同梱されています。TPMSetupData.setupBOApi がこれらを読み取り、アップサートします。実行前に、反映される内容をユーザーに表示してください。
TPMSetupData静的リソースを特定して、専用のsetup-data/サブプロジェクトに取得します(読み取り専用の管理対象リソースが、フェーズ 5a のデプロイツリー./.promotion-bo-api-deploy/force-appに含まれないようにします)。次に、対象の各 sObject が存在することを確認します。SOQL、取得手順の全容、8 個の CSV マニフェスト、describe ループについては、references/preflight-and-seed-queries.mdの「Phase 3a — Retrieve the packaged seed」を参照してください。どの手順でも失敗した場合は、出力された差分/エラーを添えて停止します。- オブジェクトごとの行数(8 オブジェクトで合計約 232 行、「
Unique_Key__cでアップサート — 冪等」)と、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)。- anon Apex 経由でメタデータウィザードのセットアップを実行します:
${PREFIX_DOT}TPMSetupData.setupMetadataWizard('<salesOrg>');→sf apex run --target-org <alias> --file ./.promotion-bo-api-deploy/setup-metadata-wizard.apex。 - バッチチェーンが完了するまで待ちます。
GenericDemoSetupDataBatchはfinish()経由で自身をチェーンするため、クラスごとにポーリングし、2 回連続でゼロになったら終了するパターンを使用して、最後にNumberOfErrors/ExtendedStatusを確認します。SOQL はreferences/preflight-and-seed-queries.md(「Phase 3b — Poll the batch chain」)にあります。Failed/Aborted/NumberOfErrors > 0の場合は停止します。 ${PREFIX_DOT}TPMSetupData.setupBOApi('<salesOrg>');について、手順 2〜3 を繰り返します。
フェーズ 4 — BO API フレームワークが有効になっていることを確認する
フェーズ 2 のクエリを再実行します。BO_API_Workflow__c が 4 行
(create/update/copy/derive、Unique_Key__c で一意)、
BO_API_Workflow_Step__c が 46 行以上、BO_API_Workflow_Workflow_Step__c の中間
行が 54 行以上あることを確認します。不足があれば停止し、不足しているものを
出力します。コンポーネントに失敗がある間は、成功したと決して報告しないでください。
フェーズ 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 を 1 回ずつ実行します。 |
inputPathRequired | 任意 | 取り込みペイロードに inputPaths が必要かどうか。デフォルトは false です。BO_API_Step_Input_Structure__c.Required__c を決定します(プラットフォームのデフォルトは true ですが、このスキルでは必ず明示的に設定します)。references/generate-and-wire.md を参照してください。 |
outputWrites | はい(1 つ以上) | {field, source} のペア。field は名前空間プレフィックスなしのターゲット API 名です(スキルが ${PREFIX_UNDER} を追加します)。source は inputPaths の値/リテラル/計算式を参照します。 |
description | はい | ステップを説明する 1 文。${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より優先されます)。 - 質問を 1 つずつ(またはまとめて)尋ね、必須項目すべてに値が入力されるまで先に進まないでください。
- ユーザーがエンティティ名を指定した後、そのエンティティの書き込み可能なフィールドを表示します。
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 固有の前提条件を 2 つ事前に明示してください。(a)子レコードごとの入力値はソースプロモーションから取得されます。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.mdの R6 を参照)。n/空欄の場合は、回答を./.promotion-bo-api-deploy/interview.jsonに保存して終了します。yの場合は Phase 5a に進みます。
Phase 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 を実行します。次に、1 つの anon-Apex トランザクション内で、BO_API_Workflow_Step__c を 1 件(Classname__c にはクラス名のみを指定)、選択したワークフローごとに BO_API_Workflow_Workflow_Step__c のジャンクションを 1 件(derive は除外)、および BO_API_Step_Input_Structure__c の行(inputPathType に応じた RecordType を設定し、Required__c は inputPathRequired に基づいて明示的に設定)を upsert します。どちらの 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 を適用し、すべての参照を同じ Sales Org 内で解決して、./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 — 報告
簡潔に状況を報告します。組織、Sales Org、dry-run フラグ、名前空間のプレフィックス、シード行の前後の件数(Phase 2 と 4)、インタビューの概要、デプロイ結果と workflow-step id およびジャンクション ID、呼び出した各ワークフローのスモークインポート ID を含めます。スモークテストは、すべて Calculated でアサーションが一致したか、失敗した場合はその内容(SOQL、期待値、実際の値)を正確に報告します。手動での対応事項も記載し、問題がなければ none とします。
ルール
- スモークインポート ID に対応する
BO_API_Transaction_Log__cレコードにStatus__c != 'Calculated'のものがある間は、成功を報告しないでください。Calculatedは最終的な成功状態です(R2)。ピックリストにProcessedは存在しません。 outputWritesのアサーションが失敗している場合、またはフェーズ開始後に実行されたGenericDemoSetupDataBatchのAsyncApexJobが実行中もしくは失敗状態にある場合は、成功を報告しないでください。- 失敗したプリフライトチェックまたは検証チェックが最初に見つかった時点で停止し、正確な失敗内容を報告してください。
- 名前空間プレフィックスをハードコードしないでください。Apex/SOQL/REST の参照はすべて、フェーズ 1 で検出した値から組み立ててください。
- Apex メソッドのシグネチャを推測しないでください。パッケージに含まれる
TPMSetupData/BO_API_*オブジェクトと、提供されている REST エンドポイントの宣言内容を使用してください。 - シード済みの BO API メタデータはパッケージに含まれるコンテンツです。
TPMSetupData.setupBOApiを呼び出す前にプレビューし、ユーザーの確認を得てください。 - ユーザーからの回答も
--preset/--interview-fileもない場合、面接の回答を捏造しないでください。停止して質問を表示してください。 - 面接に含まれていないワークフローを接続しないでください。
create/update/copyはオプトイン方式で、deriveは常に除外します。 - ハードコードされたテンプレートからスモークペイロードを組み立てないでください。ワークフローごとに
BO_API_Step_Input_Structure__cから受け付けるパスを導出し、ユーザー入力がその契約に準拠していることを事前に検証してください。 - 営業組織のフィルターなしに参照値を解決しないでください。別の営業組織に属するレコードは、この呼び出しでは無効です。
Callableステップクラスの可視性をpublicにしないでください。クラスと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と報告されます。 Required__cを明示的に設定せずにBO_API_Step_Input_Structure__cレコードを挿入しないでください。プラットフォームのデフォルト値はtrueですが、このスキルのデフォルト値はfalseであり、inputPathRequiredが設定された場合にのみ有効にします。このフィールドを省略すると、すべての任意パスが必須になります。- フェーズ 1 を終える前に、実行ユーザーの
${PREFIX_UNDER}Sales_Org__cが<salesOrg>と一致することを確認してください。空欄の場合はSOU0003が発生し、不一致の場合はcreate/updateで「見つかりません」、copyでTransformationErrorが表示されます。ユーザーレコードのみを修正してください。references/conventions-and-payload-rules.mdの R6 を参照してください。 - 実行ユーザーが
Product_Category_Share__cのAccess_Level__c='ReadWrite'を持たないカテゴリをProductFilter.Criteria.<level>が参照するペイロードを取り込まないでください。これはreferences/conventions-and-payload-rules.mdの R9 に従うフェーズ 6 のプリフライトチェックです。共有レコードを黙って挿入しないでください。