Microsoft Dynamics AX 2012 Eventing


One of the great new development improvements for Microsoft Dynamics AX 2012 is "Eventing".

For most people, that have ever done any Object oriented development or programming, eventing is a concept that most familiar.

It is a concept that is used to fire before, just before or just after a given method call takes place. These can also be design within the middle of a method as well, if a given piece of business logic within the middle of a method, make sense to tie an event too.

If you bring this to a more functional level, you can think of eventing as something that happens for a given object. For example, when a Sales Order greater than $10,000.00 is invoiced, an event is fired that automatically places an order for a pizza party, for the dept.

Chances are your customizations will want to subscribe to places, or make modifications to, code that doesn't provide any delegates. Microsoft introduced a concept of pre and post-handlers. This basically allows you to "subscribe" your code to any method in the application, without having to modify the standard application code to make that call. For the sake of customizations, we will talk about pre/post handlers and delegates.

Now it's very important to point out, that all event handler subscription elements must be static methods. These are the only types allowed, for create such an event handler subscriber, or event listener. Since pre-post handlers can only be attached in the AOT, as opposed to delegate handlers that can be subscribed at runtime, obviously public (remember, new methods in AX 2012 will be explicitly declared "private" by default), and no return type (=void), and accept an object of type XppPrePostArgs.


The XppPrePostArgs object is the juice of the meat. This gives you access to the arguments passed into the method you're subscribing to (using getArg() or args()), the return value being returned from the method you're subscribing to (getReturnValue()), and it has a method getThis() which returns the instance of the object that fired the event. Of course, you can modify the arguments being passed in, and modify the return value of the original method. But here's the thing! Technically, it is not guaranteed in what order the event subscribers will be called. This is because it depends on the order in which code is imported, models are installed, etc. So changing the return value (in post-handlers) or arguments (in pre-handlers) is possible, but since you are not guaranteed to be the last person to touch these values, it is not guaranteed that nobody will overwrite your values.

Since changing the return value cannot be guaranteed, it becomes a little less useful. On top of that, accessing arguments through the args() or getArg() methods is not the safest thing. You either access the arguments through index or through name, which you won't know works until runtime. Because of these limitations and potential issues, lightweight handlers were invented. They are lightweight in the sense that they do not provide a means to change the arguments (but you can read them - and remember certain types are passed by reference so technically you can change them), no means to change the return value, and no means to retrieve the object instance the event was thrown from. Unfortunately, the latter makes the lightweight handlers handicapped to a certain extent. All in all, depending on your situation, you will find that either one will support you in your endeavors. It's a powerful feature.
To hook up the subscriber, you either drag your subscriber method and drop it on the method you wish to subscribe to. Or you right-click the method you wish to subscribe to and select "New Event Handler Subscription".

Note that the "subscription" itself (the node in the AOT) carries a name property. Make sure to give this a unique name. Also in the properties, you can denote the subscription to be pre or post, and in case you wish to write the code in managed code, you can, by setting the EventHandlerType property to "Managed". In that case, make sure, for the class name, to include the full assembly name as well (eg: MyCompany.MyProject.MyClass).

There are two major things you need to know to appreciate this pre/post handler feature even more:
1) The "subscription" (the link between the original method and your subscriber method) has an AOT node, and is stored in the layer/model separate from the original code (so giving it a unique name is of the essence!)
2) The compilation of the original method will "append" the call to your handler to itself - this means no "lookup" overhead at runtime (if you think about this, it will answer any questions you may have around database transactions, exceptions, etc… consider the pre/post handler as if they were calls to your method at the very beginning, or very end, of the method you're subscribing to).

The key here is number 1 though, the fact that the subscription is stored separate from the original method. This is in fact the number one reason for the power of the events and models combo!

However, there is one sore point (in my opinion) in this model: managed code handlers for X++ events do not support complex types!

If events (be it pre/post events or delegate ("coded") events) have complex argument types to their methods (anything that's not a base type), they will not allow managed handlers. On top of that, the Pre/Post handler type using the XppPrePostArgs class as an argument (a complex type) does work with managed handlers. But when subscribing a lightweight handler, or subscribing to a delegate, the managed code handlers CANNOT take complex types.

First, let's create a new class in Dynamics AX 2012 called “MyEvents". We will have a Run method that calls a delegate, we'll need to create that delegate, and a main method to run the class.

To create the delegate, right-click your new class and select New > Delegate, or you can just create a new blank method and type in the method manually as shown below.


There is an abstract "XppEventArgs" class which you can inherit from. I'm using the XppPrePostArgs class (which extends XppEventArgs) to avoid creating a new class for this article. Anyway, next we create a run method which calls the delegate

Finally, we need a Main method to run our class, which will instantiate the class and call Run().

That's all for now, and has we move forward. Try this with some real world example, you will find it very interesting.

Advertisements
Posted in Uncategorized | Tagged , , | Leave a comment

Changing numerals to text in Indian Format

I have made some changes to the “numeralsToTxt_EN” method of the Global Class.
Add the below method to the Global class – which will convert the number into Indian format
 
 
static TempStr numeralsToTxt_IN(real _num)
{
int numOfPennies = (decround(frac(_num), 2) * 100) mod 100;
real test = _num – frac(_num);
 
int numOfTenths;
str 20 ones[19], tenths[9], hundreds, thousands, lakhs, crores;
 
int64 temp;
str 200 returntxt;
str 200 pennytxt;
int penny;
 
real modOperator(real a1, real a2)
{
int tmpi;
real tmp1, tmp2;
tmp1 = a1 / a2;
tmpi = real2int(tmp1);
tmp2 = tmpi;
return (tmp1 – tmp2)*a2;
}
 
real checkPower(real _test, int64 _power)
{
int64 numOfPower;
 
if (_test >= _power)
{
numOfPower = _test div _power;
if (numOfPower >= 100)
{
temp = numOfPower div 100;
returntxt = returntxt + ‘ ‘ + ones[temp] + ‘ ‘ + hundreds;
numOfPower = numOfPower mod 100;
}
if (numOfPower >= 20)
{
temp = numOfPower div 10;
returntxt = returntxt + ‘ ‘ + tenths[temp];
numOfPower = numOfPower mod 10;
}
if (numOfPower >= 1)
{
returntxt = returntxt + ‘ ‘ + ones[numOfPower];
numOfPower = numOfPower mod 10;
}
switch(_power)
{
case 10000000 :
{
returntxt = returntxt + ‘ ‘ + Crores;
_test = modOperator(_test, 10000000);
break;
}
case 100000 :
{
returntxt = returntxt + ‘ ‘ + lakhs;
_test = modOperator(_test, 100000);
break;
}
case 1000 :
{
returntxt = returntxt + ‘ ‘ + thousands;
_test = modOperator(_test, 1000);
break;
}
case 100 :
{
returntxt = returntxt + ‘ ‘ + hundreds;
_test = modOperator(_test, 100);
break;
}
}
}
return _test;
}
 
#Define.text_1(‘One’)
#Define.text_2(‘Two’)
#Define.text_3(‘Three’)
#Define.text_4(‘Four’)
#Define.text_5(‘Five’)
#Define.text_6(‘Six’)
#Define.text_7(‘Seven’)
#Define.text_8(‘Eight’)
#Define.text_9(‘Nine’)
#Define.text_10(‘Ten’)
#Define.text_11(‘Eleven’)
#Define.text_12(‘Twelve’)
#Define.text_13(‘Thirteen’)
#Define.text_14(‘Fourteen’)
#Define.text_15(‘Fifteen’)
#Define.text_16(‘Sixteen’)
#Define.text_17(‘Seventeen’)
#Define.text_18(‘Eighteen’)
#Define.text_19(‘Nineteen’)
#Define.text_20(‘Twenty’)
#Define.text_30(‘Thirty’)
#Define.text_40(‘Forty’)
#Define.text_50(‘Fifty’)
#Define.text_60(‘Sixty’)
#Define.text_70(‘Seventy’)
#Define.text_80(‘Eighty’)
#Define.text_90(‘Ninety’)
#Define.text_100(‘Hundred’)
#Define.text_1000(‘Thousand’)
#Define.text_100000(‘Lakh’)
#Define.text_10000000(‘Crore’)
#Define.text_and(‘Rupees and’)
#Define.text_paise(‘Paise Only’)
#Define.text_ruppe(‘Rupees Only’)
 
ones[1] = #text_1;
ones[2] = #text_2;
ones[3] = #text_3;
ones[4] = #text_4;
ones[5] = #text_5;
ones[6] = #text_6;
ones[7] = #text_7;
ones[8] = #text_8;
ones[9] = #text_9;
ones[10] = #text_10;
ones[11] = #text_11;
ones[12] = #text_12;
ones[13] = #text_13;
ones[14] = #text_14;
ones[15] = #text_15;
ones[16] = #text_16;
ones[17] = #text_17;
ones[18] = #text_18;
ones[19] = #text_19;
 
tenths[1] = ‘Not used’;
tenths[2] = #text_20;
tenths[3] = #text_30;
tenths[4] = #text_40;
tenths[5] = #text_50;
tenths[6] = #text_60;
tenths[7] = #text_70;
tenths[8] = #text_80;
tenths[9] = #text_90;
 
hundreds = #text_100;
thousands = #text_1000;
lakhs = #text_100000;
crores = #text_10000000;
 
 
 
test = checkPower(test, 10000000);
test = checkPower(test, 100000);
test = checkPower(test, 1000);
test = checkPower(test, 100);
 
if (test >= 20)
{
numOfTenths = test div 10;
returntxt = returntxt + ‘ ‘ + tenths[numofTenths];
numOfTenths = numOfTenths mod 10;
test = test mod 10;
}
if (test >= 1)
{
numOfTenths = real2int(test);
returntxt = returntxt + ‘ ‘ + ones[numOfTenths];
}
 
if (numOfPennies)
{
if (numOfPennies >= 20)
{
penny = numOfPennies div 10;
pennytxt = tenths[penny];
numOfPennies = numOfPennies mod 10;
}
if (numOfPennies >= 1)
{
pennytxt = pennytxt + ‘ ‘ + ones[numOfPennies];
}
returntxt = returntxt + ‘ ‘ + #text_and + ‘ ‘ + pennytxt + ‘ ‘ +#text_paise;
}
else
{
returntxt = returntxt + ‘ ‘ + #text_ruppe;
}
 
return returntxt;
}
 
Posted in Uncategorized | Tagged , | Leave a comment

AX 2012 – X++ Code to ‘Create and Post Purchase Order Invoice’

Find below job to create and post purchase order in Dynamics AX 2012

Note: Referring contoso demo data, ‘CEU’ company

static void CreatePOAndInvoice(Args _args)
{

PurchTable      purchTable;
PurchLine       purchLine;
VendTable       vendTable = VendTable::find(“3008”);
AxPurchTable    axPurchTable;
AxPurchLine     axPurchLine;
PurchFormLetter purchFormLetter;

//Create Purchase order
purchTable.initFromVendTable(vendTable);

axPurchTable = axPurchTable::newPurchTable(purchTable);
axPurchTable.parmPurchaseType(PurchaseType::Purch);
axPurchTable.parmDocumentStatus(DocumentStatus::PurchaseOrder);
axPurchTable.parmAccountingDate(systemDateGet());
axPurchTable.parmDeliveryDate(016\2012);
axPurchTable.parmPurchStatus(PurchStatus::Backorder);
axPurchTable.doSave();

//Create PurchLine for item 1000
purchLine.initFromPurchTable(purchTable);

axPurchLine = AxPurchLine::newPurchLine(purchLine);
axpurchLine.parmItemId(“1000”);
axPurchLine.parmPurchQty(10);
axPurchLine.parmPurchPrice(100);
axPurchLine.doSave();

//Posting PO Confirmation,I guess its mandatory
//You cannot do invoice without doing PO confirm
purchTable = axPurchTable.purchTable();
purchFormLetter = PurchFormLetter::construct(DocumentStatus::PurchaseOrder);
purchFormLetter.update(purchTable, strFmt(“Inv_%1”, purchTable.PurchId));

//Posting PO Invoice
purchFormLetter = PurchFormLetter::construct(DocumentStatus::Invoice);
purchFormLetter.update(purchTable, strFmt(“Inv_%1”, purchTable.PurchId));

}

Posted in Uncategorized | Leave a comment

Ax Dynamics AX2012: Table Full Text Indexes

Microsoft Dynamics AX 2012 provides full-text functionality that enables Microsoft Dynamics AX to search business data over a large volume of text data or documents.

Example:

/* Two important rules for creating Full Text Indexes [Technet] TableGroup property. A table can have a full text index only if the TableGroup property is set to Main or Group on the table. A full text index can only be created on a table with RecId index. So make sure 'CreateRecIdIndex' property flag on table is set to Yes */ static void FullTextIndexesDemo() { Query query = new Query(); QueryBuildDataSource queryBuildDatasource; QueryRun queryRun; QueryBuildRange queryRange; FullTextIndexesDemo fullTextIndexes; queryBuildDatasource = query.addDataSource(tableNum(FullTextIndexesDemo)); queryRange = queryBuildDatasource.addRange(fieldNum(FullTextIndexesDemo, AxVersion)); queryRange.rangeType(QueryRangeType::FullText); /* [Technet] Space characters are treated as implicit Boolean OR operators. There is a space character in the string parameter in call queryBRange4.value("dynamics 0");. */ queryRange.value('dynamics 0'); queryRun = new QueryRun(query); while (queryRun.next()) { fullTextIndexes = queryRun.get(tableNum(FullTextIndexesDemo)); Debug::printTab(DebugPrintTab::ActiveX, fullTextIndexes.AxVersion); } }

Happy Full indexing in 2012 ;)

Posted in Uncategorized | Tagged , , , , | Leave a comment

Hello world!

Welcome to WordPress.com! This is your very first post. Click the Edit link to modify or delete it, or start a new post. If you like, use this post to tell readers why you started this blog and what you plan to do with it.

Happy blogging!

Posted in Uncategorized | 1 Comment