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, January 23, 2015
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));
}
}
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;
}
}
/// <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());
}
}
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.
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.
"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.
Subscribe to:
Posts (Atom)
