Forum Discussion

jayaleck's avatar
jayaleck
New Contributor II
6 days ago
Solved

How do I return a range of Members?

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!

  • 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

4 Replies

  • jayaleck's avatar
    jayaleck
    New Contributor II

    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
  • BenEppel's avatar
    BenEppel
    New Contributor III
    				 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.

  • sameburn's avatar
    sameburn
    Contributor III

    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

     

    • jayaleck's avatar
      jayaleck
      New Contributor II

      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!!