初めに
- ビジネスプロセスにおいて、承認を取らないといけないような業務って多々あったりして、それをSalesforceで行うには承認プロセスを使うのが手っ取り早いのです。
- しかし、この承認プロセス周りの自動化を行おうと思うと結構複雑だったのでメモしておきます。
前提
- Chatterでの承認が有効化されていること
- Communityページに公開するレコードに対して承認プロセスが設定されていること
やりたかった自動化1: 承認されたら承認プロセス開始時のChatterポストに承認者として、承認コメントを返信
- 承認プロセスの管理は、
ProcessInstance
オブジェクトで管理される - 承認コメントは、
ProcessInstanceHistory
オブジェクトに格納される - 困ったことに、これらのオブジェクトはフローからもApexトリガからもトリガ対象のオブジェクトにすることができない。。
やったこと
- 結局、承認プロセスに自動値更新で更新された値に対してApexトリガを作った
- 例えば、対象オブジェクトの状況が変更されたら、、みたいな形
サンプルロジック
-
ProcessDefinition
オブジェクトから対象承認プロセスのIdを取得する - 申請者のユーザIdを取得する(今回はテスト環境だったので、1ユーザのみに絞っているが、本来であれば
ProcessInstance
のSubmittedById
を使用するべき) -
ProcessInstance
オブジェクトとProcessInstanceHistory
オブジェクトをJOINさせ、トリガ対象オブジェクトのレコードIdと承認プロセスIdでクエリする - 取得した
ProcessInstanceHistory
レコードの承認者ユーザId (ActorId
)と、申請者Id (SubmittedById
)が違う場合、承認者のコメントのため、取得 - トリガされたレコードの最終更新者が承認者の場合(ワークフローで承認された場合、自動値更新されているはずのため)申請者が投稿したChatterポストを取得し、
FeedComment
レコードを新たに作る
ProcessInstanceHistory
オブジェクトをJOINする際は、なぜか StepsAndWorkItems
をSOQLでは指定しないといけない点に注意
SampleApexTrigger
trigger PostChatterApprovalCommentByManager on SOBject (after update) {
for (CustomObject__c co : Trigger.New) {
if (co.Status__c == 'Something') {
// Get Target Approval Process Id
ProcessDefinition pd = [SELECT Id FROM ProcessDefinition WHERE Name = 'YourApprovalProcessName'];
// Get Submitter User Id (It is always System Admin User)
User submittedUser = [SELECT Id FROM User WHERE LastName = 'SubmitterLastName'];
// In order to get Approver's comment
ProcessInstance pi = [SELECT Id, TargetObjectId, Status,
(SELECT Comments, ActorId FROM StepsAndWorkItems) FROM ProcessInstance
WHERE ProcessDefinitionId = :pd.Id AND SubmittedById = :submittedUser.Id AND TargetObjectId = :co.Id];
String comment;
Id approverUserId;
// Get Approver's comment
for(ProcessInstanceHistory sawi : pi.StepsAndWorkItems) {
System.debug('ProcessInstanceHistory record - ' + sawi);
if(sawi.ActorId != submittedUser.Id) {
comment = sawi.Comments;
approverUserId = sawi.ActorId;
break;
}
}
System.debug(comment);
// Only post chatter if approver is updated this record
if(co.LastModifiedById == approverUserId) {
FeedItem fi = [SELECT Id FROM FeedItem
WHERE CreatedById = :submittedUser.Id AND
ParentId = :pi.TargetObjectId AND
Type = 'ApprovalPost'
ORDER BY CreatedDate DESC
LIMIT 1];
FeedComment fc = new FeedComment();
fc.FeedItemId = fi.Id;
fc.CommentBody = comment;
fc.CreatedById = approverUserId;
insert fc;
}
}
}
}
やりたかった自動化2: デフォルトだと承認プロセス開始時のChatterポストがCommunity側に表示されてしまうので、自動で社内ユーザ向けの投稿に変更したい
- まあ、普通にそんな内部ポストが見えてしまうのはありえません
- Helpによると、レコードを親にもつ投稿は、参照権限があれば見えてしまうらしい
- よって、
FeedItem
のVisibility
項目をInternalUsers
に変更すればOKのはず - 承認プロセスによって投稿された
FeedItem
レコードに対して、トリガフロー、およびApexトリガ(After Update)は動作しない(ワークフロールール、あるいはコミット後にChatter投稿が実行されるから?これはよくわからなかった)
解決策
- これはコードは不要で、トリガフローに対して非同期パスを設定すればOKだった
以上、誰かしらの参考になれば幸いです。
他にも良い方法や間違いがあれば是非是非コメントください。