Salesforce 承認プロセス周りの自動化

# 初めに
- ビジネスプロセスにおいて、承認を取らないといけないような業務って多々あったりして、それをSalesforceで行うには承認プロセスを使うのが手っ取り早いのです。
- しかし、この承認プロセス周りの自動化を行おうと思うと結構複雑だったのでメモしておきます。

## 前提
- [Chatterでの承認](https://help.salesforce.com/s/articleView?id=sf.approvals_for_chatter_enabling.htm&language=ja&type=5)が有効化されていること
- Communityページに公開するレコードに対して承認プロセスが設定されていること

## やりたかった自動化1: 承認されたら承認プロセス開始時のChatterポストに承認者として、承認コメントを返信
- 承認プロセスの管理は、[`ProcessInstance`](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_processinstance.htm?q=ProcessInstance) オブジェクトで管理される
- 承認コメントは、[`ProcessInstanceHistory`](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_processinstancehistory.htm)オブジェクトに格納される
- 困ったことに、これらのオブジェクトはフローからもApexトリガからもトリガ対象のオブジェクトにすることができない。。

### やったこと
- 結局、承認プロセスに自動値更新で更新された値に対してApexトリガを作った
    - 例えば、対象オブジェクトの状況が変更されたら、、みたいな形

#### サンプルロジック
- [`ProcessDefinition`オブジェクト](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_processdefinition.htm?q=ProcessDefinition)から対象承認プロセスのIdを取得する
- 申請者のユーザIdを取得する（今回はテスト環境だったので、１ユーザのみに絞っているが、本来であれば`ProcessInstance`の`SubmittedById`を使用するべき）
- `ProcessInstance`オブジェクトと`ProcessInstanceHistory`オブジェクトをJOINさせ、トリガ対象オブジェクトのレコードIdと承認プロセスIdでクエリする
- 取得した`ProcessInstanceHistory`レコードの承認者ユーザId (`ActorId`)と、申請者Id (`SubmittedById`)が違う場合、承認者のコメントのため、取得
- トリガされたレコードの最終更新者が承認者の場合（ワークフローで承認された場合、自動値更新されているはずのため）申請者が投稿したChatterポストを取得し、`FeedComment`レコードを新たに作る

:::note warn
`ProcessInstanceHistory`オブジェクトをJOINする際は、なぜか `StepsAndWorkItems`をSOQLでは指定しないといけない点に注意
:::

```Apex: 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](https://help.salesforce.com/s/articleView?id=sf.collab_post_visibility.htm&type=5)によると、レコードを親にもつ投稿は、参照権限があれば見えてしまうらしい
- よって、[`FeedItem`](https://developer.salesforce.com/docs/atlas.en-us.object_reference.meta/object_reference/sforce_api_objects_feeditem.htm?q=FeedItem)の`Visibility`項目を`InternalUsers`に変更すればOKのはず
- 承認プロセスによって投稿された`FeedItem`レコードに対して、トリガフロー、およびApexトリガ(After Update)は動作しない(ワークフロールール、あるいはコミット後にChatter投稿が実行されるから？これはよくわからなかった)

### 解決策
- これはコードは不要で、トリガフローに対して非同期パスを設定すればOKだった

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/484310/10162044-025b-9854-f88e-4dfa290b910f.png)

以上、誰かしらの参考になれば幸いです。
他にも良い方法や間違いがあれば是非是非コメントください。