Pages

Showing posts with label Axapta. Show all posts
Showing posts with label Axapta. Show all posts

Friday, July 5, 2013

Change CreatedDateTime field content

Since it's a table column controlled by the system, you can't update it. The same goes to the other system columns.

The compiler won't even let you write code like the following:

myTable.CreatedDateTime = DateTimeUtil::utcNow();

It'll spit out the error "The field must be a data element that allows assignment.".


However, there is a workaround to be done. The xRecord.overwriteSystemFields helps us with it. Its name is pretty straightforward. We should also grant the required permission to our code, by using the OverwriteSystemFieldsPermission permission class.

Here's how we should use it:

MyTableBuffer myTableBuffer;
    
    myTableBuffer = myTableBuffer::find();
    
    new OverwriteSystemfieldsPermission().assert();
    
    myTableBuffer.overwriteSystemfields(true);
    myTableBuffer.(fieldNum(MyTableBuffer, CreatedDateTime)) = DateTimeUtil::utcNow();
    
    myTableBuffer.insert()
    
    CodeAccessPermission::revertAssert();


There are two things to keep in mind:

  • This trick only works for insert operations. It will not work for update operations.
  • The OverwriteSystemFieldsPermission will only work for code running on the server.


To make your code run on the server, add the "server" keyworkd to its name, like this:

server public static void foo()

Just remember that the server keyword only works for static methods. In MSDN's words:

Establishes the location where the method is to be executed (on the server).
Can only be used on static methods. If the method is not static, you need to specify the location using the class property RunOn.

Thursday, July 4, 2013

Dynamics AX custom lookup in dialog

If you ever need to create a Dynamics AX dialog custom lookup, don't use the method naming approach, in which you name your method with a sufix like "_lookup". Your code will get very ugly, because the name of the fields in a dialog have very weak semantics. You'll most likely end up with methods like:

public void Fld1_1_lookup()

Instead use the new method introduced in Dynamics AX 2012, the DialogField.registerOverrideMethod method.


The method is very straightforward: you indicate what method you want to override, what is the method that overrides it, and in which form you want it to be overridden. Since it works with dialogs, you can use it on SysOperation UI Builders, on forms' dialogs and so on.

Because this method requires that the dialog is already constructed, you usually put it in "postRun" methods, like dialogPostRun or simply postRun, it depends on what you're doing. For SysOperationUIBuilders, you should call this method on the postBuild method.


For what I've seen and tested, the method that will receive the "event" we have overridden must always accept a first parameter of type FormControl, otherwise it just won't work.

Your method should look something like this:

private void lookup(FormControl _formControl)


Here's a full example of a lookup for a SysOperationUIBuilder, where we will override a lookup for a dialog field bound to the data contract:

  • Here we register the method that will be used to override the lookup method
    public void postRun()
    {
        DialogField           dlgFieldToBeOverridden;
        MyDataContract        dataContract;
        
        super();
    
        dataContract = this.getDataContractObject();
    
        dlgFieldToBeOverridden = this.bindInfo().getDialogField(dataContract, methodStr(MyDataContract, parmFieldToBeOverridden));
    
        dlgFieldToBeOverridden.registerOverrideMethod(methodStr(FormStringControl, lookup), methodStr(MyUIBuilderClass, myCustomLookup), this);
    }
    
  • And here's the method that will perform the actual lookup. For this particular example, we will assume we already have developed a new form called "MyLookupForm", and that we will be performing the lookup from a string control. You could use the SysTableLookup or the SysReferenceTableLookup approaches instead.
    private void myCustomLookup(FormControl _formControl)
    {
        Args                args            = new Args(formStr(MyLookupForm));
        FormStringControl   stringControl   = _formControl;
        FormRun             formRun;
    
        formRun = ClassFactory::formRunClassOnClient(args);
        formRun.init();
    
        stringControl.performFormLookup(formRun);
    }
    

Saturday, June 8, 2013

Execute Menu Items through code

I've seen some people trying to find out how to execute or run Menu Items programatically on the internet.

So I thought I'd best blog about it.

We have three types of Menu Items: Action, Output and Display.

An action is frequently a class that performs some business operation, an output is a report and a display is a form.

Menu Items are represented by the MenuFunction class. Here's some code example to run Menu Items through code:

MenuFunction menuFunction;

menuFunction = new MenuFunction(menuItemDisplayStr(MyDisplayMenuItem), MenuItemType::Display);
menuFunction.run();

Of course the code above may be changed to execute different kinds of Menu Items, for example, the code below runs an Output Menu Item:

MenuFunction menuFunction;

menuFunction = new MenuFunction(menuItemOutputStr(MyOutputMenuItem), MenuItemType::Output);
menuFunction.run();

And if you need to pass any arguments to the Menu Item you're trying to execute, you can pass it with an Args object. The run method accepts an Args parameter, like this:

Args args = new Args();

args.record(myArgumentRecord);

args.caller(this);

new MenuFunction(menuItemOutputStr(MyOutputMenuItem), MenuItemType::Output).run(args);

You should always use functions to get the name of the menu item instead of using a hardcoded string. This guarantees that if someone changes the name of the menu item, your code stops compiling and you have time to fix it before shipping it. The following are the three functions used to get the menu items' name. Their names are pretty straightforward:


Here's a link to a Stack Overflow question that I answered but didn't get the answer :(.

Friday, June 7, 2013

Dynamics AX Custom Reference Group Lookup

Microsoft Dynamics AX 2012 introduced a new form control type: the reference group.

And also, very often we'll have to customize the contents of the lookup, or filter them. We can do this by using the SysReferenceTableLookup class.


So go to your form's data source and find the reference field for which you want to perform the lookup. Override its lookupReference method.

The use of the SysReferenceTableLookup is very similar to the SysTableLookup: we construct a Query, add data sources and ranges to it, and then pass it to the SysReferenceTableLookup object, like the code below:


public Common lookupReference(FormReferenceControl _formReferenceControl)
{
    SysReferenceTableLookup     sysRefTableLookup;
    Query                       lookupQuery = new Query();
    QueryBuildDataSource        lookupQueryDataSource;

    // Construct the SysRefTableLookup object
    sysRefTableLookup = SysReferenceTableLookup::newParameters(tableNum(MyTable), _formReferenceControl)

    // Add the field list that will be displayed on the lookup form
    sysRefTableLookup.addLookupfield(fieldNum(MyTable, MyFirstField));
    sysRefTableLookup.addLookupfield(fieldNum(MyTable, MySecondField));

    // Construct the query's data source
    lookupQueryDataSource = lookupQuery.addDataSource(tableNum(MyTable));

    // Add ranges to the query data source
    lookupQueryDataSource.addRange(fieldNum(MyTable, MyFirstField)).value(queryValue('Foo'));
    lookupQueryDataSource.addRange(fieldNum(MyTable, MySecondField)).value(queryNotValue(''));

    // Pass the query to the lookup object
    sysRefTableLookup.parmQuery(lookupQuery);

    return sysRefTableLookup.performFormLookup();
}


Unlike the SysTableLookup, the SysReferenceTableLookup returns an object of type Common which is the record the user selected on the lookup.

For this example I created the lookup logic directly on the form, but you could always create a lookup method directly on your table, and then make the control's lookup method call the table one. It's entirely up to you.

Just don't forget to validate the user input, because filtering the lookup does not stop the user from typing in a value on the control instead of selecting the record on the lookup. You could do it by overriding the control's validate method or directly on the table with the validateField method.

Monday, June 3, 2013

Get selected records on the grid / datasource

This should also be a common task for Dynamics AX developers.

Fortunately, there's a little helper class in AX to make it easier for us, the MultiSelectionHelper.


For example, if you want to get a set of selected records in a grid, you could use it like this:

MyTableBuffer            myTableBuffer;
MultiSelectionHelper     selectionHelper = MultiSelectionHelper::construct();
Set                      selectedRecords = new Set(Types::Record);


selectionHelper.parmDataSource(myTableBuffer_DS);

myTableBuffer = selectionHelper.getFirst();

while (myTableBuffer)
{
    selectedRecords.add(myTableBuffer);

    myTableBuffer = selectionHelper.getNext();
}


The code above should be very useful when getting the list of selected records directly on the form, but if you want to get the selected records in a class that was called from a form, for example, you could use the MultiSelectionHelper like this:

public static void main(Args _args)
{    
    FormDataSource          formDataSource;    
    MyTableBuffer           myTableBuffer;
    FormRun                 caller = _args.caller();
    MultiSelectionHelper    helper = MultiSelectionHelper::createFromCaller(caller);
    Counter                 i;

    // First we need to get the correct form data source
    for (i = 1; i <= caller.dataSourceCount(); i++)
    {
        formDataSource = caller.dataSource(i);

        if (formDataSource.table() == tableNum(MyTableBuffer))
        {
            break;
        }
    }

    // We then tell the selection helper object to create ranges for the selected records
    helper.createQueryRanges(formDataSource.queryBuildDataSource(), fieldStr(MyTableBuffer, RecId));

    // Now we can traverse the selected records
    myTableBuffer = helper.getFirst();

    while (myTableBuffer)
    {
        info(myTableBuffer.RecId);

        myTableBuffer= helper.getNext();
    }
}
In the example above we received the caller form with the _args object. Then, we have to find the correct FormDataSource object. This should be the table for which we want to get the list of records. After getting the correct data source, we can then tell the selection helper object to create ranges for the selected records on the specified QueryBuildDataSource, which we can now get directly from the FormDataSource in Dynamics AX 2012, with the queryBuildDataSource method. And the last part is the same as the first code example, only this time I didn't add the selected records to a set, I just showed their RecId on the Infolog. Since we need to get the FormDataSource for this latter approach, we can only use it if our form only has one data source for the table we're trying to get.

This should be very useful when you need to perform certain action on a list of selected records. Hope it helps.

Tuesday, February 7, 2012

Dynamics AX Custom Lookup

As a Dynamics AX developer, you'll often have to perform custom lookups. Meaning that the user may only select records in another table depending on some condition on the form, or elsewhere.


There are two ways of creating custom lookups. I'll show the easiest here, and maybe show the other way in the future.

So if you're thinking you'll have to create a new form and all that, forget it, you won't. All you have to do is write some pieces of code.



First let's create our lookup method directly on the table. You can check other tables for these special lookup methods. They appear in lots of tables. Take for example the table ContactPerson. It has the following custom lookup methods by default:

  • lookupActionContactPerson
  • lookupCustContactPerson
  • lookupDirContactPerson
  • lookupDirContactPersionId
  • lookupVendContactPerson



Now, to create a custom lookup, we'll use the class SysTableLookup as it provides us a set of methods to do so.


Here's our method, to be created directly on the table DummyTable, with some dummy data and names.

public client static void lookupDummyTable(FormStringControl _ctrl,
                                           MyFilterEDT _filter)
{
  
    // Intantiantes a SysTableLookup object telling it which control activated the lookup, and
    // what table should be displayed inside the lookup form.
    SysTableLookup sysTableLookup = SysTableLookup::newParameters(tablenum(DummyTable), _ctrl);

    Query query = new Query(); // This query will be used by the lookup form.
    QueryBuildDataSource qbds;

    ;

    // The "addLookupField" method adds the the fields that will be shown inside the lookup's grid. 
    // The second, boolean parameter indicates that this field will be returned from the lookup when a
    // record is selected. In this case, we'll return our DummyPK.
    sysTableLookup.addLookupField(fieldnum(DummyTable, DummyPK), true); 
    sysTableLookup.addLookupField(fieldnum(DummyTable, Field1));
    sysTableLookup.addLookupField(fieldnum(DummyTable, Field2));
    sysTableLookup.addLookupField(fieldnum(DummyTable, Field3));


    // Using our dummy table as a DataSource Table.
    qbds = query.addDataSource(tablenum(DummyTable));

    // This is the key part, what we want to filter to be displayed in the grid.
    qbds.addRange(fieldnum(DummyTable, MyFilterColumn)).value(_filter);


    // Passes the query to the sysTableLookup.
    sysTableLookup.parmQuery(query);

    // Activates the lookup.
    sysTableLookup.performFormLookup();
}



With that little piece of code, Dynamics AX will already display a lookup form with the table you've specified as a DataSource, displaying the fields you specified with the method addLookupField and following the results criteria of the Query you've passed to it.



Now all we have to do is actually call our lookup method, by overriding the lookup of the correct field in our Form's Data Source:


public void lookup(FormControl _formControl, str _filterStr)
{
    ;

    DummyTable::lookupDummyTable(_formControl, "SomeFilter");
}


And that's it! The lookup will be performed. But there's one additional step we'll have to take here...

When the user tries to select our DummyTable reference, Dynamics AX will display the results of the query, based on the filter we've created. So the user will only be able to see what we want him to see, and so he'll only be able to select what we want. The problem is that if he types in an existing record for the DummyTable in the control, even if does not get shown in our lookup, Dynamics AX will accept that. Remember: we're only limiting what the user may see and select for that field, but we're not limiting what he can actually type in the control. So if we don't validate it somehow, Dynamics AX will run the standard validation, which will only check if a record exists for that EDT and so on. To avoid that the user does in fact type in something we don't want, we'll have to override the validate method for our field in our Data Source. The simplest logic is to check if the DummyTable record the user typed in does attend the filter we've specified:


public boolean validate()
{
    DummyTable    dummyTable;

    ;

    dummyTable = DummyTable::find(currentRecord.DummyTableRefField);

    return dummyTable.MyFilterColumn == "SomeFilter";
    
}



So if the user simply types in for a record that does not attend our filter, Dynamics AX won't let the user save that value.



This is the easiest way to effectively implement a custom lookup and validate the user input in case he doesn't select the record from the lookup. For this example I used a literal filter, a string. But in most cases, you'll have to filter according to what the user selected for another field in your form. Things should usually be dynamic.

Hope this helps...