-->

Lookups and drop downs in AX - Part I

maqk® by MAQ Khan | 9:49 PM

Lookups are pretty straight forward, drop the related field and AX should bring a nice drop down to you. However things can get pretty nasty when you apply further behaviour, the simple drop down remains no simple.

As i just wrote the one liner, drop the field and there u go. However there are other methods to achieve better results as well.

Depending on the join, the behaviour and display of the lookup depends. A RecId based master child relation field will drop the RecId of the master record. Se below;


The objects I have used to achieve the above are;

  • 2 tables named master and child. 
  • Joined child table with master as a 1 to many relations
  • Created a new form (no template), added both tables in the data sources. 
  • Dropped the relation field on the form.

Thats it, but the result is pretty rough, not helpful to the UI at all, what does that 10 digit typical surrogate means to the end user, its a nightmare for him. The good thing is, there is no nightmare for the devs :D

What MorphX has done is place an integer field on the form bind to the child data source's master table relation field. The existing relation provides the lookup behaviour automatically, no code to override, no nothing, pretty straight forward.

AX is a great RAD platform, it does not give u time to do all the coding for these small repeating things, its already there, u need to know how :D

Well the most usual practice a dev has is to copy an existing behaviour, that will do the job, but I actually went a little deeper into it and played with all the relevant features available. Lets dive in then.

The Table's Autolookup group

There exist a default group for each table, 'Autolookup', lets drop a field into it and see the difference if any;
To achieve this, i placed a data field (any field u can choose) in the Autolookup group of the master table. We have edited the master table for the simple reason that the drop down or lookup is supposed to fill from there :D



The result of this is;

As you can see, the code field has started appearing next to the unwanted RecId. This is at least a little better than the first pure raw result. Of course we still need to remove the unwanted id, but what we learned from this step is that fields present in Autolookup group of a table starts appearing in lookups belonging to that table.

Lets add another field to ensure what we learn is exactly what we just described :D adding 1 more field results in following;

Hence ensured. This means we need to fill the Autolookup group for every setup / master table so that a decent user friendly lookup appears whenever a reference is made to them.

But the unwanted rec id is still bothering, and also 1 more thing is the behaviour of the form when a particular value is selected; see below;


After a selection is made, the same rec id is appearing about which, the user have no hint what has it actually selected.

Lets see if adding an Index have an effect on this. An index is basically created on a table on fields so that searching gets fast. But it is also used as a means to achieve uniqueness on a particular set of fields.

Adding an index on a third filed has no effect on the lookup (no new image required :D)
However, removing each field from Autolookup group with an index on a third field (which was never part of the Autolookup group) brings the following result;


This means that field(s) present in Index(es) appears in the lookup of that table. However, fields in Autolookup group overrides them. We are finding core facts here, no theories, and I really enjoy this :D

The unwanted RecId is still appearing, lets see what another default group AutoIdentification has to do with it

The AutoIdentification group
By default, you can never add any field in this group by dragging your custom selection. The only way to add a field into it is to create an index.

Adding a simple, non unique default index does nothing to the AutoIdentification group, we will need this index set as a replacement key. To do this, the index must be unique (set allow duplicates to No in the property sheet) and select this unique alternate key index as the replacement key for the desired master table. There you go, you got the index field in the AutoIdentification group :D, illustration for you for precise guidance :D



This brings the indexed field 'code' in the AutoIdentification group.

But how will it effect our lookup ? Well the answer is, now AX knows your master child relationship more better, so Drag the same field once again, and to your surprise, this time it wont be a simple integer field bind to the child table, its a whole new 'Reference group' control.


Now we have 2 lookups on the form actually, the one with the previous incomplete table configuration (integer field bind to child table's master referencing field) and the new Reference group with more properties to explore :D

This has get us rid of the unwanted RecId, now we have quite a decent lookup appearing at our screen. The user will be delighted to see this as compared to the previous ones. And as we have already learned, adding more fields in a lookup can make lookups more detailed if required.

Scenario: Show more fields in lookup but select specific
Yes, how can we achieve this? That is, show some filed in the lookup , but when selected, a different field gets selected in the textbox area. What I am trying  to say is a typical code description scenario in which user opens a lookup that shows code and descriptions both, but  once a selection is made, only code is selected. How to achieve that ? Well again as so many times you have read that AX is fast RAD platform, nothing takes long time here. To achieve our this goal, we need to look at some of the properties on the reference group control.

Reference group control - Details
The key properties of this control are as follows;

  • ReferenceField: A reference field on the bound data source to use as the physical binding. This field is not displayed to the user
    In short, this is the field which will be used behind the scenes and the record will be populated with this value. In our example, the reference field is the 'master' field in the child table, the rec id of the master table's record the user selected in the drop down.
  • ReferenceFieldGroup: A field group on the primary key table to use as the logical binding. These fields are displayed to the user.
    In our example, the group selected is the AutoIdentification group, which was populated when we created a unique index, used it as alternate key and set it as master table's replacement key.
Other fields of course contains data source and data field, whenever dropping a lookup form master table, drag the field from the child table, not the master table since the changes are to be made in child table no the master one :D

Now to have multiple values displayed when lookup is opened and select one of them in the drop down, we will do the following

add multiple as many fields you want to display to the user when lookup is opened, we will add code and descr fields. And already in the AutoIdentification group, we have the code field ( the indexed field), so this will bring us the desired results. The following is the table level filed group configuration and then its result:



This is the way to do the simple lookups in AX. To conclude all this, i have written concluding points below. For more advance topic, I will write the Part II tomorrow إن شاء الله‎ 

Conclusion:

1.) Autolookup overrides index
2.) Fields from each index are fetched in lookups along with PK (RecId) when no field exists in autolookup group of primary table.
3.) When multiple indexes used on table ( & no field in autolookup), first field of each index is brought, depends on their order in index.
4.) Use Reference group control for better user friendly lookups. Reference group provides two properties 'ReferenceField' and 'ReferenceFieldGroup' which fetch relevant data in the lookups as required by the user



Modify AOT items from code - AX 2012

maqk® by MAQ Khan | 2:37 PM

The Problem

Hello guys, at times you may require a particular AOT object, like a menu item or a table, or form etc to be modified from code. Consider a task given to you to update the User CAL settings on each  menu item. Now depending on your solution, it can require hundreds of menu items to be modified one by one.

Another example would be to update the configuration key of each AOT object in your solution. In these bulk updates you need to have a means of updating each object and set its specific properties to a particular setting. Such a task was assigned to me, and yes I could have been a labor mule updating each item and going for the other, but I remembered that I am a knowledge worker, that means, I have to use my brain and work smartly with key decisions.

So I realized that there has to be a means of bulk updating each AOT item object and setting its properties to save time and labor :)

So lets take the real time example here. I have been asked to update the User Cal settings of each menu item  and set it to Task. And since I am a smart knowledge worker, I will avoid the very lengthy manual approach and will look for some smart code and already provided interface to modify existing AOT Item.

My case is specific to Menu items, but the code and approach below will work for all objects within the AOT.

TreeNode Class [AX 2012]

As per the very helpful one liner at msdn,
"The TreeNode class is used to get a handle to any node in the AOT. This class also contains methods that are used to maneuver in the tree."

This class can make you do wonders, like adding new objects (all kinds of), compiling them, and much more. This post will be specific to the problem defined in the above paras :)

So now we need to have each menu item in an iterative lop so that we modify them one by one. The question arises, how we do start with this class to modify any existing AOT item. The answer is this class's static FindNode method :)

TreeNode::findNode Method [AX 2012]

One liner from msdn to start with,
"Gets a specified node in the Application Object Tree (AOT)."

Syntax

client server public static TreeNode findNode(str path)

The idea is to pass a starting path to the path parameter. The node the path will point to can work as a parent node as well as sibling node meaning once on a valid node, we can browse both its siblings or pears as well as its sub nodes or child nodes (if it has).

In our eexample, we will give the path of Display menu item to be treated as a  parent node. From there, we will start looping all the sub nodes (display menu items). Consider the following line;

objTreeNode = TreeNode::findNode(@"\Menu Items\Display\");

Now the object objTreeNode is referencing the node shown in the image below;


Now we can loop its sub nodes from the following code:



    TreeNode    objTreeNode;

    objTreeNode = TreeNode::findNode(@"\Menu Items\Display\");

    if (objTreeNode)
    {
        objTreeNode = objTreeNode.AOTfirstChild();
        /*
        change objTreeNode object properties
        */
        while (objTreeNode)
        {
            info(objTreeNode.treeNodeName());
            objTreeNode = objTreeNode.AOTnextSibling();
        }
    }

The above will print in the info log each display menu item one by one.

Updating the properties of an AOT Item

The TreeNode class's method AOTsetProperties can be used to do this. Syntax is as follows;

public void AOTsetProperties(str properties)

The above example is modified as follows to set the View user license and maintain user liccense to Task for all display menu items found in the AOT under the provided path.

    
    TreeNode    objTreeNode;

    objTreeNode = TreeNode::findNode(@"\Menu Items\Display\");

    if (objTreeNode)
    {
        objTreeNode = objTreeNode.AOTfirstChild();
        /*
        change objTreeNode object properties
        */
        while (objTreeNode)
        {            
            objTreeNode.AOTsetProperties("PROPERTIES\n ViewUserLicense  #" + 'TASK' + "\n  MaintainUserLicense  #" + 'TASK' + "\n ENDPROPERTIES\n");
            objTreeNode.AOTsave();
            objTreeNode = objTreeNode.AOTnextSibling();
        }
    }

Happy AOT coding :)

Links


Filtering radio button items out

maqk® by MAQ Khan | 3:15 PM

Radio buttons or option buttons are used to provide user with single option selectable interface.


In AX, the usual practice is to bind a base enum with the enum type property of the radio button so that it renders all elements of the base enum as available options. When making general data forms, all base enum fields of a table render as radio button on the forms.

This is all normal and known. What I want to share in this post is a specific scenario with me and can happen with any one else as well.

Take a look at the Gender base enum.



When adding a worker, or a person in DirPerson, you specify the gender of the person which is this field 'Gender' in the AOT. Now it has the two obvious Male and Female items with a "Not so required" Unknown as well.

Ironically, the 'Unknow' option has no label defined as well. This results in a UI rendered as follows.


This yields to a common problem statement, "We may require to load specific values of a base enum in the responsible UI control (drop down / radio button)". In my case, I want to get rid of this extra "No label" option which has no significance in my simple requirement.

I searched on the internet and nothing was up to the exact mark. Further smart searching revealed this very very useful link . What the link asks you is to AutoDeckare your control, override form's run method, and there, use the control's methods to get your work done :). The code is shared as follows;


public void run()
{
   super(); 
   radioCode.delete(''); //This line removes the option whose text is equal to ''
}



And my form loads only Male and Female genders. Thats all I want. I will attempt this solution on drop down as well and will update the results here.

One other way to achieve this is leave the binding method and add custom items in the radio button.

Add custom items in Radio button control

I was unaware of the possibility of adding / removing custom items in a radio button list control until today. A There are 3 properties providing interface to add custom items as follows;

  1. Items - number of custom items to add in the radio button
  2. Item - the 1 based index of the current item you are working at the design time
  3. Text - the text of the item currently being edited to display

Binded radio button controls have the first two properties disabled. If you drop a new radio button list on the form or remove the enum type property, these 2 properties will enable.

You can add as many custom items you want. The question rises, "How to tackle custom items in code".

Handling custom items of radio button in code

Well this is not difficult either. The following code demonstrates how you gonna do it.


Happy coding.



Number Sequence Framework in Dynamics AX 2012

maqk® by MAQ Khan | 3:25 PM

Number sequence framework provides a mechanism to automatically generate alphanumeric sequence numbers on a configured field (EDT) with each new record in a table. This has very common yet significant usage in modern business applications.

I don't feel a practical example is required to understand the simple concept behind number sequences, yet I would provide one so that nothing at all remains ambiguous.


A picture worth a thousand words. Your employee number (illustrated above) is the best example to understand a sequence number and its use.

Now its clear, lets see how can we make such an auto generating and adjusting field in a table in Dynamics AX 2012.

AX provides controlled mechanisms, or frameworks to implement such features. The goal is to reduce code redundancy and rework, increase code re usability. A number sequence is such a common thing, you may require one for each entity in your system. And AX takes the responsibility that you should not be writing the same redundant code to implement number sequence in each table, hence the Number Sequence framework.


Configuring Number Sequence Framework


The main objective of this blog-post is to describe each practical step required in setting up the number sequence framework from scratch. Another post will be written to describe how to consume a configured number sequence.
Each object (new or existing one) required is mentioned with its primary usage along with its code. Simply create / modify these objects the way it is mentioned and you are done :)

Objects involved in Number sequence framework.

    1.) YourModuleEDT

    (new object)


    A new EDT is required where the new sequence number will be stored after being generated. This EDT will be consumed by the table the sequence numbers will be generated for, and the instance field will hold the generated sequence number. The consuming table and its details will be mentioned in the next post.

    2.) NumberSeqModule base enum

    (update existing system object)


    Base enums - NumberSeqModule: This holds each module for which a number sequence is to be generated. You are suppose to create a new item in this system base enum if you are configuring number sequence for a new module.

    AOS restart will be required after this change.


    3.) YourModuleClass

    (new object)


    A new class, name it NumSeqModule[yourclassmodulename] extending system class, 'NumberSeqApplicationModule'. The class details are as follows;
    /// 
    /// configure all the datatypes in use by the module.
    /// 
    protected void loadModule()
    {
        NumberSeqDatatype datatype = NumberSeqDatatype::construct();
        ;
    
        /* Setup sequence numbers */
        //Your EDT Title
        datatype.parmDatatypeId(extendedtypenum(yourEdt));
        datatype.parmReferenceHelp(literalstr("@YourRelevantLabel"));
        datatype.parmWizardIsContinuous(true);
        datatype.parmWizardIsManual(NoYes::No);
        datatype.parmWizardIsChangeDownAllowed(NoYes::No);
        datatype.parmWizardIsChangeUpAllowed(NoYes::No);
        datatype.parmWizardHighest(#HighestValue);
        datatype.parmSortField(1);
    
        datatype.addParameterType(NumberSeqParameterType::DataArea, true, false);
        this.create(datatype);    
    }

    The second method, numberSeqModule() returns the base enum value of your module from the system base enum. A copy of this method would be required at the parameter table level as well.
    public NumberSeqModule numberSeqModule()
    {
       return NumberSeqModule::Maqk;
    }

    4. MymodueParams table

    (new object)


    The table has 2 main methods provided below;
    numberSeqModule - to return the base enum value of your module
    public NumberSeqModule numberSeqModule()
    {
       return NumberSeqModule::YourModuleName;
    }
    
    
    
    numRef - This method is will be called by the datasource of the consumer form (where the sequence number will be generated). It returns the reference of the EDT the sequence numbers will be generated upon.
    static client server NumberSequenceReference numRefYourEDTId()
    {
        return NumberSeqReference::findReference(extendedtypenum(maqkEdt));
    }

    5. MyModuleParam form

    (new object)


    The main purpose of this form is to display all generated sequence numbers for your module and provide mechanism to generate the number sequence modules (via some button e.g.). You need to drop the NumberSequenceReference table in the datasources section and override a few methods. The form code is provided below;

    public class FormRun extends ObjectRun
    {    
        container                   numberSequenceModules;//this will be used to pass module base enum value to other method of the forms
        boolean                     runExecuteDirect;
        TmpIdRef                    tmpIdRef;
        NumberSeqReference          numberSeqReference;
        NumberSeqScope              scope;
        NumberSeqApplicationModule  numberSeqApplicationModule;    
    }
      public void init()
      {
          this.numberSeqPreInit();
          super();
          this.numberSeqPostInit();
      }
      void numberSeqPreInit()
      {
          runExecuteDirect   = false;
          numberSequenceModules = [NumberSeqModule::Maqk]; //get your custom module base enum
          numberSeqApplicationModule = new NumSeqModuleMaqk();
          scope = NumberSeqScopeFactory::createDataAreaScope();
          NumberSeqApplicationModule::createReferencesMulti(numberSequenceModules, scope);
          tmpIdRef.setTmpData(NumberSequenceReference::configurationKeyTableMulti(numberSequenceModules));
      }
      void numberSeqPostInit()
      {
          numberSequenceReference_ds.object(
          fieldnum(NumberSequenceReference, AllowSameAs)
          ).visible(numberSeqApplicationModule.sameAsActive());
          referenceSameAsLabel.visible(numberSeqApplicationModule.sameAsActive());
      }
      
      void executeQuery()
      {
          if (runExecuteDirect)
          {
              super();
          }
          else
          {
              runExecuteDirect = true;
              this.queryRun(NumberSeqReference::buildQueryRunMulti(
                                      numberSequenceReference,
                                      tmpIdRef,
                                      numberSequenceTable,
                                      numberSequenceModules,
                                      scope)
                              );
              numbersequenceReference_ds.research();
          }
      }
Thats all for the configuration. You can have several sequence numbers for different entities (consumer tables) for the same module. For each sequence number (for seperate entity), you will have to create a new EDT, a new consumer table (see next section), new consumer form, and a seperate numRef() method for each EDT in the param table (also mentioned there).

Using the configured Number Sequence

We will write a seperate post showing how to use all these configurations to see a number sequence in live action. So stay tuned!

Links

Microsoft: http://msdn.microsoft.com/en-us/library/aa608474.aspx

Custom code snippets in AX 2012

maqk® by MAQ Khan | 11:04 PM

Blogging is all about sharing, specially when you are a technical blogger. And sharing is caring. That said, my last blog was related to the class DictTable that provides metadata about an AOT Table. This class also has a method that returns a table buffer (common) on providing a valid table id.

There is a series of Dict classes including DictIndex and DictField. You can do wonders with these classes as they are there to do wonders. One of the wonders is just about to be shared and with no more wait, here it is:

Code Snippets in AX 2012


The post is great, just they did not mentioned that you may need to restart Dynamics AX AOS which i had to to get all gears running and all code working :)

The whole story is about two classes
  • xppSource (holds the code that on runtime, generates the text you want to appear as a template)
  • EditorScripts (calls the xppSource method when an option selected from Script menu or TAB hit after short code typed)

There are other good posts on similar topics as well like
&

Dynamics AX - Get table buffer (Common) from tableId

maqk® by MAQ Khan | 1:33 AM

The first question that flashes after reading the post title is Why ever ? The simple answer is, i was stuck into one such scenario which led me to this effort.

actually this can be very handy, if you want to declare / instantiate your table buffers in one centralized place, say a static method of a class. This will save code lines and redundancy

Scenario

To be more comprehensive, let me explain the scenario I faced for all of this
I was writing a method that would return a valid existing recId of any table that i send to the method as a param

Now a rough code sketch for this will be something as follows;
static RecId methodName(Common _common)
{
     
    select minOf(RecId) from _common;
    minVal = _common.RecId;
    select maxOf(recId) from _common;
    maxVal = _common.RecId;
   
    recordId = xGlobal::randomPositiveInt32() mod (maxVal - minVal) + minVal;
  
    return recId;
}

The method is effective, fullfilling my requirement.

However at the time of consumption, it needs a specific initialized table buffer. Heres a valid consumption call to this method.

RecId recId;
MyTable myTable; //This is the specific 'MyTable' table buffer
recId = ClassName::methodName(myTable);

At this point of time, its obvious that for any table other than the 'MyTable', i have to declare it and send it to my 'random' method. Whereas intrinsic methods like tableNum() for instance require types or classes, not initialized objects....

There should be a way to send tableId at the conumption point so that a table buffer is initialized at the target method only, thus giving me the ease and rid from declaring each specific table every time my random method is called.

In short we can say we want a method that returns a Common object for the TableId being sent to it, So what we do....... well we search harder untill we find this

Quoting from the excellent post there,

public Common findRecord(TableId _tableId, RecId _recId, Boolean _forUpdate = false)
{
    Common      common;
    DictTable   dictTable;
    ;
    dictTable = new DictTable(_tableId);
    common = dictTable.makeRecord();
 
    common.selectForUpdate(_forUpdate);
 
    select common
    where common.RecId == _recId;
 
    return common;
}


Using the DictTable class (it seems to be, although i cant find it in the AOT :) ), and the assigning the return from makeRecord instance method to common object, you just have got your specific table buffer by just providing its tableId saving you from declaring each specific table at time of method consumption,

just send tableNum(TableName) to the method and you are done. Coments are welcome :)
top