イントロダクション
このチュートリアルでは、ドライバ・キューブに格納されている許容範囲に対してデータ入力を検証するGroovyスクリプトの実装方法を示します。
バックグラウンド
この例では、ユーザーがデータ入力フォームから従業員の給与および賞与に対する更新を保存したときに実行されるスクリプトを作成します。スクリプトは、ドライバ・キューブから従業員等級データ(最小/最大許容給与および最小/最大許容賞与)をメモリーの一時グリッドにロードし、入力された給与および賞与の値を許容範囲に対して検証します。
前提条件
Cloud EPMのハンズオン・チュートリアルでは、Cloud EPM Enterprise Serviceインスタンスにスナップショットをインポートする必要がある場合があります。チュートリアル・スナップショットをインポートする前に、別のCloud EPM Enterprise Serviceインスタンスをリクエストするか、現在のアプリケーションおよびビジネス・プロセスを削除する必要があります。チュートリアル・スナップショットは、既存のアプリケーションまたはビジネス・プロセスにはインポートされません。また、現在作業中のアプリケーションまたはビジネス・プロセスを自動的に置換または復元することもありません。
このチュートリアルを始める前に、次のことが必要です。
- サービス管理者にCloud EPM Enterprise Serviceインスタンスへのアクセス権を付与します。
- このスナップショットをPlanningインスタンスにアップロードおよびインポートします。別のGroovyチュートリアルのスナップショットを以前にアップロードした場合は、同じスナップショットを引き続き使用できます。
ノート:
スナップショットのインポート中に移行エラーが発生した場合は、HSS-Shared Servicesコンポーネントを除く移行、およびコア・コンポーネントのセキュリティ・アーティファクトとユーザー・プリファレンス・アーティファクトを再実行します。スナップショットのアップロードおよびインポートの詳細は、Oracle Enterprise Performance Management Cloud移行の管理のドキュメントを参照してください。ヒント :
このチュートリアルに必要なスクリプトは、各セクション内にテキスト・ファイルとしてリンクされています。スクリプトの作成
- Calculation Managerを開き、Plan1キューブで「Groovy従業員データの検証」という名前のルールを作成します。
- ルール・エディタで、デザイナ・オプションを「スクリプトの編集」に変更し、スクリプト・タイプを「Groovyスクリプト」に設定します。
- このスクリプトをコピーして、エディタに貼り付けます。
- ツールバーで、
(保存)をクリックしてスクリプトを保存し、
(検証およびデプロイ)をクリックしてスクリプトを検証およびデプロイします。プロンプトが表示されたら、「OK」をクリックします。Calculation Managerを閉じます。


class GradeData {
Integer Grade
DataGrid.DataCell minSalary
DataGrid.DataCell maxSalary
DataGrid.DataCell minBonus
DataGrid.DataCell maxBonus
public String toString() {
return "minSalary: ${minSalary?.formattedValue}, maxSalary: ${maxSalary?.formattedValue}, minBonus: ${minBonus?.formattedValue}, maxBonus: ${maxBonus?.formattedValue}"
}
}
// Create a resource bundle loader containing localized messages needed by this rule.
def mbUs = messageBundle( ["validation.missingmember.grade":"No Grades found on the Grid."] )
def mbl = messageBundleLoader(["en" : mbUs])
Cube lookupCube = operation.application.getCube("Plan1")
DataGridDefinitionBuilder builder = lookupCube.dataGridDefinitionBuilder()
builder.addPov(['Years', 'Scenario', 'Currency', 'Period', 'Version', 'Entity'], [['FY16'], ['Current'], ['USD'], ['BegBalance'],
['BU Version_1'], ['No Entity']])
builder.addColumn(['Account'], [ ['Min Salary', 'Max Salary', 'Min Bonus', 'Max Bonus'] ])
builder.addRow(['Grades'], [ ['ILvl0Descendants("Grades")'] ])
DataGridDefinition gridDefinition = builder.build()
// Load the data grid from the lookup cube
DataGrid dataGrid = lookupCube.loadGrid(gridDefinition, false)
// Create a map of grade data (Min/Max Salary and Bonus) by grade name from the data grid.
def gradeDataMap = new HashMap()
if(dataGrid) {
println("dataGrid is not null")
dataGrid.dataCellIterator('Min Salary').each {
def gradeData = new GradeData()
gradeData.minSalary = it
gradeData.maxSalary = it.crossDimCell('Max Salary')
gradeData.minBonus = it.crossDimCell('Min Bonus')
gradeData.maxBonus = it.crossDimCell('Max Bonus')
gradeDataMap[(it.getMemberName('Grades'))] = gradeData
println(it.getMemberName('Grades') + ": " + gradeData)
}
}
DataGrid grid = operation.grid
// Construct an iterator that iterates over all data cells containing the Grade member.
GridIterator itr = grid.dataCellIterator('Grade')
// Throw a veto exception if the grid has at least one cell but does not contain any cells containing the Grade member.
if(!grid.empty && !itr.hasNext()) {
// Found 0 cells with Grade
throwVetoException(mbl, "validation.missingmember.grade")
}
// Validate the values in the grid being saved against the values in gradeDataMap.
itr.each {
GradeData gradeData = gradeDataMap[it.DataAsSmartListMemberName]
if(gradeData == null) {
println("Unable to locate grade data for grade: ${it.DataAsSmartListMemberName}, with data value: ${it.formattedValue}" )
} else {
println(it.getMemberName('Employee') + ": " + gradeData.toString())
DataCell salary = it.crossDimCell('Salary')
if(salary == null)
println("Unable to locate Salary")
else if(salary.data < gradeData.minSalary.data || salary.data > gradeData.maxSalary.data) {
salary.addValidationError(0xFF0000, "Salary is outside of the allowed range.")
}
DataCell bonus = it.crossDimCell('Bonus')
if(bonus == null) {
println("Unable to locate Bonus")
} else if(bonus.data < gradeData.minBonus.data || bonus.data > gradeData.maxBonus.data) {
bonus.addValidationError(0xFF0000, "Bonus is outside of the allowed range.")
}
}
}
スクリプト・ロジックの説明
この項では、スクリプト・ロジックを1つずつ説明します。
-
クラスを作成します。
class GradeData {
Integer Grade
DataGrid.DataCell minSalary
DataGrid.DataCell maxSalary
DataGrid.DataCell minBonus
DataGrid.DataCell maxBonus
public String toString() {
return "minSalary: ${minSalary?.formattedValue},
maxSalary: ${maxSalary?.formattedValue},
minBonus: ${minBonus?.formattedValue},
maxBonus: ${maxBonus?.formattedValue}"
}
}
ドライバ・キューブから従業員等級データ(最小/最大許容給与および最小/最大許容ボーナス)を格納するクラスGradeDataを作成します。
toString()メソッドは、GradeDataオブジェクトの文字列表現を返します。このメソッドは、Groovyによって提供されるnullセーフ演算子(?.)を使用します。疑問符の前の変数がnullの場合、その変数は続行されず、NULLが返されます。たとえば、minSalaryがnullの場合、{minSalary?.formattedValue}はNullPointerException例外をスローするのではなく、NULL値に評価されます。 - バンドル・ローダーを作成します。
- DataGridDefinitionオブジェクトによって定義されたリージョンの参照キューブからデータ・グリッドをロードします。
- データ・グリッドから等級名別に等級データ(最小/最大給与および賞与)のマップを作成します。
def gradeDataMap = new HashMap<String, GradeData>()
if(dataGrid) {
println("dataGrid is not null")
dataGrid.dataCellIterator('Min Salary').each {
def gradeData = new GradeData()
gradeData.minSalary = it
gradeData.maxSalary = it.crossDimCell('Max Salary')
gradeData.minBonus = it.crossDimCell('Min Bonus')
gradeData.maxBonus = it.crossDimCell('Max Bonus')
gradeDataMap[(it.getMemberName('Grades'))] = gradeData
println(it.getMemberName('Grades') + ": " + gradeData)
}
}
等級データ(最小/最大給与および賞与)のgradeDataMapというマップを等級名別に作成します。
gradeDataMapマップに各等級の等級データを移入します。等級がキーで、最小/最大給与および賞与が移入されたGradeDataオブジェクトが値です。 - 現在の入力グリッドのGradeメンバーを含むすべてのデータ・セルを反復するイテレータを構築します。
- 拒否例外をスローします。
- グリッド値を検証します。
// Create a resource bundle loader containing localized messages needed by this rule. def mbUs = messageBundle( ["validation.missingmember.grade":"No Grades found on the Grid."] )def mbl = messageBundleLoader(["en" : mbUs]) Cube lookupCube = operation.application.getCube("Plan1")
DataGridDefinitionBuilder builder = lookupCube.dataGridDefinitionBuilder()
builder.addPov(['Years', 'Scenario', 'Currency', 'Period', 'Version', 'Entity'], [['FY16'], ['Current'], ['USD'], ['BegBalance'], ['BU Version_1'], ['No Entity']]) builder.addColumn(['Account'], [ ['Min Salary', 'Max Salary', 'Min Bonus', 'Max Bonus'] ]) builder.addRow(['Grades'], [ ['ILvl0Descendants("Grades")'] ]) DataGridDefinition gridDefinition = builder.build()
![]()
このルールに必要なローカライズされたメッセージを含むリソース・バンドル・ローダーを作成します。
ドライバ/ルックアップ・キューブを取得します。
キューブのDataGridDefinitionBuilderを取得します。ビルダーを使用して、POVメンバー、列メンバーおよび行を追加して、データのロード元となるリージョンを作成します。
DataGridDefinitionオブジェクトを作成します。
DataGrid dataGrid = lookupCube.loadGrid(gridDefinition, false)
DataGrid grid = operation.grid
GridIterator itr = grid.dataCellIterator('Grade')
// Throw a veto exception if the grid has at least one cell but does not contain any cells containing the Grade member.
if(!grid.empty && !itr.hasNext()) {
// Found 0 cells with Grade
throwVetoException(mbl, "validation.missingmember.grade")
}
入力グリッドに、Gradeメンバーを含むセルが少なくとも1つあることを確認します。そうでない場合は、ローカライズされたメッセージで拒否例外をスローします。
// Validate the values in the grid being saved against the values in gradeDataMap.
itr.each {
GradeData gradeData = gradeDataMap[it.DataAsSmartListMemberName]
if(gradeData == null) {
println("Unable to locate grade data for grade: ${it.DataAsSmartListMemberName},
with data value: ${it.formattedValue}" )
} else {
println(it.getMemberName('Employee') + ": " + gradeData.toString())
DataCell salary = it.crossDimCell('Salary')
if(salary == null)
println("Unable to locate Salary")
else if(salary.data < gradeData.minSalary.data || salary.data > gradeData.maxSalary.data)
{
salary.addValidationError(0xFF0000, "Salary is outside of the allowed range.")
}
DataCell bonus = it.crossDimCell('Bonus')
if(bonus == null) {
println("Unable to locate Bonus")
} else if(bonus.data < gradeData.minBonus.data || bonus.data > gradeData.maxBonus.data) {
bonus.addValidationError(0xFF0000, "Bonus is outside of the allowed range.")
}
}
}
保存される入力グリッド内の値をgradeDataMapの値に対して検証します。入力グリッドの等級は、参照キューブで定義された等級階層から導出されるスマート・リスト・タイプの勘定科目です。it.DataAsSmartListMemberNameをコールすると、現在のセルのスマート・リスト値が導出されるメンバーの名前が取得され、等級データを取得するためにマップのキーとして使用できます。
「Salary(給与)」セルにクロス・ディメンションし、等級データの許容給与に対して給与を検証します。
「Bonus(ボーナス)」セルにクロス・ディメンションを入れて、等級データの許可されたボーナスに対してボーナスを検証します。給与または賞与が許可された制限内にない場合は、セルの背景色を赤に設定し、addValidationError() APIをコールしてセルに検証エラーを追加します。エラー・メッセージはセルのツールチップとして設定され、グリッド検証メッセージにも表示されます。また、このルールが保存後に実行されている場合は、保存操作が拒否されます。
スクリプトのテスト
- フォーム・マネージャで、ManageEmployeesフォームを編集し、「ロード後」および「保存前」を実行するGroovy従業員データの検証ルールを関連付けます(最初にキューブ・リストからPlan1を選択する必要がある場合があります)。
- フォームを保存し、「フォーム・マネージャ」ウィンドウを閉じます。
- 「従業員」ダッシュボードを開きます。従業員1に1000の賞与を追加し、従業員2の給与を65000に増やします。フォーム・ツールバーの
(保存)をクリックします。
- 「エラー・メッセージ」ダイアログ・ボックスで、「OK」をクリックします。
- 赤いセルの上にマウスを置くと、詳細なエラー・メッセージがツールチップとして表示されます。


- ダッシュボードを閉じて、「ジョブ」コンソールを開きます。
- 「Groovy従業員データの検証」をクリックして、ジョブの詳細を表示します。「完了」ステータスをクリックすると、参照キューブからロードされた等級データ、および各従業員の等級ごとの等級データが表示されます。





関連リンク
その他の学習リソース
docs.oracle.com/learnで他のラボを確認するか、Oracle Learning YouTubeチャネルで無料のラーニング・コンテンツにアクセスしてください。さらに、Oracle Universityにアクセスして、利用可能なトレーニング・リソースを確認してください。
製品ドキュメントについては、Oracle Help Centerを参照してください。