venerdì 17 dicembre 2021

AX 2012 - Disabilitare indice tabella via codice

Tutte le proprietà di un oggetto  dell'AOT che possono essere cambiate a mano, possono anche essere pilotate via codice usando la classe "TreeNode". Con questo job possiamo disabilitare l'indice della tabella "Accountant_BR"

 static void LILIndexDisable(Args _args)  
 {  
      TreeNode objTreeNode;  
      #Properties  
      objTreeNode = TreeNode::findNode(@"\Data dictionary\Tables\Accountant_BR\indexes\AccountantIdx");  
   
      if (objTreeNode)  
      {  
           objTreeNode.AOTsetProperty(#PropertyEnabled,"No");  
           objTreeNode.AOTsave();  
      }  
 }  

martedì 2 novembre 2021

D365FFO - Creazione indirizzo magazzino a partire dall'indirizzo di un site

 Con questo job è possibile creare un indirizzo (a partire da un indirizzo associato ad un site) e collegarlo ad un magazzino. Questo job è stato di grande aiuto:

http://axcorner.blogspot.com/2014/01/create-warehouse-addresses-through-code.html


   public static void main(Args _args)  
   {  
     InventSite                 inventSite;  
     InventLocation               inventLocation;  
     InventSiteId                inventSiteid = "K1";    //sito di partenza  
     InventLocationId              inventLocationId = "lil01"; //magazzino su cui creare l'indirizzo  
     LogisticsPostalAddress           postalAddress;  
     LogisticsPostalAddressView         postalAddressView;  
     LogisticsPostalAddress           logisticsPostalAddressSite;  
     LogisticsEntityLocationMap         locationMap;  
     container                  role, roleMap;  
     Map                     LocationRoleMap;  
     LogisticsLocation              logisticsLocation;  
     LogisticsElectronicAddress         LogisticsElectronicAddress;  
                         
   
     inventSite = inventSite::find(inventSiteid);  
     inventLocation = inventLocation::find(inventLocationId);  
   
     //quì prendo l'indirizzo sorgente del site  
     logisticsPostalAddressSite = inventSite.logisticsPostalAddress();  
             
     logisticsLocation = LogisticsLocation::find(logisticsPostalAddressSite.Location);  
     LogisticsElectronicAddress::findByLocation(logisticsLocation.RecId);  
       
   
     LogisticsPostalAddressEntity  postalAddressEntity = new LogisticsPostalAddressEntity();    
       
     postalAddressView.Street      = logisticsPostalAddressSite.Street;  
     postalAddressView.City       = logisticsPostalAddressSite.City;  
     postalAddressView.State       = logisticsPostalAddressSite.State;  
     postalAddressView.ZipCode      = logisticsPostalAddressSite.ZipCode;  
     postalAddressView.CountryRegionId  = logisticsPostalAddressSite.CountryRegionId;      
     postalAddressView.LocationName   = logisticsLocation.Description;  
   
     postalAddress    = postalAddressEntity.createPostalAddress(postalAddressView);  
         
     locationMap.initValue();  
   
     locationMap      = LogisticsEntityLocationMap::find(tableNum(InventLocationLogisticsLocation), inventLocation.RecId, postalAddress.Location);  
       
     locationMap.Entity       = inventLocation.RecId;  
     locationMap.Location      = postalAddress.Location;  
     locationMap.IsPostalAddress   = logisticsLocation.IsPostalAddress;  
     locationMap.IsPrimary      = LogisticsElectronicAddress.IsPrimary;  
     locationMap.IsPrivate      = LogisticsElectronicAddress.IsPrivate;  
   
     LocationRoleMap    = LogisticsLocationEntity::getDefaultLocationRoleFromEntity(tableNum(InventLocation));  
   
     roleMap        = map2Con(LocationRoleMap);  
     role         = conpeek(roleMap, 1);  
   
     locationMap.addEntityLocation(role ,true);  
   
     info("Terminato");  
   
   }  

giovedì 7 ottobre 2021

D365FO - New cut and paste options

 Is it possible to copy data directly with a cut and paste from excel for multiple lines?

Now yes, I'll show how.

Starting from an excel having all the lines properly ordered.


We copy using CTRL-C function the lines to be copied.

Then we open the correspondent form in D3650FO, create a new line.


And finally click CTRL-V.

It applies also to multiple rows, and in case of errors it is requested a correction by the user in interactive mode.

mercoledì 6 ottobre 2021

D365FFO - Richiamare metodi privati all'esterno della classe

Questo link mostra come usare la  reflection del C# per richiamare metodi privati statici o di istanza di una determinata classe in AX.

https://axvigneshvaran.wordpress.com/2018/01/05/how-to-access-call-private-protected-methods-variables-in-a-class-table-using-reflection/

Supponiamo di avere una classe fatta così:

 class MyClass  
 {  
      private static int sumPrivate(int a, int b)  
      {  
           return a+b;  
      }  
   
      private int sumInstancePrivate(int a, int b)  
      {  
           return a+b;  
      }  
 }  
possiamo accedere ai due metodi privati così:

 using System.Reflection;  
   
 class testReflection  
 {  
   public static void main(Args _args)  
   {  
     int      ret;  
     System.Object[] parametersArray = new System.Object[2]();  
     parametersArray.SetValue(5, 0);  
     parametersArray.SetValue(2, 1);  
       
     System.Type   myType;  
     DictClass    dt = new DictClass(classNum(MyClass));  
     Object     classInstance = dt.makeObject();  
             
     var bindFlags = BindingFlags::InvokeMethod |   
                               BindingFlags::NonPublic |   
                               BindingFlags::Static | BindingFlags::Instance;  
     
     myType = classInstance.GetType();  
   
     //richiama il metodo statico  
     ret = myType.InvokeMember(staticMethodStr(MyClass,sumPrivate),bindFlags,null,null,parametersArray);  
   
     //richiama il un metodo di instanza  
     var methodInfo = myType.GetMethod(methodStr(MyClass, sumInstancePrivate), bindFlags);  
   
     if (methodInfo)  
     {  
                // Or use “new System.Object[0]()” instead of parametersArray if you do not have any parameters  
       ret = methodInfo.Invoke(classInstance, parametersArray);   
     }  
   }  
 }  

N:B: Mina le basi della programmazione ad oggetti :)



mercoledì 7 luglio 2021

D365FFO / AX 2012 - Lookup su worker con controllo unbound

Per aprire la lookup standard sugli Employee è sufficente utilizzare i reference group:

https://community.dynamics.com/ax/f/microsoft-dynamics-ax-forum/246930/what-is-a-reference-group/684992

Se invece abbiamo un controllo unbound, possiamo aggiungere alla form un controllo stringa (con EDT = HcmPersonnelNumberId ) e nell'override del metodo lookup del controllo scrivere:

 public void lookup()  
 {  
      FormRun formRun;  
        
      formRun = FormAutoLookupFactory::buildLookupFromCustomForm(this  
                                                                              ,new Form(formStr(HcmWorkerLookUp))  
                                                                              ,AbsoluteFieldBinding::construct(fieldStr(HcmWorker, PersonnelNumber),  
                                                                              tableStr(HcmWorker)));  
      this.performFormLookup(formRun);  
 }  



lunedì 24 maggio 2021

D365FFO - Fetch mode

 In questo post vediamo il funzionamento della proprietà "Fetch Mode" presente nelle query di AX



Riporto questo link molto utile dove spiega anche altre proprietà:


Ho creato una query con SalesTable + SalesLine + CustTable strutturata così:



Se le relation hanno fetch mode 1:1 e stampiano nell’ordine SalesTable.SalesId, SalesLine.InventTransId, CustTable.AccountNum l’esecuzione darà lugo a questa sequenza: 



 Avremo quindi tutti i buffer di tutte le tabelle valorizzate. Se invece imposto 1:n l’esecuzione da luogo a questo: 


Se invece i datasource delle query sono tutti annidati a cascata così: Quindi non tutti I buffer sono disponibili, per esempio CustTable è disponibile solo all’ultima iterazione,nelle iterazioni 1,2,3 CustTable è vuota.

One2One oppure One2Many non fà differenza


mercoledì 28 aprile 2021

D365FFO Manage Views

In order to enable the views we need to access the feature management  workspace and to enable  the saved views functions starting from "Saved views" (as reported below).



In order to manage the publishing and administration of views we need to add to our user the role "Saved views administrator".

Then opening the form having grids we can see the function view enabled as reported below.


So modifying the form or the query the system will allow the saving of the view that we have opened in that moment.



We can than manage the views proceeding with the publishing as reported below.




In order to publish the View we have to select it and click on the button Publish as reported below.

While publishing it's possible to define which roles and which companies will be affected by our change.













giovedì 4 febbraio 2021

D365FFO - Deploy report da power shell

 Con questo comando possiamo deployare un report da power shell:

https://community.dynamics.com/365/financeandoperations/b/daxology/posts/options-to-deploy-ssrs-report-in-d365fo

es:

 cd K:\AosService\PackagesLocalDirectory\Plugins\AxReportVmRoleStartupTask  
   
 .\DeployAllReportsToSsrs.ps1 -PackageInstallLocation "K:\AosService\PackagesLocalDirectory" -Module ApplicationSuite –ReportName SalesInvoice.Report  

lunedì 1 febbraio 2021

D365FFO - Report Extension

 In questo post vediamo come estendere un report STD aggiungendo un nuovo parametro alla dialog di lancio. Come nota tecnica aggiungo che la modifica illustrata è valida solo dall'UP23 in poi. Con questo update infatti X++ permette di usare l'attributo [DataMemberAttribute] nelle estensioni:

https://docs.microsoft.com/en-us/business-applications-release-notes/October18/dynamics365-finance-operations/platform-extensibility4

Vedremo due casi, il primo con un report "semplice" che prevede solo DataContract + DataProvider ( ho preso come esempio il report Vendor transaction in AP --> Inquiries and report --> Vendor transaction) , e il secondo caso più complesso che prevede anche controller e la UIBuilder (ho preso come riferimento il libro giornale Italia). Nel secondo caso segnalo questo link che è stato molto utile:

https://community.dynamics.com/365/financeandoperations/f/dynamics-365-for-finance-and-operations-forum/377499/extend-contract-class-in-d365-fo-to-add-a-new-parameter

Per entrambe i casi aggiungeremo alla dialog un flag per decidere se stampare o meno la partita iva in anagrafica:

CASO 1: Vendor transaction:

1) Fare la Coc sulla data contract così:

 [ExtensionOf(classstr(VendTransListContract))]  
 final class LIL_VendTransListContract_Extension  
 {  
   private boolean showVatNum;  
   
   [  
     DataMemberAttribute('showVatNum'),  
     SysOperationLabelAttribute(literalstr("@SYS8946"))  
   ]  
   public boolean parmShowVatNum(boolean _showVatNum = showVatNum)  
   {  
     showVatNum = _showVatNum;  
     return showVatNum;  
   }  
 }  
2) Creare l'estensione della tabella VendTransListTmp ed aggiungere il nuovo campo

3) Duplicare il report  VendTransListReport aggiungendo sul layout il nuovo campo

4) Fare la Coc sulla DP aggiungendo le logiche per popolare il nuovo campo:

 [ExtensionOf(classstr(VendTransListDP))]  
 final class LIL_VendTransListDP_Extension  
 {  
   private boolean showVatNum;  
   
   public void processReport()  
   {  
     vendTable        vendTableLocal;  
     VendTransListContract  contract = this.parmDataContract() as VendTransListContract;  
     VendTransListTmp    vendTransListTmp;  
   
     showVatNum = contract.parmShowVatNum();  
   
     next processReport();  
   
     if(showVatNum)  
     {  
       vendTransListTmp = this.getVendTransListTmp();  
   
       update_recordset vendTransListTmp  
         setting LIL_VATNUM = vendTableLocal.VATNum  
         join vendTableLocal  
         where vendTableLocal.AccountNum == vendTransListTmp.AccountNum;  
     }  
   }  
 }  

5) Estendere il menu item VendTransList Modificando la proprietà "Object" con "LIL_VendTransListReport"

Ristampando il report possiamo notare il nuovo parametro e la nuova colonna popolata







CASO 2: Libro giornale italia:

Oltre a quanto fatto per il caso1, dobbiamo anche:

1) Creare la nostra controller che estenderà (estensione classica in questo caso) la controller standard, cambiando il nome del report:

 class LIL_LedgerFiscalJournalController_IT extends LedgerFiscalJournalController_IT  
 {  
   public static void main(Args _args)  
   {  
     #ISOCountryRegionCodes  
     GlobalizationInstrumentationHelper::featureRunByCountryRegionCodes([  
       [#isoIT, GlobalizationConstants::FeatureReferenceIT00011] ],  
       funcName()  
     );  
   
     LIL_LedgerFiscalJournalController_IT  controller = new LIL_LedgerFiscalJournalController_IT();  
       
     controller.parmReportName(ssrsReportStr(LIL_LedgerFiscalJournal_IT, Report));  
     controller.parmArgs(_args);  
     controller.startOperation();  
   }  
   
 }  

2) Coc sulla classe UIBuilder:

 [ExtensionOf(classStr(LedgerFiscalJournalUIBuilder_IT))]  
 final class LIL_LedgerFiscalJournalUIBuilder_IT_Extension  
 {  
   private boolean             showVatNum;  
   private DialogField           showVatNumDlg;  
   private LedgerFiscalJournalContract_IT contract;  
   
   public void build()  
   {  
     contract = this.dataContractObject() as LedgerFiscalJournalContract_IT;  
   
     showVatNumDlg = this.bindInfo().getDialogField(this.dataContractObject(), methodstr(LedgerFiscalJournalContract_IT, parmShowVatNum));  
       
     next build();  
   }  
   
   public void getFromDialog()  
   {  
     contract = this.dataContractObject() as LedgerFiscalJournalContract_IT;  
   
     showVatNumDlg = this.bindInfo().getDialogField(contract,  
             methodStr(LedgerFiscalJournalContract_IT, parmShowVatNum));  
   
     showVatNum = showVatNumDlg.value();  
   
     next getfromdialog();  
   }  
   
 }  
L'estensione del menuitem LedgerFiscalJournal_IT dovrà avere come object la nuova controller.

Nel caso del libro giornale ho avuto dei problemi alla duplica del report. Durante il deploy veniva segnalato un errore sul numero di parametri: "The number of defined parameters is not equal to the number of cell definitions in the parameter panel" . Non ho ben capito da cosa derivi, ho risolto eliminando i dataset e  il design ricreandoli copiando dallo STD. Ricreando i Dataset a mano, ho anche dovuto ricreare la struttura dei parametri divisi in gruppi come nel report STD:



Ristampando il report possiamo vedere le modifiche:








martedì 12 gennaio 2021

AX 2012 - Matrici

Può capitare a volte di dover lavorare con le matrici.Il seguente job crea una matrice 3x4 di interi in cui l'elemento A[i,j] = i*j e la stampa
 static void LIL_Matrix(Args _args)  
 {  
   int N,M;  
   System.Int32[,] a;  
   int i,j,element;  
   str s;  
   
   N = 3;  
   M = 4;  
   
   a = new System.Int32[N,M]();  
   
   /*
    per inizializzare un Char...
    System.Char c
    c = System.Char::Parse('0');
    */
   
   //inizializza matrice..  
   for(i = 0; i<N; i++)  
   {  
     for(j=0; j<M;j++)  
     {  
       a.SetValue((i*j), i, j);  
     }  
   }  
   
   //stampa matrice  
   for(i = 0; i<N; i++)  
   {  
     for(j=0; j<M;j++)  
     {  
       element = a.GetValue(i,j);  
   
       if(j == M-1)  
       {  
         s+=strFmt("%1",element);  
       }  
       else  
       {  
         s+=strFmt("%1,",element);  
       }  
     }  
   
     s += "\n";  
   }  
   
   info(s);  
 }