Functions/Get-AppDependencyOrder.ps1

function Get-AppDependencyOrder {
    [CmdletBinding(DefaultParameterSetName='FilePath')]
    param(            
        [Parameter(Mandatory=$true, ParameterSetName = "FilePath")]
        [string[]] $Path,
        [Parameter(Mandatory=$true, ParameterSetName = "AppObject")]
        [object[]] $Apps,
        [Parameter(Mandatory=$true, ParameterSetName = "AppObject")]
        [string] $ServerInstance
    )

    if($PSCmdlet.ParameterSetName -eq 'FilePath'){
        
        $AllAppFiles = @()
        $Path | ForEach-Object {
            $AllAppFiles += Get-ChildItem -Path $_ -Recurse -Filter "*.app"
        }

        $AllApps = @()
        foreach ($AppFile in $AllAppFiles) {
            $App = Get-NAVAppInfo -Path $AppFile.FullName
            $AllApps += [PSCustomObject]@{
                AppId        = $App.AppId
                Version      = $App.Version
                Name         = $App.Name
                Publisher    = $App.Publisher
                ProcessOrder = 0                            
                Dependencies = $App.Dependencies
                Path         = $AppFile.FullName
                Length       = $AppFile.Length}
        }
    }

   if($PSCmdlet.ParameterSetName -eq 'AppObject'){
        $AllApps = @()
        foreach ($App in $Apps) {
            $App = Get-NAVAppInfo -ServerInstance $ServerInstance -Id $App.AppId.Value.Guid -Version $App.Version
            $AllApps += [PSCustomObject]@{
                    AppId        = $App.AppId
                    Version      = $App.Version
                    Name         = $App.Name
                    Publisher    = $App.Publisher
                    ProcessOrder = 0                            
                    Dependencies = $App.Dependencies
                    Path         = 'unknown'
                    Length       = 'unknown'}
        }
   }
 
    # Check on AppId Uniqueness
    $Result = Get-BCAppidUnique ($AllApps)
    if ($Result) {
        Write-Error ($Result | Out-String)
    }

    # Apps are devided in two layers. The layers can be seperated by the Application proxy instead of direct dependencies.
    # To resolve this the dependency tree for each layer is calculated separatly and joined afterwards.
    # LayerOne: Microsoft System Application, Base application, Application (proxy app)
    # LayerTwo: Extensions on the base application and extensions on extensions.

    $Filter = @('Application')
    $Filter += ($AllApps | Where-Object -Property Name -eq 'Application').Dependencies.Name

    $LayerOne = @()
    $LayerOneApps = $AllApps | Where-Object -Property Name -In $Filter 
    $LayerOneApps | ForEach-Object {
        [array] $LayerOne = Add-BCAppToDependencyTree `
                            -App $_ `
                            -DependencyArray $LayerOne `
                            -AppCollection $LayerOneApps `
                            -Order $LayerOneApps.Count
    }

    $LayerTwo = @()
    $LayerTwoApps = $AllApps | Where-Object -Property Name -NotIn $Filter
    $LayerTwoApps | ForEach-Object { 
        [array] $LayerTwo = Add-BCAppToDependencyTree `
                            -App $_ `
                            -DependencyArray $LayerTwo `
                            -AppCollection $LayerTwoApps `
                            -Order ($LayerTwoApps.Count + $LayerOneApps.Count)
    }

    $FinalResult = $LayerOne + $LayerTwo

    return $FinalResult | Sort-Object ProcessOrder
}

Export-ModuleMember -Function Get-AppDependencyOrder

function Add-BCAppToDependencyTree() {
    param(
        [PSObject] $App,
        [PSObject[]] $DependencyArray,
        [PSObject[]] $AppCollection,
        [Int] $Order = 1
    )   

    foreach ($Dependency in $App.Dependencies) {
        $DependencyArray = Add-BCAppToDependencyTree `
                                -App ($AppCollection | Where-Object AppId -eq $Dependency.AppId) `
                                -DependencyArray $DependencyArray `
                                -AppCollection $AppCollection `
                                -Order ($Order - 1)
    }

    if (-not($DependencyArray | Where-Object AppId -eq $App.AppId)) {
        if($App.AppId){
            $DependencyArray += $App
            Write-Verbose ('App: {0} - Order: {1}' -f $App.Name, $Order)
       
            $Result = ($DependencyArray | Where-Object AppId -eq $App.AppId)
            if ($Result) {
                $Result.ProcessOrder = $Order
            }
        } 
    }
    else {
        if (($DependencyArray | Where-Object AppId -eq $App.AppId).ProcessOrder -gt $Order) {
            ($DependencyArray | Where-Object AppId -eq $App.AppId).ProcessOrder = $Order
        } 
    }

    $DependencyArray
}


function Get-BCAppidUnique() {
    param(
        [Parameter(Mandatory=$true)]
        [PSObject] $Apps
    )
    
   # $Appslist is sorted on AppId
    $AppsList = $Apps | Sort-Object Appid

    $Result = @()
    for ($i=1; $i -lt $AppsList.Count; $i++) {
        if ($AppsList[$i -1].AppId.Value.Guid -eq $AppsList[$i].AppId.Value.Guid) {
            # Found Duplicate Appid
            $Result += 'Found Duplicate Appid:'
            $Result += '{0}[{1}]' -f $AppsList[$i-1].Name, $AppsList[$i-1].AppId
            $Result += '{0}[{1}]' -f $AppsList[$i].Name, $AppsList[$i].AppId
        }
    }

    return $Result
}