Thursday 10 December 2015

AXBuild command in AX2012

Instead of compiling the application from AOT which takes long time there is another way to compile the whole application through AXBuild command.

Goto the path where Ax is located.
For example :
C:\Program Files\Microsoft Dynamics AX\60\Server\MicrosoftDynamicsAX\bin

then on the bin folder --> shift + right click --> open command window here --> ax32.exe is opened.

Then on the command window type

Axbuild.exe xppcompileall /s=01

NOTE : If you have one AOS then type /s=01, if you have more AOS then specify the correct AOS.


Adding filter on display fields in ax2012

Here is a sample example scenario where you add a display field on the form and make this field to be allowed to filter.

Create a table TEST



Create form TEST




Data source à TEST
Add fields Test_Id, Test_ActivityNumber, ActivityName from the datasource TEST
FormControl à Activity name à auto declaration YES
Data method à displayActivityName

Then override context method on the formcontrol and write the following code :

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');
    int b = menu.insertItem('Filter By Selection');
    int c = menu.insertItem('Remove Filter');
    ;


    q   = Test_ds.query();
    qb1 = q.dataSourceTable(tablenum(Test));
    qb1 = qb1.addDataSource(TableNum(smmActivities));
    qb1.addLink(FieldNum(Test,ActivityNumber),FieldNum(smmActivities,ActivityNumber));

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

                qb1.addRange(FieldNum(smmActivities,Purpose)).value(strtext);
                Test_ds.query(q);
                Test_ds.executeQuery();
            }
            break;

    case b:                                      // Filter By Selection

            qb1.addRange(FieldNum(smmActivities,Purpose)).value(ActivityName.valueStr());
            Test_ds.query(q);
            Test_ds.executeQuery();
            break;

    case c :                                      // Remove Filter
            q   = new Query();
            qb1 = q.addDataSource(tablenum(Test));
            qb1.clearLinks();
            qb1.clearRanges();
            Test_ds.query(q);
            Test_ds.removeFilter();
            break;

    Default:
            break;

    }
}








Wednesday 9 December 2015

Report shows no data --- Ways to solve them

When report data is not printed correctly or code wise data is perfect and data is also inserted in temp table correctly then also if data is not printed in report then follow these steps -->

1) Clear the caches
2) Delete the AUC files
3) Delete the report from the report server
4) Restart SSRS services
5) Redeploy report from visual studio
6) Check the report
7) Then also this doesnt work then restart AOS

Ways to solve workflow stopped error in ax2012


1) Check whether user is assigned to workflow execution account in the path
System Administration --> Setup --> System --> System service account -->

2) Check whether appropriate permissions are given to the user assigned to workflow account.

3) Check in server configuration -->
whether Is batch server is CHECKED.

4) Rerun the Worflow infrastructure configuration in the path
System Administration --> Setup --> Workflow --> Worflow infrastructure configuration -->

5) Check whether the batch group assigned to workflow is connected to correct AOS in the path -->
System Administration --> Setup --> Batch group

6) Do the full CIL and AOS restart and then solve the errors in Xppil folder and then check the workflow.
If it is not solved then follow these steps :
Is Batch Server -- uncheck
Stop the AOS
Rename Xppil folder
Restart the AOS
Generate full CIL and then check for errors and check the workflow.

7) Find out the exact reason for the error in Event log where AOS is installed.

8) Check Inbound ports in the path
System Administration --> Setup --> Services and Application Inegration Framework --> Inbound ports -->

Check whether sys workflow services are active if not activate them.
Deactivate AxClient and AIF services and then activate it.

9) Open configuration utility and refresh the WCF configuration.

10) Check if the SID of the admin user in the UserInfo table has the correct value.

Display method on InventOnHandItem form in ax2012

Adding two new fields on the InventOnHandItem form :
  1. Add two controls in the grid dimension and supplier.
  2. On the InventSum datasource add 2 new display methods to display data in these fields on the form.
Display method for dimension :

Display public AttributeValueText displayDimension(Inventsum _inventSum)
{
    PdmProduct              pdmProduct;
    PdmProductItem          pdmProductItem;
    PdmProductItemAttribute pdmProductItemAttribute;
    EcoResProduct           ecoResProduct;
    AttributeValueText      returnValue;
    EcoResValue             ecoResValue;
    EcoResAttribute         ecoResAttribute;

    ecoResProduct   = EcoResProduct::findByDisplayProductNumber(_inventSum.ItemId);

    select Attribute,Value from pdmProductItemAttribute
    join pdmProductItem
    where pdmProductItem.RecId     == PdmProductItemAttribute.ProductItem
    join pdmProduct
    where pdmProduct.RecId         == pdmProductItem.ProductRecId
       && pdmProduct.EcoResProduct == ecoResProduct.RecId
    join ecoResAttribute
    where ecoResAttribute.RecId    == pdmProductItemAttribute.Attribute
       && ecoResAttribute.Name     == "Dimension";
    {
        ecoResValue = EcoResValue::find(pdmProductItemAttribute.Value);
        returnValue = ecoResValue.value();
    }

    return returnValue;
}

Display method for Supplier :

Display public AttributeValueText displaySupplier(Inventsum _inventSum)
{
    PdmProduct              pdmProduct;
    PdmProductItem          pdmProductItem;
    PdmProductItemAttribute pdmProductItemAttribute;
    EcoResProduct           ecoResProduct;
    AttributeValueText      returnValue;
    EcoResValue             ecoResValue;
    EcoResAttribute         ecoResAttribute;

    ecoResProduct   = EcoResProduct::findByDisplayProductNumber(_inventSum.ItemId);

    select Attribute,Value from pdmProductItemAttribute
    join pdmProductItem
    where pdmProductItem.RecId     == PdmProductItemAttribute.ProductItem
    join pdmProduct
    where pdmProduct.RecId         == pdmProductItem.ProductRecId
       && pdmProduct.EcoResProduct == ecoResProduct.RecId
    join ecoResAttribute
    where ecoResAttribute.RecId    == pdmProductItemAttribute.Attribute
       && ecoResAttribute.Name     == "Supplier";
    {
        ecoResValue = EcoResValue::find(pdmProductItemAttribute.Value);
        returnValue = ecoResValue.value();
    }

    return returnValue;
}

4. Now on the newly added form control --> dimension  --> in the properties window -->
datasource  -- InventSum
datamethod -- displayDimension

5. supplier -->
datasource  -- InventSum
datamethod -- displaySupplier


Friday 3 April 2015

Export Data with Outbound ports in AX 2012

Outbound ports are not related with web services or network at all (talking about WCF, bindings and endpoints). You can use it in order to retreive data from Dynamics AX and send the results to either a file or a message queue (MSMQ).


The following post will show a summary of the steps I followed in order to export customers in several XML files into a directory on the AOS server.






1)  Create an outbound port specifying "File system adapter" as adapter.
You can limit it to a specific company if you want
In the operations choose : CustCustomerService.read

This service operation will be used to read a customer from AX by specifying an account number.
The output result of the operation will be the customer itself.



2)  I create a new folder on the AOS side (in the example C:\AIFOutboundTest)
    Don't forget to give the correct write access rights for the user executing your AOS Service. The AOS will need write access to create the file.

3)  Specify the newly created folder in the URI of the outbound port and save the outbound port and activate it.

4)  Create a new job to post some customer in the AIF Queue :



static void sendCustomerOutbound(Args _args)
{

    AxdSendContext axdSendContext = AxdSendContext::construct();
    AifEntityKey aifEntityKey = AifEntityKey::construct();
    Map keyData;
    AifConstraintList aifConstraintList = new AifConstraintList();
    AifConstraint aifConstraint = new AifConstraint();

    CustTable       custTable;
    ;

    custTable = CustTable::find("US-001");

    keyData = SysDictTable::getKeyData(custTable);

    aifEntityKey.parmTableId(custTable.TableId);
    aifEntityKey.parmRecId(custTable.RecId);
    aifEntityKey.parmKeyDataMap(keyData);

    axdSendContext.parmXMLDocPurpose(XMLDocPurpose::Original);
    axdSendContext.parmSecurity(false);

    aifConstraint.parmType(AifConstraintType::NoConstraint) ;
    aifConstraintList.addConstraint(aifConstraint) ;

    AifSendService::submitDefault(
        classnum(CustCustomerService),
        aifEntityKey,
        aifConstraintList,
        AifSendMode::Async,
        axdSendContext.pack());

}

5)  Once this code is executed, a new record will be created in the AIF Queue manager.

You can reach the queue manager with the following path :
System Administration > Periodic > Services and Application Integration Framework > Queue manager

Status will be ready and you will see the document service operation involved.




6)  Now the instructions are created with correct parameters. You can either setup a batch system that will automatically execute the following method, or you can simply execute it manually.

static void runAIF()
{
    AifOutboundProcessingService outbound = new AifOutboundProcessingService();    outbound.run();
}

7)  Once this method has been executed, you will be able to see the resulting newly created AIF message (XML result). For this, use the following menu : Document Log and View message.




8)  Once the message has been created, we can execute the following method :

static void sendAIF()
{
    AifGatewaySendService        send     = new AifGatewaySendService();
    send.run();
}

9)  This operation will export the message to a physical XML file on the folder we specified in the outbound port.





Tuesday 31 March 2015

Use display method values in other method in axapta

If some values in the form are displayed through display methods then if you need to use those values in another method to do some calculations then do this way

value = element.displaymethod1() + element.dislaymethod2;

Monday 30 March 2015

Using dictEnum to use enum in axapta

void clicked()
{
    str strValue;
    DictEnum    dictEnum;
    EnumId  enumId;
    AnyType    anyTypeValue;
    AcquisitionMethod   acqMethodType;
 
    super();

    enumId               =   enumNum(AcquisitionMethod);    
    dictEnum            =   new DictEnum(enumId);
    anyTypeValue    =   dictEnum.name2Value(strValue);
 
    element.createNew(anyTypeValue);
}

Can workout this way also ---->

int Value;
int indexValue;

dictEnum  =   new DictEnum(enumNum(AcquisitionMethod));
value         =   dict.symbol2Value("Hour"); 
 //Gets the value in int


nameText = dict.value2Name(value); //Gets the localized Name (for example "Customer")
transactionType = str2enum(transactionType, nameText);

info(enum2str(transactionType));

dictEnum = new DictEnum(enumId);
anyTypeValue    =   dictEnum.index2Name(dictEnum.name2Value(strValue)); //gets the index value





Display method to return enum value from select query in Axapta

display AssetDepreciation depreciationValue()
{
    AmountMSTSecondary dep;
    ;

    while select assetTrans
    where assetTrans.AssetId    == assetId
       && assetTrans.BookId     == bookId
       && assetTrans.TransType  == AssetTransType::Depreciation
    join ledgerTrans
    where ledgerTrans.Voucher   == assetTrans.Voucher
       && ledgerTrans.TransDate == assetTrans.TransDate
       && ledgerTrans.AmountMSTSecond < 0
    {
        dep += ledgerTrans.AmountMSTSecond;
    }

    return dep;
}

Friday 27 March 2015

Enabling and disabling fields in a grid based on enum values in AX 2012

AcqMethod is Combobox control whose auto declaration is made yes.
AcqMethod has two enum values AcquisitionAmt and AcquisitionAdjustmentAmount .

AcquisitionAmt and AcquisitionAdjustmentAmount mentioned in the code are the names of the fields in grid whose auto declaration property is yes.

Override the active method of the grid datasource and write the following code :

if(AcqMethod.selection() == 0)
    {
         AcquisitionAmt.enabled(true);
         AcquisitionAdjustmentAmount.enabled(false);
    }
    else if(AcqMethod.selection() == 1)
    {
         AcquisitionAmt.enabled(false);
         AcquisitionAdjustmentAmount.enabled(true);
    }

The same way if you need to disable complete record based on the enum values then :

if(AssetTransSecCur.Updated == NoYes::Yes)
    {
        AssetTransSecCur_ds.object(fieldNum(AssetTransSecCur, TransDate)).allowEdit(false);
        AssetTransSecCur_ds.object(fieldNum(AssetTransSecCur, AcquisitionAmt )).allowEdit(false);
        Update.enabled(false);
}


NOTE :

Update is a button with auto declaration yes.
AssetTransSecCur is the datasource name.

Friday 20 March 2015

Importing data from excel file using SysExcelApplication class in AX 2012

Write the following code in job :

static void importExcel(Args _args)
{
    SysExcelApplication application;
    SysExcelWorkbooks workbooks;
    SysExcelWorkbook workbook;
    SysExcelWorksheets worksheets;
    SysExcelWorksheet worksheet;
    SysExcelCells cells;
    COMVariantType type;
    int row;
    ItemId itemid;
    Name name;
    FileName filename;

    application = SysExcelApplication::construct();
    workbooks = application.workbooks();
    //specify the file path that you want to read
    filename = "C:\\Users\\Desktop\\itemDetails.xlsx";
    try
    {
    workbooks.open(filename);
    }
    catch (Exception::Error)
    {
    throw error("File cannot be opened.");
    }

    workbook = workbooks.item(1);
    worksheets = workbook.worksheets();
    worksheet = worksheets.itemFromNum(1);
    cells = worksheet.cells();
    do
    {
    row++;
    itemId = cells.item(row, 1).value().bStr();
    name = cells.item(row, 2).value().bStr();
    info(strfmt('%1 - %2', itemId, name));
    type = cells.item(row+1, 1).value().variantType();
    }
    while (type != COMVariantType::VT_EMPTY);
    application.quit();

}

Wednesday 18 March 2015

Export table data to excel file using SysExcelApplication object in AX 2012

Write the following code in a job :

static void exportToExcelFile(Args _args)
{
    InventTable inventTable;
    SysExcelApplication application;
    SysExcelWorkbooks workbooks;
    SysExcelWorkbook workbook;
    SysExcelWorksheets worksheets;
    SysExcelWorksheet worksheet;
    SysExcelCells cells;
    SysExcelCell cell;
    Filename    fileName;
    int row;
 
    fileName = "C:\\desktop\\itemDetails.xlsx";

    application = SysExcelApplication::construct();
    workbooks = application.workbooks();
    workbook = workbooks.add();

    worksheets = workbook.worksheets();
    worksheet = worksheets.itemFromNum(1);
    cells = worksheet.cells();
    cells.range('A:A').numberFormat('@');

    cell = cells.item(1,1);
    cell.value("Item");
    cell = cells.item(1,2);
    cell.value("Name");
    row = 1;

    while select inventTable
    {
    row++;
    cell = cells.item(row, 1);
    cell.value(inventTable.ItemId);
    cell = cells.item(row, 2);

    cell.value(inventTable::find(inventTable.ItemId).itemName());
    }

    workbook.saveAs(fileName);
    workbook.comObject().save();
    workbook.saved(true);
}

Tuesday 3 March 2015

Display financial dimension in a list page in AX 2012

Inorder to display financial dimension field on a list page just add a string edit form control in the grid in PurchTableListPage and mention the datasource as PurchTable and datamethod as showFinancialDimension.

showFinancialDimension is a display method written in the PurchTable methods with the following code :

display str showFinancialDimension()
{
    PurchTable  purchTable;
    DimensionAttributeValueSetStorage   dimStorage;
    counter i;
    str dimension;

    dimStorage = DimensionAttributeValueSetStorage::find(purchTable.DefaultDimension);

    for (i = 1 ; i <= dimStorage.elements() ; i++)
    {
        if(DimensionAttribute::find(dimStorage.getAttributeByIndex(i)).Name == "BusinessUnit")
        {
            dimension = dimStorage.getDisplayValueByIndex(i);
        }
    }
    return dimension;
}

Display image for an item in PurchTable in AX 2012

In the PurchTable form create a new tab page image in the line details and whenever any line is created then corresponding image for that item should be displayed in the newly created tab page image.

1) First of all check whether any image is attached for an item or not ?

To attach an image for an item :

Goto production information management -> common -> released products ->

select any item and click on the product image button in the product tab -> document handling of item number form is opened.
click on new -> select type embedded it opens attach file window -> select the image -> click ok.
refresh the page -> image is added to the item.

2) PurchTable -> design -> add tabpage image under line details tab and add window control under it and name it as image.

3) In the form methods create a new method showImage as shown below.

public void showImage()
{
    Image   itemImage;

    productImageRecId = InventTable::find(PurchLine.ItemId).RecId;

    if(productImageRecId)
    {
        select ecoResProductImage where ecoResProductImage.RefRecId == productImageRecId
        || ecoResProductImage.RefRecord == productImageRecId;

        containerImage =  ecoResProductImage.ThumbnailSize;

        itemImage = new Image();
        itemImage.setData(containerImage);
        Image.image(itemImage);
        image.widthValue(itemImage.width());
        image.heightValue(itemImage.height());
    }
}

NOTE :  image is the window form control whose auto declaration is set to yes.

4) PurchTable -> Datasource -> PurchLine -> Methods -> active

In this method call the form method as element.showImage();

Thats it , the image will be displayed on the purchTable form.

Thursday 26 February 2015

Creating a factbox in a list page in AX 2012

Today I want to create a factbox which shows only the open orders in purchTableListPage.

Query :

  • First of all create a new query PurchOpenStatus with the datasources vendTable and PurchTable.
  • PurchTable has joinmode inner join and fetchmode 1:n with relations yes.
  • Important to note that whenever you add a datasource to a query set the dynamic property on fields to yes so that all the fields are visible here.
  • As I want to get only the open orders details in the factbox i need to add a range on the purchTable datasource with purchStatus and value open order.

Parts :

  • The next step is to create a part PurchOpenStatusPart and the newly created query in the properties window.
  • On the orders and set the datasource as VendTable, repeating yes and  showCaption yes.
  • Right click on the orders and add the fields purchId, status and createdDate with the datasource as purchTable and the respective datafields and datamethods.

MenuItem :

  • Now drag this infopart into display menuitem.

Add factbox to listPage:

  • Drag this menuitem into the parts node of PurchTableListPage and set the required datasource and datasourceRelation.

      Save your changes and your factbox should be visible on the form


Friday 20 February 2015

Pass values between one form to other with the 2nd form showing filtered records based on values passed from 1st form

Todays blog is an example of how values are passed from one form to other and the 2nd form displays the filtered records based on the values passed from 1st form.

1) Create a form TripPlanner with style dialog and fields start date, end date and department (financial dimension field with lookup) and menuitem button OK.

Create the following parm method in the form methods which stores the values provided in the form in a container.

public container parmDateCon()
{
    container con;

    con = [StartDate.valueStr(),EndDate.valueStr(),Department.valuestr()];
    return con;
}

NOTE :
a) StartDate , EndDate and Department are the form controls whose auto declaration property is set to yes.

b) StartDate and EndDate are the fields from SMAServiceOrderLine_TripPlanner (My new table).

2) Override the lookup method of Department form control and add the following code

public void lookup()
{
    SysTableLookup sysTablelookup = SysTableLookup::newParameters(tableNum(DimAttributeOMDepartment),this);
    Query query = new Query();
    QueryBuildDataSource qbds;
       
    qbds = query.addDataSource(tableNum(DimAttributeOMDepartment));

    sysTablelookup.addLookupfield(fieldNum(DimAttributeOMDepartment,Value));
    sysTablelookup.addLookupfield(fieldNum(DimAttributeOMDepartment,Name));
           
    sysTablelookup.parmQuery(query);
    sysTablelookup.performFormLookup();
}

3) Create a menuitem button under Button Group in the group dialog commit with text OK and menuitem name as PlannedDetails. This PlannedDetails is the new form that gets opened on click of OK button and the values entered in TripPlanner are passed to the new form PlannedDetails.

Write the following code in the clicked method of the button.

void clicked()
{
    super();

    element.close();
}

4) The form looks like this


5)  Now create the 2nd form PlannedDetails with fields ServiceOrderId, Customer, StartDate, EndDate, Description and Driver.

Add the datasources SMAServiceOrderLine and SMAServiceOrderLine_TripPlanner

public class FormRun extends ObjectRun
{
    str                     sDate;
    str                     eDate;
    str                     dept;

    FromDateTime            startDate;
    ToDateTime              endDate;
    FromDateTime            stDate;
    ToDateTime              edDate;

    container               cont;

    QueryBuildDataSource    qbds;
    QueryBuildRange         qbrDate;
    QueryBuildRange         qbrDept;
}

6) Add the following code in the form init.

public void init()
{
    object  callerFormObject;
    callerFormObject = element.args().caller();

    if(formHasMethod(callerFormObject, literalStr(parmDateCon)))
    {
       cont =  callerFormObject.parmDateCon();
    }

    sDate = conPeek(cont,1);
    startDate = str2datetime(sDate, 213);

    eDate = conPeek(cont,2);
    endDate = str2datetime(eDate, 213);

    dept = conPeek(cont,3);

    super();
}

7) Under the datasource SMAServiceOrderLine_TripPlanner override init method and add the below code

public void init()
{
    super();

    qbds    = this.query().dataSourceTable(tableNum(SMAServiceOrderLine_TripPlanner));
    qbrDate = qbds.addRange(fieldNum(SMAServiceOrderLine_TripPlanner, DataAreaId));
    qbrDept = qbds.addRange(fieldNum(SMAServiceOrderLine_TripPlanner, Department));

    if((startDate && endDate) != 0)
    {
        qbrDept.value(queryValue(dept));

        qbrDate.value(strFmt('(((%2 > %4) && (%1 < %3)) || ((%1 < %3) && (%2 > %4)))', fieldStr(SMAServiceOrderLine_TripPlanner, StartDate), fieldStr(SMAServiceOrderLine_TripPlanner, Enddate), DateTimeUtil::toStr(endDate),DateTimeUtil::toStr(startDate)));
        info(queryvalue(qbds.toString()));
    }
}

8) Now when you click the OK button in the form TripPlanner, the values are passed from this form to new form PlannedDetails and this shows the filtered records based on the start date, end date and department values provided in the form TripPlanner.


Thursday 19 February 2015

Capturing infolog messages in AX 2012

Today I am going to explain how to capture infolog messages for a particular record and show those logs on click of a log button on a form.

1) Create a job which inserts records into a table, prints some info messages and capture them.

logTable contains three fields EmpId, name and info. info is a field created with EDT infologData and visible property is set to NO. info is a container which stores the infolog messages for the particular record.

static void logs(Args _args)
{
     logTable       insertLog;
 
     NumberSeq  numberSeq;
     InfologData  msg;

     ttsbegin;
     insertLog.initValue();

     numberSeq                          =   NumberSeq::newGetNum(InventParameters::numRefEmpID());
     insertLog.EmpId           =   numberSeq.num();
     insertLog.Name                  =   'ABC';

     info(strFmt("%1", insertLog.EmpId));
     info(strFmt("%1", insertLog.Name));
     info(strFmt("Employee with %1 & name %2 created successfully", insertLog.EmpId,insertLog.Name));
     msg        =    infolog.infologData();
     insertLog.Info = msg;

     insertlog.insert();
     infolog.clear();

     ttscommit;
}

NOTE :
i) Save compile and run this job. EmpId is generated through number sequence and name is hard coded. These two values are inserted into table.

ii) The info stored for this record are :

EMPID-0001
New
Employee with EMPID-0001 & name ABC created successfully

2)  Now create a form with grid showing the logTable datasource fields and button named log.
Write the following code in clicked method of the button

void clicked()
{
    InfologData  msg;

    msg = logTable.info;
    infolog.import(msg);
}

Now when you select this particular record and press the log button the infolog messages stored for this record are displayed in info.

Inserting records in a table through a job in AX 2012

Create a table name StudentTable and fields StudentId, StudentName respectively.
StudentId should be generated through number sequence. Creation of number sequence is shown in my previous blog.

Now create a job with the following code :

static void studentData(Args _args)
{
    studentTable   student;
    NumberSeq     numberSeq;

    ttsbegin;
    student.initValue();

    numberSeq            =    NumberSeq::newGetNum(HRMParameters::numRefSTUDID());
    student.StudentId  =    numberSeq.num();
    student.name         =    "ABC";

    student.insert();
    info(strFmt("Student with studentId : %1 and name : %2 is created successfully", student.StudentId, student.StudentName));
    ttscommit;
}

Wednesday 18 February 2015

Generating number sequence in AX 2012

First of all to create a number sequence for a field in a table create a table with name Departments and having two fields deptId and deptName.
deptId is a new EDT created with name dept_Id and deptName with name EDT.

1) Goto organisation administration -> number sequences -> number sequences -> new number sequence -> 

Fill in the fields under identification fast tab and under segments click on add

Constant ---   DEPT
Constant ---   -
Alplanumeric --- ####

below that format now appears as DEPT-####

Remember that under general smallest is 1 and largest is 9999 as number of # specified under segments is 4 so write 9999(four 9's).

save it and generate the number sequence.

2) Classes -> NumberSequenceHRM -> loadmodule

    datatype.parmDatatypeId(extendedtypenum(Dept_Id));
    datatype.parmReferenceHelp(literalstr("@SYS852"));
    datatype.parmReferenceLabel(literalstr("@SYS386"));
    datatype.parmWizardIsContinuous(true);
    datatype.parmWizardIsManual(NoYes::No);
    datatype.parmWizardIsChangeDownAllowed(NoYes::No);
    datatype.parmWizardIsChangeUpAllowed(NoYes::No);
    datatype.parmWizardHighest(9999);
    datatype.parmSortField(13);

    this.create(datatype);

you can copy and paste from the method but change the extendedtypenum, help, label, wizardhighest and sortfield number increase to next number.

3) DataDictionary -> Tables -> HRMParameters -> methods ->

create a new method numRefDeptId with the following code :

static client server NumberSequenceReference numRefDeptId()
{
    return NumberSeqReference::findReference(extendedTypeNum(Dept_Id));
}

4) In the newly created table Departments override the initValue and write the following code :

public void initValue()
{
    NumberSeq numberSeq;
    NumberSequenceReference numSeqRef;

    numSeqRef = InventParameters::numRefDeptId();
    numSeqRef = NumberSeqReference::findReference(extendedTypeNum(Dept_Id));

    super();

    numberSeq = NumberSeq::newGetNum(numSeqRef);
    this.deptId = numberSeq.num();
}

5) Create a job

static void numberSeqDeptId(Args _args)
{
    NumberSeqModuleHRM obj = new NumberSeqModuleHRM();
    obj.load();
}

save compile and run the job

6) Goto HumanResource -> Setup -> Parameters -> Human resources shared parameters

Under number sequences a new number sequence is created now with the label you mentioned now right click and view details. under general in use should be checked so that you can use this number sequence.

7) To check the number sequence goto table browser and ctrl-N 

DEPT-0001 comes under deptId


This is how a number sequence is generated and used .... :)




Friday 13 February 2015

Export data to text file in ax2012

static void textFileExport(Args _args)
{
    TextIo  textIo;
    container line;
    Filename    filename;
   
    SiteTable    site;
   
    filename = @"C:\Users\Desktop\sitedetails.txt";
   
    textIo = new TextIo(filename, 'W');
    textIo.outFieldDelimiter(';');
   
    if(!textIo || textIo.status() != IO_Status::Ok)
    {
        error("File cannot be opened");
    }
   
    while select site
    {
        line = [site.SiteId, site.Name];
        textIo.writeExp(line);
    }
}

Importing table data from CSV file to AX using CommaIo class in AX 2012

Create a class with the following code :

class importCSVCommaIO extends RunBase
{
    Filename        filename;
    DialogField     dialogFilename;
}

public static void main(Args    _args)
{
    importCSVCommaIO    importDept = new importCSVCommaIO();
    FormRun formRun;
    Args    args;

    if(importDept.prompt())
    {
        importDept.importDeptTable();
    }
}

public Object dialog()
{
    DialogRunbase       dialog = super();

    dialogFilename   = dialog.addField(extendedTypeStr(FilenameOpen));
    dialogFilename.value(filename);
    return dialog;
}

public boolean getFromDialog()
{
    fileName = dialogFileName.value();
    return super();
}

void importDeptTable()
{
    CommaIo         commaIO;
    DepartmentTable department;
    DepartmentTable departmentTable;

    Container       con;
    boolean first = true;
    FileIOPermission    readPermission;
    #file

    readPermission = new FileIOPermission(filename,#io_read);
    readPermission.assert();

    commaIO = new CommaIo(filename,'r');
    commaIO.inFieldDelimiter(',');
    CodeAccessPermission::revertAssert();

    if (commaIO)
    {

        while(commaIO.status() == IO_Status::OK)
        {
            con = commaIO.read();
            if (conLen(con) > 0)
            {
                if(first)
                {
                    first = false;
                }
                else
                {
                    ttsbegin;
                   
                    department = DepartmentTable ::find(conpeek(con,1), true);
                    if(department.RecId == 0)
                    {
                        department.DeptId       =   conpeek(con,1);
                        department.Description  =   conpeek(con,2);
                        department.insert();
                    }
                    else
                    {
                        department.selectForUpdate(true);
                        select departmentTable where departmentTable.DeptId == department.DeptId;
                        department.Description = conPeek(con,2);
                        department.update();
                    }
                     ttscommit;
                }

            }
        }

    }
}


select the csv file from the browse button and on press OK the data gets imported into the table.


Thursday 12 February 2015

Exporting data from AX to csv file using commaIO class in AX 2012

Write a job with the following code :

static void ExportDataToCSVcommaIO(Args _args)
{
    Query                                query;
    QueryBuildDataSource    qbds;
    QueryBuildRange            qbr;
    QueryRun                        qRun;
    CommaIo                         commaIO;
    Filename                          fileName;
 
    DepartmentTable          department;
    #WINAPI;
 
    fileName = WinAPI::getFolderPath(#CSIDL_DESKTOPDIRECTORY) + "\\" + "Department details" + ".csv";
 
    commaIO = new CommaIo(fileName,'W');
 
    query = new Query();
    qbds = query.addDataSource(tableNum(DepartmentTable));
    qbr = qbds.addRange(fieldNum(DepartmentTable,DeptId));
 
    qRun = new QueryRun(query);
 
    commaIO.write("DeptId","Description");
    while(qRun.next())
    {
        department = qRun.get(tableNum(DepartmentTable));
     
        commaIO.write(department.DeptId, department.Description);
    }
 
    WinAPI::createFile(fileName);
}

NOTE :

1) #CSIDL_DESKTOPDIRECTORY gets the desktop of the current user logged into.
2) A new file gets created with the specified name in the filename in the given folder path.

Exporting data using shellexecute method of WINAPI class:

fileName       = WINAPI::getTempPath() + "Department Details" + ".csv";
WINAPI::shellExecute(fileName);

When you run the job with the above code excel file with the table data gets opened automatically.


Wednesday 11 February 2015

Moving all files from a folder of specified format from one folder to other using WINAPI class in AX 2012

The following piece of code filters the source file path folder for the files with extension .docx and copies them into the destination path specified.

public void moveAllFiles()

{
    fileFilter = '\\*.docx';

    sourcePath = dialogSource.value();


    destPath = dialogDestination.value();


    [fileHandle, filename] = WinAPI::findFirstFile(sourcePath + fileFilter);


    while(fileName)

    {
        info(strFmt("%1 %2", sourcePath , fileName));

        copyFromSourceFolder = sourcePath + "\\" + fileName;

       
        copyToDestFolder = destPath + "\\" + filename;
       
        WinAPI::copyFile(copyFromSourceFolder, copyToDestFolder);

        fileName = winAPI::findNextFile(fileHandle);

    }
}

NOTE :


1) The extension format may be anything .docx .txt .xpo or anthing.

2) Here source and destination dialog fields are of type filepath.

File Operation using WinAPI in Dynamics AX 2012


Axapta’s WinAPI class has a bunch of static methods to handle files. It is a very useful class when handling files that need to be accessed outside of the AX environment
The examples below show how to utilize some of these methods.

1) Create a dialog which takes source and destination file name and file path respectively and on click of OK button COPY the file from source folder to destination folder.



Create a class example_WINAPI with the following methods :

class example_WINAPI extends RunBase
{
    FilenameOpen    fileNameOpen;

    Filename    filename;

    Filename    filePath;
    Filename    fileType;
    Filename    destPath;

    DialogField dialogSource;

    DialogField dialogDestination;

    str sourceFileName;


    str destFileName;
}


public static void main(Args    args)

{

    example_WINAPI  fileDialog = new example_WINAPI();



    fileDialog.createDialog();

}

public void createDialog()
{
    Dialog dialog;

    dialog = new Dialog("Move files");

    dialog.caption('Move file');
    dialogSource        =   dialog.addField(extendedTypeStr(FilenameOpen), "@SYS1425");
    dialogDestination   =   dialog.addField(extendedTypeStr(FilePath), "@SYS37735");

    dialogSource.value(filename);

    if(dialog.run())
    {
        this.copyFile();
    }
}

public void copyFile()
{
    sourcefileName = dialogSource.value();
    [filePath, fileName, fileType] = fileNameSplit(sourcefileName);

    destPath = dialogDestination.value();

    destFileName = destPath + "\\" + conPeek(fileNameSplit(sourceFileName),2) +    conPeek(fileNameSplit(sourceFileName),3);
    info(queryValue(destFileName));

    WinAPI::copyFile(sourceFileName, destFileName);
}


2) Create a dialog which takes source and destination file name and file path respectively and on click of OK button MOVE the file from source folder to destination folder.

Add one more method moveFile to the above class and call it from createDialog method

public void moveFile()
{
    sourcefileName = dialogSource.value();

    if(WinAPI::fileExists(sourceFileName))
    {
        [filePath, fileName, fileType] = fileNameSplit(sourcefileName);

        destPath = dialogDestination.value();

        destFileName = destPath + "\\" + conPeek(fileNameSplit(sourceFileName),2) + conPeek(fileNameSplit(sourceFileName),3);
       
        WinAPI::moveFile(sourceFileName, destFileName);
    }
    else
    {
        warning("This file doesnot exist in the specified path");
    }
}

NOTE :

In the above method one more method fileExists is used from WINAPI class to check whether the specified file is present in the path or not. If present then executes the code else throws the warning message.

3) Delete a file in a specified path :

WINAPI::deleteFile(fileName);


Thursday 5 February 2015

Simple query filter in ax 2012


The following is an example of filtering based on itemId selected.

Basically you have a form which has a grid at the top and item id , item group text fields where you need to select both item id and item group from the lookup and the grid is filtered based on the values selected.

1) In the class declaration :

class FormRun extends ObjectRun
{
    InventTable inventTable;  // create a buffer for the datasource

    QueryBuildRange qbrItemId;
    QueryBuildRange qbrItemGroup;
}

2) Add inventTable in the datasource node and write the following code in the init of that datasource

public void init()
{
    QueryBuildDataSource qbds;

    super();

    qbds           =   this.query().dataSourceTable(tableNum(InventTable));
    qbrItemId   =  qbds.addRange(fieldNum(InventTable, ItemId));
}

3) Add InventItemGroupItem in the datasource node and write override init

public void init()
{
    QueryBuildDataSource qbds;

    super();

    qbds                   =   this.query().dataSourceTable(tableNum(InventItemGroupItem));
    qbrItemGroup    =  qbds.addRange(fieldNum(InventItemGroupItem, ItemGroupId));
}

4) Add ItemId stringedit form control to the design node and override modified method in the methods node and write the following code

public boolean modified()
{
    boolean ret;
    str     value;

    ret = super();

    if(ret)
    {
        value = qbrItemId.value('*' + InventTable_ItemId1.valueStr() + "*");
     
        InventTable_ds.executeQuery();
        InventTable_ds.refresh();
        InventTable_ds.research();
    }

    return ret;
}

NOTE :

In the above code InventTable_ItemId1 is the form control name whose auto declaration is set to yes.

5) Add ItemGroupId form control to design node and override lookup method

public void lookup()
{
    SysTableLookup        sysTableLookup =     SysTableLookup::newParameters(tableNum(InventItemGroup), this);
    Query                 query = new Query();
    QueryBuildDataSource  qbdsInventItemGroup;

    qbdsInventItemGroup = query.addDataSource(tableNum(InventItemGroup));
    qbdsInventItemGroup.addOrderByField(fieldNum(InventItemGroup, ItemGroupId));

    qbdsInventItemGroup.addRange(fieldNum(InventItemGroup, Name)).value(SysQuery::valueNotEmptyString());

    sysTableLookup.addLookupfield(fieldNum(InventItemGroup, ItemGroupId),true);
    sysTableLookup.addLookupfield(fieldNum(InventItemGroup, Name),false);

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

6) Override modified method

 public boolean modified()
{
    boolean ret;

    ret = super();

    if(ret)
    {
        qbrItemGroup.value('*' + InventItemGroupItem_ItemGroup.valueStr() + '*');
   
        InventTable_ds.executeQuery();
        InventTable_ds.research();
    }

    return ret;
}