venerdì 8 febbraio 2019

AX 2012 - Cross reference update batch job

In AX i riferimenti incrociati (cross reference) non possono essere messi in batch. Questo job crea in automatico la ricorrenza nel batch job per schedulare giornalmente l'aggiornamento

 static void LIL_UpdateCrossRefBatch(Args _args)  
 {  
   xRefUpdate::truncateXrefTables();  
   
   xRefUpdateIL::updateAllXref(true, false, true);  
   
   info("Done, cross reference update batch job created.");  
 }  

martedì 5 febbraio 2019

D365FO - Gestione articoli in conto deposito

Gli items di proprietà del fornitore sono gestiti con una dimensione inventariale specifica “owner”.


Ogni valore della dimensione inventariale viene associata ad uno specifico fornitore.

La merce viene ricevuta in un magazzino D365 impiegando una tipologia di transazione chiamata “consignment replenishment order”. 

In fase di ricezione merce viene automaticamente assegnata la dimensione owner referenziata al codice fornitore, tale merce non ha valore di magazzino finchè non avviene il cambio di proprietà della merce stessa. 
Prima di impiegare la merce in produzione / vendita occorre cambiare la dimensione owner da valore fornitore a valore interno usando la transazione “inventory ownership change journal”. 

Al cambio di ownership della merce un ordine di acquisto viene generato automaticamente con riferimento a vendor/items coinvolti e nasce in stato “Ricevuto”, tale transazione sarà usata per la registrazione della fattura di acquisto. 
É possibile effettuare il conteggio della merce di proprietà del fornitore anche usando un comune giornale di counting.

martedì 2 ottobre 2018

AX 2012 - Codifica/Decodifica base64 di un file

Con questo job possiamo codificare / decodificare un file in Base64:

 static void LIL_EncodeDecode(Args _args)  
 {  
   #File  
   Set         permissionSet;  
   InteropPermission  interopPerm;  
   FileIOPermission  fileIOPerm;  
   boolean       fileExists;  
   str         path,  
             base64Encode,  
             base64ext;  
   bindata       bindata;  
   int         pos;  
   container      data;  
     
   permissionSet = new Set(Types::Class);  
   path = @"C:\Images\test.png";  
   fileIOPerm = new FileIOPermission(path,#io_read);  
   permissionSet.add(fileIOPerm);  
   
   interopPerm = new InteropPermission(InteropKind::ClrInterop);  
   permissionSet.add(interopPerm);  
   
   CodeAccessPermission::assertMultiple(permissionSet);  
   
   fileExists = System.IO.File::Exists(path);  
   
   if(fileExists)  
   {  
     bindata = new bindata();  
       
     bindata.loadFile(path);   
   
     base64Encode = bindata.base64Encode();  
   }  
     
     
   info(base64Encode);//stringa codificata  
     
   //decodifica...  
   if(base64Encode)  
   {  
     pos = strFind(base64Encode, ';', 1, 60);  
   
     if(pos)  
     {  
       base64ext = subStr(base64Encode, 1, pos-1);  
     }  
   
     pos = strFind(base64Encode, ',', 1, 60);  
   
     base64Encode = strDel(base64Encode, 1, pos);  
   
     bindata = new bindata();  
   
     data = BinData::loadFromBase64(base64Encode);  
   
     bindata.setData(data);  
   
     bindata.saveFile(@"C:\Images\test2.png");  
   }  
     
   info("Terminato!");  
 }  


martedì 7 agosto 2018

D-365 for Finance and Operations - Come modificare il valore di una variabile di tipo Date durante il debug

Utilizzando il debugger di VS-2015 durante una sessione di analisi sul flusso di un programma, per capire dove intervenire con le modifiche, mi sono imbattuto nella necessità di dove variare il valore di una variabile definita come TransDate.


Sebbene risulti particolarmente semplice variare il valore di campi stringa, numerici e booleani, mi sono accorto subito che non risulta altrettanto semplice e lineare eseguire la variazione nel caso di date.

Se proviamo a variare il valore direttamente dalla finestra di watch, otteniamo un errore di errata immissione.


Dalla mia esperienza con il debugger su applicazioni in C#, ho provato ad eseguire l'assegnazione passando per la finestra immediate, assegnando alla variabile in questione un nuovo valore tramite il costruttore dell'oggetto System.Date di .Net Framework, visto che il codice X++ su D365 alla fine gestisce oggetti di framework.

Ma anche in questo caso si ottiene un errore di errata assegnazione, però in questo il messaggio riporta che è errato il cast.


Alla fine ne consegue che è necessario eseguire il cast dell'oggetto System.Date in "Microsoft.Dynamics.Ax.Xpp.AxShared.Date" per ottenere il risultato desiderato.


 oldTransDate = (Microsoft.Dynamics.Ax.Xpp.AxShared.Date) new System.DateTime(2018,03,08)

Forse non è l'unico metodo che sia contemplato, ma sicuramente funziona.

Per gli enumerati invece:

 (Dynamics.AX.Application.InventTransType)3  

giovedì 26 luglio 2018

D365 - Inventory movement using a template

In D365 we can generate an inventory movement applying a work template and a location directive that can suggest the final location for the movement.

In order to do it we have to define a specific menuitem in the terminal warehouse as follows.


We should define the following parameters:
  • work creation process defined as "Movement by template"
  • in the field work template we can specify the work template that will we used in the generation of the works.
The work template can be defined at the path warehouse management - setup - work - work template.



When we transfer the materials the system will show us to which location we have to move items, as reported below.



Clicking OK the movement is performed.
 

AX 2012 - D365FO - Recuperare la risorsa collegata alla fase

Con questo job possiamo recuperare la risorsa primaria collegata alla fase:



partendo da prodRoute:

 public static WrkCtrId getWrkCtrId(ProdRoute _prodRoute)  
   {  
     RecId              recId;  
     WrkCtrId            wrkCtrGrpId;  
     ProdId             prodId;  
     WrkCtrActivityRequirementType  relationShipType;  
     WrkCtrActivityRequirement    wrkCtrActivityRequirement;  
       
       recId = _prodRoute.activityRequirementSet().RecId;  
   
       select WrkCtrActivityRequirement  
         where WrkCtrActivityRequirement.ActivityRequirementSet == recid;  
   
       if(WrkCtrActivityRequirement)  
       {  
         wrkCtrGrpId = WrkCtrActivityRequirement.requirementEdit();  
       }  
       
       return wrkCtrGrpId;  
   }  

oppure partendo da routeOpr:

 static WrkCtrActivityRequirementEdit getWrkCtrActivityRequirementEdit(RouteOpr _routeOpr)  
   {  
     wrkCtrActivityRequirement    wrkCtrActivityRequirement;  
     WrkCtrActivityRequirementEdit  WrkCtrActivityRequirementEdit;  
   
     select firstonly wrkCtrActivityRequirement  
       where wrkCtrActivityRequirement.ActivityRequirementSet == _routeOpr.activityRequirementSet().RecId;  
   
     if(wrkCtrActivityRequirement)  
     {  
       wrkCtrActivityRequirementEdit = wrkCtrActivityRequirement.requirementEdit();  
     }  
   
     return wrkCtrActivityRequirementEdit;  
   }  

sabato 21 luglio 2018

D365 Process Inbound ASN


We create two purchase orders having the following structure:

 

  • Purchase order: 00000375 -- Vendor: 1001 - Acme Office Supplies
    • Itemid: A0001, Quantity: 100 pcs
  • Purchase order: 00000348 -- Vendor: 1001 - Acme Office Supplies
    • Itemid: A0001, Quantity: 600 pcs
    • Itemid: P0004, Quantity: 300 ea, Batch: BN0001

 

We import the following file into AX in order to populate the ASN Inbound tables.

 

<?xml version="1.0" encoding="utf-8"?>

<Document>

  <WHSInboundLoadHeaderEntity TractorNumber="0000101">

    <WHSInboundShipmentHeaderEntity VendorShipmentId="VendASN_01" VendorAddressCountryRegionId = "USA" VendorAddressStreet = "123 Coffee Street" VendorAddressStateId = "WA" VendorAddressCity = "Redmond" VendorAddressZipCode = "98052">

      <WHSInboundLoadPackingStructureEntity LICENSEPLATENUMBER="LP_ASN_001">

        <WHSInboundLoadPackingStructureLineEntity PURCHASEORDERNUMBER="00000347" ITEMNUMBER="A0001" QUANTITY="100" UNITSYMBOL="pcs" />

      </WHSInboundLoadPackingStructureEntity>

    </WHSInboundShipmentHeaderEntity>

    <WHSInboundShipmentHeaderEntity VendorShipmentId="VendASN_02" VendorAddressCountryRegionId = "USA" VendorAddressStreet = "123 Coffee Street" VendorAddressStateId = "WA" VendorAddressCity = "Redmond" VendorAddressZipCode = "98052">

      <WHSInboundLoadPackingStructureEntity LICENSEPLATENUMBER="LP_ASN_001">

        <WHSInboundLoadPackingStructureLineEntity PURCHASEORDERNUMBER="00000348" ITEMNUMBER="A0001" QUANTITY="200" UNITSYMBOL="pcs"  />

        <WHSInboundLoadPackingStructureLineEntity PURCHASEORDERNUMBER="00000348" ITEMNUMBER="P0004" QUANTITY="300" UNITSYMBOL="ea" ITEMBATCHNUMBER="BN0001" />

      </WHSInboundLoadPackingStructureEntity>

      <WHSInboundLoadPackingStructureEntity LICENSEPLATENUMBER="LP_ASN_002">

        <WHSInboundLoadPackingStructureCaseEntity LICENSEPLATENUMBER="LP_ASN_002_C01">

          <WHSInboundLoadPackingStructureCaseLineEntity PURCHASEORDERNUMBER="00000348" ITEMNUMBER="A0001" QUANTITY="400" UNITSYMBOL="ea"  />

        </WHSInboundLoadPackingStructureCaseEntity>

      </WHSInboundLoadPackingStructureEntity>

    </WHSInboundShipmentHeaderEntity>

  </WHSInboundLoadHeaderEntity>

</Document>

 

We use the data entity "Inbound ASN" of type "XML-Attribute".



Once we import the xml file automatically the system will generate LOAD and SHIPMENTS according to the purchase orders previously created (as reported below).

 

In the meanwhile AX has also generated the packing structure related to the specific load that is visible clicking on the button "Packing structure".



Once we have followed the whole process above we can start to manage the item arrivals using the WHS terminal. In order to do it we have used the function "Mixed license plate receiving".



At the end of the process we can register the product receipt for the specific load.