Wednesday, October 28, 2020

SysLookupMultiSelectCtrl in RunBaseBatch class

  class JOBTestMultiSelect extends RunBaseBatch

{

    container       stores; 

    SysLookupMultiSelectCtrl    msCtrl;

    FormBuildStringControl  fbsCtrlMultiSelect;


    // Dialog fields


    #define.CurrentVersion(1)

    #localmacro.CurrentList

            stores

    #endmacro


    public Object dialog()

    {

        DialogRunbase       dialog = super();

        FormBuildControl    setupGroupControl;

        

        DialogGroup         dialogGrp;

        dialogGrp = dialog.addGroup('Stores');

        dialog.allowUpdateOnSelectCtrl(true);


        setupGroupControl = dialog.formBuildDesign().control(dialogGrp.formBuildGroup().id());


        fbsCtrlMultiSelect = setupGroupControl.addControl(FormControlType::String, identifierstr(RetailStoreId));

        

        return dialog;

    }


    public container IdsToRecIds(container _stores)

    {

        RetailStoreTable      storeTbl;

        RetailStoreId    storeId;

        container           storeRecIds;

        int                 i;

 

        storeRecIds = conNull();

 

        for (i = 1; i <= conLen(_stores); i++)

        {

            storeId = conPeek(_stores, i);

 

            select firstOnly RecId from storeTbl

        where storeTbl.StoreNumber == storeId;

 

            storeRecIds += [storeTbl.RecId];

        }

 

        return storeRecIds;

    }


    public container RecIdsToIds(container _stores)

    {

        RetailStoreTable      storeTbl;

        RetailStoreId    storeId;

        RefRecId            storeRecId;

        container           storeIds;

        int                 i;

 

        storeIds = conNull();

 

        for (i = 1; i <= conLen(_stores); i++)

        {

            storeRecId = conPeek(_stores, i);

 

            select firstOnly StoreNumber, RecId from storeTbl

                where storeTbl.RecId == storeRecId;

 

            storeIds += [storeTbl.StoreNumber];

        }

 

        return storeIds;

    }


    public void dialogPostRun(DialogRunbase _dialog)

    {

        FormRun formRun;

        FormStringControl       fsCtrlMultiSelect;


        super(_dialog);


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


        if (formRun)

        {

            fsCtrlMultiSelect1 = formRun.design().control(fbsCtrlMultiSelect1.id());

            //

            Query query = new Query();


            QueryBuildDataSource qbds = query.addDataSource(tableNum(RetailStoreTable));

            qbds.addSelectionField(fieldNum(RetailStoreTable, StoreRecId));

            qbds.addSelectionField(fieldNum(RetailStoreTable, StoreNumber));

            qbds.addSelectionField(fieldNum(RetailStoreTable, Name));


            qbds.addRange(fieldNum(RetailStoreTable, inventLocationDataAreaId)).value(queryValue(curExt()));

            //

            msCtrl = SysLookupMultiSelectCtrl::constructWithQuery(formRun, fsCtrlMultiSelect, query, true, stores);

            msCtrl1.set([this.IdsToRecIds(stores), stores]);


        }

    }


    public boolean getFromDialog()

    {

    ;

        stores = this.RecIdsToIds(msCtrl1.get());

        return super();

    }


    public boolean init()

    {

        return true;

    }


    protected void new()

    {

        super();

    }


    public container pack()

    {

        return [#CurrentVersion,#CurrentList];

    }


    /// <summary>

    ///    Contains the code that does the actual job of the class.

    /// </summary>

    public void run()

    {

        #OCCRetryCount

        if (! this.validate())

            throw error("");


        try

        {

            ttsbegin;


            str storeConStr = con2Str(stores);

            Info(strFmt('Selected store(s): %1', storeConStr));

            //storeSet = new Set(Types::Int64);

            //for (ctrI=1; ctrI<=conLen(stores); ctrI++)

            //{

            //    storeSet.add(conPeek(stores, ctrI));

            //}

            //SetEnumerator setEnum = storeSet.getEnumerator();

            //while (setEnum.moveNext())

            //{

            //    info (strFmt('%1', setEnum.current()));

            //}


            ttscommit;

        }

        catch (Exception::Deadlock)

        {

            retry;

        }

        catch (Exception::UpdateConflict)

        {

            if (appl.ttsLevel() == 0)

            {

                if (xSession::currentRetryCount() >= #RetryNum)

                {

                    throw Exception::UpdateConflictNotRecovered;

                }

                else

                {

                    retry;

                }

            }

            else

            {

                throw Exception::UpdateConflict;

            }

        }

    }


    public boolean runsImpersonated()

    {

        return true;

    }


    public boolean unpack(container packedClass)

    {

        Version version = RunBase::getVersion(packedClass);

    ;

        switch (version)

        {

            case #CurrentVersion:

                [version,#CurrentList] = packedClass;

                break;

            default:

                return false;

        }


        return true;

    }


    public boolean validate(Object _calledFrom = null)

    {

        if (false)

            return checkFailed("");


        return true;

    }


    static JOBTestMultiSelect construct()

    {

        return new JOBTestMultiSelect ();

    }


    // Here goes a description of the class

    static ClassDescription description()

    {

        return "@SYS70984";

    }


    static void main(Args args)

    {

        JOBTestMultiSelect tutorial_RunBase;

    ;

        tutorial_RunBase = JOBTestMultiSelect ::construct();


        if (tutorial_RunBase.prompt())

            tutorial_RunBase.runOperation();

    }


    protected boolean canRunInNewSession()

    {

        return false;

    }


}

Sunday, October 23, 2016

How to get item's attribute value

We can get specific item's attribute using below code.

public static str dGetItemAttribute()
{
InventTable inventTableChk;
AttributeValueText getItemIdAttributeValue(ItemId   _itemId,
_attributeName)
{
EcoResProductAttributeValue ecoResProductAttributeValue;
AttributeValueText          attributeValueText;
InventTable                 InventTable;
EcoResAttribute             ecoResAttribute;
EcoResValue                 ecoResValue;
;
select Product from InventTable where InventTable.itemid == _itemId
    join RecId from ecoResProductAttributeValue
        where ecoResProductAttributeValue.Product == InventTable.Product
    join Name from ecoResAttribute
        where ecoResProductAttributeValue.Attribute == ecoResAttribute.RecId
            && ecoResAttribute.Name == _attributeName
    join ecoResValue
        where ecoResValue.RecId == ecoResProductAttributeValue.Value;

if (ecoResValue)
    attributeValueText = ecoResValue.value();
else
    attributeValueText = "Value? Product attribute";

return attributeValueText;
}
;
while select ItemId from InventTableChk
{
    // get value of attribute "2. Publish Price"
    info(strFmt('%1', getItemIdAttributeValue(inventTableChk.ItemId, '2. Publish Price')));
}
}

Friday, October 21, 2016

How to create AOT Map

Map can be created from AOT > Data Dictionary > Maps.

Using Map, we can access a single field from different tables with referencing single map field. So we can simplify the code.
Example, fields named PurchId in PurchTable and SalesId in SalesTable can be accessed by the name SalesPurchId.

For Map methods, we can override tables / xRecord methods, or add our own. To access the methods that only occurs in the Map (not in the table) we have to include the segment "varMap.JJK_Map::" because of the uniqueness of the method name.

public static str dTestMap()
{
//jjk_map is a map for purchTable and salesTable
JJK_Map map;
PurchTable purchTable;
SalesTable salesTable;
;
//mapMethod only exist in JJK_Map
map.mapMethod(); //no error will be thrown if no table buffer had been assigned to the map.
purchTable = PurchTable::find("PO0001");
map = purchTable;
map.mapMethod(); //error will be thrown because the buffer had been assigned to the map.
map.JJK_Map::mapMethod(); //we need to call the method with the segment
}

Thursday, October 20, 2016

How to get a message from infolog

To get the infolog in X++, you can use RetailTransactionService::getInfologMessages method:

public static str getInfoLog()
{
str         error;
int         fromLine = Global::infologLine();
info("this is an infolog");
error = RetailTransactionService::getInfologMessages(fromLine);
info(strfmt('error: %1', error));
}

or you can use below method.

Wednesday, August 17, 2016

How to create Lookups on Dialog: SysTableLookup and registerOverrideMethod

We can create a lookup in dialog without hardcode the field object (Fld1_1_lookup) using the form control's registerOverride method.

SysTableLookup can be used in dialogs to create custom lookups.
control.registerOverrideMethod can be used to register the methods to override by an object.
Here is the example which shows basic usage of both. We will create a dialog and get a lookup of account numbers on a control.

Tuesday, August 2, 2016

How to deserialize xml into table from code

class JJK_XMLDeserializer
{
Common common;
str xmlStr;
Integer tableLevel;
}

public str deserialize()
{
XmlTextReader xmlTextReader; int level = 0;
str fieldName;
;

xmlTextReader = XmlTextReader::newXml(xmlStr, true);

while(xmlTextReader.read())
{
switch (xmlTextReader.NodeType())
{
case XmlNodeType::Element: // The node is an element.
//print ("<" + xmlTextReader.Name() + ">");
level++;
if ( level == tableLevel )
{
common.clear();
}
fieldName = xmlTextReader.name();
break;
case XmlNodeType::Text: //Display the text in each element.
//print (xmlTextReader.Value());
this.processRecord(common, fieldName, xmlTextReader.value());
break;
case XmlNodeType::EndElement: //Display the end of the element.
//print ("</" + xmlTextReader.Name() + ">");
level--;
if ( level==tableLevel-1 )
{
common.insert();
common.clear();
}
break;
}
}
return "";
}

public void processRecord(Common _common = common, str _fieldName = '', str _fieldValue = '')
{
DictTable dTable = new DictTable(_common.TableId);
DictField dField;
int i, fieldId;
str value;
;
// Loop through all the fields in the record
for (i=1; i<=dTable.fieldCnt(); i++)
{
// Get the fieldId from the field-count
fieldId = dTable.fieldCnt2Id(i);

// Find the DictField object that matches the fieldId
dField = dTable.fieldObject(fieldId);

// Skip system fields
if (dField.isSystem())
continue;

if ( dField.name()!=_fieldName )
{
continue;
}
// Convert values to string. I have just added
// a couple of conversion as an example.
// Use tableName.(fieldId) instead of fieldname
// to set the content to the field.

switch (dField.baseType())
{
case Types::Int64 :
_common.(fieldId) = str2int64(_fieldValue);
break;
case Types::Integer :
_common.(fieldId) = str2int(_fieldValue);
break;
default :
_common.(fieldId) = _fieldValue;
break;
}
return;
}
}

Test string:
'<xml >'
+'<JJK_XMLAttachment action="create">'
+'<RefRecId>555</RefRecId>'
+'<RefTableId>555</RefTableId>'
+'<XMLDocument>Test</XMLDocument>'
+'</JJK_XMLAttachment>'
+'<JJK_XMLAttachment action="create">'
+'<RefRecId>222</RefRecId>'
+'<RefTableId>222</RefTableId>'
+'<XMLDocument>Test222</XMLDocument>'
+'</JJK_XMLAttachment>'
+'</xml>';

Friday, May 20, 2016

Assembly containing type %1 is not referenced.

When AIF / web services / dll references cannot be called (with error: Assembly containing type %1 is not referenced.) Then you need to give AOS user an access to "Program Files\Microsoft Dynamics AX\60\[YOUR AOS NAME]\bin\VSAssemblies" folder.

After that, restart the AOS. If the error still persist then do the full compile.

Tuesday, March 22, 2016

How to create multiple values in query range

You can create multiple values in query range from x++, where the code is:

QueryBuildDataSource qbds = q.dataSourceTable(TableId);
QueryBuildRange qbr;
while (...)
{
    qbr = qbds.addRange(fieldNum(TableId, FieldId));    qbr.value(queryValue(TableId.fieldId));
}
QueryBuildRange qbr = q.dataSourceTable(TableId).addRange(fieldNum(TableId, FieldId));
container c;
while (...)
{
    c+= queryValue(TableId.FieldId);
}
qbr.value(con2str(c));

Tuesday, March 15, 2016

How to override form datasource with another query

You can override form datasource with another (filtered) query.

You can put the code in datasource.executeQuery()

public void executeQuery()
{
    FormDataSource fds;
    ;
    //info(strFmt('1--%1', this.query().toString()));
    queryClass.executeQuery();
 
    fds = this.formRun().dataSource();
    fds.query(queryClass.parmQuery());
 
    //info(strFmt('1.1--%1', fds.query().toString()));
    //info(strFmt('2--%1', queryClass.parmQuery().toString()));
    super();
}

Where the "queryClass" is a class that create a filtered query.

Wednesday, March 9, 2016

How to comparing 2 field in AOT Query

The rules for creating query range value expressions are:
  • Enclose the whole expression in parentheses.
  • Enclose all subexpressions in parentheses.
  • Use the relational and logical operators available in X++.
  • Only use field names from the range's data source.
  • Use the dataSource.field notation for fields from other data sources in the query.


Tuesday, March 8, 2016

How to open FilePath EDT on form

If the filepath dialog is not opening on Form, U have to add below method on form (AOT > MyForm > Method > filePathLookupTitle)

public str filePathLookupTitle()
{
    return 'Import folder';
}

Sunday, March 6, 2016

How to create AOT object lookup

static void lookupAOTTables(FormStringControl _ctrl)
{
   SysTableLookup sysTableLookup =   SysTableLookup::newParameters(tablenum(UtilidElements), _ctrl);
   Query query = new Query();
   QueryBuildDataSource queryBuildDataSource;
   QueryBuildRange qbrType;
   ;
   sysTableLookup.addLookupfield(fieldnum(UtilidElements, Id));
   sysTableLookup.addLookupfield(fieldnum(UtilidElements, Name));

   queryBuildDataSource = query.addDataSource(tablenum(UtilidElements));

   qbrType= queryBuildDataSource.addRange(fieldnum(UtilidElements, RecordType));
   qbrType.value(SysQuery::value(UtilElementType::Table));

   sysTableLookup.parmQuery(query);
   sysTableLookup.performFormLookup();
}

Saturday, February 27, 2016

Cara Membuat Lookup Dimension di report contract

Didalam artikel ini kita akan membahas bagaimana cara membuat lookup untuk memilih Dimension attribute pada report parameter.

Pertama-tama kita harus membuat UIBuilder class  yang akan dipakai oleh SSRS Report kita. Lalu di dalam class tersebut kita buat sebuah method lookup dimension.

How to add SalesParmUpdate.specQty

First add a new element in SalesUpdate enum.

example: add JJK_TestElement in SalesUpdate enum.
After that add a logic in SalesQuantity class for the new added enum. There are 2 method in SalesQuantity class that can be use to calculate qty, which are calcQtySales and calcQtyInvent.

Friday, February 12, 2016

Cara men-set dialogField dari Form

Untuk men-set value pada dialogField dari Args nya form, dapat menggunakan method: pack() dan unpack().

Thursday, September 17, 2015

Friday, April 24, 2015

Cara Mengakses External Database Menggunakan TSQL OLEDB dari X++

Pada post sebelumnya, kita sudah membahas bagaimana cara meng-akses external database menggunakan class ODBC. Tetapi class ODBC ini memiliki keterbatasan untuk versi 2012 keatas, yaitu tidak dapat mengakses database server menggunakan user id yang lain (walaupun kita sudah menggunakan loginProperty.setOther), account yang digunakan adalah AOS service account.

Thursday, September 18, 2014

Manipulasi string menggunakan char2Num, num2Char dan strAlpha pada AX 2012

Ada beberapa cara dalam menghilangkan ascii dalam AX 2012. Misalnya jika kita ingin menghilangkan enter / newLine pada alamat, maka kita dapat menggunakan syntax berikut:
strReplace(companyInfo.postalAddress().Address, num2char(10), num2char(32))
Kode diatas menunjukkan bagaimana cara untuk merubah newLine character (num2Char(10)) dengan space character (num2Char(32)).


Jika kita tidak mengetahui nilai suatu karakter, maka kita dapat menggunakan method char2Num.
strfmt('%1--%2--%3', char2Num('\n', 1), char2Num('\t', 1), char2Num(' ', 1))

Wednesday, April 30, 2014

Cara mensetting report ke pdf

Kita dapat mensetting agar report yang kita buka akan otomatis tersimpan ke dalam file PDF.
Kita harus meng-override method preRunModifyContract yang terdapat pada class Report Controller.

Contoh:

protected void preRunModifyContract() {
     FolderName lTmpPath = System.IO.Path::GetTempPath();
     FileNameOpen lFile;
     SRSPrintDestinationSettings gPrintDestSetting;
     ;
     gPrintDestSetting = new SRSPrintDestinationSettings();
     lFile = strfmt('%1file.pdf', lTmpPath);
     gPrintDestSetting.printMediumType(SRSPrintMediumType::File);
     gPrintDestSetting.filename(lFile);
     gPrintDestSetting.fileFormat(SRSReportFileFormat::PDF);
     gPrintDestSetting.overwriteFile(true);
     this.parmReportContract().parmPrintSettings(gPrintDestSetting);
}
Setelah itu kita buat Menu Item tipe Output yang akan diarahkan ke controller class ini.

Monday, April 7, 2014

Cara mengirim notifikasi melalui email

Microsoft Dynamics AX 2012 memiliki fungsi mengirimkan notifikasi melalui Email. Contoh yang sering digunakan adalah mengirimkan invoice yang terbentuk kepada Customer.

Class yang akan kita gunakan adalah: SysEmailTable::sendMail.

Email notifikasi yang dikirimkan dapat berupa plain text atau attachment. Pada artikel ini akan diterangkan email berupa plain text.

Ada beberapa langkah yang harus dilakukan agar fungsi email ini dapat digunakan: