How to use default dimensions as Dialog fields in dialogs [Dynamics AX 2012]

Friends,

This post will help you how to add default dimensions as dialog fields on the dialogs in AX 2012.

There is a new class that has been introduced in AX 2012 by name DialogFieldDimensionDefaultingController to handle this.

This class has been used in very few places in standard AX 2012 and is little tricky to pick the relevant code.

So this post will be handy for all developers at least at high level to get the default dimensions as dialog fields.

Below is the dialog with the default dimensions as dialog fields.

image

Code walkthrough:

Create a new class and methods as shown below one by one

class SR_DimensionDialog extends RunBase

{

 

    #DimensionDefaultingFieldSetElements

    DimensionAttributeValueSetStorage           dimAttrValueSetStorage;

 

    DialogFieldDimensionDefaultingController    dfDimDefaultingControllerChecksAndValues;

    DimensionDefaultingFieldSet                 dimensions;

 

    Dialog dialog;

   

    #DEFINE.CurrentVersion(1)

    #LOCALMACRO.CurrentList

          dimensions

    #ENDMACRO

}

 

Object dialog()

{

    dialog = super();

    dialog.caption("DialogFieldDimensionDefaultingController example");

    dfDimDefaultingControllerChecksAndValues = dialog.addDimensionDefaultingController();

 

    //dfDimDefaultingControllerChecksAndValues.initWithChecksAndValues(false, false, false, true, 0, "Dimensions as dialog fields example", "@SYS104593");

    dfDimDefaultingControllerChecksAndValues.initWithValues(false,false,true,0,"Dimension – dialog fields");

 

    return dialog;

}

boolean getFromDialog()

{

    dfDimDefaultingControllerChecksAndValues.save();

    dimensions = dfDimDefaultingControllerChecksAndValues.value();

 

    return super();

}

public container pack()

{

    ;

    return [#CurrentVersion, #CurrentList];

}

void run()

{

 

   // dimAttrValueSetStorage = DimensionAttributeValueSetStorage::find(Dimensions[#DimDefaultingFieldSet_AttrValueSetId]);

}

public boolean unpack(container packedClass)

{

    boolean     ret     = false;

    Version     version = RunBase::getVersion(packedClass);

 

    switch (version)

    {

        case #CurrentVersion:

            [version, #CurrentList] = packedClass;

            ret = true;

            break;

    }

    return ret;

}

static void main(Args args)

{

 

    SR_DefaultDimensionsDialog sr_DefaultDimensionsDialog;

 

    sr_DefaultDimensionsDialog = new SR_DefaultDimensionsDialog();

 

        if (SR_DefaultDimensionsDialog.prompt())

        {

            SR_DefaultDimensionsDialog.run();

        }

}

Some more methods you can try in the DialogFieldDimensionDefaultingController class are shown below.

I have used initWithValues as I am interested in only capturing the dialog values.

image

If you use InitWithChecksAndValues method, your dialog looks like below

image

However you need to write additional code to make it work though.

 

Explore the other methods based on your requirements.

 

That’s all for now.

 

Happy Dax6ng.

 

sree

Advertisements

Duplicate SSRS reports [Dynamics AX 2012]

Friends,

We all know that we will have requirements to modify an existing SSRS report but would not like to alter the original, rather create a duplicate and work on it.

But, Do we have the option in AX 2012? The answer is “NO” and it’s a real problem/hectic job for a developer to create it from scratch. Even when we try to do this from visual studio 2010, it is not allowed to change the report name as it says it exists in another layer.

Well, I have created a utility to duplicate the SSRS reports from AX. It works all fine and gets built successfully and was able to render the data as well. Once the report is duplicated, you can change the design in Visual studio.

[Note: I have posted a recent article on Duplicating SSRS with the help of wizard with all its artifacts (DP classes, Contract, UIBuilder and CSharp project)] – Here is the link

This really helps the developer in not creating the DP classes, contract classes etc. and can customize the existing DP classes by adding the new fields in temp tables.

Below is them new utility walk through:

Go to Tools >> Business Intelligence Tools >> Create duplicate SSRS reports

image

This will open all existing SSRS reports in the picker

image

Select any report : a new dialog will open with the new report name as “CopyOf” select report Name, you can modify the report name here
based on your requirements.Click on Ok button.

image

A new report gets created in SSRS reports as shown below

image

Nice right! Now lets us open this in Visual studio 2010 and you can play with the design.

You can see the new report in the Application Explorer as shown below:

image

Add this report to your project and build it. It builds successfully and is ready for preview as well.

image

Interested ones, please shoot me an email. I will send the XPO.

[I got some requests to even duplicate DP, Contract, UIBuilder and Controller classes. I am working on it. I will post the same soon]

Happy Dax6ng,

sree

CurrencyExchangeHelper class in Dynamics AX 2012 [X++]

Friends,

In Microsoft Dynamics AX 2012, the currency and exchange rate framework has been enhanced to share information across multiple legal entities.

There is a new class by name CurrencyExchangeHelper that has been introduced in AX 2012 to support this.

This class will help you to do some calculations between currencies. Some important methods to use:

calculateTransactionToAccounting

Example : This method will convert the transaction currency in to accounting currency defined in ledger Table.

static void SR_CEH_Example1(Args _args)

{

    CurrencyExchangeHelper currencyExchangeHelper;

    CurrencyCode transCurrency = ‘EUR’;

    AmountCur amountCur = 500.00;

    AmountMst amountMST;

   

    currencyExchangeHelper = CurrencyExchangeHelper::newExchangeDate(Ledger::current(), systemDateGet());

    amountMST = currencyExchangeHelper.calculateTransactionToAccounting(transCurrency, amountCur ,true);

    info(strFmt(‘%1’,amountMST));

}

 

Result :

image

calculateAccountingToTransaction

This method calculates the transaction currency amount from an accounting currency given.

static void SR_CEH_Example2(Args _args)

{

    CurrencyExchangeHelper currencyExchangeHelper;

    CurrencyCode transCurrency = ‘EUR’;

    AmountCur amountCur;

    AmountMst amountMST = 500.00;

   

    currencyExchangeHelper = CurrencyExchangeHelper::newExchangeDate(Ledger::primaryLedger(CompanyInfo::findDataArea("DUM").RecId), systemDateGet());

    amountCur = currencyExchangeHelper.calculateAccountingToTransaction(transCurrency, amountMST ,true);

    info(strFmt(‘%1’,amountcur));

}

Result :

image

While searching through, I found that there are  parmExchangeRate1 and parmExchangeRate2  methods that to calculate based on the exchange rates that have been provided. Please refer to the below example which calculates the misc charges [markup amount] based on the exchange rates defined.

 

 

Class Name : Markup >> calcMarkupAmount

image

Exploring more…will post soon!

 

Happy Dax6ng,

 

sree

FormAutoLookupFactory class and methods in AX 2012 to perform form lookups

Friends,

FormAutoLookupFactory class has been newly introduced in AX 2012 which will help for form lookups on controls [strings, integers], Reference controls etc. by returning formRun class object

Create a new form as shown below with a new string edit control

image

Override the lookup method of the String Edit and add the below code

public void lookup()

{

    HcmWorker               selectedRecord;

    Args                    args;

    FormRun                 formRun;

    Form                    workerLookupForm = new Form(formStr(HcmWorkerLookup));

    FormControl             control = this;

    FormStringControl       stringControl       = control as FormStringControl;

 

    args = new Args();

    args.name(formStr(HcmWorkerLookup));

    args.caller(this);

 

    args.parmObject(this);

 

    // position the lookup to highlight the current record

    // a string control supposed to contain a personnel number value

    selectedRecord = HcmWorker::findByPersonnelNumber(stringControl.text());

 

    if (selectedRecord.RecId != 0)

    {

        args.lookupRecord(selectedRecord);

    }

 

    // perform form lookup on the caller form control

    formRun = FormAutoLookupFactory::buildLookupFromCustomForm(stringControl, workerLookupForm , AbsoluteFieldBinding::construct(fieldStr(HcmWorker, PersonnelNumber), tableStr(HcmWorker)), args);

    stringControl.performFormLookup(formRun);

 

    //selectedRecord = formRun.selectRecordModeSelectedRecord();

 

}

There are 4 static methods in FormAutoLookupFactory to explore

image

 

For any reference controls lookups : use

 

formRun = FormAutoLookupFactory::buildReferenceLookupFromCustomForm(referenceControl, workerLookupForm, args);

 

Below is the lookup

image 

Also, There are 2 new methods added in AX 2012 to formrun class as well selectRecordModeSelectedRecord() and selectrecordMode()  that will help to get the selected records.

Please note: This is just a quick example and you need to customize it based on your requirement from the calling control

 

Happy Dax6ng,

sree

How to handle SSRS reports which will take long time to run with the messages to the user [Dynamics AX 2012– preRunValidate() method]

Friends,

We know that there are/will be some reports which will take more time to render due to the many rows/transactions. This post will help you to show appropriate warning/error messages to the end user while running the report.

The number of records that are processed by report might be large and the user experience will be affected, because the client will be locked up if printing to the screen. [msdn]

In this case, you might want to return a warning message to the user that indicates that time to process the report might be long and confirm that they want to run the report. [msdn]

Another case could be where the number of records being processed is very large and a time-out might occur in report processing and clearly we should not run this report. [msdn]

In this case, we should not run the report and therefore, should return an SrsReportPreRunState::Error enumeration value with the error message.

In AX 2012, SSRS reports , through SrsReportRunController we can easily let the user know that the report will take more time with warnings or error messages through preRunValidate method.

Example : General Journals report

Let me take the standard example of Print journal from General Ledger >> Reports >> Journal >> Print journal.

I am using standard LedgerJournalController class to help you understand preRunValidate method: this validates container before running the report. We have to override this method to do custom pre-validation for any report. Typical use of this method will be to validate if the time taken to run the report is acceptable.

In this standard example: If the query is going to retrieve more than 1000 rows, a confirmation Box will be displayed to the user as shown below.

To get the confirmation box and for the sake of the demo/understanding: I have hardcoded the rows count to 1001 as shown below. There is a new static method in QueryRun::getQueryRowCount that will get the row Count of the query.

Please note: Remove hardcoded values later. This is hardcoded only for the sake of demo/Walk through

Clearly in the below standard example : warning limit is 1000 and error limit is 100000. However this is customizable based on your requirement.

image

Now let us run this report as shown below : Click on the Ok Button.

image

Here comes the confirmation Box: If your report is long running, it may time-out. Do you want to continue?

In order to resolve/by pass this confirmation Box, a developer can change the macro #define.warningLimit to greater value

Example : #define.warningLimit(2000);

image

Now lets increase the row count : to 1000001.

Please note: Remove hardcoded values later. This is hardcoded only for the sake of demo/Walk through

image

Let us run the report One more time as shown below.

image

Here comes the error message: Running the report has been cancelled due to the time it will take to run. Adjust the parameters of the report and retry.

In order to resolve this problem, Increase the ErrorLimit macro value in the preRunValidate method.

Please note, it is not recommended to increase as it will take more time and your box cannot handle load.

Example : #define.ErrorLimit(150000);

image

That’s it for now.

Happy Dax6ng,

sree

Extensible data security Framework– Create Policies [Dynamics AX 2012]

Friends,

This is really interesting and I thoroughly enjoyed learning Policies and implementing them.

The extensible data security framework is a new feature in Microsoft Dynamics AX 2012 that enables developers and administrators to secure data in shared tables such that users have access to only the part of the table that is allowed by the enforced policy. This feature can be used in conjunction with role-based security (also supported in Microsoft Dynamics AX 2012) to provide more comprehensive security than was possible in the past. [MS Help]

Extensible data security is an evolution of the record-level security (RLS) that was available in earlier versions of Microsoft Dynamics AX. Extensible data security policies, when deployed, are enforced, regardless of whether data is being accessed through the Microsoft Dynamics AX rich client forms, Enterprise Portal webpages, SSRS reports, or .NET Services [MS help]

Let me walk through with a simple example:

Requirement is to show a particular user only those Bank accounts that belongs to the Bank group “BankCNY

Below are the Bank accounts that are available in my system. By using policy framework on roles , I can restrict user to view only bank accounts that belong to Bank group “BankCNY”

image

First of all , where are these policies in AX? They are actually in the AOT >> Security >> Policies

image 

Before we create policies, we need to create a query to use it in policies. Remember, try to optimize the queries to the best otherwise it might lead to performance issues.

To keep it very simple, Create a Query by name SR_BankAccountTable by adding data source[PrimaryTable] as “BankAccountTable” and add a range on BankGroupId field and set it to “BankCNY” as shown below

 

image

Now let us create a new Role by name SR_BankController as shown below

Go to AOT >> Security >> Roles >> New Role

image

Set the following properties of the newly created role by right clicking and going to properties

image

Now lets create duties for this role: In this example I will create one simple duty called “SR_BankAccountsMaintain

Go to AOT >> Security >> Duties >> Right click >> New Duty

image

Set the following properties for the newly create duty. Name it as “SR_BankAccountsMaintain” and provide label and description as shown below

 

image

Now, let us create a new privilege and add entry points [Menu Items] to it to provide only access to the user

Go to AOT >> Security >> Privilege >> New Privilege.

image

Name the privilege as “SR_BankAccountTableMaintain” and set the label and Description as shown below

image

Now drag and drop some menu items on to Entry points from AOT >> Menu Items >> Display as shown below

image

Now add this newly created privilege  “SR_BankAccountTableMaintain” to the duty “SR_BankAccountMaintain” which we have created.

image

Then, add SR_BankAccountsMaintain duty to the Role SR_BankController

image

Now, let us create a new Policy by name “SR_BankAccountPolicy” and set the properties as shown below

image

Set the below properties

image

In the above screen, Select the context type as “RoleName“ and the role “SR_BankController” and select the query as ”SR_BankAccountTable

PrimaryTable : should always be the first data source Table which you have added in the query. So, select BankAccountTable

A constrained table is the table or tables in a given security policy from which data is filtered or secured, based on the associated policy query. For example, in a policy that secures all sales orders based on the customer group, the Sales Order table would be the constrained table. Constrained tables are always explicitly related to the primary table in the policy [MS Help]

Context [MS help]

A policy context is a piece of information that controls the circumstances under which a given policy is considered to be applicable. If this context is not set, then the policy, even if enabled, is not enforced.
Contexts can be of two types: role contexts, and application contexts. A role context enables policy application based on the role or roles to which the user has been assigned. An application context enables policy application based on information set by the application

we are almost done, we need to add this role= to some user and verify whether he is able to see only Bank accounts that belongs to BankCNY group

Go to System Administration >> Users >> select any user >> Click on Assign roles.

Select “Bank Accounts controller”  and click on Ok Button.

image

Now , let us log in with the user credentials for which we have assigned the new Role and verify the Bank accounts.

As you see, the user can only see the Bank accounts which belong to “BankCNY” group.

image

That’s it for now.

Happy Dax6ng

sree

Get the latest exchange rates in Dynamics AX 2012 [Using X++]

Friends,

Below small snippet will help you to get the latest exchange rates as on today.

I am using x-rates URL to pull the exchange rates for this example. Please check/verify the URL before using it [free source or not].

image

static void SR_getExchangeRates(Args _args)

{

    int         curPos, endPos, startPos;

    TextBuffer  tb = new TextBuffer();

    System.Net.WebRequest webRequest;

    System.Net.WebResponse webResponse;

    str page;

    System.IO.StreamReader streamReader;

 

    try

    {

        webRequest = System.Net.WebRequest::Create("http://www.x-rates.com/d/INR/table.html");

 

        // this will throw an webexception if cannot be reached.

        webResponse = webRequest.GetResponse();

        streamReader = new System.IO.StreamReader(webResponse.GetResponseStream());

 

        tb.setText();

        page = streamReader.ReadToEnd();

 

        streamReader.Close();

 

        tb.setText(page);

        curpos   = 1;

        startPos = 1;

        tb.regularExpressions(false);

 

        tb.find(‘<a href="/d/INR/USD/graph120.html" class="menu">’, curpos);

 

        startpos = tb.matchPos();

 

        tb.find(‘</a>&nbsp;</font></td>’, startpos);

        endpos = tb.matchPos();

 

        page = tb.subStr(startpos, endpos – startpos);

        info(strFmt("1 USD = %1 INR",strreplace(page,‘<a href="/d/INR/USD/graph120.html" class="menu">’,)));

 

        // Close the webResonse

        webResponse.Close();

    }

    catch(Exception::CLRError)

    {

        throw error(AifUtil::getClrErrorMessage());

    }

}

Below is the output:

image

If anyone is interested in integrating the exchange rates in to the tables ExchangeRate , ExchangeRateType , ExchangeRateCurrencyPair  

[AX 2012] , please drop me an email/message.

 

Well, to get exchange rates for other currencies you need to modify the URL  as shown below. [Change INR to EUR etc.]

 

image

Thank you to x-rates.com

 

Happy Dax6ng,

 

 sree