クラシックビジネスルール
ビジネスルールとは、レコードが表示、挿入、更新、または削除されるとき、またはテーブルに対してクエリが実行されるときに実行する、サーバー側スクリプトです。
ビジネスルールの仕組み
ビジネスルールを構成するには、最初にビジネスルールを実行するタイミングと実行するアクションを決定する必要があります。
ビジネスルールの実行タイミング
- データベース操作に関連してビジネスルールを実行するタイミング。
- ビジネスルールが適用されるレコード操作。
| オプション | ルールが実行されるタイミング |
|---|---|
| 前 | ユーザーがフォームを送信した後で、データベース内のレコードに対してアクションが実行される前。 |
| 後 | ユーザーがフォームを送信した後で、さらにデータベース内のレコードに対してアクションが実行された後。 |
| 非同期 | ユーザーがフォームを送信した後と、ビジネスルールから作成されたスケジュール設定済みジョブがスケジューラーによって実行された後。ユーザーがフォームを送信してから、データベース内のレコードに対して何らかのアクションが実行されるまでに、システムがビジネスルールからスケジュール設定済みジョブを作成します。 注: 新しく作成されたビジネスルールはアップグレード中に実行されます。 |
| 表示 | フォームがユーザーに提示された後で、データがデータベースから読み取られた直後。 |
- 非同期ビジネスルールは、以前のバージョンのレコードにはアクセスできません。そのため、changes()、changesTo()、および changesFrom() GlideElement メソッドは非同期ルールスクリプトでは機能しません。ただし、条件ビルダーと条件フィールド (詳細ビュー) はどちらも changes()、changesTo()、および changesFrom() メソッドをサポートしています。
- ビジネスルールは、優先するように指定されるまで ACL を優先しません。詳細については、「ビジネスルールとアクセス制御ルール (ACL) の関係」を参照してください。
| オプション | ルールが実行されるタイミング |
|---|---|
| 挿入 | ユーザーが新しいレコードを作成し、システムがそれをデータベースに挿入するとき。 |
| 更新 | ユーザーが既存のレコードを変更したとき。 |
| クエリ | レコードまたはレコードリストのクエリをユーザーがデータベースに送信したとき。通常は、ビジネスルールの前にクエリ操作を使用する必要があります。 |
| 削除 | ユーザーがレコードを削除したとき。 |
ビジネスルールのアクション
- ユーザーが更新しているフォームのフィールド値を変更します。フィールド値は、そのフィールドで使用可能な特定の値、他のフィールドからコピーされた値、およびユーザーのロールによって決定される相対値に設定できます。
- ユーザーに情報メッセージを表示します。
- 親タスクの変更に基づいて子タスクの値を変更します。
- ユーザーがフォーム上の特定のフィールドにアクセスしたり、フィールドを変更したりできないようにします。
- 現在のデータベーストランザクションを中止します。たとえば、特定の条件が満たされている場合に、ユーザーがデータベースにレコードを保存できないようにします。
再帰ビジネスルールの防止
ビジネスルールスクリプトでは current.update() を使用しないでください。update() メソッドは、挿入操作と更新操作のために同じテーブルで実行するビジネスルールをトリガーします。これにより、ビジネスルールが繰り返し呼び出されます。前のビジネスルールがすべて完了すると、前のビジネスルールに加えられた変更は自動的に保存されます。後のビジネスルールは、関連する、現行のものではないオブジェクトの更新に最適です。再帰ビジネスルールが検出されると、システムはそれを停止し、システムログにエラーを記録します。current.update() はシステムパフォーマンスの問題を引き起こすため、必要ありません。
false パラメーターを指定して setWorkflow() メソッドを使用すると、再帰ビジネスルールを防ぐことができます。update() および setWorkflow() メソッドの組み合わせは、上記の通常の前と後のガイドラインが要件を満たさない特別な状況でのみ推奨されます。
スコープ対象アプリケーションのビジネスルール
すべてのビジネスルールは、プライベートアプリケーションスコープまたはグローバルスコープのいずれかに割り当てられます。
特定のテーブルのビジネスルール
ほとんどのビジネスルールは、[テーブル] フィールドで定義されている特定のテーブルで実行されます。同じスコープ内のテーブル、および別のアプリケーションスコープからの構成レコードを許可するテーブルに対して、ビジネスルールを作成できます。
ビジネスルールレコードとは異なるスコープ内のテーブルの場合は、ルールのタイプが制限されます。
- 次のいずれかのオプションを使用して、[時期] が [非同期] のルールを作成できます。
- 、、および のデータベース操作。 は選択できない。
- アクションとスクリプト ( フィールド)。
- 次のいずれかのオプションを使用して、[時期] が [次の前] であるルールを作成できます。
- 、、および のデータベース操作。 は選択できない。
- アクションのみ。スクリプトを書くことはできず、データベース トランザクションを中断することはできません。
- 異なるスコープのテーブルに他のタイプのビジネスルールを作成することはできません。
特定のテーブルに対するビジネスルールに、他のビジネスルールやスクリプトからアクセスすることはできません。
グローバルビジネスルール
グローバルビジネスルールは、[テーブル] フィールドが [グローバル] に設定されているビジネスルールですグローバルビジネスルールは、スコープ保護に応じて、複数のテーブルでアクセスでき、他のスクリプトからアクセスできます。グローバルビジネスルールの場合は、[アクセス元] フィールドを設定して、スコープの保護を定義します。
- [このアプリケーションスコープのみ]:ビジネスルールとは異なるスコープ内のアプリケーションがこのビジネスルールを呼び出すことを防止します。
- [すべてのアプリケーションスコープ]:任意のアプリケーションによるこのビジネスルールの呼び出しを許可します。注:グローバルビジネスルールはドメイン分離をサポートしていません。
スコープ対象のビジネスルールのスクリプト
ビジネスルールでスクリプトを作成すると、以下にアクセスできます。
- ビジネスルールと同じスコープ内のすべてのスクリプトインクルードとグローバルビジネスルール。
- 異なるスコープ内のアプリケーションに呼び出しを許可するスクリプトインクルードとグローバルビジネスルール。別のスコープから関数を呼び出すには、関数のスコープを指定する必要があります。
- 一意のスコープにあるビジネスルールの場合は、スコープ対象のシステム API にのみアクセスできます。
ビジネスルールの作成
レコードが表示、挿入、更新、または削除されるとき、あるいはテーブルに対してクエリが行われるときに実行される、任意のタイプのビジネスルールを作成できます。
このタスクについて
手順
ビジネスルールのグローバル変数
ビジネスルールで使用するための定義済みのグローバル変数があります。
次の事前定義されたグローバル変数を使用して、ビジネスルールスクリプトでシステムを参照します。
| グローバル変数 | 説明 |
|---|---|
| current | 参照されるレコードの現在の状態。この変数を使用する前に、以下の「null ポインター例外の防止」を参照して null を確認してください。 |
| previous | 実行コンテキスト中に行われた更新の前に参照されたレコードの状態。実行コンテキストは、最初の更新または削除操作で始まり、スクリプトと参照されたビジネスルールが実行された後に終了します。1 つの実行コンテキスト内でレコードに複数の更新が行われた場合、previous は最初の更新または削除操作の前のレコードの状態を保持し続けます。更新および削除操作でのみ使用できます。非同期操作では使用できません。この変数を使用する前に、以下の「null ポインター例外の防止」を参照して null を確認してください。 |
| g_scratchpad | スクラッチパッドオブジェクトは表示ルールで使用でき、クライアントスクリプトからアクセスできるようにクライアントに情報を渡すために使用されます。 |
| gs | GlideSystem 関数への参照。 |
変数 current、previous、g_scratchpad は、トランザクションに対して実行されるすべてのビジネスルールでグローバルです。
null ポインター例外の防止
if (current == null) // to prevent null pointer exceptions.
return; 変数の定義
ユーザー定義変数のスコープは、デフォルトでグローバルです。新しい変数が順序 100 のビジネスルールで宣言されている場合、次に実行される順序 200 のビジネスルールも変数にアクセスできます。これにより、予期しない動作が発生する可能性があります。
このような予期しない動作を防ぐために、コードは必ず関数でラップしてください。これにより変数が、関数でラップされていない他のビジネスルール内のシステム変数やグローバル変数との競合を防ぐことができます。また、current などの変数を使用するには、関数が呼び出されたときに使用可能になっている必要があります。
var now_GR = new GlideRecord('incident');
now_GR.query();
while(now_GR.next()) {
//do something
}myFunction();
function myFunction() {
var now_GR = new GlideRecord('incident');
now_GR.query();
while(now_GR.next()) {
//do something
} }ビジネスルールとクライアントスクリプトを使用したフィールド値の制御
フィールドにビジネスルールとクライアントスクリプトの両方を実装し、ユーザーがフォームとリストの両方を使用してレコード値を正しく設定できるようにします。また、編集が行われるときにフォームの値が即座に変更されるようにします。
クライアントスクリプトまたはビジネスルールのみを使用してフィールドの更新を制御する場合の問題は、フォームまたはリストのいずれかでフィールドが変更される可能性があるということです。クライアントスクリプトと UI ポリシーはフォームでのみ実行され (クライアントサイド)、リストの編集には適用されません。フォーム内のフィールドでクライアントスクリプトを実行している状態でのリスト編集を許可すると、正しくないデータがレコードに保存されるおそれがあります。クライアントスクリプトまたは UI ポリシーがフォームに適用されるシステムでは、リスト編集を無効にするか、適切なビジネスルールまたはアクセス制御を作成して、リストエディターでの値の設定を制御します。この副作用として、クライアントスクリプトに実装されたセキュリティ対策は簡単に回避されるようになります。ユーザーに必要とされるのはリスト内のフィールドを編集することだけです。
フォームのビジネスルールは動的ではありません。変更を表示するには、ユーザーがレコードを更新する必要があります。そのために、クライアントスクリプトを使用してフォームのフィールド値を制御することをお勧めします。
ビジネスルールとクライアントスクリプトの両方を使用してフィールド値を制御する場合、更新動作はシステム全体で同じです。これは、フォームのリストを使用して変更するかどうかでは、更新される値は異ならないことを意味します。これは、同じ機能を 2 回 (クライアントスクリプトに 1 回、ビジネスルールまたはアクセス制御に 1 回) 実装する必要があることを意味します。
例:ユーザーレコードのインポート中にビジネスルールを使用してメールアドレスを作成する
組織には、ユーザーのメールアドレスを first.last@company.com に設定するクライアントスクリプトがあります。管理者がこれを行うと、ユーザーの情報を入力すると即座にメールアドレスが表示されます。次に、管理者は、ユーザーの姓と名を含むスプレッドシートからユーザーの一括インポートを実行します。各ユーザーのメールアドレスは、フォームの編集時に自動的に設定されることが想定されています。クライアントスクリプトはフォーム (レコードへのインターフェイス) でのみ実行されるため、そのインターフェイスの外部からレコードにインポートされたデータに影響はなく、メールアドレスは作成されません。この問題を解決するために、管理者はインポート時に実行されるビジネスルールを実装し、メールアドレスを作成します。
例:フォームで編集できないフィールドのリスト編集を防止する
組織は、アサイン先グループが [開発] の場合、インシデントフォームの [優先度] フィールドを非表示にします。これを行うためにインシデントフォームで UI ポリシーを作成しますが、ユーザーは引き続きリストエディターを使用して [優先度] フィールドを表示および編集できます。これを修正するには、アサイン先グループが [開発] のときに [優先度] フィールドへの読み取りアクセスを防止するアクセス制御を適用します。
フィールド値としての NULL の使用
文字列 NULL は、スクリプト内で特定のロールを持つ予約語です。
予約語はすべて大文字の NULL です。Null や null などの値を持つフィールドは受け入れ可能です。NULL は、特定のフィールドをクリアするためにのみ使用します。
インポートセットデータソースから取得された NULL フィールド値は、空のフィールド値としてステージングテーブルに挿入されます。インポートセット変換マップや、[名] フィールドまたは [姓] フィールドでは、フィールド値として NULL という用語を使用しないでください。また、参照フィールドでは NULL を使用しないでください。この値は予約語ではなく用語 NULL を含む文字列として解釈されます。
ビジネスルールの表示
表示ルールは、ユーザーがレコードフォームを要求したときに処理されます。
データベースからデータが読み込まれ、表示ルールが実行されて、フォームがユーザーに表示されます。current のオブジェクトが利用可能で、データベースから取得されたレコードを表します。フィールドの変更はまだデータベースに送信されていないため、一時的なものです。クライアントには、フォームの値はデータベースの値のように見えます。値が表示ルールから変更された形跡は示されません。これは計算フィールドと同様の概念です。
表示ルールの主な目的は共有されるスクラッチパッドオブジェクト g_scratchpad を使用することです。このオブジェクトはフォームの一部としてクライアントにも送信されます。この動作は、表示されるレコードには通常含まれていないサーバーデータを必要とするクライアントスクリプトを構築する場合に役立つことがあります。ほとんどの場合、これによってクライアントスクリプトはサーバーへのコールバックが必要になります。フォームが表示される前にデータを判別できる場合は、初期ロード時にクライアントにデータを提供する方が効率的です。フォームスクラッチパッドオブジェクトは、デフォルトでは空のオブジェクトであり、データの名前:値ペアを格納するためにのみ使用されます。
// From display business rule
g_scratchpad.someName = "someValue";
g_scratchpad.anotherName = "anotherValue";
// If you want the client to have access to record fields not being displayed on the form
g_scratchpad.created_by = current.sys_created_by;
// These are simple examples, in most cases you'll probably perform some other
// queries to test or get data// From client script
if(g_scratchpad.someName == "someValue") {
//do something special
}タスクアクティブ状態管理ビジネスルール
このビジネスルールは、[状態] フィールドの変更に基づいてアクティブなフィールドの値を変更する必要があるかどうかを判断します。
タスクアクティブ状態管理ビジネスルールは、タスクレコードの状態が変更されたときに実行されます。実行順序は 50 で、他のほとんどのタスクビジネスルールの前に実行されます。
- 状態がアクティブ状態から非アクティブ状態に変わると、[アクティブ] フィールドは false に設定されます。
- 状態が非アクティブ状態からアクティブ状態に変わると、[アクティブ] フィールドは true に設定され、タスクを効果的に再アクティブ化または再オープンします。
タスクを非アクティブまたはアクティブとしてマークする各タスクテーブルでルールを作成するのではなく、ビジネスルールで (current.active.changesTo([true/false]) アクションを活用することをお勧めします。
サンプルのビジネスルールスクリプト
組織の要件に役立つサンプルのビジネスルールスクリプトを検索します。
ビジネスルールでの日付フィールドの比較
ビジネスルール内の 2 つの日付フィールドまたは 2 つの日付と時刻のフィールドを比較し、それらが正しくない場合はレコードの挿入または更新を中止できます。
たとえば、開始日を終了日より前にすることが必要になる場合があります。スクリプトの例を次に示します。
if ((!current.u_date1.nil()) && (!current.u_date2.nil())) {
var start = current.u_date1.getGlideObject().getNumericValue();
var end = current.u_date2.getGlideObject().getNumericValue();
if (start > end) {
gs.addInfoMessage('start must be before end');
current.u_date1.setError('start must be before end') ;
current.setAbortAction(true);
} }この例はグローバルスクリプトでテストされています。スコープ付きスクリプトで機能させるためには、変更が必要になる場合があります。API の変更が必要になる可能性があることに加えて、スコープ付きスクリプトではセキュリティがより厳格になります。
- u_date1 と u_date2 は 2 つの日付フィールドの名前です。これらの名前を独自のフィールド名に置き換えます。
- 最初の行では、両方のフィールドに実際に値があることを確認します。
- 次の 2 つの行では、日付の数値を持つ変数を作成します。
- 次の 2 つの行では、エンドユーザーに対して異なるアラートメッセージを作成します。1 つはフォームの上部に、もう 1 つはフォームの [u_date1] フィールドの横に表示されます。
- 日付フィールドが正しくない場合、最終行で挿入または更新は中止されます。
// Enter all start and end date fields you wish to check, as well as the previous values
// Make sure that you keep the placement in the sequence the same for all pairs
var startDate = new Array(current.start_date,current.work_start);
var prevStartDate = new Array(previous.start_date,previous.work_start);
var endDate = new Array(current.end_date,current.work_end);
var prevEndDate = new Array(previous.end_date,previous.work_end);
// The text string below is added to the front of ' start must be before end'
var userAlert = new Array('Planned','Work');
// Set the number of Previous Days you want to check
var pd = 30;
// Set the number of Future Days you want to check
var fd = 365;
// You shouldn't have to modify anything below this line
var nowdt = new GlideDateTime();
nowdt.setDisplayValue(gs.nowDateTime());
var nowMs = nowdt.getNumericValue();
var pdms = nowMs;
// Subtract the product of previous days to get value in milliseconds
pdms -= pd * 24 * 60 * 60 * 1000;
var fdms = nowMs;
// Add the product of future days to get value in miliseconds
fdms += fd * 24 * 60 * 60 * 1000;
var badDate = false;
// Iterate through all start and end date / time fields
for (x = 0; x < startDate.length; x ++) {
if ((!startDate[x].nil()) && (!endDate[x].nil())) {
var start = startDate[x].getGlideObject().getNumericValue();
var end = endDate[x].getGlideObject().getNumericValue();
if (start > end) {
gs.addInfoMessage(userAlert[x] + ' start must be before end');
startDate[x].setError(userAlert[x] + ' start must be before end');
badDate = true; }
else if ((prevStartDate[x]) != (startDate[x])) {
if (start < pdms) {
gs.addInfoMessage(userAlert[x] + ' start must be fewer than ' + pd + ' days ago');
startDate[x].setError(userAlert[x] + ' start must be fewer than ' + pd + ' days ago');
badDate = true; } }
else if ((prevEndDate[x]) != (endDate[x])) {
if (end > fdms) {
gs.addInfoMessage(userAlert[x] + ' end must be fewer than ' + fd + ' days ahead');
endDate[x].setError(userAlert[x] + ' end must be fewer than ' + fd + ' days ahead');
badDate = true ;
} } } }
if (badDate == true ) {
current. setAbortAction ( true ) ; }XML ペイロードの解析
XML 形式のフィールドは、システムの getXMLText 関数を使用して解析できます。
ecc_event 行のペイロードなど、XML 形式でデータベースに挿入されるフィールドは、システムの getXMLText 関数を使用して解析できます。getXMLText 関数は、文字列と XPATH 式を受け取ります。例:var name = gs.getXMLText("<name>joe</name>", "//name");文字列「joe」を返します。
var name = gs.getXMLText(current.payload, "//name");XPATH の詳細については、「w3schools」を参照してください。
前のビジネスルールでのデータベースアクションの中止
前のビジネスルールスクリプトで、setAbortAction() メソッドを使用して現在のデータベースアクションをキャンセルまたは中止できます。
たとえば、前のビジネスルールが挿入アクション中に実行され、スクリプトに current.setAbortAction(true) を呼び出す条件がある場合、current に保存される新しいレコードはデータベース内には作成されません。このビジネスルールは setAbortAction() を呼び出した後も実行を継続し、後続のすべてのビジネスルールは正常に実行されます。このメソッドを呼び出すと、データベースアクションの実行のみが防止されます。
isActionAborted() メソッドを使用して、現在のデータベースアクション (挿入、更新、削除) を中止するかどうかを決定できます。 isActionAborted() は新しいスレッド用に初期化され、next() メソッドはその値を明示的に false に設定します。
current.setAbortAction は、別のスコープで定義されているビジネスルールで実行された場合、優先されません。ビジネスルールでトリガーされる操作の決定
複数のデータベースアクションでトリガーされるビジネスルールのスクリプトを作成できます。
if(current.operation() == "update") {
current.updates ++; }
if(current.operation() == "insert") {
current.updates = 0; }ビジネスルールでの OR 条件の使用
OR 条件は、ビジネスルール内の任意のクエリ部分に追加できます。
var inc = new GlideRecord('incident');
var qc = inc.addQuery('priority','1');
qc.addOrCondition('priority','2');
inc.query();
while(inc.next()) {
// processing for the incident goes here
}(priority = 1 OR priority = 2) AND (impact = 2 OR impact = 3) と同等の実行をします。OR 条件の結果は、2 つの変数 qc1 と qc2 を使用して実行されます。これにより、IF 条件や WHILE ループ内など、スクリプトの後半でクエリ条件オブジェクトを操作できます。var inc = new GlideRecord('incident');
var qc1 = inc.addQuery('priority','1');
qc1.addOrCondition('priority','2');
var qc2 = inc.addQuery('impact','2');
qc2.addOrCondition('impact','3');
inc.query();
while(inc.next()) {
// processing for the incident goes here
}ビジネスルールからの Glide リストの参照
Glide リストとして定義されたフィールドは、単一のフィールドに格納される値のアレイです。
ビジネスルールを記述するときに glide_list フィールドを処理する方法の例を次に示します。通常、glide_list フィールドには、他のテーブルへの参照値のリストが含まれています。
例
たとえば、タスク内の [ウォッチリスト] フィールドは、ユーザーレコードへの参照を含む glide_list です。
次のコードに、フィールドを参照する方法を示します。
// list will contain a series of reference (sys_id) values separated by a comma
// array will be a javascript array of reference values
var list = current.watch_list.toString();
var array = list.split(",");
for (var i=0; i < array.length; i++) {
gs.print("Reference value is: " + array[i]);
}*** Script: Reference value is: 62826bf03710200044e0bfc8bcbe5df1
*** Script: Reference value is: c2826bf03710200044e0bfc8bcbe5d45
*** Script: Reference value is: 5f74e421c0a8010e01ec0d74a7ee2cc6
*** Script: Reference value is: 06826bf03710200044e0bfc8bcbe5d57また、次に示すように getDisplayValue() メソッドを使用して、参照値に関連付けられた表示値を取得することもできます。
// list will contain a series of display values separated by a comma
// array will be a javascript array of display values
var list = current.watch_list.getDisplayValue();
var array = list.split(",");
for (var i=0; i < array.length; i++) {
gs.print("Display value is: " + array[i]);
}*** Script: Display value is: Abel Tuter
*** Script: Display value is: Ashley Leonesio
*** Script: Display value is: Charles Beckley
*** Script: Display value is: Cherie FuhriindexOf("searchString") を使用して Glide リスト内の文字列を検索する
Glide リストフィールド (ウォッチリストなど) に少なくとも 1 つの値がある場合は、indexOf("searchString") を使用してメソッドに渡される文字列の場所を返します。
フィールドが空の場合は、undefined が返されます。未定義の値を返さないようにするには、次のいずれかを実行します。
- フィールドを強制的に文字列にします。例: watch_list.toString().indexOf("searchString")
- indexOf() を使用する前に、条件がある空の Glide リストフィールドを確認します。例:if (watch_list.nil() || watch_list.indexOf("searchString") == -1)
ユーザーアカウントのロック
ユーザーがアクティブでない場合は、ユーザーアカウントをロックできます。
// Lock accounts if bcNetIDStatus != active in LDAP and user does not
// have self-service, itil or admin role
var rls = current.accumulated_roles.toString();
if(current.u_bcnetidstatus == 'active' && (rls.indexOf(',itil,') > 0 ||
rls.indexOf(',admin,') > 0 ||
rls.indexOf(',ess,') > 0 )) {
current.locked_out = false; }
else {
current.locked_out = true; }
var now_GR = new GlideRecord("sys_user");
now_GR.query();
while(now_GR.next()) {
now_GR.update();
gs.info("updating " + gr.getDisplayValue());
}デフォルトのクエリ前ビジネスルール
データベースクエリの実行前に実行されるクエリビジネスルールを使用できます。
- 名前:incident query
- テーブル:インシデント
- タイミング:クエリ前
- スクリプト:
if(!gs.hasRole("itil") && gs.isInteractive()) {
var u = gs.getUserID();
var qc = current.addQuery("caller_id",u).addOrCondition("opened_by",u).addOrCondition("watch_list","CONTAINS",u);
gs.print("query restricted to user: " + u); }