Forum Discussion

ChristianW's avatar
ChristianW
Valued Contributor
4 years ago

Can I test a manual input before save and/or calculate

Hi all

I like to check a manual data entry during save.

Is this possible?

 

  • Yes, this is possible, you need a SaveDataEventHandler Business rule for it. Here are some code samples:

    First the basic function, only testing the input itself:

    Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As Object, ByVal args As SaveDataEventHandlerArgs) As Object
    	Try
    '------------------------
    ' Validate entered values
    '------------------------
    		'Only test manual input
    		If args.NewDataCell.DataCellPK.OriginId = DimConstants.Forms Then 
    '---------------------------
    'Account must be between 0-1
    '---------------------------
    			Dim accountToTest As String = "60000"
    			'Test, if it is the account to test
    			If args.NewDataCell.DataCellPK.AccountId = BRApi.Finance.Members.GetMemberId(si, dimtype.Account.Id, accountToTest) Then 
    				'The input amount
    				Dim dCellAmount As Decimal = args.NewDataCell.CellAmount
    				
    				'Test the amount
    				If dCellAmount > 1 Or dCellAmount < 0 Then
    					'Throw an error to prevent the save
    					args.Cancel = True
    					Throw New Exception("Account: " & environment.NewLine & "Value entered must be between 0% and 100%" & environment.NewLine)
    				End If
    			End If
    		End If
    		Return args.DefaultReturnValue
    	Catch ex As Exception
    		Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
    	End Try
    End Function

    And here a sample how to check if Forms and Import together have fit the test:

    Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As Object, ByVal args As SaveDataEventHandlerArgs) As Object
    	Try
    '------------------------
    ' Validate entered values
    '------------------------
    		'Only test manual input
    		If args.NewDataCell.DataCellPK.OriginId = DimConstants.Forms Then 
    '---------------------------
    'Account must be between 0-1
    '---------------------------
    			Dim accountToTest As String = "60000"
    			'Test, if it is the account to test
    			If args.NewDataCell.DataCellPK.AccountId = BRApi.Finance.Members.GetMemberId(si, dimtype.Account.Id, accountToTest) Then 
    				'Some objects as shortcuts
    				Dim originalCell As DataCell = args.NewDataCell
    				Dim originalCellPk As DataCellPk = originalCell.DataCellPK
    				'Some objects needed to retrieve the Data from the origin Import
    				Dim importCellPK As New DataCellPk(originalCellPk)
    				importCellPK.OriginId = dimconstants.Import
    
    				Dim sCubeName As String = brapi.Finance.Cubes.GetCubeInfo(si, importCellPK.CubeId).Cube.Name
    				Dim importDataUnitPk As New DataUnitPk(importCellPK)
    				Dim importDataBufferCellPk As New DataBufferCellPk(importCellPK)
    				
    				'The input amount
    				Dim dCellAmount As Decimal = args.NewDataCell.CellAmount
    				'To test before adjustment retrieve the import datacell 
    				Dim dataCellList As List(Of DataCell) = BRApi.Finance.Data.GetDataBufferDataCells(si, importDataUnitPk, importCellPK.ViewId, importDataBufferCellPk, False,True)
    				If dataCellList.Count = 1 Then
    					'If there is a result add it to the amount to test
    					dCellAmount += dataCellList(0).CellAmount
    				End If	
    				
    				'Test the amount
    				If dCellAmount > 1 Or dCellAmount < 0 Then
    					'Throw an error to prevent the save
    					args.Cancel = True
    					Throw New Exception("Account: " & environment.NewLine & "Value entered must be between 0% and 100%" & environment.NewLine)
    				End If
    													
    			End If
    		End If
    		Return args.DefaultReturnValue
    	Catch ex As Exception
    		Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
    	End Try
    End Function

    I hope this helps

  • Christian's code to check a manual input before save and/or calculate is a good example of using the Save Data Event Handler.  The Save Data Event Handler is an Event Handler that runs in order to track all save events in an application.  The Save Data Event Handler does not necessarily have Api's ( BRApi's can be used ) but does use Args object references to various objects such as data cells.  Since the Save Data Event Handler runs against any of the save events, it is important to understand what happens with the Save Data Event Handler when put under the bright lights of a production environment.  Implementations come in all shapes and sizes and one size does not fit all.  This is true for the Save Data Event Handler as well.  Here are some things to consider when deciding to leverage Christian's code to begin your Save Data Event Handler journey.

    1.  Using a Single Account -  In this example, Christian is using a single account to demonstrate the concept of checking the amount of a data cell to determine whether the save should happen or not.  This absolutely works and works well.  Here is something to think about:  When you think about this single account, this single account is not the only dimension in the data cell.  There are 19 Dimensions that make up a single data cell.  For an example, you may have a Cube Model that contains 100 UD1's, 200 UD2's, and 10,000 UD3's which make up a valid data cell intersection combination with that single account.  This is 200 million possible data cell combinations for this single account.  If a Form was built with UD1, UD2, and UD3 as rows, and the single account in the Cube View POV (all available for manual input) each one of these data cell amounts would need to be evaluated to determine if they should be saved.  This is one user that is cycling through 200 million data cells before completing a save command.  Now imagine that you have 100 users simultaneously working within a variety of different Forms doing the same thing.  Suggestion - Be aware on how the Forms are built and how many data cells that the Save Event Handler will have to process before it saves.  Testing this process in a production type environment as others are interacting is important to understand the overall impact on the system.  Again, no 1 implementation and environment is alike so parallel testing is important.  

    2.  Limiting Rule Execution By Forms -  In this example, Christian is showing how to execute the logic based on the Forms Origin.  The logic will only execute on the data cells with Forms Origin  

    If args.NewDataCell.DataCellPK.OriginId = DimConstants.Forms

     This allows a level of assurance that this logic will only run when a user is processing a Form using the Forms Origin.  So when the user clicks on Save in a Form, it will execute the logic and evaluate the data cells to determine to save the data or not.  This will happen for any Form in the application that is processed and the user clicks Save whether the Form was intended to use this logic or not.  Therefore, it would be prudent to limit the Forms that you would like the Save Event Handler to execute on.   Dim objBRApiForms As BRApiForms = BRApi.Forms.Metadata.GetForm.Form.Name.Equals("NameOfForm").  This is something to consider when introducing the Save Data Event Handler.  Should it apply to all Forms or is it specific to a Form?  

    3.  More Than 1 Account for Manual Input and Looping on More Than 1 Account -  This certainly can be done.  However, consider what was explained under item 1 above.  There will be a multiplication factor depending on how many accounts for manual input and data cells evaluated.  Another point to consider when building out Forms and how many data cells are available within the Form.   

    4.  Using BRApi's to Cross Engines -  BRApi's are very useful to cross OneStream Processing Engines.  As common practice, it is best to use Api's that are provided within each Engine.  As mentioned before, the Save Data Event Handler does not have Api's but does contain Args object references and BRApi's.  In Christian's example, since there is no Api to use, we are using the BRApi to cross into the Finance Engine to get the Account Id.  This is certainly needed in order to process this logic.  The consideration here is how often is an user saving a Form and executing the logic to cross into the Finance Engine.  Is it 1 person or 100 people simultaneously executing a Save and impacting the General servers?  Something to monitor when testing.  

    Hope these considerations help when using this Business Rule.  And remember TEST, TEST, TEST functionality and user interactions under production type environment to ensure a pleasant user experience.  Christian is providing you with the power.  It is up to you on how to use it.  Consider these 4 items when implementing in a production environment.  

  • ChristianW's avatar
    ChristianW
    Valued Contributor

    Yes, this is possible, you need a SaveDataEventHandler Business rule for it. Here are some code samples:

    First the basic function, only testing the input itself:

    Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As Object, ByVal args As SaveDataEventHandlerArgs) As Object
    	Try
    '------------------------
    ' Validate entered values
    '------------------------
    		'Only test manual input
    		If args.NewDataCell.DataCellPK.OriginId = DimConstants.Forms Then 
    '---------------------------
    'Account must be between 0-1
    '---------------------------
    			Dim accountToTest As String = "60000"
    			'Test, if it is the account to test
    			If args.NewDataCell.DataCellPK.AccountId = BRApi.Finance.Members.GetMemberId(si, dimtype.Account.Id, accountToTest) Then 
    				'The input amount
    				Dim dCellAmount As Decimal = args.NewDataCell.CellAmount
    				
    				'Test the amount
    				If dCellAmount > 1 Or dCellAmount < 0 Then
    					'Throw an error to prevent the save
    					args.Cancel = True
    					Throw New Exception("Account: " & environment.NewLine & "Value entered must be between 0% and 100%" & environment.NewLine)
    				End If
    			End If
    		End If
    		Return args.DefaultReturnValue
    	Catch ex As Exception
    		Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
    	End Try
    End Function

    And here a sample how to check if Forms and Import together have fit the test:

    Public Function Main(ByVal si As SessionInfo, ByVal globals As BRGlobals, ByVal api As Object, ByVal args As SaveDataEventHandlerArgs) As Object
    	Try
    '------------------------
    ' Validate entered values
    '------------------------
    		'Only test manual input
    		If args.NewDataCell.DataCellPK.OriginId = DimConstants.Forms Then 
    '---------------------------
    'Account must be between 0-1
    '---------------------------
    			Dim accountToTest As String = "60000"
    			'Test, if it is the account to test
    			If args.NewDataCell.DataCellPK.AccountId = BRApi.Finance.Members.GetMemberId(si, dimtype.Account.Id, accountToTest) Then 
    				'Some objects as shortcuts
    				Dim originalCell As DataCell = args.NewDataCell
    				Dim originalCellPk As DataCellPk = originalCell.DataCellPK
    				'Some objects needed to retrieve the Data from the origin Import
    				Dim importCellPK As New DataCellPk(originalCellPk)
    				importCellPK.OriginId = dimconstants.Import
    
    				Dim sCubeName As String = brapi.Finance.Cubes.GetCubeInfo(si, importCellPK.CubeId).Cube.Name
    				Dim importDataUnitPk As New DataUnitPk(importCellPK)
    				Dim importDataBufferCellPk As New DataBufferCellPk(importCellPK)
    				
    				'The input amount
    				Dim dCellAmount As Decimal = args.NewDataCell.CellAmount
    				'To test before adjustment retrieve the import datacell 
    				Dim dataCellList As List(Of DataCell) = BRApi.Finance.Data.GetDataBufferDataCells(si, importDataUnitPk, importCellPK.ViewId, importDataBufferCellPk, False,True)
    				If dataCellList.Count = 1 Then
    					'If there is a result add it to the amount to test
    					dCellAmount += dataCellList(0).CellAmount
    				End If	
    				
    				'Test the amount
    				If dCellAmount > 1 Or dCellAmount < 0 Then
    					'Throw an error to prevent the save
    					args.Cancel = True
    					Throw New Exception("Account: " & environment.NewLine & "Value entered must be between 0% and 100%" & environment.NewLine)
    				End If
    													
    			End If
    		End If
    		Return args.DefaultReturnValue
    	Catch ex As Exception
    		Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
    	End Try
    End Function

    I hope this helps

    • TonyToniTone's avatar
      TonyToniTone
      Contributor II

      Christian's code to check a manual input before save and/or calculate is a good example of using the Save Data Event Handler.  The Save Data Event Handler is an Event Handler that runs in order to track all save events in an application.  The Save Data Event Handler does not necessarily have Api's ( BRApi's can be used ) but does use Args object references to various objects such as data cells.  Since the Save Data Event Handler runs against any of the save events, it is important to understand what happens with the Save Data Event Handler when put under the bright lights of a production environment.  Implementations come in all shapes and sizes and one size does not fit all.  This is true for the Save Data Event Handler as well.  Here are some things to consider when deciding to leverage Christian's code to begin your Save Data Event Handler journey.

      1.  Using a Single Account -  In this example, Christian is using a single account to demonstrate the concept of checking the amount of a data cell to determine whether the save should happen or not.  This absolutely works and works well.  Here is something to think about:  When you think about this single account, this single account is not the only dimension in the data cell.  There are 19 Dimensions that make up a single data cell.  For an example, you may have a Cube Model that contains 100 UD1's, 200 UD2's, and 10,000 UD3's which make up a valid data cell intersection combination with that single account.  This is 200 million possible data cell combinations for this single account.  If a Form was built with UD1, UD2, and UD3 as rows, and the single account in the Cube View POV (all available for manual input) each one of these data cell amounts would need to be evaluated to determine if they should be saved.  This is one user that is cycling through 200 million data cells before completing a save command.  Now imagine that you have 100 users simultaneously working within a variety of different Forms doing the same thing.  Suggestion - Be aware on how the Forms are built and how many data cells that the Save Event Handler will have to process before it saves.  Testing this process in a production type environment as others are interacting is important to understand the overall impact on the system.  Again, no 1 implementation and environment is alike so parallel testing is important.  

      2.  Limiting Rule Execution By Forms -  In this example, Christian is showing how to execute the logic based on the Forms Origin.  The logic will only execute on the data cells with Forms Origin  

      If args.NewDataCell.DataCellPK.OriginId = DimConstants.Forms

       This allows a level of assurance that this logic will only run when a user is processing a Form using the Forms Origin.  So when the user clicks on Save in a Form, it will execute the logic and evaluate the data cells to determine to save the data or not.  This will happen for any Form in the application that is processed and the user clicks Save whether the Form was intended to use this logic or not.  Therefore, it would be prudent to limit the Forms that you would like the Save Event Handler to execute on.   Dim objBRApiForms As BRApiForms = BRApi.Forms.Metadata.GetForm.Form.Name.Equals("NameOfForm").  This is something to consider when introducing the Save Data Event Handler.  Should it apply to all Forms or is it specific to a Form?  

      3.  More Than 1 Account for Manual Input and Looping on More Than 1 Account -  This certainly can be done.  However, consider what was explained under item 1 above.  There will be a multiplication factor depending on how many accounts for manual input and data cells evaluated.  Another point to consider when building out Forms and how many data cells are available within the Form.   

      4.  Using BRApi's to Cross Engines -  BRApi's are very useful to cross OneStream Processing Engines.  As common practice, it is best to use Api's that are provided within each Engine.  As mentioned before, the Save Data Event Handler does not have Api's but does contain Args object references and BRApi's.  In Christian's example, since there is no Api to use, we are using the BRApi to cross into the Finance Engine to get the Account Id.  This is certainly needed in order to process this logic.  The consideration here is how often is an user saving a Form and executing the logic to cross into the Finance Engine.  Is it 1 person or 100 people simultaneously executing a Save and impacting the General servers?  Something to monitor when testing.  

      Hope these considerations help when using this Business Rule.  And remember TEST, TEST, TEST functionality and user interactions under production type environment to ensure a pleasant user experience.  Christian is providing you with the power.  It is up to you on how to use it.  Consider these 4 items when implementing in a production environment.  

      • ChristianW's avatar
        ChristianW
        Valued Contributor

        Thanks a lot Tony for this clarifications.

    • prachtiwari11's avatar
      prachtiwari11
      New Contributor II

      Hello 
      I have tried this way, but I am facing a challenge here. For all the cubeviews, the BR is getting hit and I am getting the exception that I want only in one report. How can I prevent that? Is there any other way too via which I can generate an exception while clicking on "Save".
      Basically, my requirement is if I enter some value manually and it doesn't match with one of my row's data, I should get an exception popup stating, "Data not matched", else I should get difference as 0 in third row and my data should get saved. 
      Any ideas how we can achieve this???

  • MarkMatson's avatar
    MarkMatson
    New Contributor III

    Note that you can also use this statement to check if it's manual input:

    If Not args.DataUpdateActivity.toString = "ManualInput" Then Return Nothing

    Not sure if this captures more events than OriginId = DimConstants.Forms

    Thanks