lunedì 8 giugno 2015

AX 2012 - Lookup su tabella temporanea

In questo Post vogliamo creare una lookup avendo a disposizione come sorgente dati una tabella temporanea. Supponiamo di voler visualizzare i prima 10 clienti visualizzando codice, nome indirizzo e un campo contatore

Creiamo una nuova tabella chiamandola CustTableTmp, impostaimo la proprietà TableType = InMemory e creiamo 4 campi per contenere le informazioni. Creiamo ora un semplice form con un campo testo su cui andremo a fare l'override del metodo lookup. Creiamo un metodo a livello di form per popolare la tabella:
 public CustTableTmp fillTmpTable()  
 {  
   CustTable    custTable;  
   CustTableTmp  CustTableTmp;  
   int       i = 1;  
   while select custTable  
   {  
     if(i > 10)   
     {  
       break;  
     }  
     CustTableTmp.clear();  
     CustTableTmp.Counter        = i;  
     CustTableTmp.CustAccount      = custTable.AccountNum;  
     CustTableTmp.CustName        = custTable.name();  
     CustTableTmp.LogisticsAddressing  = custTable.address();  
     i++;  
     CustTableTmp.insert();  
   }  
   return CustTableTmp;  
 }  
a questo punto sul controllo implementiamo il metodo lookup:
 public void lookup()  
 {  
   CustTableTmp  CustTableTmp;  
   SysTableLookup sysTableLookup;   
   CustTableTmp  = element.fillTmpTable();  
   sysTableLookup = SysTableLookup::newParameters(tableNum(CustTableTmp),this,false);  
   sysTableLookup.addLookupField(fieldNum(CustTableTmp, Counter),False);  
   sysTableLookup.addLookupField(fieldNum(CustTableTmp, CustAccount),True);   
   sysTableLookup.addLookupField(fieldNum(CustTableTmp, CustName),false);   
   sysTableLookup.addLookupField(fieldNum(CustTableTmp, LogisticsAddressing),False);  
   sysTableLookup.parmTmpBuffer(CustTableTmp);  
   sysTableLookup.performFormLookup();  
 }  
Aprendo il form vediamo il risultato:

lunedì 2 febbraio 2015

AX 2012 - Cambiare report design dinamicamente

In questo articolo vediamo come cambiare design dinamicamente ai report standard che usano la "gestione stampa". Nel mio caso il report in oggetto è il DDT fornitori (PurchPackingSlip)
I report per cui è disponibile la "gestione stampa" sono memorizzati nella tabella PrintMgmtReportFormat.

Se per questi report proviamo ad impostare via codice  il report tramite il metodo parmReportName() verrà comunque stampato il design standard.

In questo esempio aggiungiamo un nuovo layout al report della fattura che verrà stampato solo se la company è italiana

Ecco quello che occorre fare:

1) Aggiungere al metodo populate() della tabella PrintMgmtReportFormat una nuova entry così

  case #isoIT:
            addAX(PrintMgmtDocumentType::SalesOrderInvoice, #isoIT);
            break;

che aggiunge un nuovo record nella tabella PrintMgmtReportFormat .  Verificare nella nuova riga che il flag "System" sia attivo, se non lo è attivarlo

2) Aggiungere alla classe PrintMgmtDocType metodo getDefaultReportFormat() una nuova entry così: (indivituate il blocco che riguarda la fattura):

if (SysCountryRegionCode::isLegalEntityInCountryRegion([#isoIT]))
{
                return ssrsReportStr(SalesInvoice, CustomLayout);
}   

Si consiglia ota di rigenerare la tabella PrintMgmtReportFormat  cancellando prima tutti i record
e poi abdare in

Account receivable -> setup -> form -> form setup

andare sul tab general e cliccare sul pulsante "Print management"

A questo link https://technet.microsoft.com/en-us/library/dd309660.aspx alla sezione "Specify conditional settings for an original or copy record" potete trovare una soluzione alternativa che non richiede codice, ma è necessario che l'utente in fase di stampa del report clicchi sulla voce "Usa gestione stampa":



venerdì 12 dicembre 2014

AX 2012 - Creare una ledgerDimension partendo dai singoli segmenti

In questo post vediamo come creare una ledgerDimension partendo dai singoli segmenti. In questo esempio esempio abbiamo a  disposizione il conto e il centro di costo. Il codice sottostante ritorna il recId da assegnare al campo ledgerdimension:

 static void CreateLedgerDimension(Args _args)  
 {  
   LedgerDimensionAccount ledgerDimension;  
   LedgerDimensionAccount mainAccDimension;  
   RefRecId        defaultDimRecId;  
   RecId createDefaultDimension()  
   {  
     DimensionAttributeValueSetStorage  valueSetStorage = new DimensionAttributeValueSetStorage();  
     DimensionDefault          result;  
     int           i;  
     DimensionAttribute   dimensionAttribute;  
     DimensionAttributeValue dimensionAttributeValue;  
     container        conAttr = ["CentroCosto"]; //array dei nomi delle dimensioni  
     container        conValue = ["1100"];    //container dei valori  
     str           dimValue;  
     for (i = 1; i <= conLen(conAttr); i++)  
     {  
       dimensionAttribute = dimensionAttribute::findByName(conPeek(conAttr,i));  
       if (dimensionAttribute.RecId == 0)  
       {  
         continue;  
       }  
       dimValue = conPeek(conValue,i);  
       if (dimValue != "")  
       {  
         dimensionAttributeValue =  
             dimensionAttributeValue::findByDimensionAttributeAndValue(dimensionAttribute,dimValue,false,true);  
         valueSetStorage.addItem(dimensionAttributeValue);  
       }  
     }  
     result = valueSetStorage.save();  
     info(Strfmt("%1",result));  
     return result;  
   }  
   //quì la dimensione relativa al solo main account  
   mainAccDimension = DimensionStorage::getDefaultAccountForMainAccountNum("3090");   
   //il metodo createDefaultDimension crea la dimensione serve per costruire il resto del segmento  
   defaultDimRecId = createDefaultDimension();  
   //il metodo serviceCreateLedgerDimension effettua il merge delle dimensioni e ristorna il recId realtivo  
   ledgerDimension = DimensionDefaultingService::serviceCreateLedgerDimension(mainAccDimension,defaultDimRecId);  
   info(Strfmt("%1",ledgerDimension));  
 }  

Il risultato che andremo ad attenere sarà:

martedì 18 novembre 2014

venerdì 31 ottobre 2014

AX 2012 - parm di lookup multiselezione su data contract

Piccola pillola per chi utilizza lookup multiselezione sui SOF (SysOperationFramework).

Per pilotare i parametri di una lookup multiselezione nella vostra dialog di tipo SOF tramite la classe di contratto, dovrete gestire il valore tramite lista e aggiungere il seguente codice nell'intestazione del metodo di parm per non incappare in errori:

 AifCollectionTypeAttribute("return",Types::String)  

Esempio su StackOverflow:

http://stackoverflow.com/questions/18498128/report-multivalue-parameter

mercoledì 8 ottobre 2014

AX 2009 - Abilitare i filtri (Ctrl +g) su lookup via X++

In questo post vediamo come abilitare via codice i filtri wildcard sui controlli grid delle lookup di AX 2009. In questo caso la richiesta è di poter vedere di default i filtri aperti non appena si apre la lookup sul campo selezionato. La difficoltà stà nel fatto che non abbiamo un vero e proprio form, ma solo il form generato da standard quando si apre una look up. Per fare ciò occorrerà modificare la classe SysSetupFormRun che rappresenta l'unico punto in cui intercettare l'apertura della lookup: il post presente Quì è stato di grande aiuto =)

Per prima cosa effettuiamo l'override del metodo Run della classe SysSetupFormRun  :

 public void run()  
 {  
   object     obj;  
   SysSetupFormRun SysSetupFormRun;  
   int       i,classId;  
   FormControl   control;  
   #define.Ctrl_g(2855)  
   ;  
   super();  
   obj = this.args().caller();  
   if(obj)  
   {  
     classId = classIdget(obj);  
   }  
   if(classId == className2id(classStr(SysSetupFormRun)))  
   {  
     SysSetupFormRun = obj;  
     control = SysSetupFormRun.selectedControl();  
     if(SysSetupFormRun.name() == formStr(<form string>l))  
     {  
       if(control.id() == <id del controllo nel form> && this.CRFindTopmostGrid())  
       {  
         this.task(#Ctrl_g);  
       }  
     }  
   }  
 }  
Il metodo CRFindTopmostGrid() cicla i tutti i controlli del form chiamante:
 private boolean CRFindTopmostGrid()  
 {  
   FormControl fc;  
   int i;  
   ;  
   for (i=1;i<=this.design().controlCount();i++)  
   {  
     fc = this.design().controlNum(i);  
     if (this.CRFindGrid(fc))  
     {  
       return true;  
     }  
   }  
   return false ;  
 }  
mentre il metodo CRFinfGrid() verifica se il controllo passato è di tipo Grid:
 private boolean CRFindGrid(FormControl fc)  
 {  
   FormGridControl fgrc;  
   FormGroupControl fgc;  
   FormTabControl ftc;  
   FormTabPageControl ftpc;  
   str controlStr,s;  
   int i, controlId;  
   Struct struct;  
   ;  
   // check to see what kind of control it is  
   controlStr = fc.toString();  
   if(strScan(controlStr,"FormGridControl",1,strLen(controlStr)))  
   {  
     if (fc.visible())  
     {  
       fgrc = fc;  
       fgrc.enter();  
       return true;  
     }  
   }  
   else if(strScan(controlStr, "FormGroupControl",1,strLen(controlStr)))  
   {  
     fgc = fc;  
     for (i=1;i<=fgc.controlCount();i++)  
     {  
       if (this.CRFindGrid(fgc.controlNum(i)))  
       {  
         return true;  
       }  
     }  
   }  
   //  
   else if(strScan(controlStr, "FormTabControl",1,strLen(controlStr)))  
   {  
     ftc = fc;  
     for (i=1;i<=ftc.controlCount();i++)  
     {  
       if (this.CRFindGrid(ftc.controlNum(i)))  
       {  
         return true;  
       }  
     }  
   }  
   else if(strScan(controlStr, "FormTabPageControl",1,strLen(controlStr)))  
   {  
     ftpc = fc;  
     for (i=1;i<=ftpc.controlCount();i++)  
     {  
       if (this.CRFindGrid(ftpc.controlNum(i)))  
       {  
         return true;  
       }  
     }  
   }  
   return false;  
 }  
Per ottenere l'id del controllo che apre la lookup possiamo usare il seguente job che dato un form ci fornisce gli id e i nomi di tutti i componenti grafici:
 static void recurseOverAllFormControls(Args _args)  
 {  
   Form form = new Form(formstr(CLU_VendInvoiceJournal));  
   void recurse(Object _parent, int _depth = 1)  
   {  
     int   i;  
     str   name;  
     str   caption;  
     str   dashes;  
     int   id;  
     ;  
     // Used for making it pretty  
     //-->  
     i = _depth;  
     while (i)  
     {  
       dashes += '-';  
       i--;  
     }  
     //<--  
     // Used for example of how to use data  
     //-->  
     if (SysTest::hasMethod(_parent, identifierStr(caption)))  
       caption = _parent.caption();  
     if (SysTest::hasMethod(_parent, identifierStr(name)))  
       name = _parent.name();  
      if (SysTest::hasMethod(_parent, identifierStr(id)))  
       id = _parent.id();  
     info(strfmt("%1%2[%3](%4)(%5)", _depth, dashes, name, caption, id));  
     //<--  
     // Escape condition!  
     if (_parent.controlCount() == 0)  
       return;  
     // Recursive statement  
     for (i=1; i<=_parent.controlCount(); i++)  
       recurse(_parent.controlNum(i), _depth+1);  
   }  
   ;  
   recurse(form.design());  

venerdì 28 marzo 2014

AX 2012 - Lookup su dialog SSRS: Utilizzo classe UI Builder

In moltissimi casi può sorgere la necessità di aggiungere un parametro che esegua una lookup su un campo di una tabella nella finestra di dialog di un report.

Per prima cosa occorre modificare (o creare in caso non esista) la classe UI builder del report. Questa classe è necessaria quando si desidera personalizzare la dialog di un report.
In sintesi ci consente di aggiungere controlli (visibilità, abilitazione...) e lookup personalizzati ai parametri impostati nella classe Data contract del report stesso. Si presuppone la creazione quindi del metodo parm nella data contract del campo che vogliamo "lookupare".

La classe builder che andremo a creare (se non esiste già) deve innanzitutto estendere SrsReportDataContractUIBuilder. Vediamo, con un banalissimo esempio, la creazione di questa classe per un report che ha come parametro il codice cliente. Si vuole visualizzare una lookup nella SalesTable di tutti gli ordini di quel cliente

 class SimpleDemoUIBuilder extends SrsReportDataContractUIBuilder  
 {  
   DialogField dialogCustAccount;
   DialogField dialogSalesOrders;  
   SimpleDemoContract contract;  
 }  

Può essere utile effettuare l'override del metodo build per impostare la data contract e il dialog field dichiarati nella class declaration...

 public void build()  
 {  
   contract = this.dataContractObject();  
   dialogCustAccount = this.addDialogField(methodStr(SimpleDemoContract,parmCustAccount),contract);
 }  

...piuttosto che eseguire tutto nel metodo postBuild:

 public void postBuild()  
 {  
   DialogField             dialogCostingVersionId;  
   SimpleDemoContract      contract;
  
   super();
  
   contract= this.dataContractObject();  
   dialogCustAccount = this.addDialogField(methodStr(SimpleDemoContract,parmCustAccount),contract);
   dialogSalesOrders = this.addDialogField(methodStr(SimpleDemoContract,parmSalesOrders),contract);   
   .....
   dialogSalesOrders.registerOverrideMethod(methodStr(FormStringControl, lookup), methodStr(SimpleDemoUIBuilder, salesOrdersLookup), this);
  
 }  

dove l'ultima riga servirà proprio a far si che la dialog esegua il metodo di lookup sottostante:

  private void salesOrdersLookup(FormStringControl _salesOrdersLookup)  
 {  
      Query query = new Query();  
      QueryBuildDataSource qbds_SalesTable;  
      SysTableLookup sysTableLookup;  
    
      if (_salesOrdersLookup != null)
      {
                 sysTableLookup = SysTableLookup::newParameters(tableNum(SalesTable), _salesOrdersLookup);  
                 qbds_SalesTable = query.addDataSource(tableNum(SalesTable)); 
                 sysTableLookup.addLookupfield(fieldnum(SalesTable, SalesId), true);
     
                 qbds_SalesTable.addRange(fieldNum(SalesTable,CustAccount)).value(queryValue(contract.parmCustAccount());
                 sysTableLookup.parmQuery(query);  
                 // Perform the lookup  
                 sysTableLookup.performFormLookup();  
      }
 }    

Dove si effettuerà il range con il metodo parm (che in questo caso funge da getter), creato nella data contract.