メインコンテンツへスキップ
見出し画像

代理承認は承認レコードを増やさない(sys_user_delegate)【前編】


    ServiceNow で「承認者が不在の間、別の人に代わりに承認してもらう」ときに使うのが、代理承認(Delegation)です。委任元が休暇に入る前に委任先を1件登録しておくと、その期間だけ委任先が委任元あての承認を処理できます。

    この代理承認について、実装で行き詰まる相談が定期的にあります。典型は「委任先ごとに承認を絞り込みたい」「承認レコードを付け替えてリアルタイムに切り替えたい」「sysapproval_approver に承認者を足したのに、Flow が進まない」の3つです。これらは、代理承認が裏でどう動いているかを取り違えていると、いくら実装しても解決しません。

    この記事では、代理承認の実体テーブル sys_user_delegate を起点にします。承認を委任する仕組みは「承認レコードを増やす」のではなく「読み取り時に見せる」方式です。標準(プラグインなし)ではどこまでできて、どこで詰まるのか。PDI(Zurich)の OOTB 実測とともに書きます。承認の実体テーブルそのものは前回の 承認は1人1レコード(sysapproval_approver) で扱ったので、本記事はその続きにあたります。

    これは代理承認を2本に分けた前編です。前編は標準機能だけ(プラグインなし)の現状を詳しく扱い、「なぜ条件付きの絞り込みができないのか」「なぜ承認レコードを手で足しても Flow が進まないのか」を仕組みから押さえます。後編では、粒度制御用の Granular Delegation プラグインを有効化したうえで、部署・上長単位の条件付き委任が実機でどう変わるかを扱います。

    この記事で分かること

    • 代理承認の実体が sys_user_delegate(1委任=1レコード)に保存されること

    • 委任は approvals / assignments / invitations / notifications の4区分の boolean だけで、条件フィールドが無いこと

    • 代理承認は承認レコードを複製せず、読み取り時に「委任元の承認」を委任先の一覧へ展開する方式であること

    • だから sysapproval_approver に手でレコードを足す方式は Flow で進まないこと(Flow と レガシー Workflow の違い)

    • 部署・上長単位の絞り込みは標準では不可で、Granular Delegation プラグインが粒度制御の入口であること

    この記事の内容は ServiceNow の PDI(Personal Developer Instance)Zurich(ズーリッヒ)リリースの OOTB(初期設定)で確認した個人メモです。本番環境やカスタマイズ済み環境では、フロー・フィールド・件数の内容が異なる場合があります。


    代理承認(Delegation)とは

    代理承認とは、ある期間だけ、自分あての承認・割り当て・通知を、別のユーザーが代わりに受け取って処理できるようにする仕組みです。委任元あてに来るはずのものを、委任先が肩代わりする関係です。

    委任できる対象は4つに分かれています。承認(approvals)、割り当て(assignments)、会議招集(invitations)、全通知(notifications)です。承認だけを委任して割り当ては委任しない、といった区分ができます。

    この委任情報を保存するテーブルが sys_user_delegate(画面上は User Administration の Delegates)です。1つの委任関係が1レコードで、「誰が(user)誰に(delegate)、いつからいつまで(starts / ends)、何を(approvals など)」を保持します。

    今回確認した PDI(Zurich / OOTB)では、sys_user_delegate は 0件 でした。代理承認は初期データを持ちません。各ユーザーが必要になったときに自分で作る運用データだからです。

    公式の Delegate approvals and tasks to another user(Zurich) に、利用者が自分のプロフィールから委任を登録する手順が説明されています。承認の仕組みそのものは公式の Classic approvals(Zurich) が対応します。


    sys_user_delegate のテーブル構造(委任は「全か無か」)

    代理承認の仕様を正しく理解するには、まず sys_user_delegate のフィールドを見るのが早道です。


    画像

    今回確認した PDI(Zurich / OOTB)では、固有フィールドは8つでした。


    画像
    • user(User → sys_user) … 委任元。既定で現在ユーザーが入ります。

    • delegate(Delegate → sys_user) … 委任先。

    • approvals / assignments / invitations / notifications(いずれも Boolean) … 委任する対象。既定はすべて true。

    • starts / ends(Date/Time) … 委任の有効期間。ends の既定は 2100-01-01 で、実質「終わりを決めるまで有効」です。

    ここで確認したいのが、承認を「どれだけ」委任するかを表す列です。それは approvals という on/off の1つしかありません。「部署Aの承認だけ委任」「特定の上長あての承認だけ委任」といった条件を入れる列は、sys_user_delegate に存在しません。


    画像

    つまり、標準の代理承認は「この人に、この期間、承認まるごとを委任する」という全か無かのモデルです。条件付きの絞り込みができない理由は、カスタマイズ不足ではありません。テーブル設計そのものに、その余地が無いのです。この事実が、後半の「標準で絞り込めるか」の答えに直結します。


    代理承認は承認レコードを増やさない(読み取り時に展開する)

    本記事の中心はここです。代理承認を実装しようとする人の多くが、「委任したら、委任先あての承認レコードが新しく作られるのだろう」と考えます。実際は違います。

    標準の代理承認は、承認レコード(sysapproval_approver)を複製も付け替えもしません。 承認レコードは委任元あてのまま1件だけ存在します。委任先は読み取り時に、その委任元の承認を自分の一覧で見せてもらうだけです。


    画像

    この「読み取り時の展開」を担っているのが、getMyApprovals() という関数です。PDI で中身を読むと、次のような処理でした(要点のみ)。

    function getMyApprovals() {
      var u = gs.getUserID();
      var answer = [ String(u) ];              // まず自分
      var g = new GlideRecord("sys_user_delegate");
      g.addQuery("delegate", u);               // 自分が委任先である委任を探し
      g.addQuery("approvals", "true");         // 承認が委任対象で
      g.addQuery("starts", "<=", gs.daysAgo(0));
      g.addQuery("ends", ">=", gs.daysAgo(0)); // いま有効期間内のものだけ
      g.query();
      while (g.next()) answer.push(String(g.user)); // その委任元を配列に足す
      return answer;                           // [自分, 委任元1, 委任元2 ...] を返す
    }

    返しているのはユーザーの配列(自分+委任元たち)であって、承認レコードではありません。この配列を使って「approver が自分、または委任元の承認」を承認一覧のクエリ側で拾う、というのが標準の設計です。Zurich ではこの展開経路を ApprovalDelegationUtil という Script Include がまとめています。

    ここで実測にもとづく注記を1つ入れます。承認一覧のクエリを委任元まで広げる before-query の Business Rule(approval query)は、今回の PDI では inactive(無効)でした。つまり「承認レコードを複製せず、ユーザー配列で読み取り時に展開する」という仕組み自体は、コードで確認できました。ただし委任を実際に作って、委任先の一覧に承認が出るところまでの機能テストは行っていません(後編で実機確認します)。ここは「標準の設計がこの形になっている」という範囲で読んでください。

    この設計には、実務上の大きな意味があります。

    • リアルタイムの切り替えは、そもそも「付け替え」で実現していない。 委任レコードを作る/消すだけで、読み取り時に展開する対象(委任元の集合)が変わる設計です。承認レコードそのものは触らないので、付け替えも取り消しも、後始末の処理も構造上発生しません。

    • ワークフロー実行コンテキストを操作する必要が無い。 承認の正本レコードは1件のまま動かないので、フローの内部状態に手を入れる場面が発生しません。

    相談でよくある「リアルタイムに承認依頼を付け替えたい/取り消したい」という悩みは、承認レコードを複製する方式を選んだときにだけ発生します。自分で作り込んだ問題です。標準の読み取り時方式なら、この悩み自体が生まれません。


    なぜ手でレコードを足すと Flow が進まないのか

    「委任先あての承認を作ればいいなら、sysapproval_approver に承認者を1件 INSERT すればいいのでは」。そう考えて実装すると、レコードは作れるのに Flow がまったく進みません。原因は設定ミスより深いところにあります。Flow Designer と レガシー Workflow では、承認の数え方そのものが違うのです。


    画像

    PDI で承認レコードに紐づく Business Rule を読むと、2つのエンジンで再開の仕組みが分かれていました。


    画像
    • レガシー Workflow … 承認が承認/却下されると SNC - Run parent workflows (Approval) が動きます。続いて WorkflowApprovalUtils.approve() が、source 文書(document_id / source_table)を起点に承認者を数え直します。だから、対象文書に正しく紐づけた承認レコードを手で足すと、活動が再集計してくれて、フローが進むことがあります。過去に Workflow Editor での実装が動いていたとすれば、この「数え直す」性質が働いていたと考えられます。

    • Flow Designer … 承認レコードが変わると SNC - Notify Flow (Approval) が動きます。これが sn_fd.FlowAPI.notifyApprovalAction() を呼び、Flow エンジンに通知します。ただし Flow の「Ask for Approval」アクションは、自分が生成した承認レコードの集合と、その承認条件(誰か1人/全員/割合など)だけで完了を判定します。アクションの外で手挿入したレコードは、この集合に含まれないため、通知が飛んでも完了判定には数えられないと考えられます。

    これが「sysapproval_approver に追加しただけではフローが動かない」の正体です。Flow は承認レコードの集合を「アクションが握っている正本」として扱うので、外から足したレコードは、どの承認アクションにも結び付かないまま残ります。

    だからこそ、標準の代理承認は正しく動きます。委任先は新しいレコードを作らず、委任元あての既存レコード(=フローが待っている正本の1件)をそのまま承認するからです。Flow でも Workflow でも動くのは、この一点に尽きます。

    公式の Ask for Approval action(Zurich) に、Flow Designer の承認アクションが承認レコードをどう生成し管理するかが説明されています。


    標準の限界と Granular Delegation プラグイン

    ここまでで、標準の代理承認の性格がはっきりしました。期間を区切って全部委任するのは得意ですが、条件で絞り込むのは仕組み上できません。

    では部署単位・上長単位の絞り込みはあきらめるしかないかというと、ServiceNow には粒度を上げるためのプラグインが用意されています。Granular Delegation(com.glide.granular_service_delegation) です。


    画像

    PDI で仕組みを読むと、切り替えはプロパティ glide.approval.delegation.version で行われていました。

    • 未設定(今回の PDI の状態)= v1。標準の getMyApprovals を使う、全か無かのモデル。

    • v2 / v3 = Granular Delegation プラグインの GranularApprovalDelegationUtil を使う、粒度・条件つきの委任経路。


    画像

    注意したいのは、今回の PDI では Granular Delegation プラグインは inactive(無効) だったことです。関連する Script Include(GranularApprovalDelegationUtil)は、無効でも配備されています。ただし実際に粒度制御を使うには、プラグインの有効化と要件を満たせるかの検証が要ります。


    画像

    公式の Granular delegation(Zurich) に、粒度を上げた委任の考え方が説明されています。標準機能だけでは条件付きの絞り込みができない、というのがプラグインなしの現状の結論です。ここが標準の限界で、この先は後編でプラグインを有効化してから確認します。


    後編:プラグインを入れると何が変わるか(予告)

    前編(この記事)で押さえた現状は、次の3点でした。

    • 標準の代理承認は approvals など4区分の全か無かで、部署・上長単位の条件が入らない。

    • 代理承認は承認レコードを複製せず、読み取り時に見せているだけ。だからリアルタイム制御に付け替えは不要で、委任行の作成/削除で足りる。

    • sysapproval_approver に手でレコードを足しても、Flow はアクションの承認セット外を数えないので進まない。

    後編では、Granular Delegation プラグインを有効化した状態で、この現状がどう変わるかを実機で確認します。glide.approval.delegation.version を切り替えて GranularApprovalDelegationUtil 経路に入れます。条件付きの委任がどのように表現され、承認一覧の見え方がどう変わるか。そこを PDI の実測で追う予定です。

    一貫する原則は前編と同じです。承認レコードを複製しない。読み取り時に「見せる/操作できる」側で絞り込む。 レコードを複製した瞬間に、Flow が進まない問題と、委任変更時の後始末という2つの問題を同時に抱えることになるからです。


    ServiceNow における代理承認のポイント

    • 代理承認の実体は sys_user_delegate(1委任=1レコード)。今回の PDI では 0件(各ユーザーが自分で作る運用データ)でした。

    • 委任対象は approvals / assignments / invitations / notifications の4区分の boolean だけです。部署・上長・種別で絞る条件フィールドはありません。だから標準では条件付きの絞り込みができません。

    • 代理承認は承認レコードを複製・付け替えしません。getMyApprovals / ApprovalDelegationUtil が読み取り時に「委任元の承認」を委任先の一覧へ展開する方式です。だから委任の作成/削除は即反映で、付け替えも後始末も不要です。

    • sysapproval_approver に手でレコードを足す方式は、Flow では進みません。Flow が Ask for Approval アクションの承認レコード集合だけで完了判定するからです。レガシー Workflow は source 文書で数え直すので挙動が違います。

    • 粒度を上げるには Granular Delegation プラグイン(com.glide.granular_service_delegation)があります。既定は inactive です。切り替えは glide.approval.delegation.version を v2/v3 にして行います。カスタム前にまず評価するのが順序です。

    代理承認を調べるときは、まず「委任は承認レコードを作らず、読み取り時に見せているだけ」という前提に立ってください。リアルタイム制御も Flow が進まない問題も、原因の見え方が変わります。


    実機で気づいたこと

    • ends の初期値が空(無期限を表す NULL)ではなく、2100-01-01 という具体的な未来の日付になっていました。最初は無期限委任なら日付を空にするものだと思っていましたが、実際には「事実上ずっと先の日付を入れておく」という表現でした。starts / ends で有効期間を絞り込むクエリを自分で書くときは、この既定値を前提にしないと、無期限のつもりの委任が急に外れる不具合を作りかねません。

    • 同じ「delegation」の名前が付くプラグインがもう1つあり、com.snc.role_delegation(Role Delegation)でした。こちらは active でしたが、代理承認とは無関係で、開発者ロールなど別の対象を委任する機能でした。最初は代理承認の拡張機能の1つだと思い込みかけましたが、名前だけで判断すると紛らわしいプラグインが他にもあると気づきました。

    • sysapproval_approver が変わると、レガシー Workflow 向けの再集計処理(WorkflowApprovalUtils.approve())と Flow 向けの通知処理(notifyApprovalAction())が、それぞれ別の Business Rule で両方とも動く作りでした。Flow 中心の時代になった今でも、Workflow 時代の再集計ロジックがそのまま現役で残っているのは意外でした。過去に Workflow で作られた承認がまだ動いている環境を壊さないための後方互換ではないかと思います。


    次に読むなら

    承認全体がどこで誰に回り、なぜ止まるのかを俯瞰したい方は、ServiceNow 承認(Approval)完全ガイド:申請はどこで誰に回り、なぜ止まるのか が入口になります。まず後編(Granular Delegation プラグイン編)で、条件付き委任が実機でどう動くかを確認します。前提となる承認の実体は 承認は1人1レコード(sysapproval_approver)、複数人を束ねる仕組みは グループ承認:N人を1つに束ねる仕組み(sysapproval_group) で扱いました。代理承認は、この2つの上に「読み取り時の展開」を1枚かぶせた仕組みとして理解できます。


    用語

    • 代理承認 / Delegation:一定期間、自分あての承認・割り当て・通知を別ユーザーが肩代わりする仕組み

    • sys_user_delegate(Delegates):代理承認の設定を保存するテーブル(1委任=1レコード)

    • 委任元(user)/ 委任先(delegate):委任する側 / 肩代わりする側

    • 読み取り時展開:承認レコードを増やさず、承認一覧のクエリを委任元まで広げて見せる方式

    • getMyApprovals / ApprovalDelegationUtil:読み取り時展開を担う関数 / Script Include

    自分の環境で確認するときのURL

    自分のインスタンス名を先頭に付けると、同じ画面が開けます。

    Delegates 一覧   : sys_user_delegate_list.do
    フィールド一覧    : sys_dictionary_list.do?sysparm_query=name=sys_user_delegate^elementISNOTEMPTY
    プラグイン確認    : v_plugin_list.do?sysparm_query=idLIKEdelegation
    承認一覧         : sysapproval_approver_list.do?sysparm_query=state=requested
    切替プロパティ    : sys_properties_list.do?sysparm_query=name=glide.approval.delegation.version

    テーブルの基本

    • sys_user_delegate … Label:Delegate / 固有フィールド 8 / レコード 0件(OOTB)

    • 固有フィールド:user, delegate, approvals, assignments, invitations, notifications, starts, ends

    • 条件・絞り込みのフィールド:無し(=全か無かのモデル)

    読み取り時展開の仕組み(実測)

    • getMyApprovals()(global)… 返り値は「自分+有効な委任元」のユーザー配列。承認レコードは複製しない。

    • ApprovalDelegationUtil(global)… glide.approval.delegation.version(v1/v2/v3)で標準/粒度を切替。isGranularApprovalDelegationEnabled() = GlidePluginManager.isActive('com.glide.granular_service_delegation')。

    • glide.approval.delegation.version:このPDIでは未設定 → コード既定の v1(標準)。

    承認エンジン別の再開(実測)

    • レガシー Workflow:SNC - Run parent workflows (Approval) → WorkflowApprovalUtils.approve()(source 文書で再集計)。

    • Flow Designer:SNC - Notify Flow (Approval) → sn_fd.FlowAPI.notifyApprovalAction()(アクションの承認セットで判定)。

    プラグイン(実測)

    • com.glide.granular_service_delegation(Granular Delegation)… inactive。GranularApprovalDelegationUtil(global)は配備済み。

    • com.snc.role_delegation(Role Delegation)… active(承認委任とは別機能。開発/ロールの委任)。

    関連テーブル・部品(検索用)

    sys_user_delegate(Delegates), sysapproval_approver(承認の実体), sysapproval_group(グループ承認), sys_user(委任元/委任先), ApprovalDelegationUtil / GranularApprovalDelegationUtil(Script Include), glide.approval.delegation.version(切替プロパティ)


    検証範囲とおことわり

    • 確認した範囲:sys_user_delegate の固有フィールド(8・条件列なし)、レコード件数(0)、getMyApprovals / ApprovalDelegationUtil の実装(読み取り時にユーザー配列で展開する設計)をコードで確認、切替プロパティ glide.approval.delegation.version(未設定=v1)、Granular Delegation プラグインの状態(inactive)、承認再開の Business Rule(Workflow / Flow の違い)。すべて PDI(Zurich / 2026-07-28 確認)の OOTB で取得しています。

    • 確認していない範囲(断定しません):読み取り時展開を承認一覧クエリに組み込む before-query BR(approval query)は今回の PDI では inactive のため、委任先の一覧に承認が実際に表示されるところまでの機能テストは未実施(仕組みはコードで確認、後編で実機確認)。ほかに、Granular Delegation を有効化した後の条件モデルと UI、notifyApprovalAction の内部実装、Flow の承認待ち状態の物理格納先、Service Portal 上の委任 UI、本番でのカスタム承認フローも検証範囲外です。

    今回は PDI の OOTB(初期設定)を前提に、代理承認の構造を確認しました。実環境では、既存のカスタマイズによって挙動やフィールド・件数が変わります。

     
     
    ServiceNowのPDI(Personal Developer Instance)を実機で観察して、Noteに記事を書いています 実装・運用・教育の現場視点で淡々と記録中です リクエストがあればその記事も書きます 個人の観察日記です

    あなたへのおすすめ