domenica 10 maggio 2020

AX 2012 - Esplosione albero BOM in x++

In questo post vediamo come esplodere la BOM di un articolo. Ho preso spunto da questi due post e li ho rielaborati:


Il primo è interessante perchè mostra come usare la classe BOMSearch, però non è utilizzabile in quanto in AX 2012 l'enumerato InventTable.ItemType == ItemType::BOM è deprecato, il secondo è a mio avviso incompleto perchè espande la BOM più del necessario. Il fatto che un articolo abbia una BOM valida è condizione necessaria ma NON sufficiente per poterlo esplodere,  occorre che siano vere queste condizioni:

1) InventTable.PmfProductType  = BOM



2) In default order settings e anche in copertura articoli, abbiamo il default order type che ci dice se è Production o Purchase. Il campo è InventItemSetupSupplyType.DefaultOrderType


Il metodo BOMDesignerCtrl::itemCanHaveBOM(), usato nel "BOM designer" standard effettua questi due controlli.

Ecco la classe che effettua l'esplosione:

 class LIL_BomExplosionBOMSearch  
 {  
   BOMVersion   bomVersion;  
 }  

 public static LIL_BomExplosionBOMSearch construct(BOMVersion _bomVersion)  
 {  
   LIL_BomExplosionBOMSearch instance = new LIL_BomExplosionBOMSearch();  
   instance.parmBomVersion(_bomVersion);  
   return instance;  
 }  

 public void run()  
 {  
   BOMSearch search = BOMSearch::newBOMTree(this.parmBomVersion().BOMId,  
                        today(),  
                        this.parmBomVersion().ItemId,  
                        this.parmBomVersion().inventDim().configId);  
   this.BomExplode(search,0);  
 }  

 private boolean hasValidBomVersion(ItemId _itemId)  
 {  
   BOMVersion bomVersionLocal;  
   ;  
   select firstonly RecId from bomVersionLocal  
       where bomVersionLocal.ItemId == _itemid  
       && bomVersionLocal.Active  
       && bomVersionLocal.Approved  
       && bomVersionLocal.FromDate <= systemdateget ()  
       && (!bomVersionLocal.ToDate || bomVersionLocal.ToDate >= systemdateget ());  
   if (bomVersionLocal.RecId)  
   {  
     return true;  
   }  
   return false;  
 }  

 void BomExplode(BOMSearch _search, BOMLevel _level = 0)  
 {  
   BOM     bom;  
   BOMSearch  BomSearchLocal;  
   InventTable inventTable;  
   BOMId    bomId;  
   ;  
   //max tree deep level reached, trow error  
   if(_level > BOMParameters::find().BOMMaxLevel)  
   {  
     throw error("@SYS26729");  
   }  
   //root Item  
   if (_level == 0)  
   {  
     info(strFmt("%1 - %2",_level,this.parmBomVersion().ItemId));  
   }  
   while (_search.next())  
   {  
     bom = _search.BOM();  
     inventTable = bom.inventTable();  
           info(strFmt("%1 - %2",_level,bom.ItemId));  
     //verify if item can be exploded  
     if (BOMDesignerCtrl::itemCanHaveBOM(bom.inventTable()) &&  
       this.hasValidBomVersion(bom.ItemId))   
     {  
       //find active bom  
       bomId = inventTable.bomId(today(),bom.BOMQty,bom.inventDim());  
       BomSearchLocal = BOMSearch::newBOMTree(bomId,  
                     today(),  
                     bom.ItemId,  
                     bom.inventDim().configId);  
       this.BomExplode(BomSearchLocal, _level+1);  
     }  
   }  
 }  

La chiamata alla classe:

 LIL_BomExplosionBOMSearch     LIL_BomExplosionBOMSearch = LIL_BomExplosionBOMSearch::construct(bomVersion);  
 LIL_BomExplosionBOMSearch.run();  

Come nota tecnico / teorico, secondo la teoria degli alberi, questa ricerca sull'albero BOM è una ricerca di tipo DFS (Depth-first search) perchè non appena trova un nodo a livello (n) passa subito al suo primo figlio a livello (n+1):