ServiceNow研究所
検討・企画

承認(Approval)テーブルに関する通知は標準のビジネスルールを理解した上で設定する

2022/11/6 公開

承認依頼時や承認却下時など、承認に伴うメールを送信したい場合があります。ServiceNowの承認に関する通知はちょっと癖のある作りになっており、これを理解していないと上手に設定することができません。この記事では標準の作りを説明した上で、新たな通知の設定方法を示します。

標準の承認(Approval)テーブルに関する通知の仕組み

承認(Approval)テーブルはServiceNowの多くの機能から共通で用いられるテーブルです。これを考慮してか、標準で柔軟性・拡張性のある通知の仕組みが存在します。まずはそれについて説明します。

概要(図解)

図解すると、こんなイメージで動きます。ポイントは、Insert, Updateトリガの通知(Notification)ではなく、イベントトリガの通知(Notification)になっている点です。なお、標準でイベントが作成されるのは承認依頼時(state=requested)、承認否認・却下時(state=rejected)、承認キャンセル時(state=cancelled)で、それぞれに対して通知が設定されているかはテーブルによります。

標準の承認(Approval)テーブルに関する通知の仕組み(概要)

そして、イベントを作成しているのが2つのBusiness Rule(ビジネスルール)です。これらは、承認対象のレコードがタスク(Task)テーブルを継承しているテーブルのレコードの場合と、継承していないテーブルのレコードの場合で使い分けられています。以下、これらの動作について説明します。

承認対象がタスク(Task)テーブルを継承している場合

タスク(Task)テーブルを継承しているテーブルに対する承認のステータスに変更があると、”Approval Events (Task)”というビジネスルール(Business Rule)によってイベントが作成され、通知が行われます。

イベント作成部分に注目してコードを抜き出すと次のようになっています。

“Approval Events (Task)” Business Rule

if (current.state.changes() && current.state=='cancelled') {
   var event = "approval.cancelled";
   if (isRequest) // (引用者コメント) 承認対象のレコードがRequest[sc_request]テーブルのレコードかを判定
      event = "request.approval.cancelled";
   else if (isSCTask) // (引用者コメント) Catalog Task[sc_task]テーブルのレコードかを判定
      event = "sc_task.approval.cancelled";
   else if (isStdChange) // (引用者コメント) Standard Change Approval[std_change_proposal]テーブルのレコードかを判定
      event = "std_change_proposal.approval.cancelled";

   gs.eventQueue(event, current, gs.getUserID(), gs.getUserName());
}

if (current.state.changes() && current.state=='requested') {
   var event = "approval.inserted";
   if (isRequest)
      event = "request.approval.inserted";
   else if (isSCTask)
      event = "sc_task.approval.inserted";
   else if (isStdChange)
      event = "std_change_proposal.approval.inserted";

   gs.eventQueue(event, current, gs.getUserID(), gs.getUserName());
   updateTask(current, current.approver.getDisplayValue() + " requested to approve task");
}

// (引用者コメント) state=='rejected', state=='approved'の場合は省略

つまり、承認対象がRequest[sc_request]、Catalog Task[sc_task]、Standard Change Approval[std_change_proposal]テーブルのレコードであれば”<テーブル名>.approval.<inserted or rejected or cancelled>”イベントを、その他のタスク継承テーブルのレコードであれば”approval.<inserted or rejected or cancelled>”イベントを作成しています。

承認対象がタスク(Task)テーブルを継承していない場合

タスク(Task)テーブルを継承していないテーブルに対する承認のステータスに変更があると、”Approval Events (Non-Task)”というビジネスルール(Business Rule)によってイベントが作成され、通知が行われます。

イベント作成部分に注目してコードを抜き出すと次のようになっています。

“Approval Events (Non-Task)” Business Rule

function getEventPrefix(sourceTable) {
	if (sourceTable!=null && sourceTable.startsWith('kb_template')) {
		return "kb_knowledge";
	}
	return sourceTable;
}

function sendEventsNonTask() {
   if (!current.state.changes()) 
      return;

   var event = getEventPrefix(current.source_table);
   switch (current.state + "") {    
      case 'cancelled':
         event += ".approval.cancelled";
         gs.eventQueue(event, current, gs.getUserID(), gs.getUserName());
         break;
      case 'requested':
         event += ".approval.inserted";
         gs.eventQueue(event, current, gs.getUserID(), gs.getUserName());
         updateRecord(current, current.approver.getDisplayValue() + " requested to approve task");
	 break;
      case 'rejected':
         event += ".approval.rejected";
         gs.eventQueue(event, current, current.state, previous.state);
         var isAuto = ((current.operation() == 'insert') && isFD)?" auto":"";
         updateRecord(current, current.approver.getDisplayValue() + isAuto + " rejected the task.", 
               current.comments.getJournalEntry(-1));
         notifyMyFriends(current);
         break;
      case 'approved':
         var isAuto = ((current.operation() == 'insert') && isFD)?" auto":"";
         updateRecord(current, current.approver.getDisplayValue() + isAuto + " approved the task.", 
               current.comments.getJournalEntry(-1));
         break;
      default: 
   }
         
 }

タスク継承テーブルの場合と違って、switchで分岐されていて途中で切りづらかったので長めに引用しましたが、結局やっているのは“<テーブル名>.approval.<inserted or rejected or cancelled>”イベントを作っているだけです。

承認に関する通知の設定方法

これまでの仕組みを踏まえ、標準を最大限に尊重した、承認に関する通知の設定ズ方法を説明します。

承認対象がタスク(Task)テーブルを継承している場合

タスクを継承している場合は、2つ選択肢があります。どちらを選択するかはポリシーによりますが、結局どちらも標準のコンポーネントをいじる必要があり、それであればより標準の意図をくみ取っていると考えられ、柔軟性・拡張性の高い方法1がよいと考えています。

方法1: 新しいイベントを作り、”Approval Events (Task)” Business Ruleを変更する

新しいイベントを作成し、それを”Approval Events (Task)” Business Ruleから発生させるようにする方法です。この方法は標準のビジネスルール(Business Rule)を変更することにはなりますが、このビジネスルール自体が拡張を前提とした書き方になっていることから、この変更は許容され得ると考えます。

次のタスク(Task)テーブルを継承したMy Task Tableに対する承認依頼が行われたときの通知を作成する場合を例に具体的な手順を示します。

タスク(Task)テーブルを継承したMy Task Table

  1. Event Registoryにイベントを登録する
  2. “Approval Events (Task)” Business Ruleを変更し、登録したイベントを発生させるようにコードを追加する
  3. 登録したイベントに対する通知を作成する
1. Event Registoryにイベントを登録する

メニューの検索欄に”sysevent_register.list”と入力しイベントの一覧に遷移し、”New”ボタンよりイベントを登録します。今回は”mytask.approval.inserted”というイベントを登録しました。

イベントの登録方法

2. “Approval Events (Task)” Business Ruleを変更し、登録したイベントを発生させるようにコードを追加する

変更を加えた部分のみ記載します。標準のコードに従い、テーブルをチェックしてイベントを作成する一連の処理を1つ加えます。

“Approval Events (Task)” Business Ruleの変更箇所

// (追加箇所) My Task Tableに対する承認かどうかを判定
function checkMyTask() {
    var task = current.sysapproval.sys_class_name || current.source_table;
    return (task == 'u_my_task_table');
}

// (追加箇所) 判定結果を変数に格納
var isMyTask = checkMyTask();

if (current.state.changes() && current.state == 'requested') {
    var event = "approval.inserted";
    if (isRequest)
        event = "request.approval.inserted";
    else if (isSCTask)
        event = "sc_task.approval.inserted";
    else if (isStdChange)
        event = "std_change_proposal.approval.inserted";
    // (追加箇所) My Task Tableに対する承認の場合に発生させるイベントを指定
    else if (isMyTask)
        event = "mytask.approval.inserted";

    gs.eventQueue(event, current, gs.getUserID(), gs.getUserName());
    updateTask(current, current.approver.getDisplayValue() + " requested to approve task");
}
3. 登録したイベントに対する通知を作成する

最後に、イベントトリガの通知を作成します。メニュー > System Notification > Email > Notificationsより通知のリストを開き、”New”ボタンより通知を作成します。イベントトリガの通知を作成するためには、Send whenを”Event is fired”、Event nameを上記で作成したイベントに設定します。

イベントトリガの通知の作成方法

こちらの方法では、ビジネスルールを変更しますが、標準の他の通知には影響を与えません。

方法2: 標準の”approval.<inserted or rejected or cancelled>”イベントに対する通知をつぶした上で新たな作成する

二つ目の方法は、”Approval Events (Task)” ビジネスルール自体は変更せず、標準で生成される”approval.<inserted or rejected or cancelled>”イベントに対する標準の通知を非アクティブにした上で、同イベントに対する通知を新たに作成し、テーブルの判定はConditionにおいてTask typeなどで行う方法です。

上述のように、”Approval Events (Task)”ビジネスルールは承認対象がRequest[sc_request]、Catalog Task[sc_task]、Standard Change Approval[std_change_proposal]以外のタスク継承テーブルであれば、常に”approval.<inserted or rejected or cancelled>”イベントを発生させます。そして、このイベントに対しては標準で通知が設定されています。そのため、標準の通知を非アクティブにした上で、”approval.<inserted or rejected or cancelled>”イベントに対する通知を作成することで、タスク継承テーブルに対する承認に関する新たな通知を設定することができます。

しかし、特に条件を設定しなければ、Request、Catalog Task、Standard Change Approvalテーブル以外の全てのタスク継承テーブルに対して同じ通知が送信されてしまうので、下図のようにConditionにおいてTask typeなどでフィルタをかける必要があります。

方法2の場合の通知の作成方法

こちらは標準の他の通知を非アクティブにする必要がありますが、一方で標準のビジネスルール(Business Rule)をいじる必要はありません。

承認対象がタスク(Task)テーブルを継承していない場合

この場合は、“Approval Events (Non-Task)”ビジネスルールが”<テーブル名>.approval.<inserted or rejected or cancelled>”イベントを作ってくれるので、やることはイベントの登録とイベントトリガの通知の作成だけです。

  1. Event Registoryに”<テーブル名>.approval.<inserted or rejected or cancelled>”という名前のを登録する
  2. 登録したイベントに対する通知を作成する

具体的な手順は上記タスクテーブルを継承する場合を参照ください。

おわりに

承認テーブルは多くのテーブルから利用されるため、承認対象テーブルごとに承認を設定するための仕組みが標準で組み込まれています。承認に対する通知を設定するときは、これを理解した上で上手にやってあげることで、通知が増えても複雑化せずに済みます。

以上です。