Salesforce 承認プロセスの設計:スケーラビリティとベストプラクティスに関するアーキテクトの視点

背景と適用シナリオ

Salesforce アーキテクトとして、私は単に機能を実装するだけでなく、それがビジネスプロセス全体の中でどのように機能し、将来の成長にどう対応できるかを常に考えています。その中でも、Approval Process (承認プロセス) は、企業のガバナンス、コンプライアンス、および業務効率を支える上で極めて重要な自動化ツールです。これは、特定のレコード(例えば、見積、経費報告、休暇申請など)が、定められた基準と手順に従ってレビューされ、承認または却下されることを保証するための構造化されたプロセスです。

一般的な適用シナリオは多岐にわたります:

  • 営業部門: 割引率が15%を超える商談の見積もりを、営業マネージャー、そして場合によっては地域統括部長の承認を得る。
  • 人事部門: 従業員からの休暇申請を、直属の上司が承認する。採用候補者のオファーレターを、人事部長と部門長が承認する。
  • 財務部門: 5万円を超える経費報告を、経理部門がレビューし、承認する。
  • 法務部門: 標準以外の契約書を、法務担当者がレビューし、承認する。

これらのプロセスを自動化することで、メールや口頭でのやり取りに起因する遅延や見落としを防ぎ、誰が、いつ、何を承認したかという監査証跡を明確に残すことができます。アーキテクトの観点からは、これらの要件を、スケーラブルで保守性が高く、かつ Salesforce のガバナ制限内で効率的に動作するソリューションに落とし込むことが使命となります。


原理説明

Salesforce の承認プロセスは、一連のステップから構成される宣言的なフレームワークです。アーキテクチャを設計する上で、その構成要素を正確に理解することが不可欠です。

プロセスの構成要素

1. Process Definition (プロセス定義):
これは承認プロセス全体の設定を指します。対象となるオブジェクト(商談、ケースなど)を定義します。

2. Entry Criteria (エントリ条件):
レコードがどの条件を満たしたときに、この承認プロセスを開始できるかを定義します。例えば、「商談の割引率 > 15% AND 金額 > 1,000,000円」といった数式を定義します。この条件は、プロセスが不必要に起動されるのを防ぐための重要なゲートキーパーです。

3. Initial Submission Actions (初回申請アクション):
レコードが承認のために申請された直後に実行されるアクションです。一般的なアクションとして、レコードのロック(申請中の意図しない編集を防ぐため)や、メールアラートの送信、項目自動更新(例:状況を「申請中」に変更)などがあります。

4. Approval Steps (承認ステップ):
プロセスの核心部分です。各ステップには、独自のステップ条件、承認者、および却下時の動作が定義されます。承認者は、特定のユーザー、キュー、関連ユーザー(例:レコード所有者のマネージャー)、または Apex を使用して動的に決定されるユーザーなど、柔軟に指定できます。複数の承認者が必要な場合、「全員の承認が必要 (Unanimous)」か「最初の応答で決まる (First Response)」かを選択できます。

5. Final Approval / Rejection Actions (最終承認/最終却下アクション):
プロセス全体が承認または却下されたときに実行されるアクションです。最終承認時には、項目を更新して「承認済み」とし、関係者に通知を送信します。最終却下時には、レコードのロックを解除し、申請者に理由を通知することが一般的です。

6. Recall Actions (取り消しアクション):
申請者が、承認プロセスが完了する前に申請を取り消した場合に実行されるアクションです。


サンプルコード

承認プロセスは主に宣言的に設定しますが、時には Apex (エイペックス) を使用してレコードをプログラムで承認申請にかける必要があります。例えば、カスタムの Visualforce ページや Lightning Web Component から、複雑なビジネスロジックを経て申請を開始するケースです。このような場合、`Approval` クラスを利用します。

以下のコードは、Salesforce の公式ドキュメントに基づき、特定の商談レコード (Opportunity) を承認申請する方法を示しています。

// 新しい承認申請リクエストを作成します。
Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();

// 申請対象のレコードIDをセットします。
// この例では、特定の商談レコードのIDを直接指定していますが、
// 実際にはトリガのコンテキスト変数や、コントローラから渡されたIDを使用します。
req.setObjectId('006D000000B44s9');

// 承認プロセスの次の承認者を指定します(オプション)。
// この設定は、承認プロセスのステップで「申請者が承認者を選択する」が有効な場合にのみ機能します。
// req.setNextApproverIds(new Id[] {'005D00000015wI0'});

// 申請時のコメントをセットします。
req.setComments('割引率が高いため、本商談の承認を申請します。');

// 申請者を指定します(オプション)。
// 指定しない場合、コードを実行しているカレントユーザーが申請者になります。
// req.setSubmitterId('005D00000015wI0');

// 承認プロセス名を指定します(オプション)。
// オブジェクトに複数の有効な承認プロセスがあり、エントリ条件が曖昧な場合に、
// 特定のプロセスを明示的に起動するために使用します。
// req.setProcessDefinitionNameOrId('Discount_Approval_Process_V2');

// レコードが承認申請された後も、申請者がレコードを編集できるようにするかどうかを設定します。
// デフォルトはfalse(ロックされる)です。
req.setSkipEntryCriteria(false);

try {
    // 承認プロセスを開始します。
    Approval.ProcessResult result = Approval.process(req);

    // 結果をチェックします。
    if (result.isSuccess()) {
        // 成功時の処理
        System.debug('レコードが正常に承認申請されました。Instance ID: ' + result.getInstanceIds()[0]);
    } else {
        // 失敗時の処理
        for(Approval.ProcessResult.Error err : result.getErrors()) {
            System.debug('承認申請に失敗しました。');
            System.debug('  項目: ' + err.getField());
            System.debug('  エラーメッセージ: ' + err.getMessage());
        }
    }
} catch (System.DmlException e) {
    // DML例外が発生した場合の処理(例:トリガによるエラーなど)
    System.debug('承認申請中に予期せぬエラーが発生しました: ' + e.getMessage());
}

このコードは、アーキテクトがカスタムソリューションを設計する際に、標準の承認プロセスをどのようにプログラムから制御できるかを示す好例です。特に `setProcessDefinitionNameOrId` や `setSkipEntryCriteria` などのメソッドは、複雑な要件に対応する上で重要な制御を提供します。


注意事項

承認プロセスを設計・実装する際には、以下の点に特に注意を払う必要があります。これらはシステムの安定性、パフォーマンス、保守性に直接影響します。

権限 (Permissions)

承認申請、承認、却下を行うユーザーは、対象レコードに対する適切なオブジェクト権限(参照、編集)を持っている必要があります。また、承認者は「承認申請を承認」のアプリケーション権限が必要です。Apex から実行する場合、実行ユーザーの権限が適用されるため、権限が不足しているとプロセスが失敗します。

API 制限 (API Limits & Governor Limits)

アーキテクトとして最も注意すべきはガバナ制限です。

  • プロセス定義の制限: 1つのオブジェクトに対して有効化できる承認プロセスは最大300個、合計(有効・無効含む)で1,000個までです。また、1つのプロセスに含められるステップは30個までです。この制限を超えるような複雑なプロセスは、複数のプロセスに分割するか、Flow や Apex によるカスタム実装を検討すべきです。
  • DML 制限: `Approval.process()` メソッドは DML ステートメントを消費します。トリガ内から呼び出す場合、他の DML 操作と合わせてガバナ制限(1トランザクションあたり150回)を超えないように注意が必要です。
  • ロック競合: 承認プロセス中のレコードはロックされ、他のユーザーや自動化プロセスによる更新ができなくなることがあります。特に、更新頻度の高いオブジェクト(例えば、API連携で頻繁に更新されるケースオブジェクトなど)に承認プロセスを適用すると、ロック競合 (Record Locking) が発生し、トランザクションの失敗を引き起こす可能性があります。ロックの範囲と期間を最小限に抑える設計が求められます。

エラー処理 (Error Handling)

Apex から承認プロセスを呼び出す際は、必ず `try-catch` ブロックで例外を捕捉してください。`Approval.process()` の戻り値である `Approval.ProcessResult` オブジェクトを必ず確認し、`isSuccess()` が `false` の場合は `getErrors()` メソッドでエラー内容をログに記録するなどの処理が必要です。エラーの一般的な原因には、エントリ条件を満たしていない、承認者が見つからない、必須項目が入力されていない、などがあります。


まとめとベストプラクティス

Salesforce の承認プロセスは、ビジネスルールを体系化し、統制を効かせるための強力なツールです。しかし、その効果を最大限に引き出し、長期的に維持可能なソリューションを構築するためには、アーキテクトとしての戦略的な視点が不可欠です。

以下に、承認プロセス設計におけるベストプラクティスをまとめます。

  1. 宣言的なアプローチを優先する (Declarative First):
    可能な限り、標準の承認プロセス設定画面で完結させることを目指します。コードによる実装は、宣言的な機能では実現不可能な場合に限定することで、保守性を高めます。
  2. プロセスをシンプルに保つ (Keep it Simple):
    1つの承認プロセスに過剰なステップやロジックを詰め込むのは避けるべきです。プロセスが複雑になりすぎる場合は、複数のプロセスに分割するか、ビジネスプロセス自体の見直しを検討します。
  3. 承認者の動的な管理 (Use Dynamic Approvers Wisely):
    承認者を特定のユーザーとしてハードコーディングするのではなく、「マネージャー」などの階層関係や、「カスタムユーザー項目」を利用して動的に決定する設計を推奨します。これにより、組織変更が発生した際に、承認プロセス自体を変更することなく、データ(ユーザーレコードやカスタム設定)の更新だけで対応できます。
  4. 代替ソリューションを検討する (Consider Alternatives):
    非常に動的で、条件分岐が多岐にわたる複雑な承認ロジックが求められる場合、標準の承認プロセスでは限界があります。そのような場合は、Flow (フロー) を使ってカスタムの承認画面やロジックを構築する方が、柔軟性が高く、スケーラブルなソリューションとなることがあります。ただし、その場合は監査証跡やレコードロックなどを自前で実装する必要があるため、トレードオフを慎重に評価する必要があります。
  5. 徹底した文書化 (Document Everything):
    どのプロセスが、どのような条件で起動し、どのようなステップとアクションを持っているのかを必ず文書化します。これにより、将来の改修やトラブルシューティングが格段に容易になります。

結論として、Salesforce 承認プロセスは単なる自動化機能ではなく、企業の統制と効率性を支えるアーキテクチャの一部です。その設計には、ビジネス要件の深い理解と、プラットフォームの能力と限界に対する技術的な洞察の両方が求められるのです。

コメント