Friday, November 17, 2017

Dynamics 365 - Sub reports


During our upgrade, we found our sub reports stopped working. I tried looking for answers on the web and community forums, but none of them helped me solve my problem.

So I started trying all wiered things which helped me resolve my issue at the end.

Here are the things we need to keep in mind during the creation of sub reports in Dynamcis 365,

  1. The host report and the sub report must follow the same data pattern. For example, if the host report uses "Query" as data provider, all the sub reports must use "Query" as a data provider and vice versa.
  2. All the parameters in the sub report including standard AX_Parameters must be mapped in the sub report parameter mapping.

    AX_PartitionKey = [@AX_PartitionKey] 

Dynamics 365 - Extensibility on methods in a subclass that are declared in the base class


We stumbled upon this while reviewing the extensibiliy requests that we have to submit to Microsoft. As you probably all know, we can no longer overlayer in D365, so we have to adopt the new, extensibility-based approach.

In the class 'AssetProposalDepreciation' we have a customization in the 'run' method. While reviewing this customization, we reasoned that the customization can be moved to a post event on the method 'initFirstDate', which is called just before our customization. However, the class 'AssetProposalDepreciation' is a subclass of the class 'AssetProposal', and the method 'initFirstDate' is declared in the base class (and not overridden in the subclass).

Before I continue, I should mention that the approach below has been tested using the community tech preview (CTP) of D365 fall release 2017.

Attempt 1


The first thing we tried was creating an event handler class with a post event on the method 'initFirstDate' for the class 'AssetProposalDepreciation':

class MyEventAssetProposalDepreciation
{
[PostHandlerFor(classStr(AssetProposalDepreciation), methodStr(AssetProposalDepreciation, initFirstDate))]
public static void AssetProposalDepreciation_Post_initFirstDate(XppPrePostArgs args)
{
//
info("Test"); // Let's assume that this is our customization.
//
}
}

The code above resulted in a compile error; it is not possible to create a post event on the method 'initFirstDate', as it is not declared in the class 'AssetProposalDepreciation' (it is declared in the base class 'AssetProposal').

Attempt 2


The next thing we tried was using Microsoft's new Chain of Command feature. For those of you who are unfamiliar with this new feature, this short video explains it fairly well: https://blogs.msdn.microsoft.com/mfp/2017/07/04/extensible-x-chain-of-command/. So, we created the following extension class:

[ExtensionOf(classStr(AssetProposalDepreciation))]
class MyAssetProposalDepreciationClass_Extension
{
    // The method signature was copied from the class 'AssetProposal'.
    public AssetTransDate initFirstDate(AssetTransDate _assetTransDate, AssetId _assetId) 
    {
        AssetTransDate ret = next initFirstDate(_assetTransDate, _assetId);

        //
        info("Test"); // Again, this is our customization.
        //

        return ret;
   }
}

The code above works :-). 


Whenever the method 'initTransDate' is called in the class 'AssetProposalDepreciation' (e.g., this.initTransDate(...) ), the line 'AssetTransDate ret = next initFirstDate(_assetTransDate, _assetId);' in the extension class is executed first, then the original code in the method 'initFirstDate' is executed (because of the 'next' statement; the return type is assigned to the variable 'ret'), and finally our customization code is executed. 

So, using the Chain of Command feature, we have basically 'mimmicked' a post event. 

Dynamics 365 - More than one form was opened at once for the lookup control


In case you want to override the default lookup on a field , pay attention to cancelSuperCall() call as well.


If we don’t call this method then a call is first made to the default lookup and then your lookup logic will be executed.

If we don’t cancel the super call , we end up with an error “More than one form was opened at once for the lookup control.” Here is an example of how to cancel the super call.

[FormControlEventHandler(formControlStr(ProjTable, MyTable_MyField), FormControlEventType::Lookup)]
public static void 
MyTable_MyFieldCTRL_OnLookup(FormControl _sender, FormControlEventArgs _eventArgs)
{
    FormControlCancelableSuperEventArgs eventArgs = _eventArgs as FormControlCancelableSuperEventArgs;
    eventArgs.CancelSuperCall();


    MyHelper::lookupMyField(_sender);
}

Dynamics 365 - Comparison operators on enum values


As you know, in D365 the comparison (<, >, <=, >=) operators are no longer supported in certain scenarios that have to do with base enums. When you come across a situation like that, there are three different ways in which you can rewrite it:

Example situation
Select all SalesLines within my order that have a status greater than ‘backorder’.

select firstonly salesLine
where salesLine.SalesId = _salesId
&& salesLine.SalesStatus > SalesStatus::Backorder; // no longer supported in D365

Rewrite strategy #1 – explicit inclusion


Explicity mention each of the enum values you wish to include in your selection.

select firstonly salesLine
where salesLine.SalesId = _salesId
&& (salesLine.SalesStatus == SalesStatus::Delivered ||
salesLine.SalesStatus == SalesStatus::Invoiced ||
salesLine.SalesStatus == SalesStatus::Canceled);

Rewrite strategy #2 – explicit exclusion


Explicity mention each of the enum values you do NOT wish to include in your selection.

select firstonly salesLine
where salesLine.SalesId = _salesId
&& salesLine.SalesStatus != SalesStatus::None 
&& salesLine.SalesStatus != SalesStatus::Backorder;

Rewrite strategy #3 – ordered list


Use the “ordered list pattern”. This requires a lot of additional overhead so this is not the preferred way. In case you feel rewrite strategy 1 & 2 are not suitable in your particular scenario, you can consider using this third approach. Also, in situations where we have added our own enum elements to a standard AX enum, and the standard codes uses a comparison operator on that enum somewhere, this third approach is the only way that will work. Check these standard AX classes for an example (PU8 and onwards): InventTestBlockProcessComparer, InventTestBlockProcessOrderedList, InventQualityManagementBlock

Wednesday, November 15, 2017

Dynamics 365 - How to save your queries in AX7 / D365 / DAXFO365 / "The new Ax"

As you might have noticed, the nice query editor is gone, and with it, the ability to store your grid filters on forms. This is what I am talking about (From AX2012):



To similar functionality in Ax7, you need to enable URL Manipulation under the user settings (See attached screenshot at the bottom where it makes no sense).


Once this has been enabled, you will get a URL that contains your filter in a hashcode. In your browser, you can then set a bookmark to it, and reuse, or share that filter with other users.

Dynamics 365 - Display method caching

There are five different options regarding display method caching


  1. No caching – not preferred, unless you have a compelling reason 
  2. 'CacheDataMethod’ property on form controls 
  3. Add a call to ‘cacheAddMethod’ in FormDataSource’s init method
  4. Add the ‘SysClientCacheDataMethodAttribute’ to the display method – even though the compiler will allow you to put this attribute on any display method, it only actually works on display methods that are defined on a table (not on forms, data sources, or classes)
  5. Custom caching mechanism -- code it yourself

Use the attached flowchart to determine the correct caching strategy for your scenario.




Edit methods should be cached using the same strategy.

Always set the updateOnWrite parameter to true when you’re adding the SysClientCacheDataMethodAttribute, or calling cacheAddMethod on a form data source. In certain situations it would be better not to refresh during updates (like in forms where the table in question is used as a read-only data source), however this approach often leads to the introduction of errors when other developers/parties customize the method or add it to new forms where conditions are different.

Dynamics 365 - UtilElements in AX 7

In older versions of Dynamics AX, you can get information about AX application objects (metadata) through system “tables” such as UtilElements. This doesn’t work anymore in AX 7. These tables still exist, but they don’t have any data, therefore you have to migrate to another solution.

AX 7 comes with a rich framework for metadata, implemented in several assemblies in namespace Microsoft.Dynamics.AX.Metadata. But it’s too complex for simple tasks – you can make it much simpler by using MetadataSupport class. For example, the following piece of code iterates through all form names:


var forms = Microsoft.Dynamics.Ax.Xpp.MetadataSupport::FormNames();

while (forms.MoveNext()){    print forms.Current;}

Dynamics 365 - Service unavailable error when opening AX7 in web browser

We got this weird error just now. After a little digging around we found that the AOS service had stopped running... However, the AOS is no longer a Windows service.So here's how we fixed it:1) log on the VM that hosts the AOS2) open the IIS manager (click 'start', then type in 'iis', then click the 'Internet Information Services (IIS) manager'3) in the 'connections' pane on the left-hand side, drill down to the 'application pools' node and select it4) in the main windows (center of screen) look for the 'AOSService' and make sure its status is 'running'

Tuesday, November 14, 2017

Dynamics 365 - How to open standard AX Attachments dialog with a different caller or source record

Standard AX Attachments dialog always looks for the caller form's primary data source and displays all the attachments related to this (if available). 

But my requirement is to link a form which has a view or a table as a non-primary data source and  open the Attachments dialog for it.

We can achieve this by doing 1, 2 and 3


1: If the table/view is not added yet as datasource, then add it to the form as a data source and join it with the primary datasource, if it is not the primary itself.

2: Set the OnlyFetchActive property of this data source to Yes so that it only fetches recIds from the database hence minimizes the impact on performance. 


3: Override the docCursor() method of the form and return the table/view buffer.


Thats it!!

Dynamics 365 - Making dialogField as the first field on a dialog - Using Extensions


Recently I got a requirement where I need to add a field to a dialog from a class extending RunBase. I could do it by writing a post event on Dialog method . The post event calls a method on extension class which adds the field.

dfObjectId = _dialog.addField(extendedTypeStr(myEDT));


However this field was always added at the end of the Dialog which the end user do not want as they wanted this dialog field to be the first field on the dialog. I could achieve this by the following piece of code which I added just after the dialogField is added


// Move the Object Number to the top on the Dilaog
control = dfObjectId.control();
parentControl = _dialog.mainFormGroup();
parentControl.moveControl(control.id());

Here is the final code in my extension class method which was called by the post event on dialog method, 


public void myDilaog(Dialog _dialog)
{
    FormBuildControl control;
    FormBuildGroupControl parentControl;
    dfObjectId = _dialog.addField(extendedTypeStr(myEDT));


    // Move the Object Number to the top on the Dilaog
    control = dfObjectId.control();
    parentControl = _dialog.mainFormGroup();
    parentControl.moveControl(control.id());
}



Thats it!!

Dynamics 365 - Find all newly created AOT objects that are not added to the Source control (Version control)



It happen sometimes that we forget to add our changes to the version control system and at one point we don't remember what we have changed in the past. Dynamics 365 comes with a  simple "Add Items to Folder" tool, which will help us find all the missing objects and add them to source control.

In order to accomplish this, please follow the step by step guide,


Goto Team Explorer>Source Control Explorer'

















Navigate to '$/YourVSProject/Trunk/Main/Metadata' and click on 'Add Items to Folder' button on the top left



Select the Model folder (E.g. MyModel) and click 'Next' to choose from the list of objects which are not added to Version Control. Or 'Finish' to add everything missing from Version Control.

This will check all the objects missing from source control and are available in the 'Local repository' and adds them to the pending changes list.

That it!!!