Quantcast
Channel: Dynamics Ax – Shashi's Ax.Net Space
Viewing all 56 articles
Browse latest View live

Adding Address to Vendor / Customer / Contact info [AX2012]

0
0

The msdn article How To: Update a Postal Address for a Contact Person [AX 2012] talks about adding a contact info, and an address associated with this.

On further reading of the Implementing the Global Address Book Framework (White paper) [AX 2012] I found that in a similar way you can use the CustomerEntity or VendorEntity to create addresses and electronic address.

However, to create an electronic address inside the postal address (LogisticsPostalAddress) you will have to use the LogisticsElectronicAddressEntity class to create the electronic address, after you modify the static method createElectronicAddress to accept parent location. (If you create the postal address as per the document, you will have the parent location with you)


Filed under: Ax 2012, Dynamics Ax, X++ Tagged: AX2012, Dynamics Ax

[Ax 2012] Moving model from one layer to another (Step by Step guide)

0
0

Ax 2012 allows moving code within the same layer. MSDN has a topic Maintaining Installation-Specific Element IDs and Element Handles [AX 2012] which does describe the issue in a short description.

With Ax 2009 this is not a big issue as code can be exported as XPO’s with ID’s and moved into another layer. Ax 2012 creates ID’s which are installation specific, and doesn’t let importing or exporting XPO’s with Id.

This is my take on a step by step procedure to achieve this.

Step 1. Create a model and move all the objects that you want to move out of the layer.

Step 2. Create a project of this model and export the project as an xpo.

Step 3. Backup the database and delete the old model created in step 1

Step 4. Synchronize the database from the AOT. This will delete the data but we will retrieve it later.

Step 5. Import the xpo from step 2

Step 6. Synchronize the database.

Step 7. Export this new model.

Step 8. Restore the database that was backed up from step 3

Step 9. Import the new model (step 7), Synchronize and compile
This restores the I’d values of the objects and tables / fields. So no data will be lost

Step 10. Delete old model, compile / synchronize.

This should successfully move the code into the new layer and you won’t loose any data.


Filed under: Ax 2012, Dynamics Ax Tagged: ax 2012, Dynamics Ax

Dynamics Ax Debug logging (File based)

0
0

The need for logging in Ax for me came from the .Net world of using Debug.Writeline , and then looking up the debug values from sysInternals dbgview.
However, to make a very quick and just basic configurable logging, i went to write the logs into a file.
This is a quick and dirty way to add logging capabilities into Dynamics ax.

The project can be found at Codeplex: http://axug.codeplex.com/SourceControl/changeset/view/82084#1932052
To go on with the current logging, following is the code signature of the method to be called for passing the logging message

public static void LogMessage(str _processId, str _text = '', int _step = 0)

and logging can be called by

Logging::LogMessage("Testing Logging","Logging is great :)", 0);

The Macro values define if the logging capability is turned on, and also the filename (So it can be configured from the usr layer / production site)

I have tested this with Ax 2012 and it has worked pretty well so far, and i do not think Ax2009 should have an issue with it. This logging is so far restricted to file based logging, but I am looking at incorporating other forms like Debug.WriteLine, and will further look into incorporating log4Net (however i have a strong suspicion that this will be limited to server side, and only for Ax 2012).

Hope this brings enough joy to you out there.
Happy Logging

NOTE: before you run the test class in the project, please make sure to edit the Macro to specify the folder path (This can be set to a local directory, but please be advised, this method is “called from”)

UPDATE:

I have now updated the code to include 2 more logging features. Along with that made changes to the method to include the type of message being passes (i.e. warning, information or error. We shall see why)

Change set: http://axug.codeplex.com/SourceControl/changeset/82210

1. Windows Event logging. – This enables you to you write into the Windows Event log directly. The log type (info , error, warning) will correlate directly to the log icons in the event viewer :)

2. Debug log: The most simplistic logging with .Net’s System.Diagnostics.Debug.WriteLine is now included. Although i have seen that this also writes to the event log :( (as information only, probably something that is handled from x++ itself, so may not be a good idea to have it in a production server where it manages to clutter the event viewer)


Filed under: Ax 2009, Ax 2012, Dynamics Ax, X++ Tagged: AX2012, Dynamics Ax, x++

Walkthrough: Calling the Query Service with a Dynamic Query [AX 2012]

0
0

Ax2012 exposes the query service, and makes the queries available directly by a developer. these queries are static, Dynamic or User defined. Query Service [AX 2012]

Static queries are the simplest ones, all you need to do is to pass the query name, and you get the dataset in return. (Walkthrough: Calling the Query Service with a Static Query [AX 2012]) Works like a charm, except when you may want to filter on it (Lets say we want all items that are BOM types). You could filter the dataset in c# or your language of choice, but what if there are over 2000 items ?

Thats where Dynamic queries come in. There is a bit more work involved in it, but its not too much.

Firstly we need to create 2 classes. One which holds the query, and we add our ranges to it, and the second class which is a DataContract / Argument class which contains the parameters we define for the filtering. (or any special process for that matter)

In this example we will look into using the Query InventTable

1. Create the DataContract / Arguments class

We will create a class called MyInventTableQueryBuilderArgs which extends AifQueryBuilderArgs Class, and will be decorated with the DataContractAttribute, and the parmMethods with the DataMemberAttribute. This tehn exposes this class to the QueryService

[DataContractAttribute]
public class MyInventTableQueryBuilderArgs extends AifQueryBuilderArgs
{
ItemType itemType;
}

ItemType is what we will use as a filter. In order for it to be available in the QueryService, is to create a parm method
[DataMemberAttribute]
public ItemType parmItemType(ItemType _itemType = itemType)
{
itemType = _itemType;
return itemType;
}

2. Create the QueryBuilder class
Create a class called MyInventTableQueryBuilder which extends the AifQueryBuilder class. Add the MyInventTableQueryBuilderArgs as a property of the class, and use the setArgs to populate this object.
public class MyInventTableQueryBuilder extends AifQueryBuilder
{
MyInventTableQueryBuilderArgs args;
}

//Used internally to access the args object
private MyInventTableQueryBuilderArgs getArgs()
{
return args;
}

This class also requires the initialize method to be overridden, and this is where we will populate the query ranges using the args object

public void setArgs(AifQueryBuilderArgs _args)
{
if(!_args
|| classIdGet(_args) != classNum(MyInventTableQueryBuilderArgs))
{
throw error("@SYS95118");
}
args = _args;
}

[SysEntryPointAttribute]
public void initialize()
{
QueryBuildDataSource qbds;
query = new Query(queryStr(InventTable));

qbds = query.dataSourceTable(tableNum(InventTable));

SysQuery::findOrCreateRange(qbds, fieldNum(InventTable, ItemType)).value(SysQuery::value(this.getArgs().parmItemType()));

queryRun = new QueryRun(query);
}
Compile the 2 classes and then run the incremental compile

Now for the better half – Visual studio

Create a project of your choice (I am using a Console application for simplicity), and in the language of your choice (I am choosing c#)
Add a Web service reference (Right click reference and choose Add Service reference)
In the URL enter the following URL (replace with the server where Ax is installed. The port 8101 can be replaced based on the installation port if different
http://:8101/DynamicsAx/Services/QueryService
also in the same screen (after clicking GO), enter the namespace as MyQueryService

In the Main Method of the Program class, write the following code:

static void Main(string[] args)
{
int records = 0;
using (MyQueryService.QueryServiceClient client = new MyQueryService.QueryServiceClient())
{
MyQueryService.Paging paging = null;

MyQueryService.MyInventTableQueryBuilderArgs queryArgs = new MyQueryService.MyInventTableQueryBuilderArgs();
queryArgs.parmItemType = MyQueryService.ItemType.Item;

DataSet dataset = client.ExecuteDynamicQuery(“MyInventTableQueryBuilder”, queryArgs, ref paging);
if (dataset.Tables.Contains(“InventTable”))
{
DataTable datatable = dataset.Tables["InventTable"];
foreach (DataRow row in datatable.Rows)
{
records++;
string itemId = (string)row["ItemId"];
Byte itemType = (Byte)row["ItemType"];
string dataAreaId = (string)row["DataAreaId"];
Console.WriteLine(“Item: {0}, Type: {1}, Company: {2}”, itemId, itemType, dataAreaId);
}
}
}
Console.WriteLine(“Records: {0}”, records);
}

The code should now be good to run. For my installation and data, i get the following output:

Item: Office-Chair, Type: 0, Company: ceu
Item: Pack-Ribbon, Type: 0, Company: ceu
Item: SCPS_CSCL, Type: 0, Company: ceu
Item: SCPS_CSCR, Type: 0, Company: ceu
Item: SCPS_PSCL, Type: 0, Company: ceu
Item: SCPS_PSCR, Type: 0, Company: ceu
Item: SCPS_UPSCL, Type: 0, Company: ceu
Item: SCPS_UPSCR, Type: 0, Company: ceu
Item: WEE B1190, Type: 0, Company: ceu
Item: WMFlat32, Type: 0, Company: ceu
Item: WMFlat45, Type: 0, Company: ceu
Item: Work-Shirt, Type: 0, Company: ceu
Records: 298
Press any key to continue . . .

NOTE: If you do get an error regarding the message size being too big, then edit the app.config to make the
maxReceivedMessageSize="9999999" in the netTcpBinding element


Filed under: Ax 2012, Dynamics Ax, X++ Tagged: AX2012, Dynamics Ax, x++

X++ date addition issue

0
0

I came across this issue with adding dates to negative integers. Basically the issue is that you cant add a negative value to a date variable (but you can subtract a positive integer).
i.e. if x = -5, and date1 is 01/01/2012,
and you do date1 = date1 + x,
this results in a runtime error which says “Error executing code”. This error is not even caught by the catch block.

Following is a job i wrote to prove this.


static void DateOperatorIssue(Args _args)
{
    Date date1,date2,date3;
    int dateDiff;
    ;
    try
    {
        date1 = mkdate(01,01,2013);
        date2 = mkdate(10,01,2013);
        date3 = mkDate(15,01,2013);
        dateDiff = date3 - date2;
        info(strfmt("Date diff is: %1", dateDiff)); //dateDiff = 5
        date1 += dateDiff;                          //This works (You can only add positive integers, or subtract positive integers)
        info(strfmt("New date is: %1", date1));
        dateDiff = date2 - date3;
        info(strfmt("Date diff is: %1", dateDiff)); //dateDiff = -5
        date1 += dateDiff;                          //Runtime error: Error executing code: Wrong type of argument for conversion function.
        info(strfmt("New date is: %1", date1));
    }
    catch(Exception::Error)
    {
        error('ooooops');                           //This is never reached
}
}


Filed under: Ax 2009, X++ Tagged: Ax2009, runtime error, x++

Dynamics Ax Logging – Log4net

0
0

Earlier I had posted about how to achieve File based logging in Ax, however after working with log4net on another project with .net I decided to implement debugging with Ax2012. This is because popping messages in the info log isint a very refined, besides at a customers site, after turning a few configuration keys on, the logging messages can be traced without writing extra code.

Log4Net: This is a very versatile library, if you look at the configuration files or Google around this, you will notice that it supports a wide variety of logging facilities and filters that can be applied. The overhead of using this library isint much.

The advantage of using is that the logging messages can be left inside the code (and you can even mark their category, and it can be configured in a certain way so that the debugging mechanism can be switched turned on or off for that category.

Implementing log4net in AX2012:

You could follow one of these posts and run an independent c# console project and test the log4net framework like in this blog. I wont be going through that part. What i will be showing is how to implement a library once created and added to AX2012, as AX2012 has its own configuration file which will need to be amended.

The C# project:

Create a C# project, add it to the AOT and add the log4net.dll to the c# project. Note that if you use Nuget there are issues adding it into Ax as the dll needs to be referenced with Ax too. So NuGet will not exactly help.

The code is something like this for me:

 

namespace SHSAx.Logger
{
    public enum LogType
    {
        Debug,
        Info,
        Warn,
        Error,
        Fatal
    }

    public class AxLogging
    {
        private static ICollection configure = XmlConfigurator.Configure();
        private static readonly ILog log = LogManager.GetLogger("DynamicsAx_Logging");

        private readonly ILog localILog;
        //private ILog log = null;

        //Reloads config file so that any change made to the log4net section doesnt require an AOS restart
        private static void ReloadConfiguration()
        {
            ConfigurationManager.RefreshSection("log4net");
            configure = XmlConfigurator.Configure();
        }

        public AxLogging()
        {
            ReloadConfiguration();
            localILog = log;
            //log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
        }

        public AxLogging(string logName)
        {
            ReloadConfiguration();
            localILog = LogManager.GetLogger(logName);
        }

        //Logging for custom log Name
        public void WriteO(LogType logType, string message)
        {
            this.WriteO(logType, message, null);
        }
        public void WriteO(LogType logType, string message, Exception ex)
        {
            switch (logType)
            {
                case LogType.Debug:
                    localILog.Debug(message, ex);
                    break;
                case LogType.Info:
                    localILog.Info(message, ex);
                    break;
                case LogType.Warn:
                    localILog.Warn(message, ex);
                    break;
                case LogType.Error:
                    localILog.Error(message, ex);
                    break;
                case LogType.Fatal:
                    localILog.Fatal(message, ex);
                    break;
                default:
                    break;
            }
        }

        //Static logging - using base log name i.e. DynamicsAx_Logging
        public static void Write(LogType logType, string message)
        {
            Write(logType, message, null);
        }
        public static void Write(LogType logType, string message, Exception ex)
        {
            switch (logType)
            {
                case LogType.Debug:
                    log.Debug(message, ex);
                    break;
                case LogType.Info:
                    log.Info(message, ex);
                    break;
                case LogType.Warn:
                    log.Warn(message, ex);
                    break;
                case LogType.Error:
                    log.Error(message, ex);
                    break;
                case LogType.Fatal:
                    log.Fatal(message, ex);
                    break;
                default:
                    break;
            }
        }
    }
}

Create a config file called log4net.config (and this can be duplicated across to the AOS Server – More later)
Add the log4Net assembly to AX references. This is because the log4net assembly doesn’t get uploaded to the server directory, this double up is required as the log4net dll may not be stored in the csharp project in the AOT. Once this is done, then we are ready to call it from X++ or from other C# projects.

<?xml version="1.0" encoding="utf-8" ?>
  <log4net>
	<appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">
		<param name="File" value="C:\Temp\AxLogs\MyFirstLoggerAx000001.log"/>
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
		<appendToFile value="true" />
		<rollingStyle value="Size" />
		<maxSizeRollBackups value="2" />
		<maximumFileSize value="1MB" />
		<staticLogFileName value="true" />
		<layout type="log4net.Layout.PatternLayout">
			<param name="ConversionPattern" value="%d [%t] %-5p %c %m%n"/>
		</layout>
	</appender>
    <appender name="LogFileAppender2" type="log4net.Appender.RollingFileAppender">
		<param name="File" value="C:\Temp\AxLogs\MyFirstLoggerAx000002.log"/>
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
		<appendToFile value="true" />
		<rollingStyle value="Size" />
		<maxSizeRollBackups value="2" />
		<maximumFileSize value="1MB" />
		<staticLogFileName value="true" />
		<layout type="log4net.Layout.PatternLayout">
			<param name="ConversionPattern" value="%d [%t] %-5p %c %m%n"/>
		</layout>
	</appender>
	<appender name="LogFileAppenderPickList" type="log4net.Appender.RollingFileAppender">
		<param name="File" value="C:\Temp\AxLogs\MyFirstLoggerAx__PickList.log"/>
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
		<appendToFile value="true" />
		<rollingStyle value="Size" />
		<maxSizeRollBackups value="2" />
		<maximumFileSize value="1MB" />
		<staticLogFileName value="true" />
		<layout type="log4net.Layout.PatternLayout">
			<param name="ConversionPattern" value="%d [%t] %-5p %c %m%n"/>
		</layout>
	</appender>
	<appender name="TraceAppender" type="log4net.Appender.TraceAppender">
		<layout type="log4net.Layout.PatternLayout">
			<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
		</layout>
	</appender>

	<root>
		<level value="ALL" />
		<appender-ref ref="TraceAppender" />
		<appender-ref ref="LogFileAppender" />
	</root>

	<logger name="ShashiTest">
		<level value="NONE" />
		<appender-ref ref="LogFileAppender2" />
	</logger>
	<logger name="ThisIsMyLog">
	    <level value="Fatal" />
		<appender-ref ref="LogFileAppender2" />
	</logger>
	<logger name="Picking_list_posting">
	    <level value="WARN" />
		<appender-ref ref="LogFileAppenderPickList" />
	</logger>
</log4net>

The app.config doesnt need to be appended here, but adding it here would document it upto some level on where / what to edit in the Main config file

<?xml version="1.0"?>
<configuration>
	<configSections>
		<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/> <!--This is required for log4net to work-->
	</configSections>
	<log4net configsource="log4net.config"/> <!--picks up the config from log4net.config we created earlier-->
	<startup>
		<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
	</startup>
</configuration>

Now when you were creating the c# project you would have realized that you added parts to the configsections tag in the app.config and also added the log4net tags. These settings need to be transferred into the Ax config file. So in the applications server directory you will see a file ax32serv.exe.config , this is the file where you need to add the values too (: this is the same file where AIF services are registered)
Make the changes to the ax32serv.exe.config file so that we can start programming.

Log4Net’ing from X++
I create a Class called Class1 (a very original name)

public static server void writeLog()
{
    //This uses the default log name - which is hardcoded in my project
    System.Exception exc;
    SHSAx.Logger.AxLogging logging;

    SHSAx.Logger.AxLogging::Write(SHSAx.Logger.LogType::Info, "this is an info");
    SHSAx.Logger.AxLogging::Write(SHSAx.Logger.LogType::Debug, "this is a debug");
    SHSAx.Logger.AxLogging::Write(SHSAx.Logger.LogType::Error, "this is an Error");
    SHSAx.Logger.AxLogging::Write(SHSAx.Logger.LogType::Fatal, "this is an Fatal issue");
    SHSAx.Logger.AxLogging::Write(SHSAx.Logger.LogType::Warn, "this is a Warning");
}
public static server void WriteLog1()
{
    //This uses the the lognames specified dynamically (See constructor)
    SHSAx.Logger.AxLogging logging;

    logging = new SHSAx.Logger.AxLogging("Picking_list_posting");
    logging.WriteO(SHSAx.Logger.LogType::Info, "1. Started process");
    logging.WriteO(SHSAx.Logger.LogType::Info, "2. Finding inventory");
    logging.WriteO(SHSAx.Logger.LogType::Info, "3. Check inventory");
    logging.WriteO(SHSAx.Logger.LogType::Warn, "3. Batch no. not specified");
    logging.WriteO(SHSAx.Logger.LogType::Info, "3. Allocating batch BT00001");
    logging.WriteO(SHSAx.Logger.LogType::Info, "4. Posting oprder");

    logging = new SHSAx.Logger.AxLogging("ThisIsMyLog");
    logging.WriteO(SHSAx.Logger.LogType::Debug, "THIS IS MINE...!!!!!! DEBUG");
    logging.WriteO(SHSAx.Logger.LogType::Error, "THIS IS MINE...!!!!!! ERROR");
    logging.WriteO(SHSAx.Logger.LogType::Fatal, "THIS IS MINE...!!!!!! FATAL");
    logging.WriteO(SHSAx.Logger.LogType::Info, "THIS IS MINE...!!!!!! INFO");
    logging.WriteO(SHSAx.Logger.LogType::Warn, "THIS IS MINE...!!!!!! WARN");

    logging = new SHSAx.Logger.AxLogging("ShashiTest");
    logging.WriteO(SHSAx.Logger.LogType::Debug, "My Test - Debug");
    logging.WriteO(SHSAx.Logger.LogType::Error, "My Test - Error");
    logging.WriteO(SHSAx.Logger.LogType::Fatal, "My Test - Fatal");
    logging.WriteO(SHSAx.Logger.LogType::Info, "My Test - Info");
    logging.WriteO(SHSAx.Logger.LogType::Warn, "My Test - Warn");

}

I called these methods from a job to show the results. Note that this is a Server side solution. If you want this to be a client side then you may have to deploy the DLL to every client PC. For me calling a method on the server works well and all logs can be written on the server side.
You could dynamically create lognames based on the user, and then create an appender to capture only those messages.

Once done restart the AOS service and run the job.
MyFirstLoggerAx000001.log

2013-06-04 09:05:23,655 [71] INFO  DynamicsAx_Logging this is an info
2013-06-04 09:05:23,655 [71] DEBUG DynamicsAx_Logging this is a debug
2013-06-04 09:05:23,655 [71] ERROR DynamicsAx_Logging this is an Error
2013-06-04 09:05:23,655 [71] FATAL DynamicsAx_Logging this is an Fatal issue
2013-06-04 09:05:23,655 [71] WARN  DynamicsAx_Logging this is a Warning
2013-06-04 09:05:23,686 [71] WARN  Picking_list_posting 3. Batch no. not specified
2013-06-04 09:05:23,702 [71] FATAL ThisIsMyLog THIS IS MINE...!!!!!! FATAL
2013-06-04 09:05:23,718 [71] DEBUG ShashiTest My Test - Debug
2013-06-04 09:05:23,733 [71] ERROR ShashiTest My Test - Error
2013-06-04 09:05:23,733 [71] FATAL ShashiTest My Test - Fatal
2013-06-04 09:05:23,733 [71] INFO  ShashiTest My Test - Info
2013-06-04 09:05:23,733 [71] WARN  ShashiTest My Test - Warn

MyFirstLoggerAx000002.log

2013-06-04 09:05:23,702 [71] FATAL ThisIsMyLog THIS IS MINE...!!!!!! FATAL
2013-06-04 09:05:23,718 [71] DEBUG ShashiTest My Test - Debug
2013-06-04 09:05:23,733 [71] ERROR ShashiTest My Test - Error
2013-06-04 09:05:23,733 [71] FATAL ShashiTest My Test - Fatal
2013-06-04 09:05:23,733 [71] INFO  ShashiTest My Test - Info
2013-06-04 09:05:23,733 [71] WARN  ShashiTest My Test - Warn

MyFirstLoggerAx__PickList.log

2013-06-04 09:05:23,686 [71] WARN  Picking_list_posting 3. Batch no. not specified

Log result:

NOTE: to pick up the new C# project in X++ you may have to restart the AOS service. Also because we are reloading the configuration every time we call the logging class, there will be overheads. It will nice if the config file doesnt need to be changed.

Hopefully this can be turned into a more streamlined approach and we can get a common class / interface that we all can use to do some logging.


Filed under: .Net, Ax 2012, Dynamics Ax, Visual studio, X++ Tagged: .Net, AX2012, Integration, Logging, x++

AX2012 Adding same field to a lower layer

0
0

Recently we ended up adding a field to one application, in the USR layer and then adding it back to the ISV layer in another application.
The question was if we merged the ISV model into the 1st application, would the data still exist in the 1st application? Or would we have to create database scripts to restore this.

So off I went and created this flow which shows how I went about with my testing as per below.

Result: Success, There was no data loss. The fieldId stayed the same for the LIVE system in both scenarios.

Ax Table Field test


Filed under: Ax 2012, Dynamics Ax Tagged: AX2012, deployment, FieldId

AX2012 What models have been updated ?

0
0

While working with Ax 2012 and deploying models, comes a challenge of sending models out.
Probably a challenge only when there tends to be multiple models in the same layer and not all need to be sent out.

Version control knows what I changed

One of the easier ways is to look at Version control and check what has been updated.
That sounds easy? Well what if objects were never checked into version control?
What if you don’t have TFS as a version control (set up on a per model basis)

Model store to the rescue

We know what code has changed -> The Model store holds the data of all the objects in the AOT.
We know when it was changed -> It also holds the Modified date time (And it also holds ModifiedBy, so you can go pursuing the person you are after)
We know what model / layer it is in -> The model jungle holds that data

Ok, How do I get this data now? -> SQL statements.
I have 2 statements which gives the elements that have changed since a certain date, and the other one which groups it all up to say what models have been updated as a high level statement.

SQL Variables (this is where you set the parameters)

DECLARE @DATEMODIFIED nvarchar(100)
DECLARE @ELEMENTTYPESUNWANTED as TABLE (Id int not null)

---Set values here
--Date since when objects have been modified
SET @DATEMODIFIED = '2013-01-24'
--Unwanted Element types
INSERT INTO @ELEMENTTYPESUNWANTED (Id)
values (23), (24) --23: ClassHeader, 24: TableHeader

What elements have been updated?

--List Of elements that have changed--
select L.Name, SYSMM.DISPLAYNAME as 'Model', MED.ModelId,MED.MODIFIEDDATETIME, MED.CREATEDDATETIME, MED.MODIFIEDBY, ME.Name, ET.ElementTypeName, ME.ElementType
from ModelElement ME
join ModelElementData MED on MED.ElementHandle = ME.ElementHandle
join ElementTypes ET on ET.ElementType = ME.ElementType
join SYSMODELMANIFEST SYSMM on SYSMM.MODEL = MED.ModelId
join Layer L on L.Id = MED.LayerId
where MED.MODIFIEDDATETIME >= CONVERT(dateTime, @DATEMODIFIED)
and ME.ElementType not in (select Id from @ELEMENTTYPESUNWANTED)
order by MED.MODIFIEDDATETIME desc

What Models have been updated?

--List of Models That has been updated--
select L.Name, SYSMM.DISPLAYNAME, MED.ModelId
from ModelElement ME
join ModelElementData MED on MED.ElementHandle = ME.ElementHandle
join ElementTypes ET on ET.ElementType = ME.ElementType
join SYSMODELMANIFEST SYSMM on SYSMM.MODEL = MED.ModelId
join Layer L on L.Id = MED.LayerId
where MED.MODIFIEDDATETIME >= CONVERT(dateTime, @DATEMODIFIED)
and ME.ElementType not in (select Id from @ELEMENTTYPESUNWANTED)
group by SYSMM.DISPLAYNAME, MED.ModelId, L.Name

Filed under: Ax 2012, Dynamics Ax, SQL

AX 2012 – Export models in Sequence

0
0

Using powershell to import Ax model is a very straight forward approach.

I come under the situation where i have to export more than 1 model and need to let others know what sequence they go in because of their dependencies.

For this I maintain an excel sheet which the models / ID’s

So that means a few things while exporting:

1. They need to be in sequence

2. File naming conventions (Because we all have our own)

3. Painstaking process when we have to type the file name, model for each model we export.

Solution steps:

1. Powershell

2. Profits!

Ok explaining the solution, here is my powershell script which takes in the ModelId’s as a CSV string (no spaces) (so -ModelIds 32,33,45)

The database and server (database server) are defaulted (which can still be changed). Even the folder where this goes has a parameter.

It reads the modelIds that need to be exported (in the same sequence) and create file names with the suffix so that they are numbered.

You might say that if there are more than 9 models to be exported, then i need to do something with it (I havent reached that stage yet), but here is the script to share:

Param(
	#[Parameter(Mandatory=$true)]
	[String]
	$AxDBServerSource = 'SQLSERV_2012_1',
    [String]
	#[Parameter(Mandatory=$true)]
	$AxDBSource = 'ax_2012_r2_cu6_model',
#	[String]$AxDBServerDest = '.',
#	[String]
#	[Parameter(Mandatory=$true)]
#	$AxDBDest = 'AX_Standard_2012_new',
	#[Parameter(Mandatory=$true)]
	[String]$FolderTempModelStore = 'E:\temp\shashi\models'
	,[Parameter(Mandatory=$true)]
	[int[]]$ModelIds
)
#Created By: Shashi Sadasivan

function deleteLocalCache {
	Write-host 'Deleting *.AUC files'
    $localAppDataDir = "$(gc env:LOCALAPPDATA)"
	del $localAppDataDir\*.auc
}

CLS #Clears the screen
#Load the Ax Powershell script
Write-Host 'Loading Ax powershell scripts....'
& "E:\Program Files\Microsoft Dynamics AX\60\ManagementUtilities\Microsoft.Dynamics.ManagementUtilities.ps1"

#Printing the Details of values (Not necessary)
Write-Host 'Host Database: ' $AxDBServerSource'\'$AxDBSource
#Write-Host 'Destination Database: ' $AxDBServerDest'\'$AxDBDest
Write-Host 'Temporary folder:' $FolderTempModelStore

deleteLocalCache

#Export Models from source
#Delete Contents of Folder first
Write-Host 'Deleting Contents of Folder.....'
Get-ChildItem -Path $FolderTempModelStore -Recurse | Remove-Item -Force -Recurse
Write-Host 'Exporting Models to Folder......'

$AxModelListSource = Get-AXModel -Server $AxDBServerSource -Database $AxDBSource
$sequence = 0
foreach($modelId in $ModelIds)
{
	$sequence++
	$modelSource = $AxModelListSource | Where-Object {$_.ModelId -eq $modelId}
	if($modelSource -eq $null) {
		Write-Host "Model id" $modelId "not found in the application" -foregroundcolor "Red"
	}
	else {
		#Write-Host "Export Model " $modelSource.Name
		$locFileName = "$FolderTempModelStore\$($sequence)_$($modelSource.Layer)_$($modelSource.Name)_$($modelSource.Version).axmodel"
		#Write-Host $locFileName
		#Write-Host 'Exporting Model' $modelSource.Name
		Export-AXModel -Server $AxDBServerSource -Database $AxDBSource -Model $modelSource.ModelId -File $locFileName
	}
}


Filed under: Ax 2012, Dynamics Ax Tagged: ax 2012, Dynamics Ax, powershell

AX 2012 – Add elements to version control

0
0

Ive written a few jobs to scan the AOT and add it to version control.
(manged to misplace the job a few times now)

I stumbled across the following post regarding the same: Objects not in Version Control (AX 2012)

However, i changed some code in there to make sure that the job types were valid (like DEL_ names didnt get added to version control)

Following is the change i made to the code mentioned in the link above:

if(vcsSys.allowCreate(controlable))
{
    //sysTreeNode is of type SysTreeNode
    sysTreeNode = sysTreeNode::construct();
    sysTreeNode.parmTreeNode(pNode);
    if(sysTreeNode.canCreate() == true)
    {
        //do something if it isn't in version control
        info(strfmt("%1 %2",pNode.treeNodePath(), modelName));
        vcsSys.commandAdd(controlable); //SHS add to version control
    }
}

This job still requires refinement because it still adds Private projects which i want to avoid


Filed under: Ax 2012, Dynamics Ax, X++ Tagged: ax 2012, AX2012, Dynamics Ax, version control

What kind of future posts would you be interested in

AX 2012 – Model dependencies and Install Order – Part 1

0
0

Models in AX 2012 are a brilliant way to separate code between solutions in the same applications. They reduce merge time, allow code segregation and a lot more benefits.

Although, we do come to a point when code can start to depend on one another and even though it may look like it all works, at the time on installing those models into another application can cause an issue.

You say how?

Lets consider we are making changes to the VAR and the USR layer.

VAR Layer changes:

The VAR layer has the model, VAR_1 which has a class called: MyVar1Class, and this has methods method1, method2

At this stage we can export this model out and know that when installing this, it will not error out as this is not dependant on anything else.

USR Layer changes:

The USR layer has a model USR_1 which has a class MyUsr1Class with methods usr1Method1, usr1method2

At this stage, if we only take USR_1 model and put it into another application, we will not get an error. However, we add a method in class MyVar1Class called usr1MethodInVar1Class

Ok, we just complicated it. USR_1 model requires VAR_1 model.

Some more USR Layer changes:

Lets add a new model USR_2, with a class MyUsr2Class and some more methods in this. At this stage USR_2 is not dependant on the models VAR_1 or USR_1.

But if we add a method usr2MethodInUsr1Class in the class MyUsr1Class, now USR_2 is dependant on USR_1 which is dependant on the VAR_1 model.

Round robin change:

Switching back to the USR_1 model, we add a method to the class MyUsr2Class called usr1MethodInUsr2Class. This just means USR_2 is dependant on USR_1 which is dependant on USR_1 as well. The application compiles, its still a perfectly valid scenario.

So the Problem: With the above example when we export our models, and import it to another application, we will put in VAR_1 model first. Now while putting in USR_1 model, we will need the createparents switch which will create a virtual model, then we import the USR_2 model overwriting the existing model. This is a Giant mess. Not as giant as VAR code using USR layer code.

Save my day:

So I embarked on a mission to find out

  1. How could I figure out what models are dependant on what?
  2. How do i find from that, the sequence in which the models should be installed. (Some of the applications i deal with can have over 10 models in the same layer)

The answer: 42

Ummmmm, well, in some ways. There are other intergalactic species finding the ultimate question to that answer. But our answer lies in a few tables. Some of the tables conveniently hidden away from our sight, somewhere in the model database.

The model database has 2 tables I rely on for the dependency, ModelElement and ModelElementData.

ModelElement Table : This Table is a collection of all the elements in the AOT, down to every node. from tables, fields, relations, enums, EDT’s, classes methods.

ModelElementData Table : This holds the actual content of the element, be it properties for elements like Tables, fields, EDTs or method definitions for classes. This links back to the ModelElement table. So the ModelElementData can be many records for a single ModelElementRecord. In our example, if we were to overwrite the method \Classes\MyVar1Class\method1 in USR_1 model, then we would end up having 2 records in the ModelElementDataTable for method1. The ModelElementData table also holds the information to what model and layer the element belongs to.

So from the information of these 2 tables, along with helper tables like ElementTypes, Model, ModelManifest and Layer I was able to find out what elements are common between models. I could then use this to map all the dependencies and find out the ordering in which to set the models to be imported in.

More to be continued ..

In the next post I will discuss the workings of the tool that will take the database, analyse the models and output a couple of CSV files, for the  dependencies and the model order.


Filed under: Ax 2012, Dynamics Ax Tagged: AX2012, Dynamics Ax

AX2012 Moving a project to another model

0
0

Do you need to worry about this?
1. You have more than one model in your layer (Apart from the base layer i.e. ISV layer)
2. The elements in your project can belong to more that one model in the same layer.

If you answered yes to any one of them, then word of caution, do not move the project directly.
Moving a project to another Model is a different Cookie. IT MOVES ALL THE ELEMENTS INSIDE IT TO THE LAYER YOU MOVE THE PROJECT TO!!! Yes that is in CAPS.

Method 1: Create a new Project in the desired Model and then Drag the elements from the original project into this new one.

Method 2: Database Crazy: You can play with the model database to do this, but i shall hold my guns on this one. I wouldn’t recommend this right now. Hint: ModelElementData.ModelId


Filed under: Ax 2012, Dynamics Ax

AX 2012 – Model dependencies and Install Order – Part 2 – The app

0
0

Credit Nasa

From the follow up from Part 1, this post is more about how to find what models are dependent on what. I have created a console app which outputs csv files and can be downloaded here.

The inner workings:

So we know that ModelElement and ModelElementData hold the AOT objects for the respective models.

ModelElementData is the granular data. So a table field, a class method, a form control are stored here. They link to their parent using the ParentHandle field, or straight to the Main AOT object using the RootHandle.

The app takes every layer, from the ISV onwards , analyses each model and the model element data in them. It then looks if the element is used by any other layer below it OR if the root element is used in any other layer below or in the same layer.

How to use the App:

  • Set the minimum access layer – If you dont care about the SYS /SYP then set the Layer id to that of the ISV. Ofcourse if you work from VAR and beyond, then you probably dont care about ISV / ISP. So look at the Layer table and set the minimum access layer
    Set this in the Tag “MinApplicationLayer” inside the App.config file
  • Connection String – You dont need to set this up, unless you want to default to a specific Ax Database. This is good if you only care about one application. When the App runs, it will ask for user input for the database server and name. Not entering it will revert to the connection string in the App.config file

This is still a command line application, the output is still on the screen. But that’s the next phase of this app, to output the data to files or on screen. And also to make it power shell friendly

Ok, how the app works now:

Run the Exe in a command prompt: AxModel.Common.Exe (I will work on the name, I promise), put the database server, and the database name, and after  a few verbose output data in a CSV format is spit on the screen.

Copy that to excel and it should be all good from there.

The verbose output also outputs the dependencies if you want to look at it, which may be helpful.

Limitations: If you are referencing objects from one model to another in code, like a method in another object is  called from a class, then that will not be detected. This is mainly because cross reference will need to be looked at and is out of scope at this stage. I am focusing on installation dependencies

The code is available on GitHub and I should be updating it more as I conduct more tests and get more input about this.

Download Binaries


Filed under: .Net, Ax 2012, Dynamics Ax Tagged: AX2012, Dynamics Ax, My Apps

[AX2012] The AXRDCE extension caught an unexpected exception for report AutoReport

0
0

One of the errors received when running Print from a Form in AX 2012 (File > Print > Print, Or Ctrl + P) is the following:

The AXRDCE extension caught an unexpected exception for report AutoReport.

With the following message in the infolog

The error message was:
 Unable to find appropriate service endpoint information in the configuration object.
 Unable to find appropriate service endpoint information in the configuration object.
    at Microsoft.Dynamics.AX.Framework.Services.Client.Configuration.CustomConfigurationChannelFactory.UpdateServiceEndpoint(ServiceModelSectionGroup group, ChannelEndpointElement selectedEndpoint, ServiceEndpoint serviceEndpoint)
    at Microsoft.Dynamics.AX.Framework.Services.Client.Configuration.CustomConfigurationChannelFactory`1.CreateDescription()
    at System.ServiceModel.ChannelFactory.InitializeEndpoint(String configurationName, EndpointAddress address)
    at Microsoft.Dynamics.AX.Framework.Services.Client.Configuration.CustomConfigurationChannelFactory`1..ctor(Configuration configuration)
    at Microsoft.Dynamics.AX.Framework.Services.Client.Configuration.ChannelFactoryConfigurator.CreateClientChannelFactory[T](Configuration explicitConfiguration)
    at Microsoft.Dynamics.AX.Framework.Services.Client.Configuration.ClientConfigurationInternal.CreateXppChannelFactory[T](String relativeUri)
    at Microsoft.Dynamics.AX.Framework.Services.Client.XppServiceClient`1.CreateChannelFactory()
    at Microsoft.Dynamics.AX.Framework.Services.Client.ServiceClientBase`1.get_ChannelFactory()
    at Microsoft.Dynamics.AX.Framework.Services.Client.ServiceClientHelper.InvokeChannelOperation[TResult,TChannel](IServiceClient`1 client, Func`2 operationInvoker, Func`2 exceptionWrapper)
    at Microsoft.Dynamics.AX.Framework.Reporting.Shared.SRSFrameworkServiceProxy.Microsoft.Dynamics.AX.Framework.Reporting.Shared.IXppSRSFrameworkService.getRelationLinkTargetList(SRSFrameworkServiceGetRelationLinkTargetListRequest request)
    at Microsoft.Dynamics.AX.Framework.Reporting.Shared.SRSFrameworkServiceClient.Microsoft.Dynamics.AX.Framework.Reporting.Shared.ISRSFrameworkService.GetRelationLinkTargetList(String tableFieldsListEncodedStr)
    at Microsoft.Dynamics.AX.Framework.Reporting.Shared.TableFieldMenuItemService.GetRelationLinkTargetList(String fieldList)
    at Microsoft.Dynamics.AX.Framework.Reporting.Shared.TableFieldMenuItemService.Resolve(String encodedString)
    at Microsoft.Dynamics.AX.Framework.Reporting.Shared.AutomaticDrillThroughStep.Transform(XDocument report, ILocalReportContext reportContext, ILocalUserContext userContext)
    at Microsoft.Dynamics.AX.Framework.Reporting.Shared.CustomizationExtensionImplementation.ProcessReportDefinition(Byte[] reportDefinition, ILocalReportContext reportContext, ILocalUserContext userContext, Byte[]& reportDefinitionProcessed)

This Probably points to a corrupt configuration for the AIF services

What one may require to do is to Reconfigure the services by going to the Microsoft Dynamics Ax Configuration Utility and click the “Refresh” button in the “Connection” tab.

This should refresh the links to the WCF services.
Open the client using this updated config file and try it again

Other solutions that may work for you:
AX 2012 – Fatal AXRDCE Exception Error Addressed
Error printing within AX (To the screen and Physical printer)


Filed under: Ax 2012, Dynamics Ax, Errors Tagged: AX2012, Dynamics Ax, Errors

[AX2012] Compare objects between models

0
0

SQL Makes everything better

When ever adding a new hot-fix or a new model from another source we need to compare the code touch points between that and existing code.

Ax makes that easy using the compare layers tool, however that only compares layers

So the other way is to go code upgrade and use those tools which may or may not work depending on your case (for example you put a 3rd part model and want to compare what var or cus or usr layer code you need to change)

This brings to writing x++ code or so in yummy SQL

Requirements:

  1. Know what models have changed
  2. Know what models will be affected / need to be compared

Know what models have changed

You know what hotfixes have been put in and there may be more than one. so note the numbers of the hotfixes or models that were added. Use the Get-AxModel command to get the models Ids for them

Know what models will be affected / need to be compared

You will probably need to know all the Models Id’s from the higher layers. So if you have a hotfix that you added then take a note of all model id’s that are a part of isv, isp, var, vap, cus, cup, usr, usp …. you know what i mean

The Fun begins with SQL

select et.ElementTypeName, et.TreeNodeName, med1.ParentHandle, med1.ElementHandle, me.* from ModelElementData med1
join ModelElement me
on ((me.ElementHandle = med1.ParentHandle and med1.ParentHandle <> 0)
or (med1.ParentHandle = 0 and me.ElementHandle = med1.ElementHandle))
join ElementTypes et
on et.ElementType = me.ElementType

where med1.ModelId in (<model ids changed in csv>)
and med1.ElementHandle in (select medisv.ElementHandle from ModelElementData medisv
where medisv.ModelId in (<model ids affected in csv>))
order by me.ElementType

That gives us a result set with main element type and the path to it.

Hopefully this gives an easier way to figure out what has changed and reduces your time. If this post does help you let me know, I am planning to invest in a query to further give more details about each element.


Filed under: Ax 2012, Dynamics Ax, SQL Tagged: ax 2012, AX2012, Crystal reports, Dynamics Ax, SQL

AX2012 Terminating a compile worker

0
0
Moral of the story: NEVER KILL A COMPILER WORKER WINDOW EVER

There was a time I “accidentally” closed a compiler widow of a 10 worker process in the 2nd run.
Even though the main window notified that a worker terminated without any reason code, it still continued the process and reported that the compile was successful. Including an error log with a clean compile log (except for warnings, who looks at warnings!!! ;) )
The next part which is compile the CIL went a bit weird.

The CIL compiler would start and AX stared at me for about a minute (as if its doing something, even the processors reported a good 98% usage for the AOS process) and then the AX screen came back to life.

The weirdness continues

  • It didn’t report anything.
  • There was no infolog to tell me that the services were generated, or the CIL compiler had failed.
  • There was nothing in the event log.
  • I even did a AOS restart with the deletion of the xppil folder, and yet I got the same result.
  • There was no CIL compiler log generated either.

So turns out (whatever happens inside the engine) that you should NEVER KILL A COMPILER WORKER WINDOW EVER !

via arkansas-investigations.com

Filed under: Ax 2012, Dynamics Ax Tagged: AX2012, Compiler, Dynamics Ax

AX2012 – Compare model. When you put in hotfixes, updates and want to do less – Part 1

0
0
via Tamás Mészáros

So you got updated Model(s) or hotfix(es) and need to make sure your ISV or VAR or CUS / USR layer code has been merged.

Lets see what your options are:

  1. Create a project for each model and then compare then to the upper layers for changes.
    — You spend a lot of time giving yourself carpal syndrome
  2. Auto code upgrade
    — It only manages hotfixes. The code that needs to be manually reviewed can be overwhelming? (someone please correct me on that)
  3. You write some script to find out how to do this
  4. Magic beans
    — You don’t get them

Ok, so I wrote some SQL scripts to do this, however it was getting too complex for me and I knew I could do it faster with in memory processing with C#. Although I can argue with myself that SQL can do a better job with this. However, I went to the path of C#

Tables used:

  1. SysElementType – This stores what a node in the AOT is classified as. E.g. TableInstance, ClassInstanceMethod, BaseEnum. This is purely to translate an integer to a human readable value.
  2. SysModelElement – This is where the definition of each object is. So every node in the AOT is one record here. This record also links it to the Parent Element. So a record which is the table field, will have its parent element to the Table itself. A parent element can have another parent element too, e.g. a form element inside a group
  3. SysModelElementData – This is where the code for the SysModelElement is stored. And it it stored for each Model (not layer). Moment I override the code in the USR layer for the SalesFormLetter classdeclaration in Ax, it will create a record in this table with the new code. So use it as a caution, if you ever query this table, be careful because you could be transferring a “lot” of data across the wire

Combining SysModelElementData, i can find what all other models use the same element. Then link it up to sysModelElement and find more information about the AOT object that has changed.

Armed with this knowledge I went and created  C# console application

The project currently compares models. So if you want to know what has affected the VAR and USR layer models with the addition of a particular SYP layer model, then you send the list of SYP model id’s and then the list of VAR and USR layer models as a csv string. This does a fast compare and outputs what objects you need to look at.

The shortcomings / bugs of this so far:

  1. Security, menu items dont get compared because of the way this is stored
  2. Currently you need to compare models. I would like to say i need to know what objects are affected by these SYP models and then get a list of objects on all the higher layers
  3. Need to create a library instead of a console project

This project is posted on GitHub: https://github.com/shashisadasivan/AxCodeCompare.git


Filed under: Ax 2012, Dynamics Ax Tagged: AX2012, AxAdministration

AX 2012 Install Model – A Checklist

0
0

This is a checklist I follow to install Models Provided for a installed site

  1. Stop the AOS
  2. Backup the Database
  3. Install models (follow guidelines, you may be told to use the overwrite option)
  4. Merge Code (optional)
    1. Start AOS
    2. Merge Code
    3. Stop AOS
  5. Compile X++
    1. Option 1 – AxBuild.exe (RU7 and Onwards)
      1. Make sure AOS is turned off
    2. Option 2
      1. Start AOS
      2. Open Ax Client
      3. Compile
      4. Stop AOS
  6. Compile CIL
    1. Start AOS
    2. Open Ax Client
    3. Run Full CIL Compile
  7. Restart AOS
  8. Synchronize database
  9. Restart AOS

If you do restore a Live database over the TEST database, make sure to transfer the parameters as well as reset the SYSSQMSettings.GlobalGUID


Filed under: Ax 2012, Dynamics Ax Tagged: ax 2012, AX2012, AxAdministration

AX2012 Terminating a compile worker

0
0
Moral of the story: NEVER KILL A COMPILER WORKER WINDOW EVER

There was a time I “accidentally” closed a compiler widow of a 10 worker process in the 2nd run.
Even though the main window notified that a worker terminated without any reason code, it still continued the process and reported that the compile was successful. Including an error log with a clean compile log (except for warnings, who looks at warnings!!! ;) )
The next part which is compile the CIL went a bit weird.

The CIL compiler would start and AX stared at me for about a minute (as if its doing something, even the processors reported a good 98% usage for the AOS process) and then the AX screen came back to life.

The weirdness continues

  • It didn’t report anything.
  • There was no infolog to tell me that the services were generated, or the CIL compiler had failed.
  • There was nothing in the event log.
  • I even did a AOS restart with the deletion of the xppil folder, and yet I got the same result.
  • There was no CIL compiler log generated either.

So turns out (whatever happens inside the engine) that you should NEVER KILL A COMPILER WORKER WINDOW EVER !

via arkansas-investigations.com

Filed under: Ax 2012, Dynamics Ax Tagged: AX2012, Compiler, Dynamics Ax
Viewing all 56 articles
Browse latest View live




Latest Images