Tuesday 17 October 2017

How to get DateNull in axapta X++

Hi,
DateNull for the date can be checked by two ways
1.Global::DateNull()
2.or like this
 if (endDate != mkdate(1,1,1900)

The minimum value for the date in x++ is 1/1/1900 and max value is 12/31/2154

If you have utcDateTime here is how you can set dateNull and maximum value

todayDate = DateTimeUtil::minValue();
maximumDate =  DateTimeUtil::maxValue();
blankUtcDate = utcDateTimeNull();

Friday 29 September 2017

Microsoft Dynamics AX 2012 Security Policies Extensible Data Security (XDS)- Create Security Policy


Setup AOT Query range with CreatedBy as curren




SysQueryRangeUtil::currentUserId Method [AX 2012]

e.g. Using Methods in Table Filters & Query Ranges in Dynamics AX 2012


In Dynamics AX, there is a class called SysQueryRangeUtil that can be utilized in both query ranges and table filters. Using methods from this class allow you to be very precise about what dates you want to use in reports or for filtering your data.
Let’s say you have a report that you always want to run to see orders with shipping dates of the next day. It is possible to do so by using one of the methods from the SysQueryRangeUtil. The use of the letter ‘t’ will work for today’s date, but when you try to add days to it, it doesn’t work in reports. Instead, I will use the currentdate() method and add 1 to it.
Note: All methods & expressions must be surrounded by parentheses as shown below.
Figure 1 – Filtering the requested ship dates in an AX query for tomorrow (current day + 1)
On any form with a grid, you filter your data by pressing Ctrl+G. If I were to want to see open customer invoices from the last 90 days, I would filter my open customer invoices form and use the method (dayRange(-90,0)). The first number represents how many months backward from this month, and the second represents how many months forward. The same sorts of things can be done for the monthRange()yearRange(), and dateRange() methods. The best part about this is that you can of course save these filters to create views that you might use on a daily basis.
Figure 2 – Searching the grid for the past 90 days
If you are creating your query ranges in code, these methods can also be utilized whenever you are setting them. Definitely be sure to check out the SysQueryRangeUtil class as there are many more methods to use.

Here are some of them:
currentCustomerAccount()
currentVendorAccount()
currentUserId()
currentDate()
dateRange()
day()
dayRange()
greaterThanDate()
greaterThanUtcDate()
greaterThanUtcNow()
lessthanDate()
lessthanUtcDate()
lessthanUtcNow()
monthRange()
yearRange()

e.g. Custom Query Range Functions using SysQueryRangeUtil

You've probably seen these requests before. Users want to submit some report or other functionality to batch, and the query should always be run for "yesterday". It's a typical example where, as a user, it would be handy to be able to use functions in your query range. Well, you can. And in fact, you can make your own, very easily!

Enter the class "SysQueryRangeUtil". All it contains is a bunch of static public methods that return query range values. For example, there is a method called "day" which accepts an optional integer called "relative days". So, in our example of needing a range value of "tomorrow", regardless of when the query is executed, you could use day(-1) as a function. How this works in a range? Just open the advanced query window and enter your function call within parentheses.

Let's make our own method as an example. Add a new method to the SysQueryRangeUtil class, and enter the following, most interesting code you've ever encountered.


public static str customerTest(int _choice = 1)
{
    AccountNum accountNum;
    
    switch(_choice)
    {
        case 1:
            accountNum = '1101';
            break;
        case 2:
            accountNum = '1102';
            break;
    }
    
    return accountNum;
}


So, this accepts an options parameter for choice. If choice is one (or choice is not specified), the function returns 1101, if 2 it returns 1102. Save this method and open a table browser window on the CustTable table. Type CTRL+G to open the grid filters. In the filter field for the AccountNum field, enter: (customerTest(1)).


So, the string returned from the method is directly put in the range. So, you could do all sort of interesting things with this, of course. Check out some of the methods in the SysQueryRangeUtil as examples.



Happy DAXing...

Friday 7 July 2017

String validation: let say (for Universal Id)


  • First letter should be 'R' and in UPPERCASE only.
  • string length should be upto 9-char
  • String value should start with uppercase 'R' and followed by eight numbers.
  • rest eight numbers varies in 0-9 only.


public boolean validate()
{
    boolean     ret;
    int         i = 1;
    anytype     isValid;
    str         universalId = CustTable_HSI_UniversalId.valueStr(); //let say "R012345678"
    TextBuffer  txt = new TextBuffer();

    //ret = super();

    if (char2num(universalId, i) == char2num(strUpr(universalId), i))
    {
        ret = true;
    }
    else
    {
        ret = false;
        throw Global::error("Universal Id should start with uppercase R character.");
    }

    isValid = strlen(universalId);
    if (isValid == 9)
    {
        ret = true;
    }
    else
    {
        ret = false;
        throw Global::error("Universal Id should be upto 9-character.");
        }
    isValid = strFind(universalId,'R',1,1);
    if (isValid == 1)
    {
        ret = true;
    }
    else
    {
        ret = false;
        throw Global::error("Universal ID should start with uppercase R followed by eight numbers.");
    }
    universalId = substr(universalId, 2,9);
    txt.setText(universalId);
    txt.regularExpressions(true);   // activate regular expr in search

    // Regular expression to validate only digits
    if (txt.find("^[0-9]+$"))
    {
        ret = true;
    }
    else
    {
        ret = false;
        throw Global::error("Universal ID should start with uppercase R followed by eight numbers.");
    }
    ret = super();
    return ret;
}

Happy DAXing...

String to Validate in case letter (Uppercase/ Lowercase) [Regular Expression is AX ]

I would like to share a logic puzzle that baffled me today. This may likely work in many programming languages, but obviously I am going to show you X++ code.

Problem:
Given the following string, write a job that displays a list of all the characters that are uppercase: AbDtw%@32E

Job-1:

static void findCapitalLetters(Args _args)
{
    str testStr = "R12345678";
    int i = 1;
    str character;

    character = subStr(testStr, i, 1);

    //If the character is EQUAL to it's uppercase counterpart, it must be uppercase:
    if (char2num(testStr, i) == char2num(strUpr(testStr), i))
    {
        info(strFmt("'%1' at position %2 is an uppercase letter.", character, i));
    }
}

I assumed that you would write a loop that compared each character with it’s uppercase character and if the character is equal to it’s uppercase counterpart, the character must be uppercase. Let’s try that:

Job-2:

static void findCapitalLetters(Args _args)
{
    str testStr = "AbDtw%@32E";
    int i ;
    int stringLenght = strLen(testStr);
    str character;

    for (i = 1; i <= stringLenght; i += 1)

    {
        character = subStr(testStr, i, 1);
        //If the character is EQUAL to it's uppercase counterpart, it must be uppercase:
        if (char2num(testStr, i) == char2num(strUpr(testStr), i))
        {
            info(strFmt("'%1' at position %2 is an uppercase letter.", character, i));
        }
   }
}

Output:


OOPS! The characters %, @, 3 and 2 are evaluated as uppercase; this is not what I had in mind. So I assumed (wrongly again) that the answer would be to first check if the character is a letter (if it is a number or symbol, I can just ignore it). I was thinking along the lines of adding str2IntOk()

The simple solution:
It turns out, if you reverse the question and ask, “Is this character not equal to its lower case letter?”, you get the correct answer:

Job-3:

static void findCapitalLetters(Args _args)
{
    str testStr = "AbDtw%@32E";
    int i;
    int stringLenght = strLen(testStr);
    str character;

    for (i=1; i<=stringLenght; i+=1)
    {
        character = subStr(testStr, i, 1);
        //If the character is EQUAL to it's uppercase counterpart, it must be uppercase:
        if (char2num(testStr, i) != char2num(strlwr(testStr), i))
        {
            info(strFmt("'%1' at position %2 is an uppercase letter.", character, i));
        }
    }
}

Explanation:
At first the two jobs looked the same to me. I thought the issue was with the way strLwr() and strUpr() work. It wasn’t until I examined the return values that I realized the problem is with the logical operators and not the string funtions.
Consider this table:


If the character is a letter, it has a different value for upper and lower case. If the value is a symbol or number, it doesn’t have an upper or lower case, and the same value returned as upper and lower case.
In the case of numbers and symbols, the question “Is this character not equal to its lower case letter?” has a different answer than “Is this character equal to its upper case letter?”.

Of course, in both cases the real question you should ask yourself is: “What is the problem I am trying to solve?”.

Happy DAXing....

Thursday 6 July 2017

String to Validate only Alpha-numeric [Regular Expression is AX ]

Following is an Example of Regular Expressions :

public boolean validate()
{
    boolean     ret;
    anytype     isValid;
    str         universalNum = CustTable_HSI_UniversalId.valueStr(); // let say "R12345678"
    // or str         universalNum = CustTable_HSI_UniversalId.text(); // let say "R12345678"

    TextBuffer  txt = new TextBuffer();

   // ret = super();

    isValid = strlen(CustTable_HSI_UniversalId.valueStr());
    if (isValid == 9)
    {
        ret = true;
    }
    else
    {
        ret = false;
        throw Global::error("Universal Id should be upto 9-character");
        }
    isValid = strFind(CustTable_HSI_UniversalId.valueStr(),'R',1,1);
    if (isValid == 1)
    {
        ret = true;
    }
    else
    {
        ret = false;
        throw Global::error("Universal Id should start wiith 'R' character");
    }
    universalNum = substr(universalNum, 2,9);
    txt.setText(universalNum);
    txt.regularExpressions(true);   // activate regular expr in search

    // Regular expression to validate only digits
    if (txt.find("^[0-9]+$"))
    {
        ret = true;
    }
    else
    {
        ret = false;
        throw Global::error("Universal Id should start wiith 'R' character and contains 0-9 consecutive numbers");
    }
    ret = super();
    return ret;
}

String to Validate only alphabets [Regular Expression is AX ]

Following is an Example of Regular Expressions :

static void TextBuffer_regularExpression(Args _args)
{
    TextBuffer txt = new TextBuffer();
    str msg = "ABCDXYZ";

    txt.setText(msg);
    txt.regularExpressions(true);   // activate regular expr in search

     // Regular expression to validate only digits
     if (txt.find("^[A-Z]+$"))
    {
        info("string contains only alphabets");
    }
}

String to Validate only Numbers [Regular Expression is AX ]

Following is an Example of Regular Expressions :

Job-1:

static void TextBuffer_regularExpression(Args _args)
{
    TextBuffer txt = new TextBuffer();
    str msg = "98797897";
   
    txt.setText(msg);
    txt.regularExpressions(true);   // activate regular expr in search

     // Regular expression to validate only digits
     if (txt.find("^[0-9]+$"))
    {
        info("string contains only numbers");
    }
}

Job-2:

static void TextBuffer_regularExpression(Args _args)
 {
    TextBuffer txt = new TextBuffer();
    str msg = "R12345678";

    msg = substr(msg, 2,9);
    txt.setText(msg);
    txt.regularExpressions(true);   // activate regular expr in search

    // Regular expression to validate only digits
    if (txt.find("^[0-9]+$"))
    {
        info("string contains only numbers");
    }
    else
    {
        info("string contains not only numbers");
    }
 }

Number of ways to count the records

To get a table record count in AX 2012:------

1. To get a table record through count function:

In X++, how do you get a record count in table, I don't want to use while loop.?

Select a count on the RecId.
For example: 
CustTable   custTable;

select count(RecId) from custTable;

info(strfmt('Number of customers are: %1', custTable.RecId));

2. To get records through database.numberOfRowsLoaded()


    MainAccount             mainAccountLoc;
    CompanyInfo             companyInfoLoc;
    Int                               numOfUpdatedRec;
    MainAccountLegalEntity  mainAccountLegalEntityLoc;

    companyInfoLoc.clear();
    companyInfoLoc = CompanyInfo::findDataArea(FilterByLegalEntity.valueStr());

    mainAccountLoc.clear();
    mainAccountLoc = MainAccount_ds.getFirst(1);
    
        while(mainAccountLoc)
        {
            ttsBegin;
            select forUpdate mainAccountLegalEntityLoc
                where mainAccountLegalEntityLoc.MainAccount == mainAccountLoc.RecId
                &&    mainAccountLegalEntityLoc.LegalEntity == companyInfoLoc.RecId;
            mainAccountLegalEntityLoc.DefaultDimension = MainAccountLegalEntity.DefaultDimension;
            mainAccountLegalEntityLoc.update();
            ttsCommit;
            numOfUpdatedRec= MainAccountLegalEntity_ds.numberOfRowsLoaded();
            mainAccountLoc = MainAccount_ds.getNext();                       
        }
        info(strFmt('%1',numOfUpdatedRec));

3. To get updated/ inserted records through Integer counter:

    MainAccount             mainAccountLoc;
    CompanyInfo             companyInfoLoc;
    counter                       numOfUpdatedRec;
    MainAccountLegalEntity  mainAccountLegalEntityLoc;

    companyInfoLoc.clear();
    companyInfoLoc = CompanyInfo::findDataArea(FilterByLegalEntity.valueStr());

    mainAccountLoc.clear();
    mainAccountLoc = MainAccount_ds.getFirst(1);
    if (mainAccountLoc)
    {
        while(mainAccountLoc)
        {
            ttsBegin;
            select forUpdate mainAccountLegalEntityLoc
                where mainAccountLegalEntityLoc.MainAccount == mainAccountLoc.RecId
                &&    mainAccountLegalEntityLoc.LegalEntity == companyInfoLoc.RecId;
            mainAccountLegalEntityLoc.DefaultDimension = MainAccountLegalEntity.DefaultDimension;
            mainAccountLegalEntityLoc.update();
            numOfUpdatedRec++;
            ttsCommit;
            mainAccountLoc = MainAccount_ds.getNext();
        }
        info(strFmt('%1',numOfUpdatedRec));


4. X++ code to count records through Sysquery::countTotal() in query:

Following code illustrates how we can use SysQuery::countTotal() method to get the number of records in Query.

static void Query_cntRecords(Args _args)
{
    Query                query = new Query();
    QueryRun             queryRun;
    QueryBuildDataSource qbd;
    ;
    
    qbd = query.addDataSource(tablenum(CustTable));
    queryRun = new QueryRun(query);
    
    info(strfmt("Total Records in Query %1",SysQuery::countTotal(queryRun)));  
}


Happy DAXing....

Wednesday 22 February 2017

Create batch job through X++ code

Hi,

Case-1:

static void TID_ExecuteBatchJobDistribution(Args _args) { BatchHeader header; SysRecurrenceData sysRecurrenceData; Batch batch; BatchJob batchJob; RetailConnScheduleRunner _RetailConnScheduleRunner; // Class extends RunBaseBatch BatchInfo processBatchInfo; BatchRetries noOfRetriesOnFailure = 4; ; // Setup the RunBaseBatch Job header = BatchHeader::construct(); _RetailConnScheduleRunner = new RetailConnScheduleRunner(); processBatchInfo = _RetailConnScheduleRunner.batchInfo(); processBatchInfo.parmRetriesOnFailure(noOfRetriesOnFailure); processBatchInfo.parmCaption(_RetailConnSchedule.Name); // Description Batch Job processBatchInfo.parmGroupId('RTL'); // Batch Gorup processBatchInfo.parmBatchExecute(NoYes::Yes); header.addTask(_RetailConnScheduleRunner); // Set the recurrence data sysRecurrenceData = SysRecurrence::defaultRecurrence(); SysRecurrence::setRecurrenceStartDateTime(sysRecurrenceData, DateTimeUtil::addSeconds(DateTimeUtil::utcNow(), 20)); // Set range of recurrence SysRecurrence::setRecurrenceNoEnd(sysRecurrenceData); SysRecurrence::setRecurrenceUnit(sysRecurrenceData, SysRecurrenceUnit::Minute); // Set reccurence pattern header.parmRecurrenceData(sysRecurrenceData); // Set the batch alert configurations header.parmAlerts(NoYes::No, NoYes::Yes, NoYes::No, NoYes::Yes, NoYes::Yes); header.save(); // Update the frequency to run the job to every two minutes ttsbegin; select forupdate batchJob join batch where batchJob.RecId == batch.BatchJobId && batch.ClassNumber == classnum(RetailConnScheduleRunner); sysRecurrenceData = batchJob.RecurrenceData; sysRecurrenceData = conpoke(sysRecurrenceData, 8, [10]); batchJob.RecurrenceData = sysRecurrenceData; batchJob.update(); ttscommit; }

Case-2:

public static void scheduleBatch()
{
BatchHeader batchHeader; BatchInfo localBatchInfo; YourRunBaseBatchClass yourRunBaseBatchClass; SysRecurrenceData sysRecurrenceData = SysRecurrence::defaultRecurrence(); ;
yourRunBaseBatchClass = YourRunBaseBatchClass::construct();
// Retry 3-times
sysRecurrenceData = SysRecurrence::setRecurrenceEndAfter( sysRecurrenceData, 3);
//Retry 1-min
sysRecurrenceData = SysRecurrence::setRecurrenceUnit(sysRecurrenceData, SysRecurrenceUnit::Minute, 1); localBatchInfo = yourRunBaseBatchClass.batchinfo(); localBatchInfo.parmGroupId("YourBatchGroupId"); batchHeader = batchHeader::construct(); batchHeader.addTask(yourRunBaseBatchClass); batchHeader.parmRecurrenceData(sysRecurrenceData); batchHeader.save(); }

Happy DAXing...

Execute Menu Items through code

Hi,
I've seen some people trying to find out how to execute or run Menu Items programatically on the internet.

So I thought I'd best blog about it.

We have three types of Menu Items: Action, Output and Display.

An action is frequently a class that performs some business operation, an output is a report and a display is a form.

Menu Items are represented by the MenuFunction class. Here's some code example to run Menu Items through code:

?
1
2
3
4
MenuFunction menuFunction;
menuFunction = new MenuFunction(menuItemDisplayStr(MyDisplayMenuItem),
MenuItemType::Display);
menuFunction.run();

Of course the code above may be changed to execute different kinds of Menu Items, for example, the code below runs an Output Menu Item:
?
1
2
3
4
MenuFunction menuFunction;
menuFunction = new MenuFunction(menuItemOutputStr(MyOutputMenuItem),
MenuItemType::Output);
menuFunction.run();

And if you need to pass any arguments to the Menu Item you're trying to execute, you can pass it with an Args object. The run method accepts an Args parameter, like this:

?
1
2
3
4
5
6
7
Args args = new Args();
args.record(myArgumentRecord);
args.caller(this);
new MenuFunction(menuItemOutputStr(MyOutputMenuItem),
MenuItemType::Output).run(args);

You should always use functions to get the name of the menu item instead of using a hardcoded string. This guarantees that if someone changes the name of the menu item, your code stops compiling and you have time to fix it before shipping it. The following are the three functions used to get the menu items' name. Their names are pretty straightforward:


Happy DAXing...