When transferring BOMs to ERP using the BOM Window it could be necessary that they get processed from the deepest to the highest level. Otherwise problems could arise on some ERP systems.

In the Check-Boms and Transfer-Boms functions all the BOMs and their rows are accessible, therefore the order in which the BOMs are getting processed can be manipulated in the functions.


Note: When the same BOM is included multiple times the BOM Window will process the BOM only once!


With the following code we demonstrate how to process the BOM structure from the lowest to the highest level. 

In the attachments you can find a example script using these functions, keep in mind that it might be necessary to unblock the file to start it on your machine.


Sorting functions

The following sort function first determines the root BOM and then walks through the BOM tree using the Children property. At every recursion step it remembers the level of the BOM in the $bomLevelMappings.

After that the mapping gets sorted starting with the deepest level of the BOM tree to the highest. 

Using the unique property "_Name" the according BOMs can be determined and returned.

function Sort-BomsFromBottomUp($boms){
    $global:bomLevelMappings = @{}
    $allBomRows = $boms | ForEach-Object{ $_.Children } | Select-Object -ExpandProperty _Name
    $rootBom = $boms | Where-Object {-not $allBomRows.Contains($_._Name) }

    $bomLevelMappings[$rootBom._Name] = 0
    Build-BomLevelMapping 1 @($rootBom.Children | Select-Object -ExpandProperty _Name) $boms

    $sortedBoms = @()
    $bomLevelMappingsDescending = $bomLevelMappings.GetEnumerator()| Sort-Object -Property Value -Descending
    foreach($bomLevelMap in $bomLevelMappingsDescending){
        $bom = $boms | Where-Object {$_._Name -eq $bomLevelMap.Name}
        $sortedBoms += $bom
    }
    return $sortedBoms
}

function Build-BomLevelMapping($level, $rows, $allBoms){
    if($rows.Count -eq 0){
        return
    }
    $accordingRowHeaders = $allBoms |  Where-Object {$rows.Contains($_._Name)}
    foreach($bom in $accordingRowHeaders){
        if(-not $bomLevelMappings.ContainsKey($bom._Name) -or $bomLevelMappings[$bom._Name] -lt $level){
            $bomLevelMappings[$bom._Name] = $level
        }
        Build-BomLevelMapping ($level+1) @($bom.Children | Select-Object -ExpandProperty _Name) $allBoms
    }
}


Using the functions

Before looping over the passed BOMs in the Check-Boms or Transfer-Boms functions, we can call the previous Sort-BomsFromBottomUp function in order to have them sorted in the desired order.

function Check-Boms($boms){
    $sortedBoms = Sort-BomsFromBottomUp $boms
    foreach($bom in $sortedBoms) {
       #process BOM
    }  
}


Result

When running the sample script (attached to this article) a BOM Window will load a simple BOM with multiple levels.

After pressing the Check button we can see that the process starts with the BOM on the deepest level (the "Catch Assembly.iam") and works its way up.


The "Pad Lock.iam" will be the last BOM that is checked as it is on the highest level.

The same behavior applies when clicking the Transfer button: first the BOM at the deepest level gets updated to the status 'Identical' and after that the parent BOMs are processed.


See Also

Check-Boms (coolOrange wiki)

Get-Bomrows (coolOrange wiki)