Microsoft_MVP_banner

D365O – How to get current worker through X++

The current user or worker could be retrieved through following code since AX 2012 DirPersonUser::currentWorker() However, In Dynamics 365 for operation this has refactored to; HcmWorkerLookup::currentWorker()

AX 2012 : Remove an XML node through X++

Following code snippet may help you to remove an XML node from an XML message string. private str removeXMLTag(str xml) {     XmlDocument         doc = new XmlDocument();     XmlElement          nodeScript;     XmlNode             parentNode, childNode;     XmlNodeList         xmlScriptList,                         parentNodeList,                         childNodeList;     int                 i,j;     doc.loadXml(xml);     // Get the root element and its child nodes     nodeScript = doc.getNamedElement(“SalesOrder”);     xmlScriptList = nodeScript.childNodes();     for(i=0; i < xmlScriptList.length(); i++)     {         parentNode = xmlScriptList.item(i);         childNodeList = parentNode.childNodes();         for (j=0; j < childNodeList.length(); j++)         {             childNode = childNodeList.item(j);             if (childNode.selectSingleNode(“Notes”))             {                 parentNode.removeChild(childNode);             }         }     }     return doc.outerXml(); } This will remove DocuRef node along with its child nodes.  Original XML message <?xml version=”1.0″ encoding=”utf-8″?>                             <SalesOrder xmlns = “http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder”>                                         <DocuRef class = “entity”>         <Notes>Business Name</Notes> </DocuRef> </SalesOrder> XML message after node being removed <?xml version=”1.0″ encoding=”utf-8″?>                             <SalesOrder xmlns = “http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder”>                                   </SalesOrder>

AX 2012: Reading files from directory through X++

Following code can be used to read files from the specified path. With this example, I am reading a CSV files from a directory. Class declaration class readFilesFromDirectory extends RunBase {     FilePath            filePath;     Filename            filename;     CommaTextIo         fileIO;     Set                 fileSet;     CommaIo             commaIo;     str                 currentFile;     container           lineCon;     #File     #AviFiles } main method Public static void main(Args _args) {     readFilesFromDirectory  readFiles = readFilesFromDirectory::construct();     readFiles.readFilesFromDirtecory(); } Construct Public static readFilesFromDirectory construct() {     return new readFilesFromDirectory(); } readFileFromDirectory method private void readFilesFromDirtecory() {     SetEnumerator           sE;     int                     row = 1;     filePath = @”C:TempFilesToRead”;     this.getFiles();     sE = fileSet.getEnumerator();     while (sE.moveNext()) // loop through all files     {         filename = sE.current();         this.openFile();         while(this.readLine()) // loop through all lines from current file         {             this.processRow(row, lineCon);             row++;         }     } } getFiles method private void getFiles() {     System.String[]                 files;     System.Collections.IEnumerator  enumerator;     str file;     try     {         fileSet = new Set(Types::Container);         files = System.IO.Directory::GetFiles(filePath, ‘*.csv’);         enumerator = files.GetEnumerator();         while (enumerator.MoveNext())         {             file = enumerator.get_Current();             fileSet.add([file]);         }     }     catch (Exception::Internal)     {         this.processCLRErrorException();     }     catch (Exception::CLRError)     {         this.processCLRErrorException();     } } openFile method private void openFile() {     FileIOPermission fileIOPermission;     fileIOPermission = new FileIOPermission(filename, #io_read);     fileIOPermission.assert();     commaIo = new CommaIo(filename, #io_read);     if (commaIo && commaIo.status() == IO_Status::Ok)     {         commaIo.inRecordDelimiter(#delimiterCRLF);     }     else     {         throw error(strFmt(“Error when opening file %1”, filename));     } } readLine method private boolean readLine() {     boolean readLine;     if (commaIo && commaIo.status() == IO_Status::Ok)     {         lineCon = commaIo.read();         readLine = lineCon != conNull();     }     else     {         lineCon = conNull();         readLine = false;     }     return readLine; } processRow method private void processRow(int _rowIdx, container  _row) {     //conpeek(_row, 1);     //conpeek(_row, 2);     //conpeek(_row, 3);     //conpeek(_row, 4);     //conpeek(_row, 5);     //conpeek(_row, 6);     //conpeek(_row, 7);     //conpeek(_row, 8);     //conpeek(_row, 9);     //conpeek(_row, 10); } processCLRErrorException method private void processCLRErrorException(     boolean _throw = true     ) {     str strError;     CLRObject exc;     CLRObject innerExc;     CLRObject clrExcMessage;     exc = CLRInterop::getLastException();     if (exc)     {         clrExcMessage = exc.get_Message();         innerExc = exc.get_InnerException();         while (innerExc != null)         {             clrExcMessage = innerExc.get_Message();             innerExc = innerExc.get_InnerException();         }         strError = CLRInterop::getAnyTypeForObject(clrExcMessage);         error(strError);         if (_throw)         {             throw error(“Update has been cancelled”);         }     } }

AX 2012: Create sales order with delivery address from an XML file

Code snippet to create a sales order from an XML file. private void createSOFromFile() {     AxdSalesOrder   salesOrder;     AifEntityKey    key;         Filename        fileName;     RecId           salesTableRecId;     SalesTable      salesTable;         XmlDocument     xmlDoc;         salesOrder  = new AxdSalesOrder();     fileName    = @”C:TempSalesOrder.XML”;         xmlDoc = XmlDocument::newFile(fileName);         key = salesOrder.create(xmlDoc.xml(),                 new AifEndPointActionPolicyInfo(),                new AifConstraintList());     salesTableRecId = key.parmRecId();         info(strFmt(“Sales order created: %1”, SalesTable::findRecId(salesTableRecId).SalesId)); } Sample XML file <?xml version=”1.0″ encoding=”UTF-8″?> <SalesOrder xmlns:n=”http://schemas.microsoft.com/dynamics/2008/01/documents/SalesOrder”>    <SalesTable class=”entity”>       <CustAccount>C000002</CustAccount>       <InventLocationId>EMU</InventLocationId>       <CustomerRef>Cash on Delivery (COD)</CustomerRef>       <PurchOrderFormNum>4669286216,1021</PurchOrderFormNum>       <ReceiptDateRequested>2011-11-11</ReceiptDateRequested>       <DeliveryName>team03qa@gmail.com</DeliveryName>       <TableDlvAddr class=”entity”>          <City>southbank</City>          <CountryRegionId>AUS</CountryRegionId>          <LocationName>delivery address</LocationName>          <State>VIC</State>          <Street>13075 MANCHESTER RD STE</Street>          <ZipCode>3006</ZipCode>       </TableDlvAddr>       <SalesLine class=”entity”>          <ItemId>CANO</ItemId>          <SalesQty>20</SalesQty>          <SalesPrice>82.25</SalesPrice>          <SalesUnit>ltr</SalesUnit>          <LineAmount>1</LineAmount>          <LineNum>8966003144</LineNum>       </SalesLine>       <SalesLine class=”entity”>          <ItemId>CANO</ItemId>          <SalesPrice>9.9</SalesPrice>          <SalesQty>1</SalesQty>          <SalesUnit>ltr</SalesUnit>          <LineAmount>9.9</LineAmount>          <LineNum>8966003208</LineNum>       </SalesLine>    </SalesTable> </SalesOrder>  How does it work AxdSalesOrder class uses AifEntity classes to create sales order, these classes internally map the datasources of an AOT query AxdSalesOrder with the element tags in XML with attribute class = “entity”. For example; for delivery address, the XML node name is TableDlvAddr which is the datasource name in AxdSalesOrder query. Similarly, if you want to add notes or attach documents with sales order you can add DocuRefHeader node in XML file.

AX 2012: Reading XML Nodes under specific tag through X++

XML to read X++ Code  For above XML structure, I will read all nodes exist within each InstructorAPIModel node. The getElementsByTagName() method of XmlDocument class can be used which returns XmlNodeList which can then be iterated to get node’s values.   XmlDocument doc = new XmlDocument(); XmlNodeList apiModelList; XmlNodeListIterator iterator; XmlElement apiModel; XmlElement firstName; XmlElement lastName; doc.loadXml(_xmlMsg); apiModelList = doc.getElementsByTagName(‘InstructorApiModel’); iterator= new XmlNodeListIterator(apiModelList); while (iterator.moreValues()) { apiModel = iterator.value(); firstName = apiModel.getNamedElement(‘FirstName’); if (firstName) { info(strFmt(“First Name: %1”, firstName.text())); } lastName = apiModel.getNamedElement(‘LastName’); if (lastName) { info(strFmt(“Last Name: %1”, lastName.text())); } iterator.nextValue(); }

AX 2012: Reading XML Nodes through X++

XML to read X++ Code XmlDocument     doc; XmlNodeList     xmlScriptList; XmlNodeList     xmlResponseList; XmlNodeList     xmlTemplateList; XmlElement      nodeScript; XmlElement      nodeResponse; XmlElement      nodeTemplate; XMLParseError   xmlError; str xmlMsg; int i, j;     ;     // Create the XML Document doc = new XmlDocument(); doc.loadXml(xmlMsg); // Verify XML Document Structure xmlError  = doc.parseError(); if(xmlError && xmlError.errorCode() != 0) {     throw error(strFmt(“XML Error: %1”, xmlError.reason())); } // Get the root element and its child nodes nodeScript = doc.getNamedElement(“ns2:return”); xmlScriptList = nodeScript.childNodes(); for(i=0; i < xmlScriptList.length(); i++) {     nodeResponse = xmlScriptList.item(i);     xmlResponseList = nodeResponse.childNodes();     for (j=0; j < xmlResponseList.length(); j++)     {         nodeTemplate = xmlResponseList.item(j);         xmlTemplateList = nodeTemplate.childNodes();         if (nodeTemplate.selectSingleNode(‘id’))         {            info(strFmt(“ID: %1”, nodeTemplate.getNamedElement(“id”).text()));         }                      if (nodeTemplate.selectSingleNode(‘messageTemplateName’))         {            info(strFmt(“messageTemplateName: %1”, nodeTemplate.getNamedElement(“messageTemplateName”).text()));         }     } }

AX 2012: Attach documents or creates notes through X++

Came across a requirement to attach documents and create notes for a sales order after reading files from directories. Documents can be attached to a sales order manually from the following button. Code snippet to create notes or attach documents  DocuRef             docuRef;  DocuActionArchive   docuArchive;  SalesTable          salesTable = SalesTable::find(“SO00001”);  Filename            fileName = @”CTempSalesOrder.docx”;  ttsbegin;  // Code to create notes  docuRef.TypeId = ‘Note’;  docuRef.Name = “Sales order notes”;  docuRef.Notes = “Checked Postcode: 2000, Select resident type”;  docuRef.Restriction = DocuRestriction::External;  docuRef.RefTableId = tableNum(SalesTable);  docuRef.RefRecId = salesTable.RecId;  docuRef.RefCompanyId = curext();  docuRef.insert();  //Code to attach file  docuArchive = new DocuActionArchive();  docuArchive.add(docuRef, fileName);  ttscommit; DocuRef Table has field RefTable and RefRecId which could be referenced by any table in AX and with any record respectively. Do set RefCompanyId field in DocuRef table otherwise records will not create.

D365O – How to add financial dimension in grid

This post outlines the steps; how to add financial dimensions (segmented control) in a grid in D365O. Let’s assume we are adding new table and form for below explanation; New table contains two fields AccountType and LedgerDimension with relation to DimensionAttributeValueCombination table  Form looks like this; Set properties for segmented control under form design; – Auto declaration = Yes – Account type field = AccountType – Controller class = DimensionDynamicAccountController – Filter expression = %1 1. Override modified method for LedgerDimension field under form’s datasource 2. Override lookup and checkUserCustomLookup method on ledger dimension segmented control in form desgin Datasource | D365O_FinancialDimension | LedgerDimension | modified [DataSource]     class D365O_FinancialDimension     {         [DataField]         class LedgerDimension         {             void modified()             {                 super();                 D365O_FinancialDimension_ds.refresh();             }         }     } Desgin | D365O_FinancialDimension_LedgerDimension | lookup         public void lookup()         {             switch (D365O_FinancialDimension.AccountType)             {                 case LedgerJournalACType::Bank:                     BankAccountTable::lookupBankAccount(this);                     break;                 case LedgerJournalACType::Cust:                     CustTable::lookupCustomer(this);                     break;                 case LedgerJournalACType::FixedAssets:                     AssetTable::lookupAccountNum(this);                     break;                 case LedgerJournalACType::Project:                     ProjTable::lookupProjId(this, D365O_FinancialDimension);                     break;                 case LedgerJournalACType::Vend:                     VendTable::lookupVendor(this);                     break;                 default:                     super();                     break;             }         } Desgin | D365O_FinancialDimension_LedgerDimension | checkUserCustomLookup public boolean checkUseCustomLookup(int _accountTypeEnumValue, int _secondaryAccountTypeEnumValue)         {             boolean returnValue;                         LedgerJournalACType accountType = any2Enum(_accountTypeEnumValue);             switch (accountType)             {                 case LedgerJournalACType::Bank:                 case LedgerJournalACType::Cust:                 case LedgerJournalACType::FixedAssets:                 case LedgerJournalACType::Project:                 case LedgerJournalACType::Vend:                     returnValue = true;                     break;                 default:                     returnValue = false;                     break;             }                         return returnValue;         } Set D365O_FinancialDimensions form as startup object and run the form. If you just want to see MainAccount in segmented control. set the segmented control property Is default account = True

D365O – Import CSV data through Data entity

Data entity is a new concept comes with D365O release where each entity related data can be accessed from a single view. A data entity is an abstraction from the physical implementation of database tables. For example, customer related data is stored in different tables in AX which could be customer address (access from  LogisticsPostalAddress), customer name (access from DirPartyTable), and customer electronic address (access from LogisticsElectronicAddress). Complete details are well explained here  https://ax.help.dynamics.com/en/wiki/data-entities/ Let’s create new data entity to import data from a CSV file, this data entity uses only one table to keep it simple. My following posts will show how to import data into through data entities using multiple tables. Create a new table [Optional step] I created a new table for this example. Create new data entity Method 1: Method 2: Add new item for Data entity Provide table as primary datasource Click Next and mark convert labels to field names Data entity will look like this in Visual Studio With both above methods (whatever you choose) it created few artifacts in your project. – data entity – security privileges – staging table Build your project Press Ctrl + Shift + B or Right click on project and Build Synchronize your tables by Right click on project and Sync  Validate staging table properties Set the configuration key to DMF and all other properties should be same as set below. Import data using this data entity System administration | Data management Provide a name, choose data source as CSV, and select entity name from the list. Upload file to import. My sample file has this data 101, Faisal 102, Fareed 103, Mohammad It prompts with fields mapping error Click on View map and connect fields as shown Refresh page to view this screen and click Run project. Follow next few prompts and execute the batch job to import data into the staging table, verify data in the staging table. Import data into the target which will change the transfer status to completed. Open DataImport data from table browser to see data. This data entity creates view in SQL server, you can even play around with the created view in SQL server. Refernces: https://ax.help.dynamics.com/en/wiki/building-and-consuming-data-entities/

AX 2012 : Add/Remove Dynalink through X++

DynaLinks can be removed and added between datasources on form using this code; ClearDynaLinks() will remove all existing dynaLinks [these can be seen by right click on the click > Personalise > Query tab > under dataSource node AddDynalink() method has three parameters; 1. Source table field 2. Destination table 3. Destination table field The following code is implemented in the Click event of a button. void clicked() {     SalesQuotationLine_ds.query().dataSourceNo(1).clearDynalinks();     SalesQuotationLine_ds.query().dataSourceNo(1).addDynalink(fieldNum(SalesQuotationLine, QuotationId),                                                          SAB_DocQuoteGroupRelation,                                                          fieldNum(SAB_DocQuoteGroupRelation,SalesQuotationId));          }

FaisalFareed@2025. All rights reserved

Design by T3chDesigns