Friday, November 21, 2014

How to print labels in multiple languages

This is a short job to print labels in English US and English Canada. The prefix defines the label file.

static void printLabels(Args _args)
{
    int     labelNum = 1;
    int     labelIndex;
    int     labelMaxNum = 216;
    str     labelPrefix = "@CGI";
    str     labelStr;
    str     languageIdEN = 'en-us';
    str     languageIdFR = 'en-ca';

    for (labelIndex = labelNum; labelIndex <= labelMaxNum; labelIndex++)
    {
        labelStr = labelPrefix + int2str(labelIndex);
        labelStr = strFmt("%1 : %2 /// %3", labelStr,
                                            SysLabel::labelId2String2(strFmt("%1", labelStr), languageIdEN),
                                            SysLabel::labelId2String2(strFmt("%1", labelStr), languageIdFR));
        info( labelStr);
    }
}

Tuesday, November 11, 2014

How to convert a wrong date

Standard str2date function tries to fix a wrong date. For example, if we got as a parameter fantastic June 31th, it returns us June 30, which can be a bad result for your business case.



There are at least two possible solutions for that.

First, by converting the result back to a string and comparing it with the initial string.

private container cgiValidateDate(str _dateStr)
{
    date                retDate     = str2Date(_dateStr, #cgiDateFormat);
    boolean             isOK        = true;
    str                 madeDateStr = date2StrUsr(retDate, DateFlags::FormatAll);

    if(retDate == dateNull() || _dateStr != madeDateStr)
    {
        error(strFmt("Date %1 is incorrect", _dateStr));
        isOK = false;
    }

    return [isOK, retDate];
}


Second, by using .Net function tryParse.


private container cgiValidateDate(str _dateStr)
{
    date                retDate;
    utcDateTime         retDateTime;
    boolean             isOK        = true;
    
    if(!System.DateTime::TryParse(_dateStr, byref retDateTime))
    {
        error(strFmt("Date %1 is incorrect", _dateStr));
        isOK = false;
    }
    else
    {
        retDate = DateTimeUtil::date(retDateTime);
    }
    return [isOK, retDate];
}


Happy date converting!

Wednesday, November 5, 2014

Dialog field with multiple choice

This is a small tutorial class on how to work with the new AX 2012 SysLookupMultiSelectCtrl class.

There are good examples on the internet as well as a tutorial class in AX 2012 itself but I want to explain how to pack/unpack values for this field and make it mandatory.

Let's say we need to select multiple sites in the dialog.



I hope my comments inline will be enough. If not let me know, please.


class tmxMultiSelectSiteTutorial extends RunBase
{
    FormBuildStringControl      fbscMultiSite;
    FormStringControl           fscMultiSite;
    container                   siteIds;
    str                         siteIdsStr;

    SysLookupMultiSelectCtrl    multiSiteCtrl;
    DialogRunbase               dialog;

    #define.CurrentVersion(1)
    #define.Version1(1)
    #localmacro.CurrentList
        siteIdsStr
    #endmacro
}



protected Object dialog()
{
    dialog = super();
    // add a new form build control for multiple choice; string type
    fbscMultiSite        = dialog.curFormGroup().addControl(FormControlType::String, identifierstr(AnyFormControlNameYouLike));
    fbscMultiSite.label('Site');

    return dialog;
}





public void dialogPostRun(DialogRunbase _dialog)
{
    FormRun formRun;

    super(dialog);

    formRun = _dialog.dialogForm().formRun();

    if (formRun)
    {
        // to get the access to the form control we created on the dialog
        fscMultiSite = formRun.design().control(fbscMultiSite.id());
        // create multiple loookup of SysLookupMultiSelectCtrl type
        // cgiInventSite query must exist in AOT; simply SiteId and Name from InventSite table
        multiSiteCtrl = SysLookupMultiSelectCtrl::construct(formRun, fscMultiSite, querystr(cgiInventSite));
        // to underline it red; actually it does not validate; so check the Validate method
        multiSiteCtrl.setMandatory(true);
        // if we restored from last values
        if(siteIdsStr)
        {
            //then we convert the string to container
            siteIds = str2con(siteIdsStr);
            // after create the special container of SiteIds and Names
            multiSiteCtrl.set(this.siteIds2Names(siteIds));
        }
    }
}





public boolean getFromDialog()
{
    if (multiSiteCtrl)
    {
        // selected sites convert to container of RecIds
        siteIds     = multiSiteCtrl.get();
        // convert it to string for pack/unpack
        siteIdsStr  = con2Str(siteIds);
    }
    return super();
}





private container siteIds2Names(container _c)
{
    InventSite      inventSite;
    container       contSiteId, contRecId;
    int             i, cLen = conLen(_c);

    for (i = 1 ; i <= cLen ; i++)
    {
        inventSite = inventSite::findRecId(conPeek(_c, i));
        // this part will be visible
        contSiteId += [inventSite.SiteId];
        // this part will be used by SysLookupMultiSelectCtrl as invisible
        contRecId  += [inventSite.RecId];
    }
    return [contRecId, contSiteId];
}





public boolean validate(Object _calledFrom = null)
{
    boolean ret;

    ret = super(_calledFrom);

    if(!conPeek(siteIds, 1))
    {
         ret = checkFailed('Site must be selected!');
    }

    return ret;
}





public void run()
{
    InventSite  inventSite;
    int         i;
    int         conNum = conLen(siteIds);

    // any business logic for the selected sites
    for( i = 1; i<=conNum; i++)
    {
        inventSite = inventSite::findRecId(conPeek(siteIds, i));
        info(strFmt("Site: %1 - %2", inventSite.SiteId, inventSite.Name));
    }
}






Labels and Best Practices




Guys, please, do not forget that AX is really multilingual system and there are many other languages than English. Happy labelling!