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 26, 2009
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();
}
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();
}
Tuesday, August 4, 2009
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
}
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
}
AX 2009 TraceParser
This is very useful link that give some in depths about TraceParser
1. TraceParser Video Training Part I: Installation.
2. TraceParser Video Training Part II: Configuration, Collection and Importing.
1. TraceParser Video Training Part I: Installation.
2. TraceParser Video Training Part II: Configuration, Collection and Importing.
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.
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.
Tuesday, July 21, 2009
Iterate all records from grid
To iterate all record from a grid is accomplished by :
VendOutPaymForParams_FI exportSetupLocal;
for (exportSetupLocal = exportSetup_ds.getNext();exportSetupLocal;exportSetupLocal=exportSetup_ds.getNext())
{
B....
}
VendOutPaymForParams_FI exportSetupLocal;
for (exportSetupLocal = exportSetup_ds.getNext();exportSetupLocal;exportSetupLocal=exportSetup_ds.getNext())
{
B....
}
Tuesday, July 14, 2009
Getting rid of the "Loss of precision" warning message
A question came up at today's webinar where a developer had a (presumably legitimate) reason to cast a real value into an integer value. The X++ language does not allow explicit casting (there's no support for it in the language), but the compiler will do its best to satisfy the user and do the conversion on its own. In this case, however, it issues a warning message, lest this is not what the user wanted.
One solution is to use the anytype type to hold the vaue for conversion and then using the any2int function, as shown below:
static void Job47(Args _args)
{
real r = 3.13;
int i = r; // Warning is issued here
anytype a;
a = r; // Assign to an anytype variable...
i = any2int(a); // ... and back into an int
print r;
print i;
pause;
}
This should be packaged into a function, maybe called int RealToInt(real arg).
Another way would be doing the conversion in managed code (through the System.Convert::ToInt32(object) method), but the performance will not be as good because of the marshalling that needs to take place.
One solution is to use the anytype type to hold the vaue for conversion and then using the any2int function, as shown below:
static void Job47(Args _args)
{
real r = 3.13;
int i = r; // Warning is issued here
anytype a;
a = r; // Assign to an anytype variable...
i = any2int(a); // ... and back into an int
print r;
print i;
pause;
}
This should be packaged into a function, maybe called int RealToInt(real arg).
Another way would be doing the conversion in managed code (through the System.Convert::ToInt32(object) method), but the performance will not be as good because of the marshalling that needs to take place.
Subscribe to:
Posts (Atom)
