Convert Product to Product master

Changing the table inheritance of a record must be done on SQL level – the following code does this. Of course, related data must be handled as well.

static void Job1(Args _args)
{
    //change to product master
    class1::changeInheritance('EcoResProduct',tableNum(EcoResProductMaster),EcoResProduct::findByDisplayProductNumber('0119').RecId);
    //change back
    //class1::changeInheritance('EcoResProduct',tableNum(EcoResDistinctProduct),EcoResProduct::findByDisplayProductNumber('0119').RecId);
}
//Important: this code must be run on the server
public static server void changeInheritance(str _tableName, tableId _newTableId, RefRecId   _refRecId)
{
    Connection      connection;
    Statement       statement;
    str             query;
    ;
    // create connection object
    connection = new Connection();
    // create statement
    statement = connection.createStatement();
    // Set the SQL statement
    query = strfmt("update %1 set %1.InstanceRelationType ='%2' where %1.RecId = %3;", _tableName, _newTableId, _refRecId);
    info(query);
    // assert SQL statement execute permission
    new SqlStatementExecutePermission(query).assert();
    //BP Deviation documented
    statement.executeUpdate(query);
    // limit the scope of the assert call
    CodeAccessPermission::revertAssert();
}

Better Default Number Sequences

I’m not sure what you think, but I don’t like that most of the number sequences the AX 2012 Wizard creates contain the company.

The following job removes the company from all number sequences:

 

static void NumberSequenceSegments_RemoveCompany(Args _args)
{
    str                 annotatedFormat;
    str                 format;
    container           segments;
    NumberSequenceTable numberSequenceTable;
    boolean             companyFound;
    int                 i, j;
    str                 company;
    ;
    ttsBegin;
    while select forUpdate numberSequenceTable
    {
        companyFound = false;
        segments = NumberSeq::parseAnnotatedFormat(numberSequenceTable.AnnotatedFormat);
        for(i=1;i<=conLen(segments);i++)
        {
            if(conPeek(conPeek(segments,i),1) == 0)
            {
                company = conPeek(conPeek(segments,i),2);
                segments = conDel(segments,i,1); //remove company from number sequence
                companyFound = true;
                i--;
            }
            else if(companyFound && conPeek(conPeek(segments,i),1) == -1 && conPeek(conPeek(segments,i),2) == "-")
            {
                segments = conDel(segments,i,1); //remove "-" after company from number sequence
                i--;
                break;
            }
        }
        if(companyFound)
        {
            annotatedFormat = NumberSeq::createAnnotatedFormatFromSegments(segments);
            format = NumberSeq::createAnnotatedFormatFromSegments(segments, false);

            if(company)
            {
                numberSequenceTable.Txt = strFmt("%1 (%2)",numberSequenceTable.Txt,company);
            }
            numberSequenceTable.AnnotatedFormat = annotatedFormat;
            numberSequenceTable.Format = format;
            numberSequenceTable.update();
            j++;
        }
    }
    ttsCommit;
    info(strFmt("@SYS74545",j,tableId2pname(tableNum(numberSequenceTable))));
}

Enable Database Log on all Parameter- and Group-Tables

I want to make sure that I know who changed what and when on all parameter- and group-tables.

The following script will enable the database log on all those tables:

static void enableDatabaselogOnParameterTables(Args _args)
{
    #AOT
    DatabaseLog         databaseLog;
    TreeNode            treeNode;
    SysDictTable        sysDictTable;
    Name                name;
    ;
    ttsbegin;
    treeNode = TreeNode::findNode(#TablesPath);
    treeNode = treeNode.AOTfirstChild();
    while (treeNode)
    {
        name = treeNode.AOTname();
        sysDictTable = SysDictTable::newTableId(treeNode.applObjectId());
        if((sysDictTable.tableGroup() == TableGroup::Parameter || sysDictTable.tableGroup() == TableGroup::Group)
            && SysQueryForm::hasValidCountryCode(sysDictTable.id()))
        {
            select firstOnly databaseLog where databaseLog.logTable == sysDictTable.id(); //do not modify existing settings
            if(!databaseLog)
            {
                databaseLog.LogTable = sysDictTable.id();
                databaseLog.LogField = 0;
                databaseLog.LogType = DatabaseLogType::Update;
                databaseLog.insert();
                databaseLog.LogType = DatabaseLogType::EventUpdate;
                databaseLog.insert();
                if(sysDictTable.tableGroup() != TableGroup::Parameter)
                {
                    databaseLog.LogType = DatabaseLogType::Delete;
                    databaseLog.insert();
                    databaseLog.LogType = DatabaseLogType::EventDelete;
                    databaseLog.insert();
                    databaseLog.LogType = DatabaseLogType::RenameKey;
                    databaseLog.insert();
                    databaseLog.LogType = DatabaseLogType::EventRenameKey;
                    databaseLog.insert();
                    databaseLog.LogType = DatabaseLogType::Insert;
                    databaseLog.insert();
                    databaseLog.LogType = DatabaseLogType::EventInsert;
                    databaseLog.insert();
                }
            }
        }
        treeNode = treeNode.AOTnextSibling();
    }
    ttscommit;
    SysFlushDatabaseLogSetup::main();
}

How to get radio buttons on a dialog

There is no dialog.addRadioButtonContol(…) function so how do you do it?

static void Job1(Args _args)
{
    Dialog                dialog;
    FormRadioControl      formRadioControl;
    FormBuildRadioControl formBuildRadioControl;
    FormBuildGroupControl formBuildGroupControl;
    int                   formBuildRadioControlId;
    ;
    //Build dialog with radio buttons
    dialog = new Dialog("Test Dialog");
    formBuildGroupControl = dialog.mainFormGroup();
    formBuildRadioControl = formBuildGroupControl.addControl(FormControlType::RadioButton,'radiobuttons');
    formBuildRadioControlId = formBuildRadioControl.id();
    //set number of buttons
    formBuildRadioControl.items(3);
    //add descriptions
    formBuildRadioControl.item(1);
    formBuildRadioControl.text("Radiobutton 1");
    formBuildRadioControl.item(2);
    formBuildRadioControl.text("Radiobutton 2");
    formBuildRadioControl.item(3);
    formBuildRadioControl.text("Radiobutton 3");
 
    if(dialog.run())
    {
        if(formBuildRadioControl)
        {
            //get control
            formRadioControl = dialog.formRun().control(formBuildRadioControlId);
            //get index
            info(strfmt("Radiobutton %1 selected",formRadioControl.selection()+1));
        }
    }
}

Find Datatypes

I like reusing/extending data types that already exist, however it is not always easy to find them. The following job lets you search the existing data types for a keyword:

static void findEDT(Args _args)
{
    Dialog              dialog = new Dialog('Find EDT');
    SearchString        searchString;
    DialogField         dialogField = dialog.addField('SearchString');
    void searchNodes(boolean _extendedDataTypes)
    {
        TreeNode            treeNode;
        TreeNodeIterator    treeNodeIterator;
        SysDictType         sysDictType;
        SysDictEnum         sysDictEnum;
        ;
        if(_extendedDataTypes)
        {
            treeNode = TreeNode::findNode('\\Data Dictionary\\Extended Data Types');
        }
        else
        {
            treeNode = TreeNode::findNode('\\Data Dictionary\\Base Enums');
        }
        treeNodeIterator = treenode.AOTiterator();
        treeNode = treeNode.AOTfirstChild();
        treeNode = treeNodeIterator.next();
        while(treeNode)
        {
            if(treeNode.treeNodeName() like "*" + searchString + "*")
            {
                if(_extendedDataTypes)
                {
                    sysDictType = sysDictType::newTreeNode(treeNode);
                }
                if(sysDictType)
                {
                    if (sysDictType.extend() && sysDictType.enumId())
                    {
                        info(strfmt("%1 (%2 - %3 -> %4)",treeNode.treeNodeName(), sysDictType.baseType()
                            , extendedTypeId2name(sysDictType.extend()), enumId2Name(sysDictType.enumId()) ));

                    }
                    if(sysDictType.extend())
                    {
                        info(strfmt("%1 (%2 - %3)",treeNode.treeNodeName(), sysDictType.baseType()
                            , extendedTypeId2name(sysDictType.extend()) ));

                    }
                    else if (sysDictType.enumId())
                    {
                        info(strfmt("%1 (%2 - %3)",treeNode.treeNodeName(), sysDictType.baseType()
                            , enumId2Name(sysDictType.enumId()) ));

                    }
                    else
                    {
                        info(strfmt("%1 (%2)",treeNode.treeNodeName(), sysDictType.baseType()));
                    }
                }
                else
                {
                    info(strfmt("%1",treeNode.treeNodeName()));
                }
            }
            treeNode = treeNodeIterator.next();
        }
    }
    ;
    if(dialog.run())
    {
        searchString = dialogField.value();
        info(strfmt("Find %1",searchString));
        if(searchString)
        {
            setPrefix('Extended Data Types');
            searchNodes(true);
            setPrefix('Base Enums');
            searchNodes(false);
        }
    }
}

Identify country specific functionality

Add the following to SysCountryRegionCode::isLegalEntityInCountryRegion

Idea by Kim Steffensen (Microsoft Escalation engineer)

public static boolean isLegalEntityInCountryRegion(container _isoCountryCodes, selectableDataArea _selectableDataArea = curext())
{
    #Define.DEBUG_ALL(true)                 //set true to get infolog for every country evaluated
    #Define.DEBUG_SINGLECOUNTRY(false)      //set true to get infolog when active country is evaluated
    int                             i;
    str                             callingPath;
    int                             line;

    if(#DEBUG_ALL || #DEBUG_SINGLECOUNTRY)
    {   //initialize context
        callingPath = subStr(conPeek(xSession::xppCallStack(),3),4,999);
        line = conPeek(xSession::xppCallStack(),4);
    } 

    if(#DEBUG_ALL && ! #DEBUG_SINGLECOUNTRY)
    {   //print context of every country evaluated
        for(i=1; i <= conLen(_isoCountryCodes); i++)
        {
            info(strFmt("%1 %2 %3",conPeek(_isoCountryCodes,i),callingPath,line),"",SysInfoAction_Editor::newLineColumn(callingPath,line));
        }
    }
    if (conFind(_isoCountryCodes, SysCountryRegionCode::countryInfo(_selectableDataArea)))
    {
        if(#DEBUG_SINGLECOUNTRY)
        {   //print context of active country evaluated
            info(strFmt("%1 %2 %3",SysCountryRegionCode::countryInfo(_selectableDataArea),callingPath,line),"",SysInfoAction_Editor::newLineColumn(callingPath,line));
        }
        return true;
    }

    return false;
}