レコード詳細ページに承認と却下ボタンを表示して承認プロセスを実行します。
背景
お客さんから「レコード詳細ページで承認、却下を行いたいですが可能でしょうか」という要望がありました。
その要望を対応するためレコードページで承認、却下のボタンを出してみました。
SFのレコードページで「承認申請」という標準ボタンがあって承認申請を提出できます。
承認と却下の手続きは「承認履歴」から承認ページに移動して行うことが必要です。
それはSalesforce の標準の承認流れです。
Salesforce のフロー、少しのApexを使って「承認」、「却下」ボタンをレコードページに出しながら標準の承認プロセス流れをカスタマイズしていきます。
今回の作りは全オブジェクトに汎用で利用できます。もちろん承認プロセスが既にあるオブジェクトだけ動きます。
結果

作成するもの
- 承認却下を行うApex クラス(テストクラス含む)
- アクション画面フロー作成
1.承認却下を行うApex クラス(テストクラス含む)
先ずはApex クラスを作成しましょう。
Sandbox 環境で開発コンソールを開きます。ファイル / 新規 / Apex クラスをおして
名前のところに「FlowApprovalHandler」を入れます。
以下のコードをそのままコピーして保存します。変更しなくても大丈夫です。
public with sharing class FlowApprovalHandler {
@InvocableMethod(label='レコードページ承認' description='最終の未承認申請を承認する')
public static List<ApprovalResult> processLatestPendingOrders(List<ApprovalRequest> requests) {
List<ApprovalResult> results = new List<ApprovalResult>();
for (ApprovalRequest request : requests) {
try {
// 最終の未承認申請を取得
ProcessInstanceWorkitem latestPendingWorkItem = [
SELECT Id, ProcessInstanceId
FROM ProcessInstanceWorkitem
WHERE ProcessInstance.TargetObjectId = :request.recordId
AND ProcessInstance.Status = 'Pending'
ORDER BY ProcessInstance.CreatedDate DESC
LIMIT 1
];
if (latestPendingWorkItem != null) {
Approval.ProcessWorkitemRequest approvalRequest = new Approval.ProcessWorkitemRequest();
approvalRequest.setWorkitemId(latestPendingWorkItem.Id);
approvalRequest.setComments(request.comments);
if (request.action.equalsIgnoreCase('approve')) {
approvalRequest.setAction('Approve');
} else if (request.action.equalsIgnoreCase('reject')) {
approvalRequest.setAction('Reject');
} else {
throw new IllegalArgumentException('Invalid action: ' + request.action);
}
// 承認プロセスを行う
Approval.ProcessResult processResult = Approval.process(approvalRequest);
results.add(new ApprovalResult(request.recordId, processResult.isSuccess()));
} else {
// 未承認申請が見つからない場合
results.add(new ApprovalResult(request.recordId, false, 'No pending approval request found.'));
}
} catch (Exception e) {
// Handle exceptions
results.add(new ApprovalResult(request.recordId, false, e.getMessage()));
}
}
return results;
}
public class ApprovalRequest {
@InvocableVariable(label='Record ID' required=true)
public String recordId;
@InvocableVariable(label='Comment')
public String comments;
@InvocableVariable(label='Action' required=true)
public String action; // values: 'approve' or 'reject'
}
public class ApprovalResult {
@InvocableVariable
public String recordId;
@InvocableVariable
public Boolean isSuccess;
@InvocableVariable
public String errorMessage;
public ApprovalResult(String recordId, Boolean isSuccess) {
this.recordId = recordId;
this.isSuccess = isSuccess;
this.errorMessage = '';
}
public ApprovalResult(String recordId, Boolean isSuccess, String errorMessage) {
this.recordId = recordId;
this.isSuccess = isSuccess;
this.errorMessage = errorMessage;
}
}
}
Apex次はテストクラスを作成します。
そのままコピーして使えます。
@isTest
public class TestFlowApprovalHandler {
//承認テスト
@isTest
static void testApprovalProcess() {
setupTestData('approve', true);
}
// 却下テスト
@isTest
static void testRejectionProcess() {
setupTestData('reject', true);
}
// 無効テスト
@isTest
static void testInvalidAction() {
setupTestData('invalidAction', false);
}
static void setupTestData(String action, Boolean expectedSuccess) {
Account testAccount = new Account(Name='Test Account');
insert testAccount;
Approval.ProcessSubmitRequest req = new Approval.ProcessSubmitRequest();
req.setComments('Submitting for approval');
req.setObjectId(testAccount.Id);
Approval.process(req);
runTestProcessAction(action, testAccount.Id, expectedSuccess);
}
static void runTestProcessAction(String action, Id recordId, Boolean expectedSuccess) {
FlowApprovalHandler.ApprovalRequest testRequest = new FlowApprovalHandler.ApprovalRequest();
testRequest.recordId = recordId;
testRequest.action = action;
testRequest.comments = 'Test comment';
Test.startTest();
List<FlowApprovalHandler.ApprovalResult> results = FlowApprovalHandler.processLatestPendingOrders(new List<FlowApprovalHandler.ApprovalRequest>{testRequest});
Test.stopTest();
// Assertions
System.assertNotEquals(null, results, 'Results should not be null');
System.assertEquals(1, results.size(), 'There should be one result');
FlowApprovalHandler.ApprovalResult result = results[0];
System.assertEquals(expectedSuccess, result.isSuccess, 'Result success should match expected');
if (!expectedSuccess) {
System.assertNotEquals('', result.errorMessage, 'Error message should not be empty for failures');
}
}
}
ApexApex コードを保存できたら開発コンソールが閉じられます。
次はフローを作成します。
2.アクション画面フロー作成
「【汎用】承認」という名前で新規画面フローを作成します。
フローのイメージは以下の通りです。

- 画面からレコードIDを受けるためrecordId 変数をデータ型テキストで作成します。
- コメント画面に画面フローのロングテキストエリアを使って承認、却下のコメント入力欄を作成します。API参照名に CommentInput を付けます。
- プロセスインスタンスを取得ステップにプロセスインスタンスオブジェクトから申請データを取得します。条件は 「TargetObjectId = recordId かつ Status = Pending 」↓イメージ

- 未承認の申請があるかどうかを確認します。
- 承認却下アクションに先ほど作成した Apex Class を呼びます。呼ぶ時に必要な情報を設定します。
- Action というテキスト変数をデフォルト値 「approve」で作成します。
- errorMessageというテキスト変数を作成します。
- isSuccess という Boolean 変数を作成します。
- recrodIdOutput というテキスト変数を作成します。
- Apex アクションステップのインプット欄に上記ステップで作成した変数を設定します。イメージは以下の通りです。

- アクションのアウトプット欄に以下の通り設定します。

- 完了画面に「レコードが承認されました。」というメッセージを表示します。
- 未承認申請がないメッセージ画面に「このレコードで未承認申請がありません。」のメッセージを表示します。
- フローを保存と有効します。
こんな感じで汎用承認アクションフローの作成が終わりました。
却下のためにもステップ2の同じやり方で作成します。違うところはフローの名前【汎用】却下とAction というテキスト変数をデフォルト値に 「reject」を付けるだけです。
レコードページに承認、却下ボタン表示
ボタンを表示したいオブジェクトのオブジェクト設定に移動します。
ボタン、リンク、およびアクションから「新規アクション」を作成します。
アクションの種別にフローを選択して上に作成したフローを選択するだけです。
承認ボタン作成イメージ↓

次はページレイアウトに移動してボタンを表示します。却下ボタンを同じやり方で作成します。フローを選択する時逆に間違いないようにご注意ください。
以上です。
簡単でしょう!
最後に
レコードページに承認と却下ボタンを表示して承認プロセスを実行できることを紹介しました。
その他設定としてレコードの編集ページからボタンの表示条件に申請中のステータスを持つレコードだけ承認と却下ボタンを表示する設定をしたり、承認者権限を持つユーザだけボタンを表示するようにしたりする必要があるかなと思います。
今回の作りで承認と却下アクションだけを行うので項目更新などを実行したいなら承認プロセスに変更してください。
コメントを残す