Dynamics AX 2009: Write to eventlog entry

Our client requested to keep track of all Dynamics AX system errors during the User Acceptance Testing. I use the application event logs to store the information.

The following code shows you how to write event log entry with X++:

Create a new class AX_EventLog with a static method WriteEventLog:

static void WriteEventLog(Exception _exception, str _event)
{
    str eventSource = "AX event";
    str logType = "Application";
    System.Diagnostics.EventLogEntryType eventLogEntryType;
    int eventCategory = 9999;
    ;

    switch(_exception)
    {
        case Exception::Info:
            eventLogEntryType = System.Diagnostics.EventLogEntryType::Information;
            break;
        case Exception::Warning:
            eventLogEntryType = System.Diagnostics.EventLogEntryType::Warning;
            break;

        default:
            eventLogEntryType = System.Diagnostics.EventLogEntryType::Error;
    }

    if (!System.Diagnostics.EventLog::Exists(eventSource))
    {
        System.Diagnostics.EventLog::CreateEventSource(eventSource, logType);
    }

    System.Diagnostics.EventLog::WriteEntry(eventSource, _event, eventLogEntryType, eventCategory);
}

In the Info class,

image

Exception add(
    Exception _exception,
    str _txt,
    str _helpUrl = ”,
    SysInfoAction _sysInfoAction = null,
    boolean buildprefix = true)
{
    …
    AX_EventLog::WriteEventLog(_exception, _txt);
    …

}

Here we go, event log entry…

image

Advertisements
Posted in Axapta Development | Leave a comment

Argument passed to ‘KPIVALUE’ function must be a KPI name

During the past two weeks, I have been troubleshooting the Reporting Services Error on the AX2009 Enterprise Portal, especially for the ‘argument passed to the ‘KPIVALUE’ function must be a Key Performance Indicator (KPI) name’ issue.

image

In the following steps, I will show you how to troubleshoot the error with the not-functioning ‘KPI for Project Manager’ Report server report.

  • Find out the report library on the AOT.

On the SharePoint portal, click ‘Site Actions’ –> Edit pages, look for the report name.

image

Open an AX client, go to Tools –> Development tools –> Label –> Label Editor and enter ‘KPI for Project Manager’, which we will get the label ID ‘@SYS121988’.

image

In AX client, open an AOT tree, go to Menu items –> Output node, and right click and choose Find.

In the search dialog, choose All nodes and in ‘Containing text’, enter the label ‘SYS121988’.

image

Now we get the object which is ‘ProjKPI_ProjectManager’. In the AOT, go to Menu Items –> Output node and locate the ‘ProjKPI_ProjectManager’ and right click and choose properties.

image

On the properties, we get the object which is ‘ProjKPI.ProjKPI.KPI’.

  • Edit the report library in Visual Studio 2008

Go to AOT –> Report libraries, and locate ‘ProjKPI’ and right click and choose ‘Edit in Visual Studio’ which will open Visual Studio 2008.

Because in the error ‘Query execution failed for dataset ‘NetWIP’. We double click the ‘NewWIP’ dataset in Visual Studio 2008.

image

Press F5 to execute the query, put in company account value and click OK.

image

The same error as on the portal.

image

  • Debug in SQL Server Business Intelligence Development Studio

Start the SQL Server Business Intelligence Development Studio from Start –> Programs –> SQL Server 2005 –> SQL Server Business Intelligence Development Studio.

Click File –> New –> Project, and select project type ‘Import Analysis Service 9.0 Database’.

image

Click OK, then a wizard will guide you through to import the database.

image

image

image

Based on the query to build the ‘NEWWIP’ dataset:

“… 

SELECT {[MEASURES].[INDICATOR],KPIValue("Actual vs Budget Net WIP"), KPIGoal("Actual vs Budget Net WIP"),
KPIStatus("Actual vs Budget Net WIP"), KPITrend("Actual vs Budget Net WIP")} ON COLUMNS

FROM [Project Accounting Cube]

…”

The dataset is built upon [Project Accounting Cube].

Select ‘[Project Accounting Cube] in the solution explorer. and choose ‘View designer’.

image

Change to ‘KPI’ tab,and choose the ‘Actual vs Budget Revenue’

image

From the Value Expression, we can see the KPI is defined on the basis of measure ‘Budget Revenue’ and measure  ‘Actual Revenue’.

(([Measures].[Budget Revenue]-[Measures].[Actual Revenue])/[Measures].[Budget Revenue])*100

Expand the Measures in the ‘Metadata’ tab of the ‘Calculation Tools’ section.

image

OOPS!!!!!! Can’t find the ‘Budget Revenue’ measure!!!!!!!

image 

Okay, now we see, must be something wrong with the ‘Budget Revenue’ measure. Change to ‘Calculation’ tab in the same ‘Project Accounting Cube’.

Choose the ‘Budget Revenue’ Calculated Member.

image  

It is derived from another two Calculated Members – ‘Budget invoiced revenue’ and ‘Budget accrued revenue’.

Expression for ‘Budget invoiced revenue’:

image

Expression for ‘Budget accrued revenue’:

image

Both of them are based on the Dimension ‘Budget updates’, and they do not appear under the Measures as well, which means the data are not populated properly.

image

Expand the ‘Budget updates’ in the Metadata tab.

image

Here we go, we found the cause of the issue. No transactions in the Budget Update dimension.

Go to the Solution explorer, and edit the ‘Budget updates’ dimension.

image 

image

From the property, we can see the ENUM ProjTransType is from the following query:

SELECT A.ENUMITEMVALUE, A.ENUMITEMLABEL AS ENUMITEMNAME FROM [DBO].SRSANALYSISENUMS A WHERE A.ENUMID = 383 AND A.LANGUAGEID = ‘en-us’

Let us check this table from the SQL Server Management Studio. All good, the records are there. (We need to check the ProjPaymentStatus, LedgerPostingType as well.)

image

Then must be the issue that the dimension is not processed properly.

From the expression for ‘Budget invoiced revenue’ :

Sum
(
    (
        {[Budget updates].[Transaction type].&[1], [Budget updates].[Transaction type].&[2],
        [Budget updates].[Transaction type].&[3], [Budget updates].[Transaction type].&[4]},
        [Budget updates].[Posting type].&[126]
    ),
    [Measures].[Budget updates Amount] *-1
)
+
Sum
(
    (
        {[Budget updates].[Transaction type].&[5]},
        [Budget updates].[Posting type].&[127]
    ),
    [Measures].[Budget updates Amount] *-1
)

Make sure in the ProjTransBudget table (Dimension [Budget updates]) contain the records meeting the above criteria.

Process the ‘Budget updates’ dimension now.

image

image 

Click ‘Yes’ button.

image

image

Click Run to process the ‘Budget updates’ dimension.

image

Reconnect again

image

Reload the role center…

image

Posted in SSAS & BI | Leave a comment

Dynalink in Dynamics AX 2009 Enterprise Portal

The classic Dynalink is still available when we are programming AX2009 Enterprise Portal.

So normally during the Enterprise portal programming, we don’t need to pass the parameter by using CreatingDataSetRun event to create the QueryBuildRange object and specify the range value (AX did that for us already!), if we have the table or EDT relations set up properly.

Apparently the context record is passed by the QueryString parameters.

For example, the QueryString “EPProjTableInfo.aspx?WTID=624&WKEY=%5b65534%3a5637144829%5d&WCMP=DAT”, AX will retrieve the context record by using the unique pair value (WTID = TableId, WKEY = RecId).

In the user control, we can use the following code to get the context record:

using Microsoft.Dynamics.Framework.Portal.UI;

using Microsoft.Dynamics.Framework.Metadata.Ax;

using Microsoft.Dynamics.Framework.Data.Ax;

    …

 

    AxTableContext context = AxBaseWebPart.GetWebpart(this).ExternalContext;

    if (context != null &&

        context.TableId == TableMetadata.TableNum(AxBaseWebPart.GetWebpart(this).Session, “ProjTable”))

    {

        string projId = context.DateKey.GetRecord(this.AxSession).GetField(“ProjId”).ToString()’;

    }

 

In the DataSet, we can use args objects to return the context record as we do in AX client programming:

public void init()

{

    TableId tableId;

    ProjTable projTable;

    ProjId projId;

    super();

    tableId = element.args().dataSet();

    switch(tableId)

    {

        case tableNum(ProjTable):

                projTable = element.args().record();

                projId = projTable.projId;

                break;

         …

    }

    …

}

Posted in Enterprise Portal | Leave a comment

Visual Studio 2008 toolbox items not shown properly

After I used Visual Studio 2008 for Dynamics AX 2009 enterprise portal development for a couple of weeks, I found the Dynamics AX tool box items were not showing properly. Only AxLookup and AxGridView control were listed on the tool box.
 
I tried the following steps to resolve the issue:
 
1. Navigate to C:Users<username>AppDataLocalMicrosoftVisualStudio9.0 (on Vista or Longhorn) or “C:Documents and Settings<username>Local SettingsApplication DataMicrosoftVisualStudio9.0” (older Windows versions). You would need to delete these files:
* toolbox.tbd
* toolboxIndex.tbd
* toolbox_reset.tbd
* toolboxIndex_reset.tbd
 
2. Restart the Visual Studio 2008
   Right click on the tool box and select ‘Choose items’.
   In the ‘Choose Toolbox Items’ dialog, make sure all the Ax* controls are selected, and click "OK’ to close the dialog.
   Right click on the tool box again and select ‘Show all’.
 
Now you can have all the Ax controls back on the tool box.
 
Posted in Enterprise Portal | 1 Comment

Caller information in EP2009

If we want to get the caller page information in Enterprise portal 2009 which is similar to what we did in AX native client programming –

element.args().caller();

We can implement the following code in the user control:

using Microsoft.Dynamics.Framework.Portal;

private string GetElementCaller()

{

    string sPath = AxWebSession.GetPreviousURL(this.Page).ToString();

    string [] words = sPath.Split(‘/’, ‘?’);

    string caller = “”;

    foreach (string word in words)

    {

        if (word.Contains("aspx"))

        {

            caller = word;

            break;

        }

    }

    return caller;

}

Add a Label control on the ascx file with ID ‘Label1’ and put the following code in the Page_Load method

protected void Page_Load(object sender, EventArgs e)

{

   …

   if (!this.Page.IsPostBack)

   {

      this.Label1.Text = GetElementCaller();

   }

}

You will get the result like ‘EPProjTableInfo.aspx’. 

Posted in Enterprise Portal | Leave a comment

X++ code to remove identical copy

Our client asked for a job to remove the identical copy from VAR layer.
For some unknown reason, some AOT objects are touched in VAR layer but actually are identical copy. When the developer compared the VAR layer object with the one in lower layer (BUS, SYS etc.), AX showed it was an identical copy.
 
Here is the example on how you can remove the identical copy in X++ code:
static void FindAndDeleteIdenticalObjects(Args _args)
{
    SysTreeNode     comparable1, comparable2;
    TreeNode          curLevelTreeNode, upperLevelTreeNode;
    UtilIdElements    utilElements, joinUtilElements;
    ;
    while select UtilElements
        where UtilElements.utilLevel        == UtilEntryLevel::var &&
              (
                UtilElements.recordType     == UtilElementType::Form         ||
                Utilelements.recordType     == UtilElementType::Report      ||
                Utilelements.recordType     == UtilElementType::Table        ||
                Utilelements.recordType     == UtilElementType::Class         ||
                Utilelements.recordType     == UtilElementType::Enum        ||
                Utilelements.recordType     == UtilElementType::ExtendedType
              )
    {
        //Should use join if for a normal table, but not applicable for UtilElements
        //Performance hit if use exists join
        select firstonly recid from joinUtilElements
            where joinUtilElements.utilLevel     !=  UtilElements.utilLevel    &&
                  joinUtilElements.name            == UtilElements.name        &&
                  joinUtilElements.recordType   == UtilElements.recordType;
        if (joinUtilElements.RecId)
        {
            //Thanks for Jim Shepherd here
            curLevelTreeNode      = SysTreeNode::findNodeInLayer(UtilElements.recordType, UtilElements.name, UtilElements.parentId, UtilElements.utilLevel);
           
            upperLevelTreeNode  = SysTreeNode::getLayeredNode(curLevelTreenode, 1);
            comparable1              = SysTreeNode::newTreeNode(curLevelTreeNode);
            comparable2              = SysTreeNode::newTreeNode(upperLevelTreeNode);
            if (SysCompare::silentCompare(comparable1, comparable2))
            {
                info(strFmt("Element name: %1, Element type: %2", UtilElements.name, enum2str(UtilElements.recordType)));
                //Remove the node
                curLevelTreeNode.AOTdelete();
            }
       }
   }
}
Posted in Axapta Development | Leave a comment

New server-based batch framework in AX 2009

Microsoft Dynamics AX 2009 introduces a new batch framework that supports server-based batches without the need for a client. All new batch jobs in Microsoft Dynamics AX 2009 use the new batch framework. Most existing batch jobs from previous releases are being migrated to the new framework. Client-side batches are supported in Microsoft Dynamics AX 2009 but are not recommended.

From AX 2009, we don’t need to run a client to run scheduled jobs, if you specify that the batch job runs on server. The batch job runs on server by default unless the developer overrides the runImpersonated method and make it return false.
The old client-based batch processing is still in AX 2009 for backward compatibility and will be removed in future versions. The old client-based batch system, that was based on a dedicated client, cannot be replaced in some situations. For example, due to some technical problems that the compiler cannot be run on the server tier, which means the task to run a scheduled compilation or cross-reference update must be run on client-tier.

Posted in Axapta Development | 1 Comment