Thursday, August 27, 2009

RunOn property in Axapta

The class has a RunOn property that have three values: Client, Called From and Server. Objects created from the class will then live at the location specified.
If you choose Called from, the object will live at the tier where the code creating it (by calling the new constructor) is running.
Classes extending other classes will also inherit the RunOn property. You cannot change it if it is Client or Server. If it is Called from, you can leave it or change it to Client or Server.
But someone may wonder that menu items have their RunOn properties, what will happen if the RunOn property of a given menu item pointing to a class is set to Server, whereas the class's RunOn property is set to Client. The answer is that only if the Class's RunOn property is set to Called From, the objects will be created determined by menu item's RunOn property.
Also there is another situation, the class has static main method which has a RunOn property as well. What will happen if the menu item's RunOn property is set to client, whereas main method has a server modifier. The answer is it will determined by main method's modifier.
Axapta will create the objects according to the prioritized sequence of Class's RunOn property, Class's main method's modifier, menu item's RunOn property. And please notice that in Fat Client mode, even you can set the Class's RunOn property to Server, Axapta will still create the objects in client side instead of Server side.

Wednesday, August 5, 2009

Create and Post Free Text Invoice in AX

Here is a sample class which is called via Dialog framework to create & post free text invoice using X++ code.

Job:
public void freeTextInvoicePostTestJob()
{
Dialog dialog;
DialogField dlgCustAcc;
DialogGroup dialogPeriodLengthGroup, dialogPeriodLengthGroup1;
DialogField dlgLedgerAcc;

dialog = new Dialog("Free-Text Invoice");
dialogPeriodLengthGroup1 = dialog.addGroup('Cust Table');
dlgCustAcc = dialog.addField(extendedTypeStr(CustAccount));
dialogPeriodLengthGroup = dialog.addGroup('Ledger Table');
dlgLedgerAcc = dialog.addField(extendedTypeStr(LedgerAccount));


if(dialog.run())
{
if(dlgCustAcc.value() && dlgLedgerAcc.value() != '')
FreeTxtInvoiceCreatePost::main(dlgCustAcc.value(), dlgLedgerAcc.value());
else
throw error(strfmt("Either CustAccount or LedgerAccount info is missing."));
}
}

class FreeTxtInvoiceCreatePost
{
}
static void main(CustAccount _custAccount, LedgerAccount _ledgerAccount)
{
CustInvoiceTable custInvoiceTable;
CustInvoiceLine custInvoiceLine;
CustTable custTable;
LedgerTable ledgerTable;
CustPostInvoice custPostInvoice;
LineNum lineNum;
int i;

ttsbegin;
custTable = CustTable::find(_custAccount);
custInvoiceTable.initFromCustTable(custTable);
custInvoiceTable.insert();
ttscommit;

for(i=1; i<=100; i++)
{
ttsbegin;
ledgerTable = LedgerTable::find(_ledgerAccount);
custInvoiceLine.clear();
custInvoiceLine.initValue();
custInvoiceLine.LedgerAccount = ledgerTable.AccountNum;
custInvoiceLine.initFromCustInvoiceTable(custInvoiceTable);
custInvoiceLine.AmountCur = 10.00;
custInvoiceLine.Description = 'FreeTxIv' + int2str(i);
custInvoiceLine.TaxItemGroup = 'full';
custInvoiceLine.ParentRecId = custInvoiceTable.RecId;

lineNum += 1;
custInvoiceLine.LineNum = lineNum;
custInvoiceLine.insert();
ttscommit;
}

custPostInvoice = new CustPostInvoice(custInvoiceTable);
custPostInvoice.run();
}

Updating records and calling super

Before a record in AX gets updated an external application gets started. This external application is able to do additional update on the same record in AX by using AIF. As the performance of the external route is slow, an additional check is needed to start this process only if needed.

So what do we have?

A. Do I need to compare this with this.orig() before or after super?
B. Do I need to call this external application before or after super?

If we call the external application before super, AX will complain that another user has updated the record. So we have to do it after super. Fine, but what happens…. After super the original record gets equal to the current record. Ouch so the difference between the record and the original record only exists before super. The solution is to make a local buffer variable in the update method that references the original record before calling super. This buffer will still be used after calling super.

Example:

void update()
{
CustTable ctBuffer = this.orig();
Super();
If(this.name != ctBuffer.name)
//call external application
}

Monday, August 3, 2009

Where do you want your code to run today

Much of the X++ code you write can run on either the Application Object Server, or the Client. The client can be the Win32 client, or BC.NET for Enterprise Portal. MorhpX generally will not direct your code to run on a specific tier, but you as an application developer can control it.

Whether code runs on the server or on the client has performance and security implications, and should be given careful consideration as a part of design. Occasionally it will be correct to methods run on either tier, but usually this is not the case.

As MS move forward implementing tasks in upcoming release, developers will identify which resources to grant access to for a given task. These resources include tables/fields and server entry points. A server entry point is a specially decorated method that runs on the server. Tables/fields accessed inside this server entry point need NOT be identified with the task, greatly reducing the complexity and effort required to implement the task. If the server entry point is NOT executed on the server, then table/field access is still validated, and will fail if the code accesses a table/field that is not included with the task.

So, in short:
Set the RunOn AOT property to Server or Client for new classes, unless you have a reason to leave the default
Include considerations about where the code runs in your design, and look for it when reviewing design documents
Declare static class methods, table static and instance methods with either the "server" or the "client" keyword, unless you have a reason not to
Look for ways to limit direct table and field access in form methods, try to move this into a class

This page MSDN describes where methods run:
http://msdn.microsoft.com/en-us/library/aa891949.aspx. Additional information to cover more object types at http://msdn.microsoft.com/en-us/library/aa634829.aspx.