Forum Discussion

DavidW's avatar
DavidW
New Contributor II
2 years ago

Calling finance rule from XFBR rule

This post is to illustrate how to call a public function of a Finance Rule from within XFBR rule.

The Finance Rule has a function called EntityText8 splitter, and I need to call it from within a XFBR rule. To make it work, first need to make the Finance Rule "True" - Contains Global Functions for Formulas (in Rule Properties) and refer to the specific rule assembly in XFBR rule properties (screenshot2).

The XFBR rule reads:

'--------------------------------------------------------------------------------------------------------------------------------
'Returns the Entity in Text8 or emtpy
'--------------------------------------------------------------------------------------------------------------------------------
'BRString(SeedingXFBRStrings, Text8Entity, curEntity=|CVEntity|, curScenario=|CVScenario|)
'XFBR(SeedingXFBRStrings, Text8Entity, curEntity=|CVEntity|, curScenario=|CVScenario|)
 
If args.FunctionName.XFEqualsIgnoreCase("Text8Entity") Then
 
Dim curEntity As String = args.NameValuePairs.XFGetValue("curEntity")
 
Dim curScenario As String = args.NameValuePairs.XFGetValue("curScenario")
Dim scenarioId As Integer = BRApi.Finance.Members.GetMember(si, DimType.Scenario.Id, curScenario).MemberId
Dim curScenarioType As String = BRApi.Finance.Scenario.GetScenarioType(si, scenarioId).Id
 
Dim entityId As Integer = brapi.Finance.Members.GetMemberId(si, DimType.Entity.Id, curEntity)
Dim entityText8 As String = BRApi.Finance.Entity.Text(si, entityId, 8, curScenarioType, si.WorkflowClusterPk.TimeKey)
 
If entityText8.Length > 0 Then
'Define reference to business rule
Dim text8Splitter As New OneStream.BusinessRule.Finance.FcstSeeding.MainClass
 
If Not text8Splitter Is Nothing Then
Dim srcEntity As String
With text8Splitter.entityText8Splitter(entityText8)
srcEntity = .srcEntity
End With
Return srcEntity
Else
Return entityText8
End If
 
Else
Return ""
End If
 
End If
  • RobbSalzmann's avatar
    RobbSalzmann
    Valued Contributor II

    Code reuse rocks 🙂 .  It looks like the requirements for EntityText8 may be evolving over time.  Consider encapsulating the overall text processing functionality in its own class and create methods in the class to deal with the different parsing/mangling/processing needs, currently being dealt with using multiple IF statements. 

    Place the class in a BR of its own with other text processors:

    Imports System
    Imports System.Collections.Generic
    Imports System.Data
    Imports System.Data.Common
    Imports System.Globalization
    Imports System.IO
    Imports System.Linq
    Imports System.Windows.Forms
    Imports Microsoft.VisualBasic
    Imports OneStream.Finance.Database
    Imports OneStream.Finance.Engine
    Imports OneStream.Shared.Common
    Imports OneStream.Shared.Database
    Imports OneStream.Shared.Engine
    Imports OneStream.Shared.Wcf
    Imports OneStream.Stage.Database
    Imports OneStream.Stage.Engine
    
    Namespace OneStream.BusinessRule.DashboardStringFunction.TextProcessors
        Public Class SomeOtherTextProcessor
            '... other string parsing/mangling/processing used in multiple places
        End Class
    
        Public Class YetAnotherTextProcessor
            '... other string parsing/mangling/processing used in multiple places
        End Class
    
    	Public Class EntityText8Processor
    	    Private _srcEntity As String
    	    Private _fcstSrcEntity As String
    	    Private _operSign As String
    	    Private _srcU6 As String
    
    	    Public ReadOnly Property SrcEntity As String
    	        Get
    	            Return _srcEntity
    	        End Get
    	    End Property
    
    	    Public ReadOnly Property FcstSrcEntity As String
    	        Get
    	            Return _fcstSrcEntity
    	        End Get
    	    End Property
    
    	    Public ReadOnly Property OperSign As String
    	        Get
    	            Return _operSign
    	        End Get
    	    End Property
    
    	    Public ReadOnly Property SrcU6 As String
    	        Get
    	            Return _srcU6
    	        End Get
    	    End Property
    
    	    Public Sub New(entityText8 As String)
    	        _srcEntity = entityText8
    	        _fcstSrcEntity = String.Empty
    	        _operSign = String.Empty
    	        _srcU6 = String.Empty
    	    End Sub
    
    	    Public Sub ProcessForecastSource()
    	        Dim startIdx As Integer = InStr(_srcEntity, "FcstSource") + 10
    	        _fcstSrcEntity = Mid(_srcEntity, startIdx, 101)
    
    	        Dim hasComma As Boolean = InStr(_fcstSrcEntity, ",") > 0
    	        If hasComma Then
    	            _operSign = Mid(_fcstSrcEntity, InStr(_fcstSrcEntity, ",") + 1, 1)
    	            _fcstSrcEntity = Left(_fcstSrcEntity, InStr(_fcstSrcEntity, ",") - 1)
    	        End If
    
    	        _srcEntity = Left(_srcEntity, startIdx - 11)
    	    End Sub
    
    	    Public Sub ProcessU6()
    	        Dim u6Index As Integer = InStr(_srcEntity, "U6#")
    	        _srcU6 = Mid(_srcEntity, u6Index, 101)
    	        _srcEntity = Left(_srcEntity, u6Index - 2)
    
    	        Dim hasComma As Boolean = InStr(_srcU6, ",") > 0
    	        If hasComma Then
    	            _operSign = Mid(_srcU6, InStr(_srcU6, ",") + 1, 1)
    	            _srcU6 = Left(_srcU6, InStr(_srcU6, ",") - 1)
    	        End If
    	    End Sub
    	End Class
    End Namespace
    

    With this class, you can add new "processors" for each new requirement. by creating another "Process<thisToProcess>" sub. 

    Consume the class:

    'Returns the Entity in Text8 or emtpy
    '--------------------------------------------------------------------------------------------------------------------------------
    'BRString(SeedingXFBRStrings, Text8Entity, curEntity=|CVEntity|, curScenario=|CVScenario|)
    'XFBR(SeedingXFBRStrings, Text8Entity, curEntity=|CVEntity|, curScenario=|CVScenario|)
     
    If args.FunctionName.XFEqualsIgnoreCase("Text8Entity") Then 
    	Dim curEntity As String = args.NameValuePairs.XFGetValue("curEntity")	 
    	Dim curScenario As String = args.NameValuePairs.XFGetValue("curScenario")
    	Dim scenarioId As Integer = BRApi.Finance.Members.GetMember(si, DimType.Scenario.Id, curScenario).MemberId
    	Dim curScenarioType As String = CStr(BRApi.Finance.Scenario.GetScenarioType(si, scenarioId).Id)	 
    	Dim entityId As Integer = brapi.Finance.Members.GetMemberId(si, DimType.Entity.Id, curEntity)
    	Dim entityText8 As String = BRApi.Finance.Entity.Text(si, entityId, 8, curScenarioType, si.WorkflowClusterPk.TimeKey)
    	
    	If entityText8.Length > 0 Then
    		Dim text8Splitter As New OneStream.BusinessRule.DashboardStringFunction.TextProcessors.EntityText8Processor(entityText8)
    		
    		If InStr(text8Splitter.SrcEntity, "FcstSource") > 0 Then
    			text8Splitter.ProcessForecastSource()
    		End If
    
    		If InStr(text8Splitter.SrcEntity, "U6#") > 0 Then
    		    text8Splitter.ProcessU6()
    		End If
    		
    		entityText8 = text8Splitter.SrcEntity 
    	End If
    	Return entityText8
    End If

     

  • RobbSalzmann's avatar
    RobbSalzmann
    Valued Contributor II

    Hi DavidW,
    What does the function entityText8Splitter do?  IOW, what changes when you pass in srcEntity?

    For better readability, post your code in a preformatted element:

    '--------------------------------------------------------------------------------------------------------------------------------
    'Returns the Entity in Text8 or emtpy
    '--------------------------------------------------------------------------------------------------------------------------------
    'BRString(SeedingXFBRStrings, Text8Entity, curEntity=|CVEntity|, curScenario=|CVScenario|)
    'XFBR(SeedingXFBRStrings, Text8Entity, curEntity=|CVEntity|, curScenario=|CVScenario|)
    
    If args.FunctionName.XFEqualsIgnoreCase("Text8Entity") Then
    	
    	Dim curEntity As String = args.NameValuePairs.XFGetValue("curEntity")
    	
    	Dim curScenario As String = args.NameValuePairs.XFGetValue("curScenario")
    	Dim scenarioId As Integer = BRApi.Finance.Members.GetMember(si, DimType.Scenario.Id, curScenario).MemberId
    	Dim curScenarioType As String = BRApi.Finance.Scenario.GetScenarioType(si, scenarioId).Id
    	
    	Dim entityId As Integer = brapi.Finance.Members.GetMemberId(si, DimType.Entity.Id, curEntity)
    	Dim entityText8 As String = BRApi.Finance.Entity.Text(si, entityId, 8, curScenarioType, si.WorkflowClusterPk.TimeKey)
    	
    	If entityText8.Length > 0 Then
    'Define reference to business rule
    		Dim text8Splitter As New OneStream.BusinessRule.Finance.FcstSeeding.MainClass
    		
    		If Not text8Splitter Is Nothing Then
    			Dim srcEntity As String
    			With text8Splitter.entityText8Splitter(entityText8)
    				srcEntity = .srcEntity
    			End With
    			Return srcEntity
    		Else
    			Return entityText8
    		End If
    		
    	Else
    		Return ""
    	End If

     

     

  • DavidW's avatar
    DavidW
    New Contributor II

    Rob, Thanks for pointing out how to use the formatted style.

    EntityText8splitter is a "string processor" function, returns different values (srcEntity, fcstSrcEntity, etc, ...). Instead of using more and more Entity Text fields for the same purpose (Forecast seeding in this case), Splitters are created to process and return drivers of "Seeding" exercise in different use cases. This is our response to the facts that logics are getting more complicated as business requirements change, and we have limited Text field for each member.

     

    		Public Function entityText8Splitter(entityText8 As String) As (srcEntity As String, fcstSrcEntity As String, operSign As String, srcU6 As String)
    		
    			Dim srcEntity, fcstSrcEntity, operSign, srcU6 As String			
    			
    			srcEntity = entityText8
    			fcstSrcEntity = ""
    			operSign = ""
    			srcU6 = ""
    			
    			If Instr(entityText8,"FcstSource") > 0 Then	'Test FcstSource existance
    			
    				fcstSrcEntity = Mid(entityText8, Instr(entityText8,"FcstSource")+10, 101)			
    								
    				If Instr(fcstSrcEntity,",") > 0 Then
    					operSign = Mid(fcstSrcEntity, Instr(fcstSrcEntity,",")+1, 1)
    					srcEntity = Left(srcEntity, (srcEntity.Length - fcstSrcEntity.Length -10 -1)) 'FcstSource and ","
    					fcstSrcEntity = Left(fcstSrcEntity,Instr(fcstSrcEntity,",")-1)
    				Else					
    					srcEntity = Left(srcEntity, (srcEntity.Length - fcstSrcEntity.Length -10 -1))
    				End If
    			End If
    				
    			If	srcEntity.XFContainsIgnoreCase("U6#") Then 'Test U6# existance
    				srcU6 =  Mid(srcEntity, Instr(srcEntity, "U6#"),101)
    				srcEntity = Left(srcEntity, Instr(srcEntity, "U6#")-2)
    				If	Instr(srcU6, ",") > 0	Then 'Test , existance
    					operSign = Mid(srcU6, Instr(srcU6, ",")+1, 1)
    					srcU6 = Left(srcU6, Instr(srcU6, ",")-1)
    				End If
    			End If		
    		
    			Return (srcEntity, fcstSrcEntity, operSign, srcU6)
    			
    		End Function

     

  • DavidW's avatar
    DavidW
    New Contributor II

    Yes, that's a very logical and concise solution. Thanks, Rob.