Monday, February 24, 2014

Creating Custom AIF Service in Ax 2009 / 2012

Creating the below custom service to print the available physical quantity of particular item.

1. Creating a class 'InventOnHandService'

2. Create a method under this class 'itemOnHandPhysical'

//[SysEntryPointAttribute(true)]
public InventQty itemOnHandPhysical(ItemId  _itemId = '')
{
    SalesLine       salesLine;
    InventOnhand    inventOnHand;
    InventMovement  movement;
    InventQty       qty = 0;

    select firstOnly salesLine
        where salesLine.ItemId == _itemId;

    if (salesLine)
    {
        movement     = InventMovement::construct(salesLine);
        inventOnhand = InventOnhand::newPhysicalUpdate(movement,
                                                       movement.inventdim());

        qty          = inventOnHand.availPhysical();
    }

    return qty;
}

3. Create new service 'InventOnHandService'. Change the Class property to 'InventOnHandService'

4. Add Operation 'itemOnHandPhysical'.

5. To test the above custom service, create a job 'InventOnHandService_CustomService'

static void InventOnHandService_CustomService(Args _args)
{
    // Customer Service class
    InventOnHandService     inventService;
    inventQty inventQty;
    ;
    inventService = new InventOnHandService();
    inventQty = inventService.itemOnHandPhysical('1001');
    info(num2str(inventQty,0,2,1,1));
}

Monday, February 17, 2014

Run Batch Job automatically in Ax 4.0

In Class 'SysStartupCmdBatchRun', add the code in infoRun method.

void infoRun()
{
    BatchRun batchRun = new BatchRun();
;

    batchRun.parmUseForm(true);
    batchRun.parmGroupId(parm); //added
    batchRun.run();
}
in the client configuration, mention the batchgroup name like startupcmd=batch_invoice

Batch Job

Creating sample batch job

class AV_RunbaseBatchDemo extends RunbaseBatch
{
    NoYes           displayMessage;
    Description     message;

    DialogField     fieldDisplayMessage;
    DialogField     fieldMessage;

    #define.CurrentVersion(1)

    #localmacro.CurrentList
        displayMessage,message
    #endmacro
}
protected Object dialog()
{
    DialogRunbase dlg;
    ;

    dlg                 = super();
    fieldDisplayMessage = dlg.addFieldValue(typeId(NoYes), displayMessage, "Display message", "Indicates whether to display a message or not.");
    fieldMessage        = dlg.addFieldValue(typeId(Description), message, "Message", "The message to display");

    return dlg;
}
public boolean getFromDialog()
{
    boolean ret;
    ;

    ret             = super();
    displayMessage  = fieldDisplayMessage.value();
    message         = fieldMessage.value();

    return ret;
}
public void initParmDefault()
{
    super();
    displayMessage  = NoYes::Yes;
    message         = "Hello World!";
}
public container pack()
{
    return [#CurrentVersion, #CurrentList];
}
public void run()
{
    if (displayMessage)
    {
        info(message);
    }
}
public boolean unpack(container _packedClass)
{
    Integer version = RunBase::getVersion(_packedClass);
    ;

    switch (version)
    {
        case #CurrentVersion:
            [version, #CurrentList] = _packedClass;
            break;

        default:
            return false;
    }

    return true;
}
public static AV_RunbaseBatchDemo construct()
{
    return new AV_RunbaseBatchDemo();
}
public static ClassDescription description()
{
    return "Runbase batch demo";
}
public static void main(Args    _args)
{
    AV_RunbaseBatchDemo runbaseBatchDemo = AV_RunbaseBatchDemo::construct();
    ;
    if (runbaseBatchDemo.prompt())
    {
        runbaseBatchDemo.run();
    }
}

Dependent lookup in dialog class

class dialogDepLookup extends runbase
{
    DialogRunbase       dlg;
    DialogField     fieldCustAccount;
    DialogField     fieldCustTransDate;

    CustAccount     custAccount;
    TransDate       transDate;

    #define.CurrentVersion(1)

    #localmacro.CurrentList
        custAccount,transDate

    #endmacro
}
protected Object dialog()
{
    dlg = new DialogRunbase("test",this);


    //dlg                 = super();
    fieldCustAccount = dlg.addFieldValue(typeId(CustAccount),'', "Customer Account");
    fieldCustTransDate = dlg.addFieldValue(typeId(TransDate), datenull(), "Transaction Date");

    return dlg;
}
public void dialogPostRun(DialogRunbase _dialog)
{
    super(_dialog);

    _dialog.dialogForm().formRun().controlMethodOverload(true);
    _dialog.dialogForm().formRun().controlMethodOverloadObject(this);
}
void Fld2_1_lookup()
{
    Object control = dlg.formRun().controlCallingMethod();
    ;

    this.transDate_Lookup(control, fieldCustAccount.value());
}
public boolean getFromDialog()
{
    boolean ret;
    ;

    ret             = super();
    custAccount  = fieldCustAccount.value();
    transDate         = fieldCustTransDate.value();

    return ret;
}
public container pack()
{
    return [#CurrentVersion, #CurrentList];
}
void run()
{
    //#OCCRetryCount
    //setprefix("@SYS3761");

    try
    {
        info(custAccount);
        info(date2strxpp(transDate));
    }
    catch (Exception::Deadlock)
    {
        retry;
    }   
}
public void transDate_Lookup(FormStringControl _ctrl,CustAccount _custAccount)
{
    SysTableLookup          sysTableLookup = SysTableLookup::newParameters(tablenum(CustTrans),_ctrl);
    Query                   query = new Query();
    QueryBuildDataSource    queryBuildDataSource = query.addDataSource(tablenum(CustTrans));
    ;

    sysTableLookup.addLookupfield(fieldnum(CustTrans,TransDate));
    queryBuildDataSource.addRange(fieldnum(CustTrans,AccountNum)).value(_custAccount);
    queryBuildDataSource.orderMode(orderMode::GroupBy);
    queryBuildDataSource.addSortField(fieldnum(CustTrans,TransDate));

    sysTableLookup.parmQuery(query);
    sysTableLookup.performFormLookup();
}
public boolean unpack(container _packedClass)
{
    Integer version = RunBase::getVersion(_packedClass);
    ;

    switch (version)
    {
        case #CurrentVersion:
            [version, #CurrentList] = _packedClass;
            break;

        default:
            return false;
    }

    return true;
}
public static void main(Args    _args)
{
    dialogDepLookup dialogDepLookup = new dialogDepLookup();
    ;
    if (dialogDepLookup.prompt())
    {
        dialogDepLookup.run();
    }
}

Create new project through X++

static void createProject(Args _args)
{
    sysprojectfilterrunbase upgradeproject;
    utilelements            theelements;
    ;

    upgradeproject = new sysprojectfilterrunbase();
    upgradeproject.parmProjectNode(systreenode::createProject('test'));
    upgradeproject.grouping(sysprojectgrouping::AOT);

    while select name, utilLevel, RecordType, ParentId from theelements
    where theelements.utilLevel == UtilEntryLevel::usr
    {
        try
        {
            theelements.reread();
            upgradeproject.doUtilElements(theelements);
        }
        catch (exception::Error)
        {
            throw error('error');
        }
    }

    upgradeproject.write();
    info('finish');
}

Creating context (Right-click) menu for display field

Creating right click filter options for display field disCustName (Customer Name)

Add the override method 'Context' for the name field

public void context()
{
    int             selectedMenu;
    formrun         fr;
    Args            ag;
    Name            strtext;
    querybuilddataSource qb1;
    queryrun    qr;
    query       q;
    PopupMenu menu = new PopupMenu(element.hWnd());
    int a = menu.insertItem('Filter By Field'); //2
    int b = menu.insertItem('Filter By Selection'); //3
    int c = menu.insertItem('Remove Filter'); //4
    ;

    selectedMenu = menu.draw();
    print selectedMenu;pause;
    switch (selectedMenu)
    {
    case -1: //Filter by field
            break;
    case 2:
            ag = new args('SysformSearch');
            fr = new formrun(ag);
            fr.run();
            fr.wait();
//Reading User entered value for filter process
            strtext = '';
            strtext = fr.design().controlName('FindEdit').valueStr();

            if(strtext)
            {
//Creating a query for filter
                q   = smmBusRelTable_ds.query();
                qb1 = q.dataSourceTable(tablenum(smmBusRelTable));
                qb1 = qb1.addDataSource(TableNum(CustTable));
                qb1.addLink(FieldNum(smmBusRelTable,CustAccount),FieldNum(CustTable,AccountNum));
                //strtext = strreplace(disCustName.text(),',','*');
                info(strtext);
                qb1.addRange(FieldNum(CustTable,Name)).value(strtext);
                smmBusRelTable_ds.query(Q);
                info(qb1.toString());
                smmBusRelTable_ds.executeQuery();
            }
            break;

    case 3:   // Filter By Selection
         
            q   = smmBusRelTable_ds.query();
            qb1 = q.dataSourceTable(tablenum(smmBusRelTable));
            //qb1.clearRanges();
            qb1 = qb1.addDataSource(TableNum(CustTable));
            qb1.addLink(FieldNum(smmBusRelTable,CustAccount),FieldNum(CustTable,AccountNum));
            //info(disCustName.text());
            strtext = strreplace(disCustName.text(),',','*');
            qb1.addRange(FieldNum(CustTable,Name)).value(strtext);
            smmBusRelTable_ds.query(Q);
            info(qb1.toString());
            //qr = new queryrun(smmBusRelTable_ds.query());
            smmBusRelTable_ds.executeQuery();
            break;
    case 4 :   // Remove Filter
            q   = new Query();
            qb1 = q.addDataSource(tablenum(smmBusRelTable));
            qb1.clearLinks();
            qb1.clearRanges();
            smmBusRelTable_ds.query(Q);
            smmBusRelTable_ds.removeFilter();
            break;

    Default:
            break;
    }

}

Using DictTable

Truncate table using DictTable object

class SqlPermissionJobCls
{
}
static void main(args _args = null)
{
    DictTable dictTable = new DictTable(tablenum(TestTable));
    str sqlTableName;
    SqlDataDictionary sqlTable;

    if (dictTable && dictTable.enabled())
    {
        sqlTableName = dictTable.name(DbBackend::Sql);
        sqlTable = new SqlDataDictionary();
        // Try to truncate only if the table does exist
        // in the SQL database.
        if (sqlTable.tableExist(sqlTableName))
        {
            new SqlDataDictionaryPermission(
                methodstr(SqlDataDictionary, tableTruncate)).assert();
            sqlTable.tableTruncate(tablenum(TestTable));
            CodeAccessPermission::revertAssert();
        }
    }
}

Using sub-query through X++

static void SelectAsExpression3Job(Args _args)
{
    int64 nRecId,
        nCount;
    str sAccountNum,
        sName;
    ;
    // Test_1.a
    sAccountNum = (select firstonly AccountNum from CustTable
        order by AccountNum desc
        where 0 == 0 // 'where' must occur after 'order by'.
        ).AccountNum;
    info(strFmt("Test_1.a: %1", sAccountNum));

    // Test_1.b
    sAccountNum = (select maxof(AccountNum) from CustTable).AccountNum;
    Global::info(strFmt("Test_1.b: %1", sAccountNum));

    // Test_2.c
    nRecId = (select maxof(RecId) from CustTable
        where CustTable.Blocked == CustVendorBlocked::No).RecId;
    info(strFmt("Test_2.c: %1", nRecId));

    // Test_2.d
    nRecId = (select count(RecId) from CustTable
        where CustTable.Blocked == CustVendorBlocked::No).RecId;
    info(strFmt("Test_2.d: %1", nRecId));

    // Test_3
    sName = (select Name from AssetTable
        where AssetTable.AssetId ==
            (select AssetId from AssetTrans
                where AssetTrans.AssetId == 'VEHC-005' //"CNC-01"
            ).AssetId).Name;
    info(strFmt("Test_3: %1", sName));
}

Populating Data source Using Temp Table

Form init method

public void init()
{
    custtable        nut; // regular table in AOT
    custtable        tmp; // instead of creating in AOT a temporary table
                                   // we can create a temporary instance of the preivous regular table
    ;
     super();

    // here we make it temporary;
    // it will disappear from memory when loses the scope
    // in other words, when we close the form
    tmp.setTmp();

    // simply selecting some of records from the regular table
    while select nut
        where nut.City=='Toronto'
    {
        // and putting them in the temporary one
        tmp.data(nut);
        tmp.doInsert();

    }
    // finally to show them on the form
    // we set the form data source to the temporary table
    custtable.setTmp();
    custtable.setTmpData(tmp);
}

Using external websevice

class CampaignClients
{
}
public static server void getClient()
{
    COM     myXmlHttp;
    url     webServiceUrl;
    str     method;
    str     userName;
    str     password;
    str     strXML;
    str     strTxt;
    str     result;
    str     fileName;
    str     listValue;
    int     listLength;
    int     i;


    System.Exception netExcepn;
    InteropPermission permission;
    FileIOPermission    fileIOPerm;
    COM             receiveCom;
    XMLDocument     xmlDocu;
    XMLDocument     doc = XMLDOcument::newBlank();
    XMLElement      node;
    XMLNodeList     elemlist;
    str s_Json;
    ;

    try {

    permission = new InteropPermission(InteropKind::ComInterop);
    permission.assert();

    myXmlHttp = new COM('Microsoft.XMLHTTP'); // used ms xml http com object to communicate with the web service
    webServiceUrl   = strfmt("https://api.createsend.com/api/v3/clients.json");
    method          = "GET";
    fileName = "c:\\temp\\getRateFile.xml";
    xmlDocu = XmlDocument::newBlank('utf-8');

    CodeAccessPermission::revertAssert();
    fileIOPerm = new FileIOPermission(fileName, 'r');
    fileIOPerm.assert();

    xmlDocu.load(fileName);

    s_Json ='{"CompanyName": "Karya","Country": "India","TimeZone": "(GMT+10:00) Canberra, Melbourne, Sydney"}';

    CodeAccessPermission::revertAssert();
    permission = new InteropPermission(InteropKind::ComInterop);
    permission.assert();

    myXmlHttp.open(method,webServiceUrl,false,"2b4c0d6d781cbb43983e88674154cd39",password);
    myXmlHttp.setRequestHeader("content-type","text/xml"); //application/x-www-form-urlencoded
    myXmlHttp.send(s_Json);

    strTxt =strrtrim(myXMLHttp.responseText());
    info(strTxt);

    receiveCom  = myXMLHttp.responseXML();

    doc.loadXml(receiveCom.xml());
    elemList = doc.GetElementsByTagName("v3:Amount");
    listLength = elemList.length();

    for (i=0; i < elemList.length(); i++)
    {

        info(elemList.item(i).innerXml());
    }

    }
    catch (exception::CLRError)
    {
     netExcepn = CLRInterop::getLastException();
     info(netExcepn.ToString());
    }
    catch(exception::Error)
    {

     netExcepn = CLRInterop::getLastException();
     info(netExcepn.ToString());
    }
}

Create XML through X++


class ExportToXML
{
}
public static void main(Args _args)
{
    #AviFiles
    smmCampaignSelection smmCampaignSelection;
    SysOperationProgress    progress = new SysOperationProgress();
    int                     total, updateCount, i;
    ContactPerson           contactPerson;
    XmlDocument doc;
    XmlElement nodeXml;
    XmlElement nodeSubs;
    XmlElement nodeSub;
    XmlElement nodeTable;
    XmlElement nodeEmail;
    XmlElement nodeName;
    XmlElement nodeCustomFields;
    XmlElement nodeCustomField;
    XmlElement nodeKey;
    XmlElement nodeValue;
    XmlElement nodeReSub;
    XmlElement nodeQueueSub;
    XmlElement nodeRestartSub;

    boolean _includeInvalid = false;
    smmCampaignTable _smmCampaignTable;

    #define.filename('D:\\campaignMultipleSubs.xml')
    /*#define.filename(strfmt("%1\%2_%3_%4_%5.csv", @"\\gs1melb17\axenvironments$\Prod\Campaigns\",
                                          curUserId(),
                                          _smmCampaignTable.CampaignId,
                                          strkeep(date2str(systemdateget(),321,2,1,2,1,4),"0123456789"),
                                          strkeep(time2str(timenow(),1,1),"0123456789"))) //TODO: To parameterise
*/
    ;
    _smmCampaignTable = smmCampaignTable::find('8012');

    if (!_includeInvalid)
        select count(Recid) from smmCampaignSelection where
            smmCampaignSelection.CampaignId == _smmCampaignTable.CampaignId
            //&& smmCampaignSelection.GS1_CampaignStatus != GS1_CampaignStatus::Invalid
            ;
    else
        select count(Recid) from smmCampaignSelection
            where smmCampaignSelection.CampaignId == _smmCampaignTable.CampaignId
            ;

    total = smmCampaignSelection.RecId;

    progress.setCaption("Export Communication List to Web");
    progress.setAnimation(#AviUpdate);
    progress.setTotal(total);

    ttsbegin;
    doc = XmlDocument::newBlank();
    nodeXml = doc.createElement('xml');

    doc.appendChild(nodeXml);
    nodeTable = doc.createElement('AddSubscribers');
    nodeXml.appendChild(nodeTable);
    nodeSubs = doc.createElement('Subscribers');
    nodeTable.appendChild(nodeSubs);
    i = 0;
    while select forupdate smmCampaignSelection
        where smmCampaignSelection.CampaignId == _smmCampaignTable.CampaignId
    {
        i++;
        //if (!_includeInvalid && smmCampaignSelection.GS1_CampaignStatus == GS1_CampaignStatus::Invalid)
            //continue;

        progress.setText(strfmt("Processing %1 of %2..", updateCount, total));
        progress.setCount(updateCount, 1);

        //smmCampaignSelection.gs1_ValidateLine();

        if (smmCampaignSelection.ContactPersonId == "274227")
            debug::assert(true);

        contactPerson = ContactPerson::find(smmCampaignSelection.ContactPersonId);

        if(contactPerson)
        {
            nodeSub = doc.createElement('Subscriber');
            nodeSubs.appendChild(nodeSub);

            /*apiKey,
                                                   _smmCampaignTable.GS1_CampaignListId,
                                                   contactPerson.Email,
                                                   contactPerson.Name,
                                                   contactPerson.ContactPersonId,
                                                   smmCampaignSelection.CampaignId,
                                                   _smmCampaignTable.CampaignGroupId);
                                                   */

            //ContactPerson Email
            nodeEmail = doc.createElement('EmailAddress'); //fieldstr(ContactPerson, Email));
            nodeEmail.appendChild(doc.createTextNode(contactPerson.Email));
            nodeSub.appendChild(nodeEmail);
            //ContactPerson Name
            nodeName = doc.createElement('Name'); //fieldstr(ContactPerson, Name));
            nodeName.appendChild(doc.createTextNode(contactPerson.Name));
            nodeSub.appendChild(nodeName);
            //CustomeFields
            //if(i==1)
            {
                nodeCustomFields = doc.createElement('CustomFields');
                nodeSub.appendChild(nodeCustomFields);
            }
            //CustomField
            nodeCustomField = doc.createElement('CustomField');
            nodeCustomFields.appendChild(nodeCustomField);
            //smmCampaignSelection CampaignId
            //nodeKey = doc.createElement(fieldstr(smmCampaignSelection, CampaignId));
            nodeKey = doc.createElement('Key');
            nodeKey.appendChild(doc.createTextNode(smmCampaignSelection.CampaignId));
            nodeCustomField.appendChild(nodeKey);
            //smmCampaignTable CampaignGroupId
            //nodeValue = doc.createElement(fieldstr(smmCampaignTable, CampaignGroupId));
            nodeValue = doc.createElement('Value');
            nodeValue.appendChild(doc.createTextNode(_smmCampaignTable.CampaignGroupId));
            nodeCustomField.appendChild(nodeValue);

            updateCount++;
        }
    }
    nodeReSub = doc.createElement('Resubscribe');
    nodeReSub.appendChild(doc.createTextNode('true'));
    nodeTable.appendChild(nodeReSub);

    nodeQueueSub = doc.createElement('QueueSubscriptionBasedAutoResponders');
    nodeQueueSub.appendChild(doc.createTextNode('false'));
    nodeTable.appendChild(nodeQueueSub);

    nodeRestartSub = doc.createElement('RestartSubscriptionBasedAutoresponders');
    nodeRestartSub.appendChild(doc.createTextNode('true'));
    nodeTable.appendChild(nodeRestartSub);

    doc.save(#filename);
    //return updateCount ? true : false;
    info(updatecount ? 'true' : 'false');
}

Creating new form through X++

static void createForm(Args _args)
{
Args args;
Form form;
FormRun formRun;
FormBuildDesign formBuildDesign;
FormBuildDataSource formBuildDataSource;
FormBuildGridControl formBuildGridControl;
FormBuildStringControl formBuildStringControl;
FormBuildStringControl formBuildStringControl2;
FormBuildTabControl formBuildTabControl;
FormBuildTabPageControl formBuildTabPageControl;
FormBuildTabPageControl formBuildTabPageControl2;
FormStringControl formStringControl;
FormGridControl formGridControl;
DictTable dictTable;
int idx;
int idx2;
int idx3;

;

// Create the form header.
form = new Form();

// Add a data source to the form. ID 77 refers to the CustTable.
dictTable = new DictTable(tablenum(CustTable));
formBuildDataSource = form.addDataSource(dictTable.name());
formBuildDataSource.table(dictTable.id());

// Create the form design.
formBuildDesign = form.addDesign("Design");
formBuildDesign.caption("myForm");

// Add tabbed page controls, a grid control, and string controls.
formBuildTabControl =
formBuildDesign.addControl(FormControlType::Tab, "Overview");

formBuildTabPageControl =
formBuildTabControl.addControl(FormControlType::TabPage, "Overview");
formBuildTabPageControl.caption("Overview");

formBuildTabPageControl2 =
formBuildTabControl.addControl(FormControlType::TabPage,"Details");
formBuildTabPageControl2.caption("Details");

formBuildGridControl =
formBuildTabPageControl.addControl(FormControlType::Grid,"Table Grid");
formBuildStringControl =
formBuildTabPageControl2.addControl(FormControlType::String,"Table String");
formBuildStringControl2 =
formBuildTabPageControl2.addControl(FormControlType::String,"Table String");

// Add data fields to controls.
formBuildGridControl.addDataField
(formBuildDataSource.id(),dictTable.fieldName2Id("AccountNum"));
formBuildGridControl.addDataField
(formBuildDataSource.id(),dictTable.fieldName2Id("Phone"));
formBuildGridControl.addDataField
(formBuildDataSource.id(),dictTable.fieldName2Id("Name"));
formBuildGridControl.addDataField
(formBuildDataSource.id(),dictTable.fieldName2Id("Address"));
formBuildStringControl.dataSource(formBuildDataSource.id());
formBuildStringControl.dataField(2);
formBuildStringControl2.dataSource(formBuildDataSource.id());
formBuildStringControl2.dataField(3);

//FormBuildDesign.removeControl(FormControlID).

args = new Args();
args.object(form);

// Create the run-time form.
formRun = classfactory.formRunClass(args);

formRun.run();
formRun.detach();
}

Using document service (AIF)

static void Services_CustTable_Create(Args _args)
{
    // Customer Service class
    CustCustomerService     custService;
    CustCustomer                customer;

    // Data object of Service class
    CustCustomer_CustTable  custTable;
    AifEntityKeyList               entityKeyList;
    AccountNum                    accountNum;
    ;

    //Service instance
    custService =  CustCustomerService::construct();

    customer = new CustCustomer();
    customer.createCustTable();
    custTable = customer.parmCustTable().addNew();

    custTable.parmName("Cust_Service");
    custTable.parmCustGroup("20");
    custTable.parmCurrency("EUR");
    custTable.parmPartyType(DirPartyType::Organization);

    // Create Customer
    entityKeyList = custService.create(customer);

    if(entityKeyList)
        accountNum = entityKeyList.getEntityKey(1).parmKeyDataMap().lookup(fieldnum(CustTable, AccountNum));
        infolog.messageWin().addLine(accountNum);
}

Export Data into CSV

Using CommaIO

static void ExportDataToCSV(Args _args)
{
    Query                             q;
    QueryBuildDataSource    qbds;
    QueryBuildRange            qbr;
    QueryRun                       qr;
    CommaIO                       commaIO;
    FileName                        fileName;
    InventTable                     inventTable;
    ;

    fileName       = WINAPI::getTempPath() + "ItemDetails" + ".csv";
    commaIO      = new CommaIO(fileName,'W');

    q                  = new Query();
    qbds             = q.addDataSource(tablenum(InventTable));
    qbr               = qbds.addRange(fieldnum(InventTable,ItemId));

    qr                = new QueryRun(q);

    commaIO.write("ItemId","Item Name","Item Type","Item GroupId","Dimension GroupId","Model GroupId");
    while( qr.next() )
    {
        inventTable = qr.get(tablenum(InventTable));

        commaIO.write(inventTable.ItemId,inventTable.ItemName,enum2str(inventTable.ItemType),inventTable.ItemGroupId,
                      inventTable.DimGroupId,inventTable.ModelGroupId);
    }

    WINAPI::shellExecute(fileName);
}

Another way by using TextIO

public void ExportDataToCSVDelimit()
{
    Query q;
    QueryBuildDataSource qbds, qbdsworker;
    QueryBuildRange qbr;
    QueryRun qr;
    FileName fileName;
    A_PTSTransferToPayItems A_PTSTransferToPayItems;
    DictTable dictTable;
    DictField dictField;
    int fldCnt;
    HcmWorker hcmworker;
    boolean exported = false;
    TextIO file;
    container line;

    #File
    ;
    fileName       = JmgParameters::find().PayFileName;
    file = new TextIO(filename, 'W'); //#io_write);
    file.outFieldDelimiter(';');// for semicolon seperator

    if (!file || file.status() != IO_Status::Ok)
    {
        throw error("File cannot be opened.");
    }
    q = this.queryRun().query();

    qbdsworker = q.dataSourceTable(tableNum(HcmWorker));
    qbds = qbdsworker.addDataSource(tablenum(A_PTSTransferToPayItems));
    qbds.addLink(fieldNum(HcmWorker,RecId),fieldNum(A_PTSTransferToPayItems,HcmWorkerRecId));

    qr = new QueryRun(q);

    while(qr.next())
    {
        A_PTSTransferToPayItems = qr.get(tablenum(A_PTSTransferToPayItems));

        A_PTSTransferToPayItems = qr.get(tablenum(A_PTSTransferToPayItems));
        line = [A_PTSTransferToPayItems.HcmWorkerRecId,A_PTSTransferToPayItems.ProfileDate,A_PTSTransferToPayItems.PayType,
                      A_PTSTransferToPayItems.PayUnit,'Dim1','Dim2'];
        file.writeExp(line);
    }
}

Another way to export data into CSV file

public void Job_File_IO_TextIo_Write_Read()
{
    TextIo txIoRead, file,
    txIoWrite;
    FileIOPermission fioPermission;
    container containFromRead;
    int xx,
        iConLength;
    str sTempPath,
        sFileName = "Test_File_IO.txt",
        sOneRecord;
    str strdate,name,temp;
    date currentDate = today();
    List list;
    ListEnumerator enumerator;
    Query q;
    QueryBuildDataSource qbds, qbdsworker;
    QueryBuildRange qbr;
    QueryRun qr;
    container line;
    A_PTSTransferToPayItems A_PTSTransferToPayItems;
    boolean exported;

    strdate= date2Str
        (currentDate,
        123,
        DateDay::Digits2,

        DateSeparator::Hyphen,
        DateMonth::Digits2,
        DateSeparator::Hyphen,

        DateYear::Digits4
        );

    #File
    // Get the temporary path.
    //sTempPath = WINAPI::getTempPath();
    name = JmgParameters::find().PayFileName;
    list = strSplit(name,".");
    enumerator = list.getEnumerator();

    while(enumerator.moveNext()) {
        temp = enumerator.current();
        break;
    }
    sfileName = strFmt("%1_%2.csv",temp,strdate);
    //info("File is at: " + sFileName);

    // Assert permission.
    fioPermission = new FileIOPermission(sFileName ,"RW");
    fioPermission.assert();

    // If the test file already exists, delete it.
    /*if (WINAPI::fileExists(sFileName))
    {
        WINAPI::deleteFile(sTempPath + sFileName);
    }*/

    // Open a test file for writing.
    // "W" mode overwrites existing content, or creates the file.
    txIoWrite = new TextIo( sTempPath + sFileName ,"W");
    txIoWrite.outFieldDelimiter(','); // for semicolon seperator
    /*
    // Write records to the file.
    txIoWrite.write("Hello        World.");
    txIoWrite.write("The sky is blue.");
    txIoWrite.write("");
    txIoWrite.write("// EOFile");

    // Close the test file.
    txIoWrite = null;

    // Open the same file for reading.
    txIoRead = new TextIo(sTempPath + sFileName ,"R");
    // Read the entire file into a container.
    containFromRead = txIoRead.read();

    // Loop through the container of file records.
    while (containFromRead)
    {
        sOneRecord = "";
        iConLength = conLen(containFromRead);
        // Loop through the token in the current record.
        for (xx=1; xx <= iConLength; xx++)
        {
            if (xx > 1) sOneRecord += " ";
            sOneRecord += conPeek(containFromRead ,xx);
        }
        info(sOneRecord);

        // Read the next record from the container.
        containFromRead = txIoRead.read();
    }
    */
    q = this.queryRun().query();
    //info(q.toString());
    qbdsworker = q.dataSourceTable(tableNum(ProjWorkerSetup));
    qbds = qbdsworker.addDataSource(tablenum(A_PTSTransferToPayItems));
    qbds.addLink(fieldNum(ProjWorkerSetup,Worker),fieldNum(A_PTSTransferToPayItems,HcmWorkerRecId));

    if(payDate)
    {
        qbr = qbds.addRange(fieldNum(A_PTSTransferToPayItems,ProfileDate));
        qbr.value(queryValue(payDate));
    }
    qr = new QueryRun(q);
    //info(q.toString());
    line = conNull();
    while(qr.next())
    {
        A_PTSTransferToPayItems = qr.get(tablenum(A_PTSTransferToPayItems));
        line = [HcmWorker::find(A_PTSTransferToPayItems.HcmWorkerRecId).name(),A_PTSTransferToPayItems.ProfileDate,A_PTSTransferToPayItems.PayType,
                      A_PTSTransferToPayItems.PayUnit,'',''];
        //file.writeExp(line);
        txIoWrite.write(line);
        exported = true;
    }
    CodeAccessPermission::revertAssert();
    if(exported == true)
         info(strFmt("Pay Item exported successfully %1",sfileName));
    // Close the test file.
    txIoRead = null;
    // Delete the test file.
    //WINAPI::deleteFile(sTempPath + sFileName);

    // revertAssert is not really necessary here,
    // because the job (or method) is ending.
    CodeAccessPermission::revertAssert();
}

DB Synchrozation through X++

DB Synchronization through code

static void forceDbSynchronize(Args _args)
{
    Dictionary                             dict;
    int                                        idx, lastIdx, totalTables;
    TableId                                  tableId;
    Application                            application;
    SysOperationProgress           progress;
    StackBase                            errorStack;
    ErrorTxt                                 errorTxt;
    ;

    application = new Application();
    dict = new Dictionary();
    totalTables = dict.tableCnt();
    progress = new SysOperationProgress();
    progress.setTotal(totalTables);
    progress.setCaption("@SYS90206");
    errorStack = new StackBase(Types::String);

    lastIdx = 0;
    try
    {
        for (idx = lastIdx+1; idx <= totalTables; idx++)
        {
            tableId = dict.tableCnt2Id(idx);
            progress.setText(dict.tableName(tableId));

            lastIdx = idx;
            application.dbSynchronize(tableId, false, true, false);
            progress.incCount();
        }
    }
    catch (Exception::Error)
    {
        errorTxt = strFmt("Error in table '%1' (%2)", tableId, dict.tableName(tableId));
        errorStack.push(errorTxt);
        retry;
    }

    setPrefix("@SYS86407");
    errorTxt = errorStack.pop();
    while (errorTxt)
    {
        error(errorTxt);
        errorTxt = errorStack.pop();
    }
    info('Sychrnonization is now done.');
}



Sunday, February 16, 2014

Updating fields without affecting system fields value

Updating fields without affecting system fields value

Public str SystemFields_OverWrite()
{
SalesTable salesTable;

;
salesTable = SalesTable::find("SO-100005",true);
ttsbegin;
new OverwriteSystemfieldsPermission().assert();
salesTable.overwriteSystemfields(true);
salesTable.SalesName = "River Hotel Test";
salesTable.(fieldnum(SalesTable,modifiedDateTime)) = salesTable.modifiedDateTime;
salesTable.doUpdate();
salesTable.overwriteSystemfields(false);
CodeAccessPermission::revertAssert();
ttscommit;
return "Success";

}

Creating Workflow in Ax 2012

Example for Workflow creation in Customer details form

1. Create query 'CustApprWorkflowQry' for custtable.

2. Create Enum 'CustApprWorkflowState' for Workflow Status.

3. Add the above enum in Custtable table.

4. Add the modified override method 'canSubmitToWorkflow' in custtable table.

//BP Deviation Documented
public boolean canSubmitToWorkflow(str _workflowType = '')
{
    boolean ret;
 
    ret = this.RecId != 0 && this.CustApprWorkflowState == CustApprWorkflowState::NotSubmitted;
   
    return ret;
}

5. Add the new method 'UpdateCustWorkflowState' in custtable table.

//BP Deviation documented
public static void UpdateCustWorkflowState(RefRecId _recId, CustApprWorkflowState _state)
{
    CustTable custTable = CustTable::findRecId(_recId, true);

    ttsBegin;

    custTable.CustApprWorkflowState = _state;
    custTable.update();

    ttsCommit;
}

6. Create Workflow Type 'CustWFApprovalDemo' using wizard.

All the required classes and menuitems will be created automatically. Do the following changes.

In CustWFApprovalDemoEventHandler Class, 
add code in Canceled method
//BP Deviation Documented
public void canceled(WorkflowEventArgs _workflowEventArgs)
{
CustTable::UpdateCustWorkflowState(_workflowEventArgs.parmWorkflowContext().parmRecId(),   CustApprWorkflowState::PendingCancellation);
}
add code in Completed method
//BP Deviation Documented
public void completed(WorkflowEventArgs _workflowEventArgs)
{
    CustTable::UpdateCustWorkflowState(_workflowEventArgs.parmWorkflowContext().parmRecId(), CustApprWorkflowState::Completed);
}
add code in Started method
//BP Deviation Documented
public void started(WorkflowEventArgs _workflowEventArgs)
{
    CustTable::UpdateCustWorkflowState(_workflowEventArgs.parmWorkflowContext().parmRecId(), CustApprWorkflowState::Submitted);
}

In CustWFApprovalDemoDocument Class, add the above created queryname in getQueryName method
public queryName getQueryName()
{
    return querystr(CustApprWorkflowQry);
}

7. Create Workflow Approval 'CustWFApprovalDemo' using wizard.

All the required classes and menuitems will be created automatically. Do the following changes.

//BP Deviation Documented
public void canceled(WorkflowElementEventArgs _workflowElementEventArgs)
{
        CustTable::UpdateCustWorkflowState(_workflowElementEventArgs.parmWorkflowContext().parmRecId(),  CustApprWorkflowState::PendingCancellation);
}
//BP Deviation Documented
public void changeRequested(WorkflowElementEventArgs _workflowElementEventArgs)
{
        CustTable::UpdateCustWorkflowState(_workflowElementEventArgs.parmWorkflowContext().parmRecId(),  CustApprWorkflowState::ChangeRequest);
}
//BP Deviation Documented
public void completed(WorkflowElementEventArgs _workflowElementEventArgs)
{
CustTable::UpdateCustWorkflowState(_workflowElementEventArgs.parmWorkflowContext().parmRecId(), CustApprWorkflowState::Approved);
}
//BP Deviation Documented
public void returned(WorkflowElementEventArgs _workflowElementEventArgs)
{
    CustTable::UpdateCustWorkflowState(_workflowElementEventArgs.parmWorkflowContext().parmRecId(),  CustApprWorkflowState::Returned);
}
//BP Deviation Documented
public void started(WorkflowElementEventArgs _workflowElementEventArgs)
{
    CustTable::UpdateCustWorkflowState(_workflowElementEventArgs.parmWorkflowContext().parmRecId(),   CustApprWorkflowState::Submitted);
}

8. Create new Class 'WorkflowSubmitManager' for Submit / Re-Submit

//Generic class which can be used for all workflow activation on submit/resubmit
public class WorkflowSubmitManager
{
    WorkflowSubmitDialog  workFlowSubmitDialog;
    Common                workflowConfiguration;
    RefRecId              recordRecId;
    WorkflowCorrelationId workFlowCorrelationId;
    WorkflowTypeName      workFlowTypeName;
    WorkflowComment       note;

    WorkflowVersionTable  workflowVersionTable;
    WorkflowWorkItemTable workflowWorkItemTable;
    FormDataSource        callerDS;
    FormRun               callerForm;
    Common                callerRecord;

    boolean               isSubmit;
}
private boolean canSubmit()
{
    boolean ret = true;

    if (CustTable::findRecId(recordRecId).address() == "")
    {
        ret = checkFailed("Customer Address not defined");
    }

    return ret;
}
private void init(Args _args)
{
    ;

    callerForm            = _args.caller();
    callerRecord          = _args.record();
    callerDS              = callerRecord.dataSource();
    recordRecId           = callerRecord.RecId;
    workflowVersionTable  = callerForm.getActiveWorkflowConfiguration();
    WorkflowWorkItemTable = callerForm.getActiveWorkflowWorkItem();

    // Construct dialog to prompt user to enter submit comments/note
    workFlowSubmitDialog = WorkflowSubmitDialog::construct(workflowVersionTable);

    isSubmit = this.isCalledFromSubmitMenuItem(_args.menuItemName());
}
private boolean isCalledFromSubmitMenuItem(str _menuItemName)
{
    str name = _menuItemName;
    ;

    if (strScan(name, "Resubmit", 1, strLen(name)) > 0)
    {
        return false;
    }
    else
    {
        return true;
    }

}
public RefRecId parmRecordRecId(RefRecId _recordRecId = recordRecId)
{
    recordRecId = _recordRecId;

    return recordRecId;
}
private void refreshCallerWorkflowControls()
{
    ;

    if (callerDS)
    {
        ttsBegin;
        this.updateWorkflowState();
        ttsCommit;

        callerDS.reread();
        callerDS.refresh();
    }

    //Refresh/update workflow controls on called form
    callerForm.updateWorkflowControls();
}
private void reSubmit()
{
    ;

    Workflow::cancelWorkflow(workflowWorkItemTable.CorrelationId);
}
public void run(Args _args)
{
    ;

    // Initializing params from caller argument object
    this.init(_args);

    //Bydefault canSubmit returns true always, you can add validations here if any
    if (this.canSubmit())
    {
        workFlowSubmitDialog.run();

        //User enters note and clicked Ok for document processing
        if (workFlowSubmitDialog.parmIsClosedOK())
        {
            note = workFlowSubmitDialog.parmWorkflowComment();

            // Activate workflow i,e submit document for processing
            if (isSubmit)
            {
                this.submit();
            }
            else
            {
                this.reSubmit();
            }

            //Update workflow state for caller record and refresh to reflect new state
            this.refreshCallerWorkflowControls();
        }
    }

}
private void submit()
{
    ;

    ttsBegin;

    try
    {
        workFlowCorrelationId = Workflow::activateFromWorkflowConfigurationId(workflowVersionTable.ConfigurationId, recordRecId, note, false);
    }
    catch(Exception::Error)
    {
        throw error ("Error on workflow activation.");
    }

    ttsCommit;
}
private void updateWorkflowState()
{
    ;
    //Add case for each caller table and update workflow state accordingly
    switch (callerRecord.TableId)
    {
        case tableNum(custTable):
            CustTable::UpdateCustWorkflowState(callerRecord.RecId, CustApprWorkflowState::Submitted);
            break;
    }
}
public static WorkflowSubmitManager constructWith(RefRecId _recId)
{
    WorkflowSubmitManager submitManager = new WorkflowSubmitManager();
    ;

    submitManager.parmRecordRecId(_recId);

    return submitManager;

}
public static void main(Args args)
{
    WorkflowSubmitManager submitManager = new WorkflowSubmitManager();


    submitManager.run(args);

}

Add this class with the WF Type menuitem 'CustWFApprovalDemoSubmitMenuItem' object property and with WF Approval menuitem 'CustWFApprovalDemoSubmitMenuItem' object property.

9. Add the workflow for Customer details form.

Change WorkflowEnabled property to Yes, Workflow Datasource property to Custtable and Workflow Type to 'CustWFApprovalDemo' in CustTableListPage form Design property.

10. Create new record. Workflow bar will be displayed.