Power Automate で稟議書を実装してみた5 (Power Automate 編3)

## 経緯

[前回](https://qiita.com/miyamiya/items/1ef9cab407499a64477e) は、承認ルートの階層分繰り返す処理に入り、承認ルートがなくなった時に抜ける処理まで説明しました

今回は少し長いですが、承認ルートがあった時に下記の処理を行うところを説明します

1. 確認者にメールを送り
2. 承認者に承認依頼を送り、承認されたときと否認された情報を登録し
3. 承認数が設定を越えていたら、次の承認ルートに進み、越えていなかったら否認として処理する

[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-05_21h08_23-211x300.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-05_21h08_23.png)


## グループマスタリストから、アイテムを取得

申請経路に設定されたグループの参照先を取得し、登録されている承認者と確認者を取得します
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_03h22_58-300x119.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_03h22_58.png)

この処理を **SharePoint コネクタの「項目の取得」** で行います
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_03h25_43-300x119.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_03h25_43.png)

ID は下記のようになっています

```json
@{body('作成されたアイテムに設定された承認ルートの取得')?[variables('動的列名用オブジェクト')['PathTitle']]?['Id']}
```

1 週目だと `variables('動的列名用オブジェクト')['PathTitle']` で**「Path1」**という文字列になり、`body('作成されたアイテムに設定された承認ルートの取得')?['Path1']` で参照列の値 **「グループ1」** となり、`グループ1?['Id']` で参照列の ID を取得しています
つまり、**グループマスタのグループ名「グループ1」のアイテムを取得する**という処理です

## 確認者分メールを送る

確認者として登録されたユーザーに確認メールを送ります
繰り返し処理を、**コントロールコネクタの「Apply to each」**で行います

Apply to each を使うことにより、設定された値(コレクション)の分処理を行いますが、値が空だと処理は行われません

**以前の手順から出力を選択**は、動的コンテンツから「グループマスタリストから、アイテムを取得」の「確認者」を選びます
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_03h58_14-300x173.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_03h58_14.png)

続いて、右にある **・・・** をクリックして、設定をクリックします
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_04h03_49-300x100.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_04h03_49.png)

コンカレンシー制御をオンにして、並列処理の次数を 20 としています
これにより、メール送信を待たずに確認者に登録されたユーザーへどんどんメールを送ることが可能となります
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_04h05_49-300x164.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_04h05_49.png)

### 確認メールの送信

次に、**Office 365 Outlook コネクタの「メールの送信(V2)」**を使って、メールを送信します
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_03h56_27-232x300.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_03h56_27.png)

* 宛先は「確認者 Email」としています
* 件名、本文、は適当に作成しています  
  本文例

```html
<p>@{triggerBody()?['Author']?['DisplayName']} さんからワークフローが届いています<br>
<br>
@{triggerBody()?['Title']}<br>
<br>
<a href="@{triggerBody()?['{Link}']}">申請文はこちら</a> </p>
```
* 差出人は「登録者 Email」としています

## 承認者分メールとチャットを送る

続いて、承認者として登録されたユーザーに承認依頼(メール)と通知するためにチャットを送りますが、こちらは、**コントロールコネクタの「Apply to each」** を並列分岐として追加します
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h15_42-300x74.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h15_42.png)

**以前の手順から出力を選択**は、動的コンテンツから「グループマスタリストから、アイテムを取得」の「承認者」を選びます
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h18_05-300x216.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h18_05.png)

こちらも先程と同様に、右にある **・・・** をクリックして、設定をクリックして、コンカレンシー制御をオンにして、並列処理の次数を 20 としています

この設定を行わないと、順次処理になってしまい、A さんが承認、または、否認をしたら B さんの承認依頼が作成されて、というように一人づつの処理になってしまいます

### 承認を作成

次に **承認コネクタの「承認を作成」** を使って、承認を作成します
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h35_49-300x176.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h35_49.png)

* Assigned to は「承認者 Email」としています
* Title, Details は適当に作成しています  
  Details 例

```markdown
## @{triggerBody()?['Author']?['DisplayName']} さんからワークフローが届いています
* @{triggerBody()?['Title']}
```

* Item link は「申請ワークフローリストにアイテムが追加されたとき」の「アイテムへのリンク」を使います
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h38_28-300x202.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h38_28.png)

依頼のメールは次のように届き、メールで承認や拒否を行うことができます
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h08_10-300x160.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h08_10.png)

### メッセージをフローボットとしてユーザーに投稿する

承認依頼が届いていることを、チャットでも通知すると親切と思います
**Microsoft Teams コネクタの「メッセージをフローボットとしてユーザーに投稿する」** を使います
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h52_12-300x195.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h52_12.png)

* Recipient は「承認者 Email」としています
* Message は適当に作成しています
  Message 例
  
```markdown
* タイトル  
  @{body('承認を作成')?['title']}

* [承認依頼はこちら](@{body('承認を作成')?['respondLink']})
* [申請文はこちら](@{body('承認を作成')?['itemLink']})
```
* 詳細オプションを表示して、IsAlert を「はい」にしています

### 承認を待機する

承認を作成したら、ユーザーのアクションを待つ必要があるので、**承認コネクタの「承認を待機」**を使います
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h54_31-300x190.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_14h54_31.png)
承認 ID は、「承認を作成」の Approval ID を使います

### 承認アクション時、登録されたアイテムの最新状態を取得

ユーザーからのアクションがあったとき、登録されたアイテムの最新状態から更新したいので、アイテムを取得します
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h05_19-300x119.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h05_19.png)

## 承認結果を判定

ユーザーのアクションが承認だったかどうかの判定を行います
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h12_31-300x123.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h12_31.png)

「承認を待機」の結果が「承認」、または、「Approve」かどうかを判定しています
「または」を使っているのは、言語設定の問題などを考慮したつもりです

また、今回試したフローでは、はいの場合といいえの場合の違いは、承認数に値を加えるかどうかだけですので、次の項目からは並行して説明します

### 現在の承認数に +1 、現在の否認数に +1

先程「承認アクション時、登録されたアイテムの最新状態を取得」で取得した承認数、または否認数をカウントアップする処理です

**変数コネクタの「変数の設定」**を使います

[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h27_30-300x35.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h27_30.png)

「現在の承認数に +1」の時

```
@{add(int(body('承認アクション時、登録されたアイテムの最新状態を取得')?[variables('動的列名用オブジェクト')['PathAcceptTitle']]),1)}
```

「現在の否認数に +1」の時

```
@{add(int(body('承認アクション時、登録されたアイテムの最新状態を取得')?[variables('動的列名用オブジェクト')['PathRejectTitle']]),1)}
```

動的列名用オブジェクトから、列名を取得している部分ですが、1 周目だと下記の文字列になります

* `variables('動的列名用オブジェクト')['PathAcceptTitle']` には「Path1Accept」
* `variables('動的列名用オブジェクト')['PathRejectTitle']` には「Path1Reject」

最新状態のアイテム情報から、その列名の値を取得して整数(int)に変換後、式 add を使って 1 足しています

### 承認ログと承認コメントの生成、否認ログと否認コメントの生成

応答はコレクションになっているため、**コントロールコネクタの「Apply to each」** を使って値を取得します

以前の手順から出力を選択に、「承認を待機」の応答を設定します
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h50_26.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h50_26.png)

### 承認ログの生成、否認ログの生成

だれが承認(or 否認)したというログを残したいので、ログの生成を行います

**変数コネクタの「変数の設定」**を使います

[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h55_06-300x40.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_15h55_06.png)

違いは、利用する動的コンテンツと文言が微妙に違うだけです

「承認ログの生成」の時

```
[@{convertFromUtc(utcNow(), 'Tokyo Standard Time', 'yyyy-MM-dd HH:mm:ss')}] @{items('承認ログと承認コメントの生成')?['responder']?['displayName']}さんが承認しました
@{body('承認アクション時、登録されたアイテムの最新状態を取得')?['Log']}
```

「否認ログの生成」の時

```
[@{convertFromUtc(utcNow(), 'Tokyo Standard Time', 'yyyy-MM-dd HH:mm:ss')}] @{items('否認ログと否認コメントの生成')?['responder']?['displayName']}さんが否認しました
@{body('承認アクション時、登録されたアイテムの最新状態を取得')?['Log']}
```

### 承認コメントの生成、否認コメントの生成

承認(or 否認)時のコメントを残したいので、コメントの生成を行います
こちらも、**変数コネクタの「変数の設定」**を使います
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h01_54-300x51.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h01_54.png)

「承認コメントの生成」の時

```
[@{convertFromUtc(body('承認を待機')?['completionDate'], 'Tokyo Standard Time', 'yyyy-MM-dd HH:mm:ss')}]  @{items('承認ログと承認コメントの生成')?['responder']?['displayName']}さんが承認しました
@{items('承認ログと承認コメントの生成')?['comments']}

@{body('承認アクション時、登録されたアイテムの最新状態を取得')?[variables('動的列名用オブジェクト')['PathCommentTitle']]}
```

「否認コメントの生成」の時

```
[@{convertFromUtc(body('承認を待機')?['completionDate'], 'Tokyo Standard Time', 'yyyy-MM-dd HH:mm:ss')}] @{items('否認ログと否認コメントの生成')?['responder']?['displayName']}さんのコメント
@{items('否認ログと否認コメントの生成')?['comments']}

@{body('承認アクション時、登録されたアイテムの最新状態を取得')?[variables('動的列名用オブジェクト')['PathCommentTitle']]}
```

現在のコメントを取得している部分で、動的列名用オブジェクトから列名を取得している部分ですが、 1 周目だと下記の文字列になります

* `variables('動的列名用オブジェクト')['PathCommentTitle']` には「Path1Comment」

### 承認登録データ、否認登録データ

登録処理はまとめて行うので、登録データを生成しています

[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h11_06-300x69.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h11_06.png)

「承認登録データ生成」の時

```json
{
  "Log": "@{variables('ログ')}",
  "CountResult": @{variables('承認数一時保持変数')},
  "CountResultTitle": "@{variables('動的列名用オブジェクト')['PathAcceptTitle']}",
  "Comment": "@{variables('コメント一時保持変数')}",
  "CommentResultTitle": "@{variables('動的列名用オブジェクト')['PathCommentTitle']}"
}
```

「否認登録データ生成」の時

```json
{
  "Log": "@{variables('ログ')}",
  "CountResult": @{variables('承認数一時保持変数')},
  "CountResultTitle": "@{variables('動的列名用オブジェクト')['PathRejectTitle']}",
  "Comment": "@{variables('コメント一時保持変数')}",
  "CommentResultTitle": "@{variables('動的列名用オブジェクト')['PathCommentTitle']}"
}
```

オブジェクトのプロパティを説明すると次のようになります

* Log には、生成されたログ
* CountResult には、カウントアップ後の承認数(or 否認数)
* CountResultTitle には、カウントアップ後の数値を更新する列名
* Comment には、ユーザーが記入したコメント
* CommentResultTitle には、コメントを更新する列名

### 承認・否認データの更新

更新は今まで同様に、**SharePoint コネクタの「SharePoint に HTTP 要求を送信します」**を使います
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h22_17-300x268.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h22_17.png)

今までとの違いはボディだけですが、周回や条件(承認 or 否認)により、登録する列が違うため、更新する列名も動的に作られているのは今までと違います

ボディの例

```json
{
    '__metadata': {'type': 'SP.Data.ApplyListItem'},
    'Log': '@{variables('登録用データ')['Log']}',
    '@{variables('登録用データ')['CountResultTitle']}': '@{variables('登録用データ')['CountResult']}',
    '@{variables('登録用データ')['CommentResultTitle']}': '@{variables('登録用データ')['Comment']}'
}
```

これで承認者分のメールとチャットを送る部分は終わりです

## 承認数が設定値以上の時、次の承認経路へ進む

### 最新の情報を取得

[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h36_40-300x118.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h36_40.png)

承認ルートに設定されたすべての承認者のアクションが終わった後、承認した人数が、必要承認数を越えているかの判定を行います

まずはその前に、今まで同様、最新のアイテムを取得する必要があります
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h30_39-300x121.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h30_39.png)

### 承認数が設定値以上か

承認数が設定値以上か判定します
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h37_52-300x84.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h37_52.png)

左辺は、最新のアイテムから、現在周回の承認数を取得しています

```
@body('最新の情報を取得')?[variables('動的列名用オブジェクト')['PathAcceptTitle']]
```

右辺は、承認ルートの設定から、必要承認数を取得しています

```
@body('作成されたアイテムに設定された承認ルートの取得')?[variables('動的列名用オブジェクト')['PathApprovalsTitle']]
```

### はいの場合

ログを生成して、ログを更新しています
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h42_45-248x300.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h42_45.png)

ログに設定している値の例

```
[@{convertFromUtc(utcNow(), 'Tokyo Standard Time', 'yyyy-MM-dd HH:mm:ss')}] 承認されたので次の承認経路に移ります
@{body('最新の情報を取得')?['Log']}
```

更新時のボディの例

```json
{
    '__metadata': {'type': 'SP.Data.ApplyListItem'},
    'Log': '@{variables('ログ')}'
}
```

### いいえの場合

いいえの場合は、否認とみなしてステータスとログを更新後、終了フラグに true を設定しています
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h46_15-198x300.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_16h46_15.png)

ログに設定している値の例

```
[@{convertFromUtc(utcNow(), 'Tokyo Standard Time', 'yyyy-MM-dd HH:mm:ss')}] 承認数が足りないため否認されました
@{body('最新の情報を取得')?['Log']}
```

更新時のボディの例

```json
{
    '__metadata': {'type': 'SP.Data.ApplyListItem'},
    'Log': '@{variables('ログ')}',
    'Status': '@{variables('ステータス用オブジェクト')['Rejected']}'
}
```

## 承認ルートのループカウンターを 1 プラス

「承認ルートの階層分繰り返す」処理の最後に、変数の承認ルートカウンターに 1 足します
**変数コネクタの「変数の値を増やす」**を使います
[![](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_18h15_55-300x70.png)](https://1bed.allright.life/wp-content/uploads/2020/05/2020-05-06_18h15_55.png)
これで、承認されている時には次のループに移ることができます

## 考察

こういうように文章に起こしながら、改めて見直すと直したいところが多数ありますね...
次回は終了処理部分の説明を行います

## 関連記事

自分のためのメモに近い記事なのですが、長いのでインデックスつけます

* [Power Automate で稟議書を実装してみた1 (GUI で環境準備編)](https://qiita.com/miyamiya/items/2f8290fa4541cc3eb953)
* [Power Automate で稟議書を実装してみた2 (CUI で環境準備編)](https://qiita.com/miyamiya/items/9d28749ed9bdec8a6cee)
* [Power Automate で稟議書を実装してみた3 (Power Automate 編1)](https://qiita.com/miyamiya/items/35ca2d1cd05bc77396ce)
* [Power Automate で稟議書を実装してみた4 (Power Automate 編2)](https://qiita.com/miyamiya/items/1ef9cab407499a64477e)
* Power Automate で稟議書を実装してみた5 (Power Automate 編3)
* [Power Automate で稟議書を実装してみた6 (Power Automate 編4)](https://qiita.com/miyamiya/items/5f5a81b16901ed9e61cc)