Thursday, September 26, 2013

jumpRef variations

jumpRef method is overriden for enabling the 'View details' feature, when you right click on a field.
If your datasource on a form has relations to multiple tables with the same field, the form may choose the undesired table to reference.

You can do this either on the datasource field or the form control. Once you have overridden the method add code similar to the example below.
    Args    args;
    FormRun  formRun;

    args = new Args(formstr(VendTable));
    formRun = classfactory.formRunClass(args);

Notice the record parameter, it is the actual table record you want to open.

Imagine a situation where you open a form B by clicking a button on form A. The clicked method of the button had the above code. And you want to perform some operation after the form B is closed. You could use the closedOk method of form B and call any method on form A like below.
    if (formRun.closedOk())

Instead of formRun, a more secure and neater approach would be to use the MenuFunction like below. Its secure because it uses the menu item on which we can control access. copyCallerQuery() is optional. In some instances, when you have a grid of a different datasource than the main datasource with no relation to main datasource table, its records wont be populated if you don't use copyCallerQuery.
    menuFunction = new MenuFunction(MenuItemDisplayStr(VendTable), MenuItemType::Display);   

AX also provides two classes, smmUtility and MenuFunction, which provide out of the box methods to achieve above functionality. Some examples picked up from standard AX.

smmUtility: openMenuItemForm and performJumpRef

smmUtility::peformJumpRef(tablenum(smmQuotationReasonGroup), smmQuotationReasonGroup::find(this.text()).RecId);

MenuFunction: runClient
public void jumpRef()
    Args argsProductForm;

    argsProductForm = new Args();


Thursday, September 19, 2013

Using Macros - #localmacro #if and #define

Most of us are familiar with Macros. Unfortunately, they are seldom used to their potential.
From MSDN, 
A macro is a variable known to the precompiler. The variable can have a value that is a sequence of characters, but it is not required to have a value. The #define directive tells the precompiler to create the macro variable, including an optional value. The #if directive tests whether the variable is defined, and optionally, whether it has a specific value.

 static void SimpleDefineIfJob(Args _args)  
   str sTest = "Initial value.";  
   #define.MyMacro // MyMacro is now defined.  
     sTest = "Yes, MyMacro is defined.";  
   // Notice the non-code sentence line causes no X++ compiler error,  
   // because the X++ compiler never sees it.  
     The X++ compiler would reject this sentence.  
     sTest = "No, MyMacro is not defined.";  

Again from MSDN,
The #localmacro directive is a good choice when you want a macro to have a value that is several lines long, or when your macro value contains a closing parenthesis. The #localmacro directive is a good choice when you want your macro value to be lines of X++ or SQL code.
One particular use i found is when you want to suppress or replace particular where clauses from a select statement. #localmacro works wonderfully.

 static void LocalMacroInSelect(Args _args)  
   VendTable            vendTable;  
   boolean             showAllRecords = false;  
   while select firstOnly10 vendTable  
   if (showAllRecords)  
     #WhileLoopFilter(where vendTable.blocked == CustVendorBlocked::All)  
     #WhileLoopFilter(where vendTable.blocked == CustVendorBlocked::Payment)  

Read Enum Elements

A quick job to fetch enum elements using a for loop. Much better than having to manually type the values.

 static void EnumValues(Args _args)  
   DictEnum      enum = new DictEnum(enumName2Id("ABC"));  
   int         i;  
   for (i=0; i < enum.values(); i++)  
     info(strFmt("%1-%2", enum.index2Label(i), enum.index2value(i)));