Showing posts with label dynamics AX 2012. Show all posts
Showing posts with label dynamics AX 2012. Show all posts

Saturday, November 22, 2014

Using Multi-select lookup in Dynamics AX EP Listpage

Recently i worked on a task where we have to filter EP listpage based on multi-select lookup having enum values. I wants to share my experience in this post about how we can achieve this task generally.

To display enum in multi-select lookup is not always easy task:


To implement this scenario, i have created a new Tmp table ssyTmpHelp and add the single string field to hold enum values. Than i created new DataSet and add this Tmp table as a dataset and write following code on DS init() method:

public void init()
{
    ssyTmpHelp ssyTmpHelpLocal;
    EnumId   enumId   = enumNum(ssyHelpStatus);
    DictEnum dictEnum = new DictEnum(enumId);
    int      cnt  = dictEnum.values();
    int      counter;

    for(counter = 0; counter < cnt; counter ++)
    {      
        ssyTmpHelpLocal.clear();

        ssyTmpHelpLocal.HelpStatus = dictEnum.index2Value(counter);
        ssyTmpHelpLocal.HelpStatusDescription = dictEnum.index2Label(counter);

        ssyTmpHelpLocal.insert();

    }

    ssyTmpHelp.setTmpData(ssyTmpHelpLocal);
    ssyTmpHelp_ds.research();

    super();
}

Now create a new EP web form and add following script  to display this multi-select lookup control along with label and a text box control that will display comma (;) separated values when user select one or more values from lookup:

Place following C# code on the back end:

using System;
using System.Web.UI;
using System.Linq;
using System.Web.UI.WebControls;
using Microsoft.Dynamics.Framework.Portal.UI;
using Microsoft.Dynamics.Framework.Portal.UI.WebControls;
using Microsoft.Dynamics.AX.Framework.Services.Client;
using Microsoft.Dynamics.AX.Framework.Portal.Data;
using Proxy = Microsoft.Dynamics.Framework.BusinessConnector.Proxy;
using ApplicationProxy = Microsoft.Dynamics.Portal.Application.Proxy;
using Microsoft.Dynamics.Framework.BusinessConnector.Adapter;
using Microsoft.Dynamics.Framework.Portal;
using Microsoft.Dynamics.Portal.Application.Proxy;
using Microsoft.Dynamics.Framework.BusinessConnector.Session;
using Microsoft.Dynamics.Framework.Portal.UI.WebControls.WebParts;
using System.Collections.Generic;
using System.Web;
using System.Globalization;

public partial class ssyHelpExternalListPageFilter: AxBaseUserControl
{  
    private static readonly string ssyHelpStatus = "ssyHelpStatus";
    private static readonly string WebParameterKey = "WP";
    private static readonly string ssyHelpExternalListPage = "ssyHelpExternalListPage";
    public String Status = string.Empty;
    private const string ValueColumnName = "Value";
    private const string PromptColumnName = "HelpStatusDescription";
    private const string RecIdColumnName = "RecId";

    protected void Page_Load(object sender, EventArgs e)
    {
        if (this.AxQueryString != null && !String.IsNullOrEmpty(this.AxQueryString.WP))
        {
            if (this.Session_SelectedFilter == null)
            {
                this.Session_SelectedFilter = ssyHelpStatusMultiSelect.Text;
            }
        }
        else
        {
            if (this.Session_SelectedFilter != null)
            {
                if (!this.Page.IsPostBack)
                {
                    this.RedirectWithSelectedFilter(this.Session_SelectedFilter);
                }
            }
        }
        if (ssyHelpStatusMultiSelect.Text == String.Empty)
        {
            ssyHelpStatusMultiSelect.Text = this.Session_SelectedFilter;
        }
    }

    private const string SESSION_CustomFilter = "TSSelectedFilter";
    private string Session_SelectedFilter
    {
        get
        {
            ISession session = this.AxSession;
            if (session != null && session.CanCacheItems)
            {
                return session.GetItemFromCache(SESSION_CustomFilter) as string;
            }
            return "0";
        }
        set
        {
            ISession session = this.AxSession;
            if (session != null && session.CanCacheItems)
            {
                session.SetItemInCache(SESSION_CustomFilter, value as string);
            }
        }
    }

    private void RedirectWithSelectedFilter(string dropdownKey)
    {
        AxUrlMenuItem urlMenuItem;
     
        urlMenuItem = new AxUrlMenuItem(ssyHelpExternalListPage);
        urlMenuItem.ExtraParams.Add(WebParameterKey, dropdownKey);

        HttpContext.Current.ApplicationInstance.CompleteRequest();
        Response.Redirect(urlMenuItem.Url.OriginalString, false);
    }

    AxQueryString AxQueryString
    {
        get
        {
            return AxQueryString.GetCurrent(this.Page);
        }
    }


    #region multi-select
    protected void ssyHelpStatusMultiSelectLookupMethod(object sender, AxLookupEventArgs e)
    {      
        AxLookup lookup = sender as AxLookup;
     
        lookup.AllowMarking = true;
        lookup.AllowPaging = false;

        DataSetView datasetView = this.ssyTmpHelp.GetDataSourceView("ssyTmpHelp").DataSetView;
        Microsoft.Dynamics.AX.Framework.Portal.Data.DataSet dataset = datasetView.DataSet;
        //dataset.Init(true);
        lookup.LookupDataSet = datasetView.DataSet;
     
        AxBoundField promptField = AxBoundFieldFactory.Create(lookup.LookupDataSetViewMetadata.ViewFields[PromptColumnName]);
        lookup.Fields.Add(promptField);
        //salesUnitLookup.DataBind();

        if (!string.IsNullOrEmpty(this.ssyHelpStatusMultiSelect.Text))
        {
            string[] allSelectedValue = this.ssyHelpStatusMultiSelect.Text.Split(';');

            this.setssyHelpStatusRecIds(lookup.LookupDataSetView, allSelectedValue);
        }

        if (!string.IsNullOrEmpty(this.Status))
        {
            IEnumerable viewKeys = getViewDataRowKeys(lookup.LookupDataSetViewMetadata, lookup.LookupDataSetView);
            if (viewKeys.Count() > 0)
            {
                lookup.SetMarkedRows(viewKeys);
            }
        }
        lookup.SelectField = PromptColumnName;
     
    }
    private IEnumerable getViewDataRowKeys(DataSetViewMetadata dataSetViewMetadata, DataSetView dataSetView)
    {
        string[] selectedValues = null;
        TableMetadata tableMetadata = MetadataCache.GetTableMetadata(TableMetadata.TableNum("ssyTmpHelp"));

        selectedValues = Status.Split(';');

        List viewDataKeys = new List();

        foreach (string val in selectedValues)
        {
            Dictionary dict = new Dictionary();
            dict.Add(RecIdColumnName, val);

            List index = new List();
            index.Add(tableMetadata.DefaultUniqueIndex);

            viewDataKeys.Add((IAxViewRowKey)AxViewDataKey.CreateFromDictionary(dataSetViewMetadata, dict, index.ToArray()));
        }

        return viewDataKeys;
    }

    private void setssyHelpStatusRecIds(DataSetView dataSetView, string[] selectedColumns)
    {
        this.Status = string.Empty;
        List recIdsList = new List();

        dataSetView.MarkAllLoadedRows(true);

        // Get all the chosen recids
        foreach (DataSetViewRow row in dataSetView.GetMarkedRowsSet())
        {
            string prompt = (string)row.GetFieldValue(PromptColumnName);// Change to something else
            if (selectedColumns.Contains(prompt) && !string.IsNullOrEmpty(prompt))
            {
                Int64 rec = (Int64)row.GetFieldValue(RecIdColumnName);

                recIdsList.Add(rec.ToString());
            }
        }

        dataSetView.MarkAllLoadedRows(false);

        this.Status = this.getConcatenatedStringFromList(recIdsList);
    }

    private string getConcatenatedStringFromList(IEnumerable itemsList)
    {
        string finalString = string.Empty;
        int count = 0;

        foreach (string item in itemsList)
        {
            count++;
            finalString += item;
            if (count < itemsList.Count())
            {
                finalString += ";";
            }
        }

        return finalString;
    }
 
    protected void lookup_OkClicked(object sender, AxLookupEventArgs e)
    {
        List selectedOptionsList = new List();

        // Get all the chosen recids
        foreach (DataSetViewRow row in e.LookupControl.LookupDataSetView.GetMarkedRowsSet())
        {
            Int64 rec = (Int64)row.GetFieldValue(RecIdColumnName);
            string prompt = (string)row.GetFieldValue(PromptColumnName);// Change this to something else

            if (!string.IsNullOrEmpty(prompt))
            {
                selectedOptionsList.Add(prompt);
            }
        }

        this.ssyHelpStatusMultiSelect.Text = getConcatenatedStringFromList(selectedOptionsList);
     
        this.Session_SelectedFilter = ssyHelpStatusMultiSelect.Text;
        this.RedirectWithSelectedFilter(ssyHelpStatusMultiSelect.Text);//ssyHelpStatusMultiSelect.Text);

    }
    #endregion


}

Save above code and deploy the web form.

Next is to link this form with your EP listpage, Go to your EP list page > Edit page and add new webpart as shown below:

Now override initializeQuery() method of your listpage interaction class and add following code:

public void initializeQuery(Query _query)
{
    ssyHelpStatus           ssyHelpStatus;
    str 30        ssyHelpStatusStr;
    List          ssyHelpStatusList = new List(Types::String);
    ListEnumerator  listEnumerator;
    QueryBuildRange queryBuildRange;
    container       conRangeCriteria;
    str             range = '';

    if(this.listPage().listPageArgs().parameters())
    {      
        ssyHelpStatusStr = this.listPage().listPageArgs().parameters();
        ssyHelpStatusList = Global::strSplit(ssyHelpStatusStr, ';');

        listEnumerator = ssyHelpStatusList.getEnumerator();
        while (listEnumerator.moveNext())
        {
            conRangeCriteria += listEnumerator.current();
        }
        range = SysOperationHelper::convertMultiSelectedValueString(conRangeCriteria);
        range = strReplace(range, ";" , ", ");

        queryBuildRange = SysQuery::findOrCreateRange(_query.dataSourceTable(tableNum(ssyHelp)), fieldNum(ssyHelp, Status));
        queryBuildRange.value(range);
        queryBuildRange.status(RangeStatus::Hidden);

    }
    super(_query);
}

Monday, June 24, 2013

Split Purchase order lines

Sometime we need to split purchase order line into multiple lines with Quantity = 1, here is a piece of code through which you can split purchase order line into multiple lines. You can easily alter the below code based on your Quantity factor.

static void SplitPurchLine(Args _args)
{
    PurchQty    qty;
    PurchTable  purchTable;
    PurchLine   purchLine;
    
    purchTable = PurchTable::find("PO-0001");
    
    while select forUpdate purchLine
        where  purchLine.PurchId == purchTable.PurchId && purchLine.PurchQty > 1
    {
        qty = purchLine.PurchQty;

        while (qty > 1)
        {
            PLPurchLineSplit::update(purchLine, 1);
            qty -= 1;

        }
    }
}

Thursday, June 20, 2013

Dynamics AX Cheat Sheet

I have compiled general events required to perform upon certain actions. I named it as Dynamics AX Cheat Sheet. I will update it time to time based on new ideas and will try to enhance the UI of this cheat sheet. Your valuable comments and suggestions are most Welcome.


Dynamics AX Cheat Sheet
Dynamics AX Cheat Sheet














Happy Daxing!!!!

Wednesday, February 1, 2012

Logon failed for the unattended execution account

Following are steps that need to be performed if you start getting login failed for unattended execution account error.

“An error occurred during client rendering.
 The report server has encountered a configuration error. Logon failed for the unattended execution account
 Logon failed (rsLogonFailed) Logon failure: unknown user name or bad password.”

If this happens then it means there are unattended execution account was configured and there is some problem with the account mentioned in the configurations. Either it has changed in Active Directory setup and the password has been expired or changed. This is optional setting but if you have configured it then it must be updated when there is change in the account information or password. Otherwise you may start getting the aforementioned error message.

 To configure the account:

 Launch the reporting services configuration manager and connect to the report server.

  1. Go to Execution Account and select the Specify an execution account checkbox.
  2. Enter the account information and click on Apply button. This must be the domain account.
Go to the report manager and access the reports and it should now run properly.

Wednesday, August 24, 2011

Dynamics AX 2012 Price list

If you are interested in knowing the cost of a Dynamics AX 2012 license, Microsoft has provided a an Excel sheet that can help you estimate the cost of your implementation.


You can download the sheet from here


Saturday, June 25, 2011

We have done it, We proud to be the part of Dynamics AX 2012 development and now its ready for City of Redmond

As My company own the public sector solution for the Dynamics AX 2012 release and I am glad to see the implementation of Microsoft Dynamics AX 2012 for the City of Redmond USA, see this case study:
Microsoft Case Study: Microsoft Dynamics AX 2012 - City of Redmond

Wednesday, June 22, 2011

Upgrade Dynamics AX 4.0/AX 2009 to Dynamics AX 2012

Setup source Environment
  
1.      Import the PreProcessing (databaseupgrade\xpo\UpgradeAX5.xpo) XPO, located in the installation CD folder and Un check "Import with ID values:"
2.      Open the PreProcessing Checklist "SysCheckList_PreUpgrade50" located in the AX50PreUpgradeFramework project.
3.      The Preprocessing Checklist appears, if your checklists has this @ABC123 instead of text, and then do this to get the missing label file. To apply the new label files in your AX4/5 machine (if you are working with preprocessing framework):
·         Copy the label file to the label folder in your AX4/2009 machine
·         Restart the AOS
·         Label folder in AX4/2009 is a sub folder under ...\Application\Appl\ where you can find *.ald files in it.
4.      Run through the PreProcessing Checklist Items to prepare the database for Upgrade
·         If upgrading AX 2009, and the upgrade scripts don't run after opening the cockpit, jump to step #12 and follow the steps there to setup the batch server (AX4 does not require setting up a batch server). Then come back to this point and continue.
·         If upgrading AX4, You need to compile the ReleaseUpdate* classes and the ReleaseUpdateCockpit form.
·         If upgrading AX4, when running the cockpit run multiple instances of AX4:
o   Start the new Microsoft Dynamics AX client.
o   Select Basic > Periodic > Batch > Processing.
o   A batch dialog appears.
o   Add DataUpgrade in the Group field, and click OK.
5.      In the Inventory Dimension Group Upgrade checklist Item, click on the “Map dimension groups 1:1” button (Do not click on the “Assign identical groups” button) and then click on the “Set to Ready For Upgrade” button.
6.      In the System Parameters checklist Item, select “en-us” as the default language and click on the “Set to Ready for Upgrade” button.
7.      In the Company Priority setup, click on the “Set to Ready for Upgrade” button.
8.      In the Product Upgrade Form, click on the Synchronize button and then on the Product Mapping -> Map all items 1:1. Click on the “Set to Ready for Upgrade” button after doing these steps.
9.      In the Units form, click on the “Automatically assignment” button.
a.       Set all decimals to 2
b.      Set all Unit classes to “Length”
c.       Click on the “Validate” button to make sure no errors are found
d.      Click on the “Set to Ready for Upgrade” button.
10.  In the Pre-Upgrade of Unit Conversions checklist item click on the Validate button and then on the “Set to Ready for Upgrade” button.
11.  In the Pre-Upgrade of Unit Texts click on the Validate button and then on the “Set to Ready for Upgrade” button.
12.  In the Pre-Upgrade Data checklist item you might need to configure the Batch Server and Batch Server Groups if the Live PreProcessing scripts don’t start running. In order to do so, go to Administration\Setup\Server Configuration
13.  Make sure that only the machine you are using has the Is Batch Server checkbox checked. Now go to the Batch Server Groups tab and select the DataUpdate Batch Server Group
14.  Another configuration that is required to start running these jobs is the Batch Group form. You can access this in Administration\Setup\Batch Groups
15.  Select the DataUpdate Batch Group and go to the Batch Servers tab.
16.  Make sure the machine you’re running the upgrade on is on the Selected Servers list on the left side pane.
17.  After running the Live PreUpgrade, continue with the next checklist items (Validate Pre Upgrade, check Single User Mode and Single User Mode Upgrade)
18.  Once the checklist is finished, the PreProcessing stage is done. Uninstall AX50 (don’t drop the database) and you are ready to go to the AX6 steps.

Starting an Upgrade from the Target Environment

1.      Install Dynamics AX 2012.
2.      Setup Ax 2012 pointing the AOS to a new Database . Specify a different database name for the Model Database. Make sure you select the "Register Database for Upgrade checkbox:"
3.      At this stage, you should have 3 databases in your system: Database
·         The AX50 PreProcessed database
·         The new AX6 database
·         The new AX2012 model database
4.      Start AX 2012 and run through the Upgrade checklist
5.      In the Provide License Information step, specify the license.
6.      At this point, the Target Environment upgrade process is started. Make your way through the first five checklist items.

Data Upgrade Stages

Source DB connection step:
In the Source DB connection step, specify the server name where the Source Database is located and the Source Database name. Click OK once this information is entered.
PreSynchronize step:
This step loads the Upgrade cockpit. Depending on which stage you started the Upgrade Process; you might need to configure the Batch Groups and Batch Servers. Once this configuration is set, click the Run button. PreSync scripts should start running at this stage.
Create Tables Step:
This step synchronizes the database. No special steps need to be taken here.
Generate table and field mapping:
This step generates table and field mapping between source and target systems. There should be no mapping with error.
Generate Bulk Copy and Script Prioritization Step:
Bulk Copy Priorities and Script-Table dependencies are resolved in this step. No special steps need to be taken here.
Launch Data Upgrade Step:
This step loads the Upgrade cockpit. Once the cockpit is loaded, click on the Run button and the Post Sync scripts should start running.
This is where the data is actually copied from the Source Database to the Target Database based on the Mappings found in the Generate Table and Field Mapping step.


Monday, June 20, 2011

Dynamics AX 2012 Programming model

The following table lists which programming model or models are best used in each development pattern.


<>   <>
Design Pattern

Programming Model


Strongly typed .NET interop to X++

Services

Object-oriented development in X++

Customization
Use to access functionality implemented in X++ from .NET. Where appropriate, you should use eventing to decouple customization business logic implemented in .NET.
Not recommended
Layer-based X++ source code customization is supported. Where appropriate, you should use eventing to decouple customization business logic implemented in X++.
Integration
Not recommended
Use service interfaces to access Microsoft Dynamics AX.
Not recommended
External application development
Use if your Windows application runs only on the AOS server.
Use service interfaces to access Microsoft Dynamics AX in all other cases, including applications that are not Windows-based.
Not recommended
Report development
Not recommended
Use service interfaces when implementing data methods as your report data source type.
Use when developing an RDP data source type for your reports
Enterprise Portal application development
Use from Enterprise Portal applications to access business logic that is in X++ classes.
Use services to access Microsoft Dynamics AX functionality, especially if the application is Internet facing.
Not recommended
 


Monday, May 30, 2011

How to Debug batch jobs and service operations in Dynamics AX 2012

All batch jobs and service operations now run in managed code (IL) and require different debugging steps.  Rather than setting breakpoints within X++, you need to set them within the IL code that corresponds to the X++ code and debug in Visual Studio.

  1. Open Visual Studio as 'administrator' and attach the debugger to the Ax32Serv.exe process.
  2.  Note that it may also be necessary to change the ‘Attach to’ selection to ‘Managed (v4.0) code’ and make sure “just my code” is unchecked in VS: tools->options->debugging->General
  3. Once done, open up the file you want to debug in Visual Studio. All of the X++ code is compiled into IL and can be found in the following directory after deployment: ..\Program Files\Microsoft Dynamics Ax\6.0\Server\AxaptaDev\Bin\XppIL\source\
  4. Set a breakpoint in the file you opened.
  5. Go to Ax and run the process in batch mode, or execute the service operation. This will end up hitting your breakpoint, provided you set it in the right place.
If you make some changes to X++ code and want those changes reflected in the generated IL code, you need to do the following AFTER you have compiled the X++ code.Once done, your changes will be reflected in your next debugging session:

Dynamics AX 2012 AXUtil.exe command-line utility for models

A new command-line utility, called axutil.exe is used to work with models and model files. You can use this utility to create, export, import, uninstall, and list installed models. You can use the axutil.exe Help for an overview of various commands and parameters used by each command. The setup program uses this utility to install the Microsoft Dynamics AX 2012 application.

Breakpoints in forms button click() method is now operational in Dynamics AX 2012

Great news for the developers, the pain point on Dynamics AX 2009 related to the unavailability of breakpoint in form's button click() method is no longer exist in Dynamics AX 2012. User can insert breakpoint in from's button click method as he can do it in other locations. Traditionally user write 'breakpoint' to debug the code on form's click() method.

Dynamics Ax 2012 - accessing the correct QueryBuildDataSource for a FormDataSource

Traditionally within Forms it has been rather cumbersome to get to the QueryBuildDataSource for a specific FormDataSource within Forms. You had to write code such as this:

public void FormDataSource::init()
{

    QueryBuildDataSource qbds;
    super();
    qbds = this.query().dataSourceTable(tableNum(CustTable));

}

That’s all fine and dandy if you have a simple Form, but on some Forms you might have multiple instances of CustTable, especially with the advent of ReferenceDataSources. In these cases you might fall back to using the name of the FormDataSource to ensure you get the correct QueryBuildDataSource:

public void FormDataSource::init()
{

    QueryBuildDataSource qbds;
    super();
    qbds = this.query().dataSourceName(this.name());

}

This is also problematic in the cases that a different query is applied to the Form, such as in the CopyCallerQuery from a List Page to a Details Page. This makes it all very confusing and difficult, and to tell you the truth, we have a very long method in the kernel that finds the correct QueryBuildDataSource for the respective FormDataSource. The good news is those methods are now exposed to X++!
With Dynamics AX 2012 you can now use the methods “FormDataSource::queryBuildDataSource()” and “FormDataSource::queryRunQueryBuildDataSource()”. The first method will return you the QueryBuildDataSource in the FormDataSource.query(), while the second will return you the QueryBuildDataSource in the FormDataSource.queryRun().query(). There’ll be another blog entry discussing when using each is appropriate, but the short of it is use the first whenever you want to modify the QueryBuildDataSource and have the modification stay throughout the life of the Form (such as adding developer ranges that you never want the user to be able to change). The second should be used when you want to make modifications that will just live for the current “executeQuery” call and will be cleared whenever the user clicks on the “Clear Filters” button.
Want to see any additional APIs? Let us know and we can consider adding them.

Non admin debugging in Dynamics AX 2012

1.) Add the admin to the roles that you want to test. Note that it is ok to leave the admin user in the system administration role.
2.) Open a dev workspace and close the application work spaces.
3.) Set applicable breakpoints.
4.) Create a job with the following line of code and execute: SecurityUtil::sysAdminMode(false);
5.) So at this point the current session is in “non-admin mode”.
6.) Use Ctrl+W to open the application work space. You should notice that few menus / menu items are available
7.) Run the test and any break points set will be hit.

Current constraints / limitations

When SecurityUtil::sysAdminMode(…) is invoked, it is only applicable to the current session. So you must use CTRL+W to open the app workspace; launching a new client will not work.
Since this is scoped to the current session scenarios like run-as are not supported and also services / EP is not supported at this point.