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

ChristianW
Valued Contributor

Hi all

I like to check a manual data entry during save.

Is this possible?

 

2 ACCEPTED SOLUTIONS

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

View solution in original post

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.  

View solution in original post

12 REPLIES 12

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

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.  

Thanks a lot Tony for this clarifications.

Tobias
New Contributor III

Hi,

Is there also an option the restrict by cube e.g. just running when saving data in a specific cube? I have a version which checks the data cells and retrieve the cube name from that, but doing so, I have the data volume topic.

As the api.pov is not valid in the event handler, it might not be possible, moreover as you might have multiple cubes in one form. Also an "exit function" is not stopping the loop.

Do you have an idea on that?

Regards,
Tobias

 

Hi Tobias,

 

This piece of code is an example of how to get the Cube Name to be used to restrict by Cube

tdimitrie_1-1641395748198.png

As for the "exit function" not stopping the loop, you may want to speak to someone on the AAS team for further discussion

Tony

 

 

Tobias
New Contributor III

Hi Tony,

Thanks. That is what I did as a workaround, but it keep the looping / performance issue.

Regards,
Tobias

Ok.  It does sound like this could be related to the data cell volumes.  That piece of code is looking at the Cube ID/Name from within the cell and then doing something based on criteria you define.  So if you have a lot of data cells in your Form, and you are cycling through each data cell to evaluate a value AND evaluate the Cube ID/Name before Save, this could be something in the code but could also be due to data cell volumes.  Without knowing the size of the Form, have you tried testing with a smaller amount of data cells in your Form to evaluate performance?  If not, this would be something to try before discussing with AAS.  

Hi Tony,

Would you also use it to run "Calc on save"? Relevant for Planning forms. 

I guess you could trigger BRApi.Utilities.ExecuteDataMgmtSequence

Thanks

Liliya

Hello LiliyaBarabash

I'm not exactly sure what you are asking so if I misinterpret with my response, let me know.  

Since Planning Forms often are developed as Forms as Dashboard, the Button Dashboard Component is used to execute a Calculate for the Form.  The Button Dashboard Component also has a Save Action object that can put controls on the data saving process prior to committing data to the Data Record tables.  I would prefer to use this method when possible vs. using the SaveDataEventHandler.  

Hi Toni,

You interpret my question correctly. I understand that you give the preference to the Dashboard Button. The only disadvantage of it, is that it forces to use Dashboards in cases where is could be just a cube view. Any performance concerns if using the save SaveDataEventHandler? If crafted carefully, one can ensure that the relevant calculation runs on the relevant cube view.

Thanks

Liliya

 

Thanks Tony for the explain the level of details. I have a quick question on getting the form name 

 

Dim objBRApiForms As BRApiForms = BRApi.Forms.Metadata.GetForm.Form.Name.Equals("PocForm")

When I try to compile it it is throwing an error

Argument not specified for parameter 'si' of 'Function GetForm(si As SessionInfo, uniqueID As Guid) As XFFormEx'.

It is looking for the 2 parameters si & guid.

 

Could you please get me the correct form name ? 

 

Meanwhile I took the other approach the below and it is working.

 

						Dim objBRApiForms As XFFormsForWorkflow  = BRApi.Forms.Metadata.GetForms(si,si.WorkflowClusterPk)
						'Get the List of Required Forms
						Dim A As  List (Of XFFormSummaryInfo) = objBRApiForms.RequiredForms
						'Variable for Hold Form Name
						Dim b As String = String.Empty
						'Loop throiugh the WF Required Forms
						For Each item As XFFormSummaryInfo In A
							 b= item.Name

Next

 

Thanks

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