lunedì 16 marzo 2020

D365 FFO - lookup su formcontrol unbound

Questo link mostra come fare la lookup via CoC sul campo di un datasource:

https://community.dynamics.com/365/financeandoperations/f/dynamics-365-for-finance-and-operations-forum/357208/lookup-on-form-datasource-field

Per fare la lookup su un controllo unbound dobbiamo fare così:

 [ExtensionOf(formControlStr(ProjTableWizard,FormControl))]  
 final class LILProjTableWizard_Extension  
 {  
   public void lookup()  
   {  
     next lookup();
  
     Query query = new Query();  

     query.addDataSource(tableNum(CustGroup));  
     
     SysTableLookup sysTableLookup = SysTableLookup::newParameters(tableNum(CustGroup),this);  
     systableLookup.addLookupfield(fieldNum(CustGroup, CustGroup), true);  
     systableLookup.addLookupfield(fieldNum(CustGroup, Name), false);  
     
     sysTableLookup.parmQuery(query);  
     sysTableLookup.performFormLookup();  
   }  
 }  

In questo modo invece possiamo effettuare la CoC sul metodo clicked di un pulsante:

 [ExtensionOf(formControlStr(WHSShipPlanningListPage,Transfer))]  
 final class MCSWHSShipPlanningListPageTransfer_Extension  
 {  
   void clicked()  
   {  
     FormDataSource      WHSShipmentTableDS;  
     FormControl      control = this as FormControl;  
     WHSShipmentTableDS = control.formRun().dataSource(formDataSourceStr(WHSShipPlanningListPage,WHSShipmentTable)) as FormDataSource;  
     next clicked();  
     WHSShipmentTableDS.research(true);  
   }  
 }  

domenica 1 marzo 2020

D365FFO - Cercare l'id dell'etichetta in base al testo

In questo post vediamo un job (runnableClass) per cercare il codice dell'etichetta in base al testo. Nel esempio vogliamo cercare tutte le etichette che contengono "Data Fattura":

Questi due post sono stati di grande aiuto:

Questo mostra come ricavare il codice dell'etichetta cercando in base al testo:

https://community.dynamics.com/365/financeandoperations/f/dynamics-365-for-finance-and-operations-forum/272482/how-to-get-label-id-from-a-label-text/810927

Questo post invece indica come ricavare tutti i fileID delle etichette:

https://community.dynamics.com/365/financeandoperations/f/dynamics-365-for-finance-and-operations-forum/243050/enumerate-label-prefix-for-label-search-functionality


 Questo è il job preso dal primo link e modificato:

   public static void main(Args _args)  
   {  
     int j;  
     //parametri - start  
     str TextToFind = "Data fattura"; //stringa da cercare  
     LanguageId languageId = "it";    //lingua  
     boolean matchExactly = true;     //trova parola esatta oppure no  
     str   LabelFile = "SYS";         //Label file Id  
     //parametri - end  
     container result;  
     Map txtToLabelIdMap = new Map(Types::String, Types::String);  
     ClrObject labels = new ClrObject("System.Collections.Generic.Dictionary`2[System.String,System.String]");  
     System.Globalization.CultureInfo cultInfo = System.Globalization.CultureInfo::CreateSpecificCulture(languageId);  
     labels = Microsoft.Dynamics.Ax.Xpp.LabelHelper::GetAllLabels(LabelFile, cultInfo);  
     if(labels)  
     {  
       ClrObject labelsEnumerator = labels.GetEnumerator();  
       while(labelsEnumerator.MoveNext())  
       {  
         ClrObject keyValuePair = labelsEnumerator.get_Current();  
         var currentlabelId = keyValuePair.get_Key();  
         var currentTxt = keyValuePair.get_Value();  
         if(matchExactly)  
         {  
           if(currentTxt == TextToFind)  
           {  
             if (!Microsoft.Dynamics.Ax.Xpp.LabelHelper::IsLegacyLabelId(currentlabelId))  
             {  
               currentlabelId = strFmt("%1%2:%3","@",LabelFile,currentlabelId);  
             }  
             result += strFmt("%1,%2 -> %3",LabelFile,currentlabelId,currentTxt);  
           }  
         }  
         else  
         {  
           if(strScan(currentTxt, TextToFind, 0, strLen(currentTxt)))  
           {  
             if (!Microsoft.Dynamics.Ax.Xpp.LabelHelper::IsLegacyLabelId(currentlabelId))  
             {  
               currentlabelId = strFmt("%1%2:%3","@",LabelFile,currentlabelId);  
             }  
             result += strFmt("%1,%2 -> %3",LabelFile,currentlabelId,currentTxt);  
           }  
         }  
       }  
     }  
     //container dei risultati  
     for(j = 1;j<=conLen(result);j++)  
     {  
       info(conPeek(result,j));  
     }  
   }  

Lanciando il job otterremo il seguente risultato:


mercoledì 12 febbraio 2020

Notepad ++ Plug in x++

In questo post segnalo un utile plug in per l'editor Notepad++ per aggiungere tra i linguaggi l'x++.
Il plug è scaricabile da quì:

https://jrabascal.wordpress.com/2014/07/07/creating-user-defined-language-x-into-the-notepad/

Nel link trovate le instruzioni per l'installazione

Ho apportato qualche piccola modifica alle colorazioni delle parole chiavi, l'evidenziazione delle etichette e dei blocchi.

Potete scaricare il file da quì:

https://mega.nz/#!WE9lRY7S!6A-3Wf_vDcEFdFehS5Tikh3pf5cHUzDQfgaPbml0B4w

Questo un esempio:


giovedì 16 gennaio 2020

AX 2012 - SSRS - Errore su report parameter ‘AX_CompanyName’


Mi è capitato varie volte di imbattermi nel seguente errore quando vado a stampare il primo report dopo il riavvio dei servizi di reporting. L'errore scompare dalla seconda stampa. 

"The DefaultValue expression for the report parameter ‘AX_CompanyName’ contains an error: Request for the permission of type 'System.Security.Permissions.EnvironmentPermission, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed. (rsRuntimeErrorInExpression)"

L'errore non risulta quindi bloccante ma è piuttosto fastidioso, soprattuto quando capita ad un utente. Quello che posso suggerirvi è di modificare come segue il file rssrvpolicy.config all'interno delle cartelle di SSRS, impostando l'autorizzazione "FullTrust" al posto di "Esegui" per "Report_Expressions_Default_Permissions".

Configurazione iniziale:
<CodeGroup
class="UnionCodeGroup"
version="1"
PermissionSetName="Execution"
Name="Report_Expressions_Default_Permissions"
Description="This code group grants default permissions for code in report expressions and Code element. ">
[...]
</CodeGroup>

Configurazione modificata:

<CodeGroup 
class="UnionCodeGroup" 
version="1" 
PermissionSetName="
FullTrust
Name="Report_Expressions_Default_Permissions" 
Description="This code group grants default permissions for code in report expressions and Code element. "> 

[..] 

</CodeGroup>



Fonte:

https://community.dynamics.com/ax/b/axsupport/archive/2012/02/02/microsoft-dynamics-ax-2012-reporting-extensions-error-system-security-permissions-environmentpermission-while-running-report

AX 2012 - Job Fieldname print

In this post I'll share a job made in order to print an infolog having all the fields of a table in ax with some useful information like, field name, label, help text, data type.

 static void AGIA_TableFieldNames_2(Args _args)    
 {    
      DictTable dt           = new SysDictTable(77);    
      FieldId _fieldId     = dt.fieldNext(0);    
      DictField                _dictField;    
      Dialog                dialog;    
      DialogField           myDialogField;    
      tablename                id;    
      dialog                     = new dialog();    
      myDialogField           = dialog.addField(extendedTypeStr(tablename), "TableName : ");  
      dialog.run();   
      if(dialog.closedOk())    
      {    
           id = myDialogField.value();    
           dt = new sysdicttable(tableName2id(id));    
           info(strFmt("Field Name; Field Label; Field Type; Field Help; Field Mandatory; Field Lenght"));    
           if(_fieldId)    
           while(_fieldId)    
           {    
                _dictField =dt.fieldObject(_fieldId);   
                if(_dictField)  
                {  
                     if(!_dictField.isSystem())  
                     {  
                          info(strFmt("%1; %2; %3; %4; %5; %6", _dictField.name(),_dictField.label(),_dictField.baseType(),_dictField.help(),_dictField.mandatory(), _dictField.stringLen()));    
                          _fieldId= dt.fieldNext(_fieldId);  
                     }  
                }  
           }    
           else  
           {  
                info(strFmt("Missing table %1", id));  
           }                 
      }    
 }   
Con questo job invece possiamo stampare il contenuto di una tabella (lista dei campi e valore) in maniera dinamica:
 static void LIL_PrintTableValue(Args _args)  
 {  
   DictTable  dt;  
   DictField  dictField;  
   Common   common;  
   Counter   i=0;  
   FieldId   fieldId;  
       
   dt = new DictTable(tableNum(InventSite));  
   
   common = dt.makeRecord();  
    
   while select common  
   {  
       
     fieldId = dt.fieldNext(0);  
       
     while (fieldId)  
     {  
       dictField = dt.fieldObject(fieldId);  
         
       if(dictField.configurationKeyId() != configurationkeynum(SysDeletedObjects60))  
       {  
         info(strFmt("%1 = %2",fieldId2name(dt.id(),dictField.id()),common.(dictField.id())));  
       }  
         
       fieldId = dt.fieldNext(fieldId);  
     }  
       
     info("---------------------");  
   }  
 }  

mercoledì 26 giugno 2019

AX 2012 - Job per creazione progetto da CSV

In questo post pubblico un job che ho scritto che serve per creare un progetto privato leggendo un csv contenente una lista di tipi e nomi degli elementi dell'AOT

 static void LIL_ProjectElementAdd(Args _args)  
 {  
   TextIo         inFile;  
   container        line;  
   Counter         records;  
   SysOperationProgress  simpleProgress;  
   container        fileContainer;  
   Counter         loopCounter;  
   CustTable        CustTable;  
   InventTable       InventTable;  
   str           filename,  
               properties,  
               objectType,  
               objectName,  
               aotType,aotPath;  
   TreeNode        projectNode,  
               tempNode;  
   ProjectGroupNode    groupNode;  
   boolean         managed;  
   
   #OCCRetryCount  
   #AviFiles  
   #File  
   #aot  
   #DMF  
   
   filename = WinAPI::getOpenFileName(0,  
                 [WinAPI::fileType(#csv),#AllFilesName + #csv],  
                 strFmt(@'C:\users\%1\Desktop',WinApi::getUserName()),  
                 "@SYS53008"  
                 );  
   try  
   {  
     inFile = new TextIo(filename, 'r');  
     inFile.inRecordDelimiter('\n');  
     inFile.inFieldDelimiter(';');  
     while (inFile.status() == IO_Status::OK)  
     {  
       fileContainer += [infile.read()];  
     }  
     inFile = null;  
   }  
   catch  
   {  
     throw error(strFmt("@SYS18678", filename));  
   }  
   
   simpleProgress = SysOperationProgress::newGeneral(#aviUpdate, "Importazione...", conLen(fileContainer));  
   
   records = 0;  
   
   projectNode = SysTreeNode::createProject("MyPrivateProject");  
   
   groupNode  = projectNode.AOTadd("Object");  
   properties = groupNode.AOTgetProperties();  
   
   for (loopCounter = 2; loopCounter <= conLen(fileContainer) - 1 ; loopCounter++)  
   {  
     line = conPeek(fileContainer,loopCounter);  
     objectType = conPeek(line,1);  
     objectName = conPeek(line,2);  
   
     switch(objectType)  
     {  
       case "BaseEnum" :  
         aotType = #BaseEnums;  
         aotPath = #BaseEnumsPath;  
         managed = true;  
         break;  
   
       case "Class" :  
         aotType = #Classes;  
         aotPath = #ClassesPath;  
         managed = true;  
         break;  
   
       case "ExtendedDataType" :  
         aotType = #ExtendedDataTypes;  
         aotPath = #ExtendedDataTypesPath;  
         managed = true;  
         break;  
   
       case "Form" :  
         aotType = "Forms";  
         aotPath = #FormsPath;  
         managed = true;  
         break;  
   
   
       case "Macro" :  
         aotType = "Macros";  
         aotPath = #MacrosPath;  
         managed = true;  
         break;  
   
       case "Menu" :  
         aotType = "Menus";  
         aotPath = #MenusPath;  
         managed = true;  
         break;  
   
   
       case "Table" :  
         aotType = "Tables";  
         aotPath = #TablesPath;  
         managed = true;  
         break;  
   
       case "Map" :  
         aotType = "Maps";  
         aotPath = #TableMapsPath;  
         managed = true;  
         break;  
   
       case "Query" :  
         aotType = #Queries;  
         aotPath = #QueriesPath;  
         managed = true;  
         break;  
   
       case "SecurityDuty" :  
         aotType = "SecDuties";  
         aotPath = #SecDutiesPath;  
         managed = true;  
         break;  
   
       case "SecurityPrivilege" :  
         aotType = "SecPrivileges";  
         aotPath = #SecPrivilegesPath;  
         managed = true;  
         break;  
   
       case "SecurityPrivilege" :  
         aotType = "SecPrivileges";  
         aotPath = #SecPrivilegesPath;  
         managed = true;  
         break;  
   
       case "SecurityProcessCycle" :  
         aotType = "SecProcessCycles";  
         aotPath = #SecProcessCyclesPath;  
         managed = true;  
         break;  
   
       case "SecurityRole" :  
         aotType = "SecRoles";  
         aotPath = #SecRolesPath;  
         managed = true;  
         break;  
   
       case "SSRSReport" :  
         aotType = "SSRSReports";  
         aotPath = #SSRSReportsPath;  
         managed = true;  
         break;  
   
       case "VisualStudioProjectCSharp" :  
         aotType = "VSProjectsCShar";  
         aotPath = #VSProjectsCSharpPath;  
         managed = true;  
         break;  
   
       default:  
         managed = false;  
         warning(strFmt("Unable to add %1, %2",objectType,objectName));  
     }  
   
     if(objectName && managed)  
     {  
       tempNode = TreeNode::findNode(aotPath);  
       groupNode.addNode(tempNode.AOTfindChild(objectName));  
     }  
   }  
   projectNode.AOTsave();  
 }  

L'output sarà il seguente: