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...