Skip to main content
May 16, 2025
Solved

How do I return a range of Members?

  • May 16, 2025
  • 3 replies
  • 0 views

We have a large flat hierarchy, members starting with Px_1000 to Px_9999. For example, P1_1000_27 Project 1, P5_1001 Project Yellow.... P7_9999 Project Bob. The numbers are unique (no two same number). We want a report that allows users to enter a "From" - "To" to pull back members in that range. For instance, only pull back members in the range "From" 500 To "532". Have an XFBR where I'm parsing the string, that's working, looping through and but only returning the number. The issue is that I need the entire string and description of the member. That's where I'm stuck. If anyone has done this before, would very much like know how you coded the XFBR. Thank you!

Best answer by jayaleck

Thank you! Same concept I share what I came up with,

If args.FunctionName.XFEqualsIgnoreCase("UavailableModNumberCheck") Then
 
Dim FromModNumber As Integer
Dim ToModNumber As Integer
 
'Validation that they entered numbers in, then converting to an integer.
If args.NameValuePairs("From") = String.Empty Or args.NameValuePairs("To") = String.Empty Then
Return "None:Name(From/To cannot be blank)"
Else
FromModNumber = Integer.Parse(args.NameValuePairs("From"))
ToModNumber = Integer.Parse(args.NameValuePairs("To"))
End If
 
'Validation of number entry
If FromModNumber < 1000 Or ToModNumber > 9999 Then
Return "Name(Mod Number entry must be between 1000-9999)"
Else If FromModNumber > ToModNumber Then
Return "Name(*From* Mod Number must be less than *To* Mod Number)"
Else If FromModNumber = ToModNumber Then
Return "Name(*From* Mod Number must not be the same as the *To* Mod Number)"
End If
 
Dim ModList As List(Of MemberInfo) = BRApi.Finance.Metadata.GetMembersUsingFilter(si, "Flow", "F#Flow.Base.Where(Name DoesNotContain Jay)", True)
Dim ModSplit As New List (Of String)
Dim ModNumbersList As New List (Of Integer)
Dim PDONumbersList As New List (Of String)
 
'Loop through modlist, put mod numbers into separate list and mod name in a separate list.
For Each FlowMod In ModList
ModSplit = StringHelper.SplitString(FlowMod.Member.Name, "_")
If ModSplit(2) >= FromModNumber And ModSplit(2) <= ToModNumber Then
'List if integers as mod number
ModNumbersList.Add(Integer.Parse(ModSplit(2)))
'List of mod name
PDONumbersList.Add(FlowMod.Member.Name)
'Brapi.ErrorLog.LogMessage(si, $"FlowMod {FlowMod.Member.Name}")
End If
Next
ModNumbersList.Sort()
 
Dim i As Integer = FromModNumber
Dim iToString As String = String.Empty
Dim ReturnString As String = "F#None:Name(Unavailable Mod numbers between " & FromModNumber & "-" & ToModNumber & ")"
 
While i <= ToModNumber
 
If ModNumbersList.Contains(i) Then
 
iToString = i.ToString
 
If i = iToString
For Each PDOName In PDONumbersList
If PDOName.Contains(iToString)
ReturnString = ReturnString & ", F#None:Name(" & PDOName & ")"
End If
Next
End If
Else
'Mod Number is open
End If
 
i = i + 1
End While
 
Return ReturnString

3 replies

May 19, 2025

Hi jayaleck​

You should be able to perform some kind of string manipulation using linq on a member list

However based on your description it is difficult to understand how consistent your naming convention is e g are you only trying to retrieve a range from the numbers after the first underscore and before second underscore (if it exists)? You have provided examples of both

Similarly in your 500 to 1000 example, do the members have the same number of characters in the section you want to perform a range on.  E.g 500 and 1000 or 0500 and 1000?

There are a lot of variables here that are unknown based on your description. If you can flesh out your use case here a bit I suspect you will get a better response 

Hope this helps

Sam

 

jayaleckAuthor
May 19, 2025

Good questions! It will always be 2 alpha/numeric characters, it could be P1 or PA, then underscore, then a 4-digit integer, starting with 1000, ending 9999. There will never be a leading 0. My apologies, there was a typo, it should have 1500 - 1532. Thank you!!

May 20, 2025
				 If	args.FunctionName.XFEqualsIgnoreCase("Get_Range") Then
					' XFBR(YourXFBRRule,Get_Range, min = 4000, max = 4999,ReturnDesc = True, dimensionName = YourDimName, TopMemberName = YourValidParent)
					Dim dimensionName As String = args.NameValuePairs("dimensionName")
					Dim TopMemberName As String = args.NameValuePairs("TopMemberName")
					Dim ReturnDescStr As String = args.NameValuePairs("ReturnDesc")
					Dim ReturnDesc As Boolean = False
					
					If Not String.IsNullOrEmpty(ReturnDescStr) Then
					    Boolean.TryParse(ReturnDescStr.Trim(), ReturnDesc)
					End If
					Dim minCode As Integer = args.NameValuePairs("min").XFConvertToInt
					Dim maxCode As Integer = args.NameValuePairs("max").XFConvertToInt			
					Return Me.GetMemberRange(si,minCode,maxCode,ReturnDesc,dimensionName,TopMemberName)					
				End If

				Return Nothing
			Catch ex As Exception
				Throw ErrorHandler.LogWrite(si, New XFException(si, ex))
			End Try
		End Function
		
Public Function GetMemberRange(si As SessionInfo, minCode As Integer, maxCode As Integer, ReturnDesc As Boolean, ByVal dimensionName As String, ByVal TopMemberName As String) As String
    Dim results As New List(Of Tuple(Of Integer, String))
    Dim dimToken As String = GetDimensionToken(si, dimensionName)
    Dim memberFilter As String = dimToken & TopMemberName & ".base"

    ' Step 1: Get all base members
    Dim allMembers As List(Of MemberInfo) = BRApi.Finance.Metadata.GetMembersUsingFilter(si, dimensionName, memberFilter, True)

		For Each member As MemberInfo In allMembers
		    Dim name As String = member.Member.Name
		    Dim desc As String = member.Member.Description
		
		    ' Try to extract trailing digits only
		    Dim digitChars As Char() = name.Reverse().TakeWhile(AddressOf Char.IsDigit).Reverse().ToArray()
		    If digitChars.Length > 0 Then
		        Dim digitsOnly As String = New String(digitChars)
		
		        Dim code As Integer
		        If Integer.TryParse(digitsOnly, code) Then
		            If code >= minCode AndAlso code <= maxCode Then
		                Dim formatted As String = dimToken & name
		                If ReturnDesc Then
		                    formatted &= $":Name({desc})"
		                End If
		                results.Add(Tuple.Create(code, formatted))
		            End If
		        End If
		    Else
		        ' Optional: log members skipped due to no trailing digits
		        ' BRApi.ErrorLog.LogMessage(si, $"Skipped member: {name} (no trailing digits)")
		    End If
		Next

    ' Step 3: Sort and return
    Dim sortedMembers As List(Of String) = results.OrderBy(Function(x) x.Item1).Select(Function(x) x.Item2).ToList()
    Return String.Join(", ", sortedMembers)
End Function
	
Public Function GetDimensionToken(si As SessionInfo, dimensionName As String) As String
    ' Retrieve the DimPk for the specified dimension
    Dim dimPk As DimPk = BRApi.Finance.Dim.GetDimPk(si, dimensionName)

    ' Get the DimTypeId
    Dim dimTypeId As Integer = dimPk.DimTypeId

    ' Map DimTypeId to the corresponding dimension token
    Select Case dimTypeId
        Case 0 : Return "E#"
        Case 2 : Return "S#"
        Case 5 : Return "A#"
        Case 6 : Return "F#"
        Case 9 : Return "U1#"
        Case 10 : Return "U2#"
        Case 11 : Return "U3#"
        Case 12 : Return "U4#"
        Case 13 : Return "U5#"
        Case 14 : Return "U6#"
        Case 15 : Return "U7#"
        Case 16 : Return "U8#"
        Case Else : Return "" ' Unknown or unsupported dimension type
    End Select
End Function	

Something like this should get you to what you need. This XFBR needs the min and max of the range, the dimension name, a Return Description Boolean, and a parent to get a list of valid members. The code is extracting the trailing integers out of the member name, checking if its in the range, and adding it to a list, which is used to create the return string. You may need to replace the .base with a different expansion depending on what members you want to populate.

jayaleckAuthorAnswer
May 20, 2025

Thank you! Same concept I share what I came up with,

If args.FunctionName.XFEqualsIgnoreCase("UavailableModNumberCheck") Then
 
Dim FromModNumber As Integer
Dim ToModNumber As Integer
 
'Validation that they entered numbers in, then converting to an integer.
If args.NameValuePairs("From") = String.Empty Or args.NameValuePairs("To") = String.Empty Then
Return "None:Name(From/To cannot be blank)"
Else
FromModNumber = Integer.Parse(args.NameValuePairs("From"))
ToModNumber = Integer.Parse(args.NameValuePairs("To"))
End If
 
'Validation of number entry
If FromModNumber < 1000 Or ToModNumber > 9999 Then
Return "Name(Mod Number entry must be between 1000-9999)"
Else If FromModNumber > ToModNumber Then
Return "Name(*From* Mod Number must be less than *To* Mod Number)"
Else If FromModNumber = ToModNumber Then
Return "Name(*From* Mod Number must not be the same as the *To* Mod Number)"
End If
 
Dim ModList As List(Of MemberInfo) = BRApi.Finance.Metadata.GetMembersUsingFilter(si, "Flow", "F#Flow.Base.Where(Name DoesNotContain Jay)", True)
Dim ModSplit As New List (Of String)
Dim ModNumbersList As New List (Of Integer)
Dim PDONumbersList As New List (Of String)
 
'Loop through modlist, put mod numbers into separate list and mod name in a separate list.
For Each FlowMod In ModList
ModSplit = StringHelper.SplitString(FlowMod.Member.Name, "_")
If ModSplit(2) >= FromModNumber And ModSplit(2) <= ToModNumber Then
'List if integers as mod number
ModNumbersList.Add(Integer.Parse(ModSplit(2)))
'List of mod name
PDONumbersList.Add(FlowMod.Member.Name)
'Brapi.ErrorLog.LogMessage(si, $"FlowMod {FlowMod.Member.Name}")
End If
Next
ModNumbersList.Sort()
 
Dim i As Integer = FromModNumber
Dim iToString As String = String.Empty
Dim ReturnString As String = "F#None:Name(Unavailable Mod numbers between " & FromModNumber & "-" & ToModNumber & ")"
 
While i <= ToModNumber
 
If ModNumbersList.Contains(i) Then
 
iToString = i.ToString
 
If i = iToString
For Each PDOName In PDONumbersList
If PDOName.Contains(iToString)
ReturnString = ReturnString & ", F#None:Name(" & PDOName & ")"
End If
Next
End If
Else
'Mod Number is open
End If
 
i = i + 1
End While
 
Return ReturnString