Category Archives: Development

Posts about development in Dynamics AX 4.0 or Axapta 3.0

“Reversed” wild card search

Sometimes you would want to do a “reversed” wild card search, for example if you want to find out if a customer in any way matches a wild card stored in another table.

Here is a scenario:

You have a table and a form that stores customers that should be blocked in any way.
Here you can use the standard AX wild cards to indicate that a group of customers with partly the same name should be blocked.

Then, you want to find out if a specific customer should be blocked according to the table above.

Here is what you would need to do:

First of all you have to make sure that the table has the property CacheLookup set to ‘EntireTable’. Otherwise this wild card search won’t work.

Then, you can write this piece of code to check if the specific customer matches any of the wild cards in the table FO_BlockCustomer.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void FO_ReversedWildCardSearch(Args _args)
{
    FO_BlockCustomer tblBlockCustomer;
    CustTable        tblCustTable = CustTable::find('DE-010');
    Name             name;
 
    if (tblCustTable)
    {
        name = tblCustTable.name();
 
        select firstOnly tblBlockCustomer where
        (name like tblBlockCustomer.Name);
 
        if (tblBlockCustomer.RecId)
            info(name + " should be blocked");
        else
            info(name + " should NOT be blocked");
    }
}

Checking customer ‘DE-010’ will return the following info log.

Sales line number not unique

There are cases in all current versions of Microsoft Dynamics AX where the line number on sales order lines is not incrementally assigned and can therefore be assigned in duplicates.

For the purpose of the post i have added the field “Linenum” to the grid in the sales order form.

As you can see below the line numbers are assigned incrementally when creating order lines in an ordinary fashion. (one by one).
2016-05-27 10-35-15

Some sellers might decide to add a few empty sales lines to the order before picking the item number like this.
2016-05-27 10-44-41

At this point the line number still looks normal.
But lets see what happens when when we start picking values for these lines and saving them.
2016-05-27 10-48-05

In this example i start by assigning an item number for the last row and then for the first row.
As soon as the item number is assigned and i leave the field, the line number is changed to 3.

The first thing you might ask yourself is: “Why the heck would you create order lines this way?”
That i can’t answer, but i know for sure that it’s not totally uncommon.

If sales line number are not unique you might run in to problems with some customizations that uses the line number to identify the sales line.

Our solution for this is to enforce a more “normal” behavior by adding a piece of code that deletes all cached sales lines every time a sales line is saved.

Simply add this method to the data source “SalesLine” in the form “SalesTable”.

private void HandleMultipleCacheLines()
{
    SalesLine   salesLineLineNum;
    ;
 
    salesLineLineNum = salesLine_ds.getFirst();
 
    while (salesLineLineNum.SalesId)
    {
        if (!salesLineLineNum.RecId)
        {
            salesLine_ds.cacheRemoveRecord(salesLineLineNum);
        }
 
        salesLineLineNum = salesLine_ds.getNext();
    }
 
    salesLine_ds.reread();
    salesLine_ds.refresh();
}

And call the new method from within the method “write” on the data source “SalesLine”.
Put the call right below “super();”.

Please let me know if you have a another solution.

Date and UtcDateTime interval for previous month

This code calculates the first and last date of the previous month.

1
2
3
4
5
6
7
8
9
10
11
12
static void EP_dateMonthInterval(Args _args)
{
    date            fromDate;
    date            toDate;
    date            baseDate = systemDateGet();
    ;
 
    toDate      = dateStartMth(baseDate) - 1;
    fromDate    = dateStartMth(toDate);
 
    info(strFmt("%1 - %2", fromDate, toDate));
}

UtcDateTime version of the same job.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static void EP_UtcdateMonthInterval(Args _args)
{
    UtcDateTime     fromDate;
    UtcDateTime     toDate;
    UtcDateTime     baseDate = DateTimeUtil::utcNow();
    ;
 
    // Remove this month number of days.
    toDate = DateTimeUtil::addDays(
    baseDate, -(DateTimeUtil::day(baseDate) -1));
 
    // Add the rest of this days time minus one second.
    toDate = DateTimeUtil::addSeconds(
    toDate, -(DateTimeUtil::time(toDate)+1));
 
    // Remove the number of days we are on in
    // the previous month and add one second to 
    // get to the first of the month.
    fromDate = DateTimeUtil::addSeconds(
    DateTimeUtil::addDays(
    toDate, -(DateTimeUtil::day(toDate))), 1);
 
    info(strFmt("%1 - %2", fromDate, toDate));
}

Maps and MapEnumerators

This code put five records of the table CustTable in a map with the key AccountNum. Then the map is looped and the and AccountNum and customer Name is printed to the infolog.

The last part of the code finds the last AccountNum from from the loop directly from the Map, but after a check is done if the value is in the map. Performing a lookup for a value that doesn´t exist in map will result in a error.
Continue reading Maps and MapEnumerators

Advanced filter, day/month/year

It’s been a while since the last post but here is a useful tip to improve your filtering in Dynamics AX 2009. Thanks to Filip Carnbäck.

If you need to set an advanced filter on an interval you can use the following:

dayRange
monthRange
yearRange

Use dayRange for example like this: (dayRange(-2,2)) This will filter out all transactions two days before today and two days from today. Use monthRange and yearRange in the same way. This feature is among other useful when you setup a report to print in batch and the filter on date must change for each printout.

Also try these features when you filter:

(greaterThanDate(-2))

(lessThanDate(0))

Example dayRange

No valid document identified from the entity key

I recently encountered this problem while using the AIF framework in Dynamics AX 2009. I got it in the AIF Queue manager when one of my messages failed.
Since the message “No valid document identified from the entity key” doesn´t really give you a good clue of that the problem really is, I had to do some debugging. I found that the error occurred when the class AifConstraintListCollection did not contain any constraints and the message(document) was associated with a query.

Continue reading No valid document identified from the entity key

Using Regular Expressions in Dynamics AX

When working with strings, ex. replacing text, validating content or simply checking for existence, It can sometimes be a good idea to consider using Regular Expressions (RegExp), insted of using ordinary string manipulation methods.

X++ itself does not seem to support Regular Expressions, but we can use the .NET functionality.

Below is an example method of how to validate that an email address is written in a proper format.

1
2
3
4
5
6
7
8
9
10
11
Static Server boolean validateEmail(EMail   _eMail)
{
    Str MatchEmailPattern =
    @"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$";
    System.Text.RegularExpressions.Match myMatch;
    ;
 
    myMatch = System.Text.RegularExpressions.Regex::Match(
    _eMail, MatchEmailPattern);
    return(myMatch.get_Success());
}

It simply returns true if the input is a properly formatted email.

To replace text in a string you could do something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
static void Job1(Args _args)
{
    Str EmailPattern =
    @"\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b";
    Str ReplacedText;
    ;
 
    ReplacedText =
    System.Text.RegularExpressions.Regex::Replace(
    "My email address is: rikky@haxx.net",
    MatchEmailPattern,"harry@haxx.net");
 
    print ReplacedText;
    pause;
 
}

This will replace ALL found email addresses.

Below is an example of how to “find” all matches.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void Job6(Args _args)
{
    Str MatchEmailPattern =
    @"\b[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}\b";
    System.Text.RegularExpressions.Match myMatch;
    ;
 
    myMatch = System.Text.RegularExpressions.Regex::Match(
    "Your email: youremail@host.net, and my"
    +"email: myemail@anotherhost.com", MatchEmailPattern);
 
    while (myMatch.get_Success())
    {
        print myMatch.get_Value();
        myMatch = myMatch.NextMatch();
    }
 
    pause;
}

Notice the difference between the RegExp pattern in this example and the first example.

The first pattern checks if the input is valid.

And this pattern checks for ALL valid matches.

For more information about regular expressions visit:
www.regular-expressions.info

Create class and methods in x++

This job does three things.

1. Creates a new class, compiles and saves it.

2. Finds the class and adds a new static method to it.

3. Calls the new method using DictClass.

Continue reading Create class and methods in x++

Simple field lookup in form

This is a simple way to add a custom form lookup to a new field with x++ code. This example is done with a test table named FO_TestTable that has the three fields “TestField1”, “TestField2” and “TestField3”. The table FO_TestTable has an index named “TestIdx”.

I have added a field on the table CustTable named TestField1. On this field that now can be found on CustTable form on the CustTable datasource node, I have added the “Override method” “lookup”.

Below you can see how I have edited the method lookup to fit my needs. Notice that the sysTableLookup is based on an ordinary query, this means that you can manipulate this query as you see fit.

Continue reading Simple field lookup in form