venerdì 31 agosto 2012

AX 2009 - Logica testata righe: impostazioni e zero codice

La logica testata-righe in un form è una procedura piuttosto comune e richiesta.

Essa è rappresentabile in più modi (ad esempio modificando il metodo "active" del datasource di testata),
ma la procedura più semplice e veloce, se non avete particolari pretese, si esegue tramite due semplici impostazioni.


Il primo step di questa semplice soluzione consiste nell'impostare una normale relazione nella tabella di linea che colleghi logicamente il campo in comune fra le due tabelle:




Ora, dopo aver aggiunto i due datasource al Form che ci interessa , si imposta la proprietà JoinSource del datasource di linea con quello di testata come da immagine:



Ora vi basterà aggiungere due grid nel design del form, una avente i campi della tabella di testata e una avente i campi della tabella di linea.
Quando selezionerete una riga dalla grid di testata saranno mostrate automaticamente nella grid di linea le righe ad essa associate!

AX 2009 - Importazione da XML

Il codice sottostante importa i dati in SalesTable, SalesLine dal file XML generato tramite esportazione:
 public void run()  
 {  
     SalesTable         SalesTable;  
     SalesLine          SalesLine;  
     int             i,numSales;  
     SalesStatus         SalesStatus;  
     InventTransId        InventTransId;  
     SalesId           salesid;  
     SysOperationProgress    prog = new SysOperationProgress();  
     XMLNodeList         ReqNodeList;  
     XMLNodeList         LinesNodeList;  
     SysOperationProgress    simpleProgressTask;  
     XMLDocument         doc;  
     XMLNode           rootNode;  
     XMLNodeList         salesLines;  
     XMLElement         element,lineElement;  
     ;  
     doc = new XMLDocument();  
     doc.load(fileName);  
     rootNode          = doc.documentElement();  
     ReqNodeList         = rootNode.selectNodes("/Sales/SalesTable"); // caricamento della lista degli ordini  
     numSales          = ReqNodeList.length();  
     simpleProgressTask     = SysOperationProgress::newGeneral(#aviUpdate, 'Sales import...',numSales);  
     ttsbegin;  
     prog.setCaption("Import in AX in progress...");  
     prog.setAnimation(#AviTransfer);  
     i = 0;  
     for(element = ReqNodeList.nextNode(); element; element = ReqNodeList.nextNode()) // ciclo sugli ordini cliente  
     {  
       simpleProgressTask.incCount(1);  
       simpleProgressTask.setText(strfmt("Percent: %1"+'%', (i/numSales)*100));  
       simpleProgressTask.update(true);  
       salesId = element.selectSingleNode("@SalesId").value();  
       if(SalesTable::find(salesId)) //l'ordine è gà prensente, occorre fare un update  
       {  
         select forupdate SalesTable where SalesTable.SalesId == salesId;  
         SalesTable.SalesStatus       = str2enum(SalesStatus, element.selectSingleNode(fieldstr(SalesTable, SalesStatus)).innerText());  
         //Inserire quì gli altri campi di testata da importare  
         SalesTable.Update();  
       }  
       else // ordine non presente, occorre crearlo e fare l'insert  
       {  
         SalesTable.SalesId     = salesId;  
         SalesTable.SalesStatus   = str2enum(SalesStatus, element.selectSingleNode(fieldstr(SalesTable, SalesStatus)).innerText());  
         //Inserire quì gli altri campi di testata da importare  
         SalesTable.Insert();  
       }  
       LinesNodeList = element.selectNodes("Lines/SalesLine"); // caricamento della lista delle righe dell'ordine che stò ciclando  
       for(lineElement = LinesNodeList.nextNode(); lineElement; lineElement = LinesNodeList.nextNode()) // ciclo sulle righe  
       {  
         InventTransId = lineElement.selectSingleNode("@InventTransId").value();  
         if(SalesLine::findInventTransId(InventTransId)) // la riga ordine è gà prensente, occorre fare un update  
         {  
           select forupdate SalesLine where SalesLine.InventTransId == InventTransId;  
           SalesLine.LineNum      = str2num(lineElement.selectSingleNode(fieldstr(SalesLine, LineNum)).innerText());  
           //Inserire quì gli altri campi di riga da importare  
           SalesLine.Update();  
         }  
         else // la riga d'ordine non è presente, occorre crearlo e fare l'insert  
         {  
           SalesLine.InventTransId   = InventTransId;  
           SalesLine.LineNum      = str2num(lineElement.selectSingleNode(fieldstr(SalesLine, LineNum)).innerText());  
           //Inserire quì gli altri campi di riga da importare  
           SalesLine.Insert();  
         }  
       }  
       i++;  
     }  
     ttscommit;  
 }  

giovedì 30 agosto 2012

AX 2009 - Esportazione dati in XML

Come primo articolo vedremo come esportare in XML dati di una tabella e successivamente effettuare l'import in AX.
Come esempio esporteremo dati dalle tabelle SalesTable e SalesLine collegate tra di loro secondo la logica testata-righe,  tale soluzione si può comunque estendere a qualsiasi tabella.
Il campo che lega le due tabelle è SalesId.

L'XML che andremo a creare rispecchierà tale logica nella sua struttura.

In particolare esporteremo due campi di testata (SalesId, SalesStatus) e due campi di riga(InventTransId, LineNum)

L'avanzamento complessivo della procedura è dato dalla progressbar.

 public void export()
{
    SalesTable          SalesTable, ST;
    SalesLine           SalesLine;
    #AviFiles
    SysOperationProgress simpleProgressTask, simpleProgressReq;
    int numSales;
    int row;
    XmlDocument         doc;
    XmlElement          nodeTable;
    XmlElement          nodeXml;
    XmlElement          nodeStatus;
    XmlElement          linesRoot;
    XmlElement          nodeSalesLine;
    XmlElement          nodeLineNum;

    ;

    doc = XmlDocument::newBlank(); // creo un XML vuoto

    nodeXml = doc.createElement('Sales'); // imposta il nodo radice
    doc.appendChild(nodeXml); // aggiungi il nodo radice al documento

    row = 0;

    select count(recId) from ST;
       numSales = ST.RecId;

    simpleProgressTask = SysOperationProgress::newGeneral(#aviUpdate, 'Sales export...', numSales);

    new InteropPermission(InteropKind::ClrInterop).assert();

    while select  SalesTable
    {

        simpleProgressTask.incCount(1);
        simpleProgressTask.setText(strfmt("Percent: %1"+'%', (row/numSales)*100));
        simpleProgressTask.update(true);

        nodeTable = doc.createElement(tablestr(SalesTable));
        nodeTable.setAttribute(fieldstr(SalesTable, SalesId), SalesTable.SalesId); // imposto il valore dell'attributo dell'elemento
        nodeXml.appendChild(nodeTable);

        nodeStatus = doc.createElement(fieldstr(SalesTable, SalesStatus));
        nodeStatus.appendChild(doc.createTextNode(enum2str(SalesTable.SalesStatus))); // il nome dell'elemento coincide con il nome del campo
        nodeTable.appendChild(nodeStatus);

        // aggiungi quì altri campi di testata da esportare

        linesRoot = doc.createElement('Lines'); //elemento radice che conterrà le righe
        nodeTable.appendChild(linesRoot);

        while select  SalesLine
            where SalesLine.SalesId == SalesTable.SalesId
        {
            nodeSalesLine = doc.createElement(tablestr(SalesLine));
            nodeSalesLine.setAttribute(fieldstr(SalesLine, InventTransId), SalesLine.InventTransId);
            linesRoot.appendChild(nodeSalesLine);

            nodeLineNum = doc.createElement(fieldstr(SalesLine, lineNum));
            nodeLineNum.appendChild(doc.createTextNode(num2str(SalesLine.LineNum,-1,-1,-1,-1)));
            nodeSalesLine.appendChild(nodeLineNum);

            // aggiungi quì gli altri campi di riga da esportare

        }

        row++;
    }


    doc.save(fileName);

    CodeAccessPermission::revertAssert();

    return;

}



Questo il file XML che andremo ad ottenere:

 
 <?xml version="1.0" encoding="utf-8"?>  
 <Sales>   
  <SalesTable SalesId="C0901542">  
   <SalesStatus>Ordine aperto</SalesStatus>  
   <Lines />  
  </SalesTable>  
  <SalesTable SalesId="C0901724">  
   <SalesStatus>Ordine aperto</SalesStatus>  
   <Lines>  
    <SalesLine InventTransId="926038">  
     <lineNum>32,00</lineNum>  
    </SalesLine>  
    <SalesLine InventTransId="926038">  
     <lineNum>33,00</lineNum>  
    </SalesLine>  
    <SalesLine InventTransId="926038">  
     <lineNum>34,00</lineNum>  
    </SalesLine>  
    <SalesLine InventTransId="925879.1">  
     <lineNum>110,00</lineNum>  
    </SalesLine>  
   </Lines>  
  </SalesTable>  
 </Sales>  

Benvenuti!

Ciao a tutti!
Questo blog nasce con l'intento di fornire soluzioni e approcci alle problematiche più comuni (e non) agli sviluppatori (o presunti tali) di Microsoft Dynamics AX (per chi non lo conoscesse http://www.microsoft.com/en-us/dynamics/erp-ax-overview.aspx).
Ovviamente sarà nostra premura pubblicare tali soluzioni nella maniera più corretta possibile, ma è anche nostro obiettivo coinvolgere più persone possibili, specialmente in italia dove non esistono particolari punti di riferimento.
A presto con i primi articoli....