Friday, January 23, 2015

Add trailing 0's to a number to display to a user. Ex: 12.78 VS 12.7800

So if you have dealt with displaying and transferring numbers that have trailing 0's within AX you will notice that all trailing 0's get dropped when you display the value to the user via code or try to use the variable in any way except tying the value/field directly to a control even though they get stored within the DB with trailing zeros.

For example if you display the following

real test1;

test1 = 2.3450000;

info(strFmt("%1", test1));




The user will be given/displayed a value of 2.345 instead if 2.3450000  most people wouldn't mind this but if you work somewhere that has to display the full length of a value tested then this no longer works.

The following method will take in any real value and apply either a default of 3 decimals or allow you to override that value with anything you specify



 /// <summary>
/// Add trailing zeros to a number if needed and return as string so we can display and pass it to other functions without 0's being droped
/// </summary>
/// <param name="numberToModify">
/// Number to add trailing 0's to
/// </param>
/// <param name="overrideDefaultDecimal">
/// Should we override the default decimal count of 3 and provide our own
/// </param>
/// <param name="decimalOveride">
/// Number of decimals to apply to number (override)
/// </param>
/// <returns>
/// String value with trailing 0's
/// </returns>
static str AddTrailingZeros(FWMDecimalToConvert numberToModify, boolean overrideDefaultDecimal = false,  int decimalOveride = 0)
{
    str formatType = "N";
    System.Double resultTemp;
    str formattedResult;
    int numberOfDecimals = 3;
   
    //check to see if we should override the default decimal precision
    if(overrideDefaultDecimal)
    {
        numberOfDecimals = decimalOveride;
    }
    //convert the rounded value to a double type so we can use outside classes to handle adding trailing zeros
    resultTemp = System.Convert::ToDouble(numberToModify);

    //convert the converted value back to string with trailing zero's
    formattedResult = resultTemp.ToString(formatType + int2str(numberofDecimals));
    return formattedResult;
}



So now if you call the following

real test1, test2;

test1 = 2.3450000;

test2 = 2.1;

info(strFmt("%1 vs %2", test1, ClassName::AddTrailingZeros(test2)));



You will get 2.35 vs 2.100

or you could call info(strFmt("%1 vs %2", test1, ClassName::AddTrailingZeros(test2, true, 7)));

You will get 2.35 vs 2.1000000

and you can then transfer that value anywhere without anything ever being dropped. The downside to this method is your variable(number) is now stored as a string instead of a real. I guess you cant have your cake and eat it to.

This method uses .net formatting functions to accomplish this so you could also change this function to format as percentages, dollars or whatever you like. This is defined via 'formatType = "N"'  You can check out the available format types at https://msdn.microsoft.com/en-us/library/dwhawy9k(v=vs.110).aspx



Friday, December 19, 2014

How to list all of the files in a folder and sub folder via AX

The following examples will show you how to list all of the files in a folder and sub folder.

 static void GetFilesInFoldersAndSubFolders(Args _args)
{
    System.String[] filePaths = System.IO.Directory::GetFiles(@"folder location", "*.*", System.IO.SearchOption::AllDirectories); //get listing of all files within the folder
    int fileCount = filepaths.get_Length(); //get how many files were found
    int currentFileCount;
   
    //go throw each one of the files that were found
    for(currentFileCount = 0; currentFileCount < fileCount ; ++currentFileCount)
    {
        info(filepaths.GetValue(currentFileCount));
    }
}

Send xml to BarTender via TCP sockets and get response back using UTF-8 encoding

This is an example that can be used to send print jobs/print previews (return jpgs/images via raw text) to the BarTender labeling system or using this example as a foundation to talk to your own servers via tcp sockets

 /// <summary>
/// Send print job btxml string to bartender and get a response back
/// </summary>
/// <param name="defaultXML">
/// the BTXML to send to bartender
/// </param>
/// <returns>
///BarTenderStaus if it was successfull or not
/// </returns>
public BarTenderStaus sendPrintJobTCP(str defaultXML)
{
    str                 response;
    XmlTextReader       xmlTextReader;
    boolean             msgFound;
    System.Text.Encoding encoding;
    str                 errorId;
    boolean             errorsFound;
    str                 message;
    SysOperationProgress previewProgress;
    System.Net.Sockets.TcpClient barTenderClient;
    System.Net.Sockets.NetworkStream stream;
    System.IO.StreamReader reader;
    System.Byte[] send_bytes;
    #avifiles
    try
    {
        //setup progress bar so we can let the user know what we are doing
        previewProgress = SysOperationProgress::newGeneral(#AviPublish2Web, 'Sending Print Job To BarTender', 7);

        if(serverSettings.BarTenderServerName == "" || serverSettings.BarTenderServerPortNumber == 0)
        {
            throw error("No valid BarTender server has been defined. Please contact IT.");
        }
        //update progress bar
        previewProgress.incCount();
        previewProgress.setText("Task: Reading XML...");

        message = defaultXML;
        //check to see if we have something available to send to the server
        if(message == "")
        {
            throw error("No items are available to print");
        }

        //update progress bar
        previewProgress.incCount();
        previewProgress.setText("Task: Connecting to BarTender Server...");
        //make connection to bartender and open stream with UTF8 encoding
        barTenderClient = new System.Net.Sockets.TcpClient(serverSettings.BarTenderServerName, serverSettings.BarTenderServerPortNumber);
        stream = barTenderClient.GetStream();
        reader = new System.IO.StreamReader(stream, System.Text.Encoding::get_UTF8());

        //update progress bar
        previewProgress.incCount();
        previewProgress.setText("Task: Sending Label to BarTender...");
        //define the stream encoding as UTF8
        encoding = System.Text.Encoding::get_UTF8();
        //convert the xml to bytes to send to bartender
        send_bytes = encoding.GetBytes(message);
        //send the xml bytes to bartender
        stream.Write(send_bytes, 0, send_bytes.get_Length());
        //update progress bar
        previewProgress.incCount();
        previewProgress.setText("Task: Reading Response From BarTender...");
        //get the response bartender sends back
        response = reader.ReadToEnd();
        //close the bartender stream
        stream.Close();
        //update progress bar
        previewProgress.incCount();
        previewProgress.setText("Task: Analyzing XML Response from BarTender...");
        //we need to parse the xml response string into xml nodes
        xmlTextReader = XmlTextReader::newXml(response, true);
        //loop through all of the xml nodes
        while(xmlTextReader.read())
        {
            //check to see what type of xml node we are currently on
            switch (xmlTextReader.NodeType())
                {
                    case XmlNodeType::Element:
                        //check to see if we are on the message node
                        if(xmlTextReader.Name() == "Message")
                        {
                            //read the message attributes
                            while (xmlTextReader.MoveToNextAttribute())
                            {
                                //check to see if the attributes node is severity
                                if(xmlTextReader.Name() == "Severity")
                                {
                                    //check to see if we are looking at an error
                                    if(xmlTextReader.Value() == "Error")
                                    {
                                        //error has been found
                                        msgFound = true;
                                        errorsFound = true;
                                    }
                                }
                                else if(xmlTextReader.Name() == "Id")
                                {
                                    //define what the id attribute is incase it is an error
                                    errorId = xmlTextReader.Value();
                                }
                            }
                        }
                        break;
                    case XmlNodeType::Text:
                        //check to see if the last node we were on was a message and if it was an error
                        if(msgFound)
                        {
                             //display error message to user in a friendly manner
                             setPrefix("BarTender Error " + errorId);
                             error(xmlTextReader.Value());
                             //reset message found flag
                             msgFound = false;
                        }
                        break;
                    case XmlNodeType::EndElement: //Display the end of the element.
                        break;
                    default:
                        break;
                }
        }

        //update progress bar
        previewProgress.setText("Task: Finished");
        previewProgress.setTotal(7);
        previewProgress.hide();
        //check to see if any errors occured
        if(!errorsFound)
        {
            return BarTenderStaus::Success;
        }
        else
        {
            return BarTenderStaus::Failure;
        }
    }
    catch
    {
        error("There was an unknown error communicating with the BarTender server. Please restart your computer and try again. If the issue continues please alert IT of this issue.");
        //update progress bar
        previewProgress.setText("Task: Finished");
        previewProgress.setTotal(7);
        previewProgress.hide();
        return BarTenderStaus::Failure;
    }
}

Thursday, October 23, 2014

C# Accessing AX Session Information & Defining which AOS to connect to via code

So while working on the last post I ran into an issue where I couldn't change which aos the data was being pulled in from so I wrote the following function that gets server information so you can see basic information on what connection is being used. Along with defining which AOS the code pulls from by defining a server within code so I can switch back and forth between DEV & TEST

You can download the C# visual studio project here: download now!


Its good to note that around the web the following "logon" statement is posted

// Logon to Dynamics AX
Session axSession = new Session();
axSession.Logon(null, null, null, null);

However in order to define which AOS server to connect to via code we need to do the following

// Logon to Dynamics AX
Session axSession = new Session();
axSession.Logon(null, null, "AOSServerName", null);




The following code are just some ways to pull in basic server information


private void GetAOSInfo_Click(object sender, EventArgs e)
{
 
try
{
 
// Logon to Dynamics AX
Session axSession = new Session();
axSession.Logon(null, null, axServerName, null);


 
//get server information

string axInfoAOSInstance = axSession.CreateObject("XSession").Call("AOSName").ToString();
string axInfoClientName = axSession.CreateObject("XSession").Call("clientComputerName").ToString();
string axInfoLoginDateTime = axSession.CreateObject("XSession").Call("loginDateTime").ToString();
string axInfoMasterSessionId = axSession.CreateObject("XSession").Call("sessionId").ToString();
string axInfoUserId = axSession.CreateObject("XSession").Call("userId").ToString();



CustomerOutput.Clear();
 
CustomerOutput.AppendText(("AOS Server: " + axInfoAOSInstance));
CustomerOutput.AppendText((Environment.NewLine + "Client Computer Name: " + axInfoClientName));
CustomerOutput.AppendText((Environment.NewLine + "Session Id: " + axInfoMasterSessionId));
CustomerOutput.AppendText((Environment.NewLine + "Login DateTime: " + axInfoLoginDateTime));
CustomerOutput.AppendText((Environment.NewLine + "User Id: " + axInfoUserId));



 
 
//log off ax
axSession.Logoff();

}
 
catch (Exception ex)
{

CustomerOutput.Clear();

CustomerOutput.AppendText(ex.Message.ToString());

}

}
 

Accessing AX CustTable & DirPatyTable (Customer Info) via C# via Linq.

In order to support legacy applications sometimes you need to pull in data from AX into standalone applications. Below will show you how to pull in customer id's + names (CustTable + DirPartyTable)
The following examples will show you how to manually loop through that data or tie it to a combo box via Linq.

You can download the C# Visual Studio Project Here: download now!

Step 1. Start new project within visual studio that has access to an aos via the application explorer
Step 2. Adding the following references to the visual studio project

C:\Program Files (x86)\Microsoft Dynamics AX\6.0\Client\Bin\

Microsoft.Dynamics.AX.Framework.Linq.Data.dll
Microsoft.Dynamics.AX.Framework.Linq.Data.Interface.dll
Microsoft.Dynamics.AX.Framework.Linq.Data.ManagedInteropLayer.dll
Microsoft.Dynamics.AX.ManagedInterop.dll


Then on your form/class add the following using classes

using Microsoft.Dynamics.AX.ManagedInterop;
using Microsoft.Dynamics.AX.Framework.Linq.Data;
using System.Linq;

Step 3. Right click on project and add to AOT
Step 4. After step 3 you can start to add objects from the application explorer into the project. Add this time we should add the table CustTable & DirPartyTable

The following code block will show you how to connect to ax, query the tables and loop through the data or tie it to multiple combo boxes. Its good to note that my name space for this project was Win2AX.

       


 
 


public void loadCustomers(Boolean listInTextBox)
{
           

try
{
               

isCustomersLoaded = false;

// Logon to Dynamics AX (usering windows logon info)
Session axSession = new Session();
axSession.Logon(null, null, axServerName, null);

// Create a query provider needed by the Linq Provider
QueryProvider provider = new AXQueryProvider(null);

//Connect to the table proxy's for the CustTable and DirPartTable tables within AX.
QueryCollection<Win2AX.CustTable> customerCollection = new QueryCollection<Win2AX.CustTable>(provider);

QueryCollection<Win2AX.DirPartyTable> partyCollection = new QueryCollection<Win2AX.DirPartyTable>(provider);



 
//select the account num and dirparty name so we can provide an account num + name

//its good to note that when I didnt include a field list the app crashed. I think linq is limited to the amount of data that can be processed

var allCustomers = (from customer in customerCollection
join partyDesc in partyCollection on customer.Party equals partyDesc.RecId
orderby customer.AccountNum ascending
select new { customer.AccountNum, partyDesc.Name, Description = customer.AccountNum + " - " + partyDesc.Name }).ToList();

allCustomers.Insert(0, new { AccountNum = "<Select Customer>", Name = "<Select Customer>", Description = "<Select Customer>" });

if (listInTextBox)
{

CustomerOutput.Clear();
//go through all of the records

foreach (var custTable in allCustomers)
{
//output the current record
CustomerOutput.AppendText(Environment.NewLine + "Customer Account: " + custTable.AccountNum + " Description: " + custTable.Name);
}

}
else if (!listInTextBox)
{
//bind all combo boxes to our linq list

//combination (Customer Id - Name)
CustomerList.DataSource = allCustomers;
CustomerList.DisplayMember = "Description";
CustomerList.ValueMember = "AccountNum";

//Customer Id only
CustomerIdSelection.DataSource = allCustomers;
CustomerIdSelection.DisplayMember = "AccountNum";
CustomerIdSelection.ValueMember = "AccountNum";

//Name Selection / Account Num Value
CustomerNameSelection.DataSource = allCustomers;
CustomerNameSelection.DisplayMember = "Name";
CustomerNameSelection.ValueMember = "AccountNum";

//signal that all combo boxes are loaded so we can handle comboboxes selected index changes correctly

isCustomersLoaded = true;
}

 
//log off ax
axSession.Logoff();

}
catch (Exception ex)
{

CustomerOutput.Clear();
CustomerOutput.AppendText(ex.Message.ToString());

}

Friday, October 10, 2014

Failed to create a session; confirm that the user has the proper privileges to log on to Microsoft Dynamics

Currently we have a developer working/coding in an active environment where users are currently using the system (non-prod) and had an issue generating good incremental CIL. During this time some of the users started to see the following message

"Failed to create a session; confirm that the user has the proper privileges to log on to Microsoft Dynamics"

In order to resolve this issue try the following

File/Tools/Options/Development/ uncheck the option for "Execute business operations in CIL"

This ended up fixing our issue, but after reading multiple other blogs about AX if this doesn't work then the next step is to run a full CIL. If that doesn't work you should restart the AOS and run the full CIL again.