問題管理モジュールの自動設定構成
Problemを1件保存するたびに、裏では多数の処理が動きます。状態を進めると別の状態項目にも同じ値が入る。担当グループを変えると保存できずエラーになる。クローズすると解決コードが自動で入る。どれもスクリプトを書いた覚えがないのに起きます。Australia の PDI をデフォルト設定のまま開き、それぞれの処理が何をしているかを確かめました。
検証環境:Australia PDI / OOTB / 2026-08-31 確認
押さえておくこと
ビジネスルールの中心は、2つの状態項目の同期・タスクの連鎖・ナレッジ化
state と problem_state は order 0 と order 1,000,000 の2件のルールで同期される。中身はスクリプトを持たない宣言的なフィールドコピー
クライアントスクリプトは解決コードに応じたラベルの切り替えと、状態遷移モーダルの画面調整を担う
データポリシーは状態と解決コードの組み合わせごとに、必須項目を細かく出し分ける
通知8件のうち2件は、イベント名が別テーブル用だったり空欄だったりする
ビジネスルールが動くタイミング
ビジネスルールは、実行されるタイミングで4種類に分かれます。



before が13件と最多です。データベースへ書き込む前に、状態の遷移可否を判定したり、確定日時のような項目を補正したりします。
after は6件です。書き込みが終わってから動きます。状態が変わったことをイベントとして発行する処理や、ナレッジ記事の下書きを作る処理が含まれます。
display は4件です。フォームを画面に出す前に実行されます。problem_state や resolution_code の値を g_scratchpad に積み、クライアントスクリプト側から参照できるようにする処理が中心です。
async は2件で、いずれも「非推奨(アップグレード時には実行されない)」という注記が付いています。重複マーク時の関連レコード移動と、Problem Taskのカスケードクローズを担っており、現行の状態遷移フローの一部として動いてはいるものの、プラットフォーム側からは古い実行方式として扱われています。
主要なビジネスルールの処理内容
25件のうち、動きが分かりやすい18件を挙げます。


いくつかを詳しく見ます。
insert_problem(before / order 100)
条件は current.number.nil()。番号が未採番のときだけ動きます。処理は2行です。
current.number = getNextObjNumberPadded();
gs.addInfoMessage(gs.getMessage("Created Problem {0}", current.number));採番と同時に、画面へ「Created Problem PRB0010023」のような確認メッセージを出します。
Update Problem State to Assess(before / order 100)
条件は current.state + '' === ProblemState.STATES.NEW。新規状態のときに動き、problem_state を Assess(2)へ進めます。ただし prb_model が設定されている場合は、先にモデルの遷移可否を確認してから値を入れます。
Abort changes on group(before / order 100)
フィルタは assigned_toISNOTEMPTY^assignment_groupVALCHANGES^assignment_groupISNOTEMPTY。担当者が入ったまま担当グループを変えたときに動きます。
var grp = new GlideRecord('sys_user_grmember');
grp.addQuery('group.sys_id', assignment_group);
grp.addQuery('user', assigned_to);
grp.query();
if (!grp.hasNext()) {
gs.addErrorMessage(...);
current.setAbortAction(true);
}担当者が新しいグループのメンバーかどうかを sys_user_grmember で確認し、所属していなければエラーを出して保存そのものを中断します。「担当グループを変えたら保存できなくなった」という相談の答えがここにあります。
Update fields when Problem is Confirmed(before / order 100)
条件は state=103^confirmed_atISEMPTY。Root Cause Analysis に入った最初の保存だけ動き、confirmed_at に現在時刻、confirmed_by に操作者を入れます。Fix in Progress に入るときの fix_at / fix_by も同じ形の別ルールで記録されます。
Sync Workaround fields(before / order 100)
フィルタは workaroundVALCHANGES。処理は次の3行です。
var workaround = current.workaround.toString();
if (workaround.length > 0)
current.workaround_applied = true;
else
current.workaround_applied = false;Workaround欄に本文を書くと、チェックボックスの workaround_applied が自動で連動します。担当者はチェックを操作する必要がありません。本文の有無だけで判定されます。
Update fields when Problem is Resolved(before / order 120)
条件は state=106^resolved_atISEMPTY。
current.resolved_at = gs.nowDateTime();
current.resolved_by = gs.getUserID();
if (current.resolution_code == ProblemState.RESOLUTION_CODES.RISK_ACCEPTED) {
current.closed_at = gs.nowDateTime();
current.closed_by = gs.getUserID();
}Risk Acceptedで解決した場合は、Resolved状態を経由せず、解決日時とクローズ日時を同時に記録します。problem.acceptrisk.move_to_closed(値true)というプロパティの動作と一致する処理です。
Update fields on Problem Closure(before / order 120)
条件は state=107^closed_atISEMPTY。closed_at・closed_byに加えて、実時間(calendar_stc)と営業時間(business_stc)の両方を計算します。オープンからクローズまでの所要時間が、担当者の入力なしで記録として残ります。
Update fields on Re-analyzing a Problem(before / order 110)
条件は「Resolved・Closed・Fix in Progress から Root Cause Analysis へ戻ったとき」です。
current.reopened_at = new GlideDateTime();
current.reopened_by = gs.getUserID();
current.reopen_count = current.reopen_count + 1;
current.setValue("closed_at", "");
current.setValue("resolved_at", "");
current.setValue("resolution_code", "");再分析に戻すと、クローズ・解決関連の項目がまとめてクリアされます。一度確定した解決情報は、Re-Analyzeを押した瞬間に消えるという前提で運用します。
Problem Create Knowledge(after / order 100)
条件は「problem_state が Fix in Progress(4)に変わり、かつ knowledge が true」。処理はプロパティ glide.knowman.submission.workflow の値で分岐します。
true なら kb_submission に候補として提出する
false なら kb_knowledge に直接下書きを作る
作られる記事の本文(text)には、work_notes の内容がそのまま入ります。Incidentのナレッジ化がコメント(利用者向け)を使うのに対し、Problemは作業メモ(内部向け)を使う点が異なります。作業タスクの記録を丁寧に書く運用が、そのままナレッジ記事の質につながります。
参考:ServiceNowの保存で実際に動くBusiness Ruleは何本か? problemテーブルで定義と実測を比べる … 1件保存したときに実際に発火した件数
他のテーブルへ連鎖する処理
Problemの保存が、別のテーブルの更新を引き起こす場合があります。
Cascade closure of Problem Tasks(async・非推奨 / order 101)
条件はstateがClosedに変わったとき、またはプロパティ problem.closed.cancel_open_tasks が true でクローズしたときです。紐づく problem_task を検索し、未完了のものをまとめてキャンセルします。重複マーク時は、対象の problem_task を複製先の Problem へ移す処理も担います。
incident.problem_id を実際に使う唯一のアクティブなルール
incident の辞書定義には problem_id というフィールドは存在しません。それにもかかわらず、SNC - ITIL - Update Related Incidents(after / order 999)というルールが、このフィールドを検索条件として使っています。
var inc = new GlideRecord("incident");
inc.addActiveQuery();
inc.addQuery("problem_id", current.sys_id);
inc.query();
while (inc.next()) {
if (inc.incident_state == 3) inc.incident_state = 2;
if (!current.close_notes.nil())
inc.work_notes = "Related problem " + current.number + " closed with the following Close Notes:\n\n" + current.close_notes;
inc.update();
}発火条件は problem_state.changesTo(4)(Fix in Progressへの遷移)です。紐づく incident のうち Resolved(3)のものを In Progress(2)へ巻き戻し、close_notesの内容を作業メモへ転記します。ルール名は「Update Related Incidents」ですが、実際の処理は解決の巻き戻しで、名前と処理が一致していません。
同じフィールドを使うルールは他に2件ありますが、いずれも非アクティブです(SNC - ITIL - Close Related / SNC - ITIL - Resolve Related Incidents)。メタデータからは見えないフィールドを、有効なルールが1件だけ実際に運用している状態です。
参考:Update Set(sys_update_set):設定を運ぶ2層の箱 … 隠しフィールドが移送でどう扱われるかの背景
state と problem_state の同期
Problemには状態を表す項目が2つあります。state(task から継承、101-107の6値)と problem_state(problem固有、選択肢を持たない整数)です。

ルール名orderフィルタ中身Copy State to Problem State0stateVALCHANGESproblem_state SAMEAS state(スクリプトなし)Copy Problem State to State1,000,000problem_stateVALCHANGESstate SAMEAS problem_state(スクリプトなし)
orderが0と1,000,000という両極端の値になっています。すべてのルールの最初と最後に挟み込む設計です。
2件とも script フィールドが空欄でした。JavaScriptを書かない「Field values」形式の簡易ルールで、SAMEAS という宣言的な条件式だけでコピーを実現しています。他の18件がJavaScriptで具体的な処理を書いているのに対し、この2件だけは仕組みそのものが違います。
途中のルールがどちらの項目を更新しても、最後に必ず同期が取られます。自社でビジネスルールを追加するとき、片方の項目だけを書き換えると、もう一方の値に上書きされます。
クライアントスクリプトの画面での処理
ブラウザ側で動くスクリプトは10件あり、すべてが有効でした。


動きが分かりやすいものを見ます。
Rename Closed fields on change / on load
解決コード(resolution_code)に応じて、closed_at・closed_by・close_notes のラベルを画面上で切り替えます。
resolution_codeclosed_at のラベルclose_notes のラベル(未設定)ClosedClose notescanceledCanceled DateCanceled reasonduplicateMarked Duplicate Date(変更なし)fix_applied(かつstate=Closed)Completed Date(変更なし)risk_acceptedRisk Accepted DateRisk accepted reason
データポリシーの必須項目一覧にある「Canceled reason」「Risk accepted reason」は、辞書のラベルではなく、実行時にこのクライアントスクリプトが付け替えた表示名です。
Empty assigned_to on group change(onChange / assignment_group)
担当グループを変えると、担当者の欄を空にします。サーバ側の Abort changes on group と対になる処理です。画面では自動で空にし、それでも不整合が残る場合はサーバ側で保存を止めます。
Worknotes mandatory onChange of priority(onChange / priority)
優先度を変えると、作業メモが必須になります。元の値へ戻すと必須は解除されます。「なぜ優先度を変えたか」を記録させる仕組みです。
Display Duplicate of field message(onChange / duplicate_of)
複製先を選ぶと、GlideAjax(ProblemUtilsClientSNC)でサーバへ問い合わせ、複製先がClosedの問題だった場合に警告メッセージを出します。「未完了のProblem Taskがキャンセルされます」という内容で、Cascade closure of Problem Tasksが実行することを事前に知らせます。
Modals for Problem state transition(onLoad)
Assess・Fix・Mark Duplicate・Cancel・Accept Riskの各ダイアログを開いたとき、モバイル判定を行い、不要なボタンや関連リンクを非表示にして画面の余白を整えます。状態遷移専用のモーダルビューが、通常フォームとは別に用意されていることが分かります。
参考:Client ScriptとUI Policyの違い(フォーム挙動) … 2つの使い分け
UIポリシーの制御対象
UIポリシーは15件あり、すべてが有効でした。


条件が具体的なものを挙げます。
Hide Resolution Code when state is not resolved or closed
条件は state NOT IN 106,107。Resolved・Closed以外では解決コードの欄を隠します。状態を進めるまで、解決の理由を選ばせません。
Show 'Workaround applied' only when 'Resolution code' is 'Risk Accepted'
条件は resolution_code=risk_accepted。Risk Acceptedを選んだときだけ「Workaround applied」の欄を表示します。恒久対策をせずリスクを受け入れる場合に、暫定策の適用有無を別途記録させる設計です。
Priority is managed by Data Lookup - set as read-only
条件なし(常時適用)。優先度を読み取り専用にします。影響度と緊急度から dl_problem_priority が自動計算するため、直接編集させない方針です。
Make 'Workaround' mandatory when 'Workaround applied' is checked
条件は workaround_applied=true。数少ない on_load=false(保存時にも評価される)ポリシーで、Workaround本文を必須にします。
UIポリシーの多くは reverse_if_false がtrueでした。条件から外れると、必須や非表示の設定が自動で戻ります。
データポリシーによる必須制御
画面を問わず効く必須制御は8件でした。


state・resolution_codeの組み合わせで、必須にする項目が細かく分かれています。
状態・解決コード必須になる項目resolution_code = DuplicateDuplicate ofresolution_code = CanceledCanceled reason(close_notesのラベル変更後)resolution_code = Risk AcceptedRisk accepted reason(同上)state = Fix in Progress、または resolution_code = Fix AppliedCause notes・Fix notesstate IN (Assess, RCA, Fix in Progress, Resolved, Closed)Assigned tostate = Resolved、または resolution_code = Fix AppliedResolution codestate = ClosedResolution code
8件すべてが enforce_ui=true かつ、prb_modelISEMPTY^ORprb_model=<既定モデル> という条件が付随しています。Problem Modelを使わない場合、または既定モデルを使う場合にのみ、この必須制御が効く仕組みです。カスタムモデルを導入すると、必須項目の判定はモデル側のロジックに委ねられます。
UIポリシーは画面でしか効きません。データポリシーはREST APIやインポートセット経由でも効きます。解決情報だけがデータポリシーで守られているのは、記録として欠かせない項目だからです。
参考:APIやインポートにも効く必須制御(Data Policy) … 画面外からの投入はここで止まる
通知の送信条件と宛先
有効な通知は8件でした。Problem管理に無関係な通知は含まれていません。


宛先の設計を見ると、担当者へ送るもの(割り当て時・作業メモ追記時)、利用者へ送るもの(クローズ時)、グループへ送るもの(担当者不在のまま担当グループが変わったとき)の3系統に分かれます。
Problem assigned to my group の条件が具体的です。
assigned_toISEMPTY ^ assignment_groupVALCHANGES ^ assignment_groupISNOTEMPTY「担当者が決まらないままグループが変わったとき」だけグループ全員へ送ります。担当者が決まっていれば、その人だけに届きます。
イベント発行元を1件ずつ突き合わせると、食い違いが見つかりました。「problem events」ビジネスルールが実際にキューへ積むイベントは problem.inserted / problem.updated / problem.assigned / problem.escalated の4種だけです。ところが通知定義は problem.worknoted や problem.assigned.to.group というイベント名も参照しています。これらを発行するビジネスルールは、problem・problem_taskのアクティブなスクリプトの中には見つかりませんでした。Flow Designerなど別の経路が担っている可能性がありますが、この記事の調査範囲では特定できていません。
参考:イベントはキューに積まれてから動く(sysevent) … イベントの仕組み
参考:メール通知が飛ぶまでの4段階(Notification) … 通知が届かないときはここから追う
UIアクションの処理内容
フォーム上のボタンは22件が有効でした。


主要な処理の中身を見ます。
Resolve
表示条件は current.canWrite() && new ProblemStateUtils().canResolve(current)。押すと次を実行します。
g_form.setValue("state", g_scratchpad.STATE.RESOLVED);
g_form.setValue("resolution_code", g_scratchpad.RESOLUTION_CODES.FIX_APPLIED);
g_form.save();stateだけでなく、resolution_codeもFix Appliedへ自動で入れています。データポリシーが「Resolvedなら解決コード必須」という制約を持っていますが、標準ボタン経由の保存では、この制約が空欄で引っかかることはありません。
Complete
表示条件は canWrite() && canComplete()。処理はResolveとほぼ同じ形です。
g_form.setValue("state", g_scratchpad.STATE.CLOSED);
g_form.setValue("resolution_code", g_scratchpad.RESOLUTION_CODES.FIX_APPLIED);
g_form.save();stateをResolvedを経由せず直接Closedへ進めます。担当者が解決報告を挟まずクローズまで一気に進めたい場合のボタンです。
Communicate Workaround
表示条件は「Workaroundに本文があり、複製先が未設定」。サーバ側の処理は次のとおりです。
current.workaround_communicated_at = time;
current.workaround_communicated_by = gs.getUserID();
current.update();
gs.eventQueue('communicate.workaround', current, current.workaround);回避策を利用者へ伝えた日時と担当者を記録し、communicate.workaround イベントを発行します。Fix版の「Communicate Fix」も同じ形で、fix_notesの伝達を記録します。
Create Normal Change / Create Emergency Change
条件は gs.hasRole("itil,sn_change_write") && current.rfc.nil()。まだ変更が紐づいていないProblemに対し、通常変更または緊急変更として change_request を新規作成します。作られた変更は rfc 項目でProblemに紐づきます。
Accept Risk
表示条件は canWrite() && canShowAcceptRisk()。必須項目が入力済みかを確認したうえで、専用のヘルパー(ProblemModalUIHelpers)を呼び出します。恒久対策をせず、リスクを受け入れたままクローズへ進める経路です。
表示条件の多くが ProblemStateUtils のメソッド呼び出しになっている点も特徴です。条件式ではなくスクリプトで判定しているため、「ボタンが出ない」理由を調べるときはScript Includeの中身まで見る必要があります。
参考:ServiceNowのResolveボタンはどこで動く? UI Actionの表示条件とClient/Server処理 … Incident側での表示条件と処理の中身
1回の保存で処理が動く順序
ここまでの仕組みが、保存ボタンを1回押したときにどう重なるかを整理します。

保存から通知までは、次の順序で処理が進みます。
クライアントスクリプトとUIポリシー:ブラウザ側で入力とラベルを制御する
データポリシー:画面を問わず必須をチェックする
ACL:書き込む権限があるかを判定する。タイムスタンプ系はrole=nobodyでUIから直接書けない
ビジネスルール(before):状態遷移の可否を判定し、値を補正する
データベースへ書き込み
ビジネスルール(after):state同期・タスク連鎖・イベント発行を行う
通知:イベントを受けてメールを送る
この順序が分かると、障害の切り分けが速くなります。画面で止まるならクライアント側、保存を押した瞬間に止まるならデータポリシーかACL、保存できたのに後続が動かないならafterのルールです。
参考:ロールを付けたのに見えない理由:ACL(sys_security_acl) … 権限判定の仕組み
独自の処理を追加するときの order 設計
既存25件のorderを見ると、値の付け方に規則があります。
orderの範囲使われ方例0最初に必ず動かすCopy State to Problem State100〜120通常の処理insert_problem / Update fields on Problem Closure999他の処理の後に動かすSNC - ITIL - Update Related Incidents1,000,000最後に必ず動かすCopy Problem State to State
独自のルールは、既存が使っていない帯(500番台や1,500番台)を選ぶのが安全です。アップグレードで標準ルールのorderが変わっても、自社のルールが巻き込まれません。
同じorderのルールが複数ある場合、実行順は保証されない点にも注意が要ります。順序に依存する処理を書くなら、必ず異なる値を割り当ててください。
無効になっている定義
有効なものだけを見ていると、止まっている処理を見落とします。
種別有効無効ビジネスルール252クライアントスクリプト100UIポリシー150UIアクション221通知80
無効なUIアクションは「Create Project」の1件だけでした。条件は gs.hasRole('project_manager')。PPM(プロジェクト管理)へProblemから直接プロジェクトを起票する導線ですが、OOTBでは無効になっています。導入するプラグイン構成によっては有効化を検討する対象です。
無効なビジネスルール2件(SNC - ITIL - Close Related / SNC - ITIL - Resolve Related Incidents)は、いずれも incident.problem_id を使う処理でした。有効な SNC - ITIL - Update Related Incidents 1件だけが、この連携を実際に担っています。
実機で気づいたこと
state同期の2件だけスクリプトを持たない
25件のビジネスルールのうち23件はJavaScriptで具体的な処理を書いていましたが、Copy State to Problem State と Copy Problem State to State の2件だけは script フィールドが空欄でした。中身は problem_state SAMEAS state という宣言的な条件式のみです。
この2件は「状態が変わったらもう一方へ値をコピーする」という単純な処理のため、JavaScriptを書かずにField values形式で実装されています。自社でカスタム状態管理を追加する場合、複雑な変換が不要なら同じ手法を使う選択肢があります。order 0と1,000,000という両端の値は、他の処理に割り込まれない固定値です。必ず最初と最後に走ります。
ResolveボタンはFix Appliedを黙って埋める
データポリシーは「Resolvedなら解決コード必須」というルールを持っていますが、標準の「Resolve」「Complete」ボタンはどちらも resolution_code を FIX_APPLIED にあらかじめセットしてから保存します。
つまり、標準ボタンだけを使う運用では、この必須制御が発動する場面は実質ありません。REST APIやインポートセット経由でstateだけをResolvedへ更新しようとした場合に、初めてこの制約が効きます。API連携を組むときは、resolution_codeを明示的に渡す設計にします。
通知のイベント名が2件で食い違っている
「Problem Closed」というメール通知の event_name は incident.updated でした。Problem用のイベントではなく、Incidentのイベント名がそのまま設定されています。「Problem Closed inform Opener」は event_name が空欄で、条件式による起票判定を持ちません。
「problem events」ビジネスルールが実際にキューへ積むイベントは4種類だけです。problem.worknoted や problem.assigned.to.group を発行する処理は、problem・problem_taskのアクティブなスクリプトからは見つかりませんでした。通知が届かない相談を受けたら、まずイベント名が実際に発行されているものと一致しているかを確認します。
非推奨のasyncルールが2件残っている
「Cascade closure of Problem Tasks」と「Copy related records to original Problem」は、どちらもasyncルールです。whenの表示は「非推奨(アップグレード時には実行されない)」となっていました。それでも通常の保存では実行され、未完了のProblem Taskの一括キャンセルという重要な処理を担っています。
古い実行方式のまま重要な処理が置かれている状態です。メジャーアップグレードの際は、この2件が対象に含まれているか確認する価値があります。
実務で順番に検討する箇所
自動処理を調整するとき、次の順序で確認します。

既存25件の把握:order順に並べて、自社の処理を差し込む位置を決める
状態の同期:state と problem_state の両方を更新する前提で設計する
before か after か:遷移可否の判定や値の補正ならbefore、他レコードの更新やイベント発行ならafter
クライアント側かサーバ側か:ラベル切り替えだけならUIポリシー、GlideAjax等の問い合わせが要るならクライアントスクリプト
API経由の制御:画面外からの投入も止めるならデータポリシー
イベント名の整合:通知が参照するイベント名を、実際に発行しているルールと突き合わせる
無効な定義の扱い:Create Projectのような無効なUIアクションを有効化するか判断する
用語
OOTB:Out of the Box。プラグイン追加やカスタマイズを行っていない、製品のデフォルト状態
PDI:Personal Developer Instance。ServiceNow が無償で提供する個人検証用インスタンス
g_scratchpad:displayのビジネスルールからクライアントスクリプトへ値を渡す入れ物
Problem Model:状態遷移のルールをレコード単位で切り替える仕組み。prb_model で指定する
宣言的なフィールドコピー:JavaScriptを書かず「SAMEAS」のような条件式だけで項目間コピーを行うビジネスルールの実装方式
検証範囲とおことわり
検証環境:Australia PDI / OOTB / 2026-08-31 確認
件数はすべてOOTBの定義の数です。本番環境やカスタマイズ済み環境では異なります
スクリプトの内容は先頭部分を読んで動きを整理したものです。全文の網羅的な検証は行っていません
実際にレコードを保存して処理の順序を観測する検証は行っていません
problem.worknoted / problem.assigned.to.group の発行元はこの記事の調査範囲では特定できていません
通知の本文テンプレートは確認していません
problem_task側のビジネスルール13件は代表のみを扱い、全文は検証していません
付録:自分の環境で確認するときのURL
ビジネスルールのタイミング別:/sys_script_list.do?sysparm_query=collection=problem^active=true^GROUPBYwhen
ビジネスルールをorder順に:/sys_script_list.do?sysparm_query=collection=problem^active=true^ORDERBYorder
クライアントスクリプト:/sys_script_client_list.do?sysparm_query=table=problem^GROUPBYtype
UIポリシー:/sys_ui_policy_list.do?sysparm_query=table=problem
データポリシー:/sys_data_policy2_list.do?sysparm_query=model_table=problem
通知:/sysevent_email_action_list.do?sysparm_query=collection=problem^active=true
UIアクション:/sys_ui_action_list.do?sysparm_query=table=problem^active=true
イベント定義:/sysevent_register_list.do?sysparm_query=nameLIKEproblem
関連記事
Client ScriptとUI Policyの違い(フォーム挙動) … 画面側の使い分け
APIやインポートにも効く必須制御(Data Policy) … 画面外からの投入はここで止まる
ServiceNowの保存で実際に動くBusiness Ruleは何本か? problemテーブルで定義と実測を比べる … 1件保存したときに実際に発火した件数