簡介

本教學課程示範如何實作 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 的移轉文件。

秘訣:

此教學課程所需的命令檔會連結為每個區段內的文字檔。

建立程序檔

  1. 開啟 Calculation Manager,並在 Plan1 立方體中建立名為 Groovy 驗證員工資料的規則。
  2. 新建物件
  3. 在「規則編輯器」中,將「設計工具」選項變更為編輯命令檔,然後將「命令檔類型」設為 Groovy 命令檔
  4. 規則編輯器選項
  5. 複製此命令檔並貼到編輯器中。
  6. 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.")  
            }
        }
    }
    
  7. 在工具列上,按一下 儲存 (儲存) 以儲存命令檔,然後按一下 驗證和部署 (驗證並部署) 以驗證並部署命令檔。提示時,按一下確定。關閉 Calculation Manager。

說明命令檔邏輯

在本節中,我們將一次描述一個區段的指令檔邏輯。

  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。例如,如果 minSalary 為空值,{minSalary?.formattedValue} 會評估為 NULL 值,而不是發出 NullPointerException 異常狀況。

  2. 建立搭售品載入器。
  3. // 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 物件。

  4. 從 DataGridDefinition 物件所定義區域的查尋立方體載入資料方格。
  5. DataGrid dataGrid = lookupCube.loadGrid(gridDefinition, false)  
  6. 從資料方格中,依薪等名稱建立薪等資料 (最低 / 最高薪資與獎金) 對應。
    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 物件為值。

  7. 建構重複程式,以重複目前輸入方格中包含「等級」成員的所有資料儲存格。
  8. DataGrid grid = operation.grid
    GridIterator itr = grid.dataCellIterator('Grade')
  9. 發出否決例外。
  10. // 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")
    }

    驗證輸入方格至少有一個包含「等級」成員的儲存格。如果不是,則在本土化訊息中發出否決例外。

  11. 驗證方格值。
  12. // 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 會取得從中導出目前儲存格之智慧列示值的成員名稱,然後在對應中作為索引鍵來取得等級資料。

    交叉調暗至「薪資」儲存格,然後根據成績資料中允許的「薪資」來驗證「薪資」。

    橫向展開「紅利」儲存格,並針對成績資料中允許的「紅利」驗證「紅利」。如果「薪資」或「紅利」不在允許的限制內,請將儲存格的背景顏色設為紅色,並呼叫 addValidationError() API 在儲存格上新增驗證錯誤。錯誤訊息將設為儲存格的工具提示,也會顯示在「方格驗證訊息」中。此外,如果此規則在「儲存後」執行,則系統會否決儲存作業。

測試程序檔

  1. 在「表單管理程式」中,編輯 ManageEmployees 表單,並將「Groovy 驗證員工資料」規則與載入後儲存前建立關聯 (您可能需要先從「立方體」清單中選取 Plan1)。
  2. ManageEmployees 表單與相關聯的規則。
  3. 儲存表單並開啟「表單管理程式」視窗。
  4. 開啟員工儀表板。為員工 1 新增 1000 紅利,將員工 2 的薪資增加至 65000 。在表單工具列中,按一下 儲存 (儲存)。
  5. 「員工」儀表板。
  6. 在「錯誤訊息」對話方塊中,按一下確定
  7. 「員工」儀表板。
  8. 將滑鼠游標停留在紅色儲存格上方,即可看到詳細的錯誤訊息作為工具提示。

    錯誤訊息。

    錯誤訊息。

  9. 關閉儀表板並開啟「工作」主控台。
  10. 工作主控台的「最近活動」窗格。
  11. 按一下 Groovy 驗證員工資料以顯示職務詳細資料。按一下已完成狀態,以查看從查尋立方體載入的職等資料,以及每個員工職等的職等資料。
  12. 日誌訊息。

其他學習資源

docs.oracle.com/learn 上探索其他實驗室,或在 Oracle Learning YouTube 頻道上存取更多免費學習內容。此外,請造訪 Oracle University 以檢視可用的訓練資源。

如需產品文件,請造訪 Oracle Help Center