Thursday, November 27, 2014

Copy Custom field from PO line to Invoice line

Sometime we have a scenario where we have to copy custom field value from PO to invoice, to do this you have create similar field in VendInvoiceInfoLine table and set/copy the value from PO line to invoice line on VendInvoiceInfoLine.defaultRaw() method or in class VendDocumentLineType_Invoice.

Happy DAXing !!!!

Monday, November 24, 2014

Add Action pane AxActionPaneControl to EP form

To add Action pane with default Save and Close buttons, here are the steps:

Create two web menus in AX
Add following code on ASP.NET script:
<%@ Register Assembly="Microsoft.Dynamics.Framework.Portal, Version=6.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
 Namespace="Microsoft.Dynamics.Framework.Portal.UI.WebControls" TagPrefix="dynamics" %>
<%@ Register Src="AxBaseUserControl.ascx" TagName="AxBaseUserControl" TagPrefix="Ax" %>
<%@ Register Assembly="Microsoft.Dynamics.Framework.Portal.SharePoint, Version=6.3.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
Namespace="Microsoft.Dynamics.Framework.Portal.SharePoint.UI.WebControls" TagPrefix="dynamics" %>

dynamics:AxDataSource ID="youDS" runat="server" DataSetName="youDS"
    ProviderView="yourTable">
dynamics:AxDataSource

dynamics:AxActionPaneControl ID="AxActionPaneControlTop" runat="server"
DataMember="yourDS_Current" DataSourceID="yourDS"
WebMenuName="axToolBarInfo" EnableMenuItemHelpText="True" EnableTheming="True"
dynamics:AxActionPaneControl ID="AxActionPaneControlEdit" runat="server" EnableTheming="True"
DataMember="youDS_Current" DataSourceID="youDS" EnableMenuItemHelpText="True" WebMenuName="axToolBarCreate"

Now write following code in C# code behind:

using System;
using System.Web.UI;
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;

public partial class Form1: System.Web.UI.UserControl
{
    protected const string axToolbar_Info = "axtoolbarinfo";
    protected const string axtoolbar_Create = "axtoolbarcreate";
    private const string MENU_ITEM_SaveAndClose = "epsaveandclose";
    private const string MENU_ITEM_Close = "epclose";

    protected void Page_Load(object sender, EventArgs e)
    {
        this.SetupMode();
    }

    void Page_Init(object sender, EventArgs e)
    {
        this.AxActionPaneControlTop.SetMenuItemProperties += new EventHandler(AxActionPaneControlTop_SetMenuItemProperties);
        this.AxActionPaneControlTop.ActionMenuItemClicking += new EventHandler(AxActionPaneControlTop_ActionMenuItemClicking);
        this.AxActionPaneControlTop.ActionMenuItemClicked += new EventHandler(AxActionPaneControl_ActionMenuItemClicked);

        this.AxActionPaneControlEdit.SetMenuItemProperties += new EventHandler(AxActionPaneControlTop_SetMenuItemProperties);
        this.AxActionPaneControlEdit.ActionMenuItemClicking += new EventHandler(AxActionPaneControlTop_ActionMenuItemClicking);
        this.AxActionPaneControlEdit.ActionMenuItemClicked += new EventHandler(AxActionPaneControl_ActionMenuItemClicked);
    }
    ApplicationProxy.EPFormAction FormMode
    {
        get
        {
            return (ApplicationProxy.EPFormAction)Convert.ToInt16(
            this.Page.Request.QueryString.Get("mode")); // This mode is the param set in web menu item url, i.e. mode=1
        }
    }

    private ISession AxSession
    {
        get
        {
            AxBaseWebPart webpart = AxBaseWebPart.GetWebpart(this);
            return webpart == null ? null : webpart.Session;
        }
    }

    private void SetupMode()
    {
        //Proxy.Info objInfoLog = new Proxy.Info(this.AxSession.AxaptaAdapter);
        //objInfoLog.add(Proxy.Exception.Warning, Convert.ToString(this.FormMode));
        switch (this.FormMode)
        {
            case ApplicationProxy.EPFormAction.EditMode:

                this.AXForm.DefaultMode = DetailsViewMode.Edit;
                this.AXForm.AutoGenerateEditButton = true;
                this.AXForm.AutoGenerateInsertButton = false;
             
                break;

            case ApplicationProxy.EPFormAction.CreateMode:
                this.AXForm.DefaultMode = DetailsViewMode.Insert;
                this.AXForm.AutoGenerateEditButton = false;
                //this.AXForm.AutoGenerateInsertButton = true;
                this.AxActionPaneControlTop.Visible = false;
                this.AxActionPaneControlEdit.Visible = true;
               
                break;

            default:
                this.AXForm.DefaultMode = DetailsViewMode.ReadOnly;
                this.AXForm.AutoGenerateEditButton = false;
                this.AXForm.AutoGenerateInsertButton = false;
                this.AxActionPaneControlTop.Visible = true;
                this.AxActionPaneControlEdit.Visible = false;
                break;
        }
    }
 
    void AxActionPaneControlTop_ActionMenuItemClicking(object sender, ActionMenuItemClickingEventArgs e)
    {
        e.RunMenuItem = false;
    }

    void AxActionPaneControl_ActionMenuItemClicked(object sender, ActionMenuItemEventArgs e)
    {

        switch (e.MenuItem.MenuItemAOTName.ToLower())
        {
            case MENU_ITEM_SaveAndClose:
                this.AXForm.InsertItem(true);
                DialogHelper.Close(CloseDialogBehavior.RefreshDataSource);
                break;
            case MENU_ITEM_Close:
                this.AXForm.DoCancel();
                DialogHelper.Close(CloseDialogBehavior.CloseOnly);
                break;
        }
    }

    void AxActionPaneControlTop_SetMenuItemProperties(object sender, SetMenuItemPropertiesEventArgs e)
    {
        string menuItemName = e.MenuItem.MenuItemAOTName.ToLower(System.Globalization.CultureInfo.InvariantCulture);

        switch (menuItemName)
        {
            case MENU_ITEM_Close:
                // Close should not trigger validation
                ((AxActionMenuItem)e.MenuItem).CausesValidation = false;
                break;
            //case MENUITEMNAME_SaveAndClose:
        }
    }
     protected override void OnInit(EventArgs e)
    {
        this.SetupMode();
        base.OnInit(e);
    }
}



Add Grid and AxToolbar in Dynamics AX EP details form

Here are the steps you can follow to add Grid with AxToolbar in any EP detail form:

  1. Create new WebActionMenu for Add and Delete (i.e. YourDetailFormAdd), Label=‘Add’, NormalImage= 11421)
  2. Create new Web menu YourDetailForm and drag both action menus to this web menu
  3. In your MasterDetail form in VS where you wants to place this detail grid, add new AxToolBar control and set WebMenuName= YourDetailForm, ID= YourDetailFormToolBar
  4. Add AxGridView control and set DS, AllowDelete,AllowEdit,AllowInset to True. And set Datasource and DataMember. Finally set DataKeyName=RecId.
  5. Open .cs file of your Form and declare two constants for menu items you created in AX:
  6. protected const string YOURDETAILForm_ADD= “yourdetailformadd";
  7. protected const string YOURDETAILFORM_DELETE= “yourdetailformdelete";
  8. Create new property to get your CurrentRow of your datasource.
  9. For Grid, we have to write event for (and called from Page_Load):
  10. RowUpdated
  11. RowEditing
  12. RowDeleting
  13. RowInserted
  14. RowCreated
  15. RowCancelingEdit
  16. For ToolBar, we have to write following event and called from OnInit
  17. ActionMenuItemClicking
  18. ActionMenuItemClicked
  19. SetMenuItemProperties
Here is the example of C# code behind:

using System;
using System.Web.UI;
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;

public partial class YourForm: System.Web.UI.UserControl
{
    protected const string COSTTYPE_ADD = "hrminjuryincidentcosttypeadd";
    protected const string COSTTYPE_DELETE = "hrminjuryincidentcosttyperemove";
    
 
    protected void Page_Load(object sender, EventArgs e)
    {
        this.SetupMode();

        if (this.WebPart != null)
        {
            // Cost type
            AxCostGrid.RowUpdated += new GridViewUpdatedEventHandler(CostGrid_RowUpdated);
            AxCostGrid.RowCancelingEdit += new GridViewCancelEditEventHandler(CostGrid_RowCancelingEdit);
            AxCostGrid.RowEditing += new GridViewEditEventHandler(CostGrid_RowEditing);
            AxCostGrid.RowDeleting += new GridViewDeleteEventHandler(CostGrid_RowDeleting);

        }
        
    }

    protected override void OnInit(EventArgs e)
    {
        base.OnInit(e);

        // Cost Type
        this.InjuryCostToolBar.ActionMenuItemClicking += new EventHandler(InjuryCostToolBar_ActionMenuItemClicking);
        this.InjuryCostToolBar.ActionMenuItemClicked += new EventHandler(InjuryCostToolBar_ActionMenuItemClicked);
        this.InjuryCostToolBar.SetMenuItemProperties += new EventHandler(InjuryCostToolBar_SetMenuItemProperties);
        this.AxCostGrid.RowInserted += new EventHandler(CostGrid_RowInserted);
        this.AxCostGrid.RowCreated += new GridViewRowEventHandler(CostGrid_RowCreated);
        this.AxCostGrid.RowEditing += new GridViewEditEventHandler(CostGrid_RowEditing);

    }

    ApplicationProxy.EPFormAction FormMode
    {
        get
        {
            return (ApplicationProxy.EPFormAction)Convert.ToInt16(
            this.Page.Request.QueryString.Get("mode")); // This mode is the param set in web menu item url, i.e. mode=1
        }
    }
    private ISession AxSession
    {
        get
        {
            AxBaseWebPart webpart = AxBaseWebPart.GetWebpart(this);
            return webpart == null ? null : webpart.Session;
        }
    }
    private void SetupMode()
    {
        //Proxy.Info objInfoLog = new Proxy.Info(this.AxSession.AxaptaAdapter);
        //objInfoLog.add(Proxy.Exception.Warning, Convert.ToString(this.FormMode));
        switch (this.FormMode)
        {
            case ApplicationProxy.EPFormAction.EditMode:
                
                this.HRMInjuryIncidentForm.DefaultMode = DetailsViewMode.Edit;
                this.HRMInjuryIncidentForm.AutoGenerateEditButton = true;
                this.HRMInjuryIncidentForm.AutoGenerateInsertButton = false;
                break;
            
            case ApplicationProxy.EPFormAction.CreateMode:
                this.HRMInjuryIncidentForm.DefaultMode = DetailsViewMode.Insert;
                this.HRMInjuryIncidentForm.AutoGenerateEditButton = false;
                this.HRMInjuryIncidentForm.AutoGenerateInsertButton = true;
                break;

            default:
                this.HRMInjuryIncidentForm.DefaultMode = DetailsViewMode.ReadOnly;
                this.HRMInjuryIncidentForm.AutoGenerateEditButton = false;
                this.HRMInjuryIncidentForm.AutoGenerateInsertButton = false;
                
                this.AxCostGrid.AllowEdit = false;
                this.AxCostGrid.AllowDelete = false;
                break;
        }
    }

    /// COST TYPE
    private DataSetViewRow CostInjuryCurrentRow
    {
        get
        {
            try
            {
                DataSetView dsv = this.AxHRMInjuryIncidentDS.GetDataSet().DataSetViews[this.AxCostGrid.DataMember];
                return (dsv == null) ? null : dsv.GetCurrent();
            }
            // CurrentRow on the dataset throws exception in empty data scenarios
            catch (System.Exception)
            {
                return null;
            }
        }
    }

    private AxBaseWebPart WebPart
    {
        get { return AxBaseWebPart.GetWebpart(this); }
    }

    void CostGrid_RowDeleting(object sender, GridViewDeleteEventArgs e)
    {
        addClicked = false;
    }

          
    void CostGrid_RowEditing(object sender, GridViewEditEventArgs e)
    {
        addClicked = false;
    }
     
    void CostGrid_RowCreated(object sender, GridViewRowEventArgs e)
    {
        addClicked = false;
    }

    void CostGrid_RowInserted(object sender, GridViewInsertedEventArgs e)
    {
        addClicked = false;
    }
    
    void InjuryCostToolBar_SetMenuItemProperties(object sender, SetMenuItemPropertiesEventArgs e)
    {
        long record = 0;

        AxCostGrid.AllowEdit = this.FormMode != ApplicationProxy.EPFormAction.InfoMode;// true;
        
        if (this.CostInjuryCurrentRow != null)
            record = (long)this.CostInjuryCurrentRow.GetFieldValue("RecId");

        // Set the properties on the toolbar
        switch (e.MenuItem.MenuItemAOTName.ToLower())
        {
            case COSTTYPE_DELETE:
                e.MenuItem.Disabled = !(this.HRMInjuryIncidentDS.GetDataSet().DataSetViews[this.AxCostGrid.DataMember].Count > 0 ? true : false);
                break;

            case COSTTYPE_ADD:
                if (addClicked)
                {
                    e.MenuItem.Disabled = true;
                }
                else
                {
                    if (this.AxCostGrid.AllowEdit)
                    {
                        e.MenuItem.Disabled = false;
                    }
                    else
                    {
                        e.MenuItem.Disabled = true;
                    }
                }
                break;
        }
    }

    void InjuryCostToolBar_ActionMenuItemClicked(object sender, ActionMenuItemEventArgs e)
    {
        switch (e.MenuItem.MenuItemAOTName.ToLower())
        {
            // The add button 
            case COSTTYPE_ADD:
                try
                {
                    this.AxCostGrid.AllowInsert = true;

                    this.AxCostGrid.CreateRow();

                    addClicked = true;

                    // Disable the add button
                    e.MenuItem.Disabled = true;
                }
                catch (System.Exception exception)
                {
                    AxExceptionCategory exceptionCategory;
                    // Check if the exception was a fatal exception
                    if (!AxControlExceptionHandler.TryHandleException(this, exception, out exceptionCategory))
                    {
                        // Fatal exception, throw so as to be handled
                        throw;
                    }
                }
                break;

            // The delete button 
            case COSTTYPE_DELETE:
                try
                {
                    int selectedIndex = this.AxCostGrid.SelectedIndex;
                    if (selectedIndex != -1)
                    {
                        this.AxCostGrid.DeleteRow(selectedIndex);
                    }
                }
                catch (System.Exception ex)
                {
                    AxExceptionCategory exceptionCategory;
                    // This returns true if the exception can be handled here
                    if (!AxControlExceptionHandler.TryHandleException(this, ex, out exceptionCategory))
                    {
                        // The exception was fatal - in this case we re-throw.
                        throw;
                    }
                }
                break;
        }
    }

    void InjuryCostToolBar_ActionMenuItemClicking(object sender, ActionMenuItemClickingEventArgs e)
    {
        switch (e.MenuItem.MenuItemAOTName.ToLower())
        {
            case COSTTYPE_ADD:
            case COSTTYPE_DELETE:
                e.RunMenuItem = false;
                break;
        }
    }

    
    void CostGrid_RowCancelingEdit(object sender, GridViewCancelEditEventArgs e)
    {
        try
        {
            // Only call CancelEdit when new row since we are manually adding it
            if (this.CostInjuryCurrentRow.IsNew)
                this.CostInjuryCurrentRow.CancelEdit();
        }
        catch (System.Exception exception)
        {
            AxExceptionCategory exceptionCategory;
            // Check if the exception was a fatal exception
            if (!AxControlExceptionHandler.TryHandleException(this, exception, out exceptionCategory))
            {
                // Fatal exception, throw so as to be handled
                throw;
            }
        }

        CancelRowEdit();
    }

    private void CancelRowEdit()
    {
        AxCostGrid.EditIndex = -1;
        AxCostGrid.AllowEdit = false;
    }


    void CostGrid_RowUpdated(object sender, GridViewUpdatedEventArgs e)
    {
        if (e.Exception == null)
        {
            DataSetViewRow dsvr = this.CostInjuryCurrentRow;

            if (dsvr != null)
            {
                bool rowSaved = true;
                try
                {
                    if (dsvr.IsNew)
                        dsvr.EndEdit();
                }
                catch (System.Exception ex)
                {
                    rowSaved = false;

                    AxExceptionCategory exceptionCategory;
                    // This returns true if the exception can be handled here
                    if (AxControlExceptionHandler.TryHandleException(this, ex, out exceptionCategory))
                    {
                        // If non fatal run error compensation logic
                        if (exceptionCategory == AxExceptionCategory.NonFatal)
                        {
                            // Keeps the current row editable i.e. reverts to the previous UI state
                            e.KeepInEditMode = true;
                        }
                    }
                    else
                    {
                        // The exception is system fatal - in this case we re-throw.                                    
                        throw;
                    }
                }

                if (rowSaved)
                {
                    CancelRowEdit();
                }
            }
        }
    }


    protected AxBoundField GetField(string name)
    {
        AxBoundField attendeeField = null;

        // Get the field which needs to have the customized lookup
        foreach (DataControlField field in this.AxCostGrid.DataControlFieldCollection)
        {
            AxBoundField boundField = field as AxBoundField;
            // If the field is not null and has the required data field
            if (boundField != null && String.Compare(boundField.DataField, name, true) == 0)
            {
                attendeeField = boundField;
                break;
            }
        }

        return attendeeField;
    }
}

Happy DAXing !!!!!

Dynamics AX EP form in Edit and Insert mode

Generally when we worked on designing detail EP form we have two options to open EP form in Insert and Edit mode, either design two separate EP forms with same piece of code except Designmode property set to Inset or Edit. Other way is to create single EP form and write code behind to open form in either mode. to do this design your EP form and create two web menu items and set parameters as follow:

mode=1 :- edit
mode=2 :- insert

Open your fom C# code behind and write following code:

    protected void Page_Load(object sender, EventArgs e)
    {
        this.SetupMode();
        
    }      

    ApplicationProxy.EPFormAction FormMode
    {
        get
        {
            return (ApplicationProxy.EPFormAction)Convert.ToInt16(
            this.Page.Request.QueryString.Get("mode")); // This mode is the param set in web menu item url, i.e. mode=1
        }
    }
    private ISession AxSession
    {
        get
        {
            AxBaseWebPart webpart = AxBaseWebPart.GetWebpart(this);
            return webpart == null ? null : webpart.Session;
        }
    }
    private void SetupMode()
    {
        //Proxy.Info objInfoLog = new Proxy.Info(this.AxSession.AxaptaAdapter);
        //objInfoLog.add(Proxy.Exception.Warning, Convert.ToString(this.FormMode));
        switch (this.FormMode)
        {
            case ApplicationProxy.EPFormAction.EditMode:
                
                this.Form1.DefaultMode = DetailsViewMode.Edit;
                this.Form1.AutoGenerateEditButton = true;
                this.Form1.AutoGenerateInsertButton = false;
                break;
            
            case ApplicationProxy.EPFormAction.CreateMode:
                this.Form1.DefaultMode = DetailsViewMode.Insert;
                this.Form1.AutoGenerateEditButton = false;
                this.Form1.AutoGenerateInsertButton = true;
                break;

            default:
                this.Form1.DefaultMode = DetailsViewMode.ReadOnly;
                this.Form1.AutoGenerateEditButton = false;
                this.Form1.AutoGenerateInsertButton = false;
                break;
        }
    }

You are all set to see this dynamic behavior.  just to recap here are the steps:


  1. Design and deploy new form with default ReadOnly mode 
  2. Create separate web menu items for Edit and Insert operations and set property Parameter=‘mode=1’ for Edit or ‘mode=2’ for insert.
  3. Get the session object by creating property AxSession
  4. Create FormMode() method in Form1.aspx.cs to get mode via a query string.
  5. Create SetupMode() method in Form1.aspx.cs 


Happy DAXing !!!!

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);
}

Tuesday, August 19, 2014

EP component installation issue with SharePoint 2013 - Server was unable to process request

During installation of EP component on sharepoint 2013 if you face following issue 

Server was unable to process request. ---> Could not retrieve a valid Windows identity. ---> The message could not be dispatched because the service at the endpoint address 'net.pipe://localhost/s4u/022694f3-9fbd-422b-b4b2-312e25dae2a2' is unavailable for the protocol of the address.

To resolve this issue, in SharePoint Central Administration, click System Settings > Manage services on server. Start the Claims to Windows Token Service 


Tuesday, August 5, 2014

Service oriented architecture example - Pizza as a service

To understand service oriented architecture, here is a simple 'Pizza' example which demonstrate how IaaS, Paas and SaaS works.

 

Happy Daxing !!!

Thursday, May 22, 2014

Things you did not know about Microsoft Dynamics AX 2012 R3

Here are 6 things you might not know about Microsoft Dynamics AX 2012 R3:

1. There are no longer extra GL entries generated from the two voucher item posting strategy
This corrects a design issue when the distribution framework was introduced in AX2012.  Fewer entries are made to the GL and the normal effect of posting a receiving to accrued purchases shows in a more sensible way.

2. There are now additional Inventory Storage Dimensions for Inventory Status and License Plate
Inventory statuses can also be tied to blocking features to only allow inventory that is truly available for immediate sale

3. The reservation Hierarchy determines how dimensions are used in the warehouse
The reservation hierarchy is the key to warehouse management and automation and can be configured differently for items that require more detailed manual reservation.  Understanding the hierarchy is important to the overall system automation and flow.

4. Items must be assigned to a storage dimension that is Warehouse Manageable from the beginning
Items must be associated with the correct storage dimension at creation.  Changing the storage dimension after transactions exist is not supported.  Microsoft will be introducing conversion utilities for existing customer installations post release.  Make sure to work closely with your account manager and solution delivery manager to plan your upgrade.

5. AX 2012 R3 will be available in the Cloud on Microsoft’s Azure server platform
Once released this could be the fastest and most cost effective way to deploy both development and UAT infrastructures for Dynamics AX.  There are still costs directly with Microsoft to run Azure servers but the overall speed of setup and the elimination of any hardware purchase make this a compelling offering.  And with Azure you only pay for server time used!

6. PowerQuery is a free download as an addin to Excel 2013 and 2010 that can integrate directly with your instance of AX2012 company data
PowerQuery allows an AX user to connect directly with Queries and Services exposed to the document framework of the Excel Addin. This allows easier creation of business intelligence reports on AX data directly connecting to the AX company database.  This does not require Office365 and can run standalone.



Copied from http://www.uxceclipse.com/pages/blog-detail.aspx?newslist=Blog%20List&itemId=19 just for reference.

Tuesday, May 20, 2014

Microsoft Dynamics AX report viewer control exception workaround

If you are facing exceptions in Event Viewer related to Microsoft Dynamics AX report viewer control like:

An unexpected exception occurred in the Microsoft Dynamics AX report viewer control. Contact your system administrator for assistance.
The type 'Microsoft.SharePoint.Portal.Analytics.UI.ReportViewerMessages, Microsoft.SharePoint.Portal, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c' could not be found

OR

An unexpected exception occurred in the Microsoft Dynamics AX report viewer control. Contact your system administrator for assistance.

Object reference not set to an instance of an object.
System.NullReferenceException
   at Microsoft.Reporting.WebForms.ReportViewer.OnPreRender(EventArgs e)

Here is the simple workaround of these issues:

you have to comment out the ReportViewerMessages line in appSettings section in the report server web.config


So far, I haven’t seen any issues by commenting out this entry.


Saturday, April 26, 2014

Upgrade SSRS reports from Dynamics AX 2012 to AX 2012 R2

Sometime upgrade from Dynamics AX 2012 to AX 2012 R2 is really painful specially when we have huge customization on existing object related to EP, SSRS reports and of course Forms. I got following error when i tried to deploy some modified report which upgrade to R2:

Error      Message (12:17:24 pm) An error occurred while deploying the report ProjListTransProj. 
This might be because the SQL Server Reporting Services extensions have not been installed on the report server, or configuration information has not been entered in the Report Servers form in Microsoft Dynamics AX.

Error      Message (12:17:24 pm) System.Web.Services.Protocols.SoapException: Error while loading code module: ‘Microsoft.Dynamics.Framework.Metadata.AX, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35’. Details: Could not load file or assembly 'Microsoft.Dynamics.Framework.Metadata.AX, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The system cannot find the file specified.
   at Microsoft.ReportingServices.WebServer.ReportingService2005Impl.CreateReport(String Report, String Parent, Boolean Overwrite, Byte[] Definition, Property[] Properties, Warning[]& Warnings)
   at Microsoft.ReportingServices.WebServer.ReportingService2005.CreateReport(String Report, String Parent, Boolean Overwrite, Byte[] Definition, Property[] Properties, Warning[]& Warnings)

Info        Message (12:17:24 pm) Report name: ProjListTransProj
Design names: ProjListTransProj.Report
Deployment status for design names: Error
Assembly names: ProjListTransProjReport.BusinessLogic.dll, DrillThroughCommon.dll
Deployment status for assembly names: Success, Success

One solution if this error is to open respective VS report project in VS environment and build it and deploy it. But in some cases its not working. 
so the other workaround is to hack the code, you should export the report which throw above error, and open the XPO in notepad and search the tag 6.0.0.0 and replace it with 6.2.0.0. You should also search tag "6.0.14.0" and replace it with "6.0.15.0".
Here is the sample:
#
#    Microsoft.Dynamics.Framework.Reports, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
#    Microsoft.Dynamics.Framework.Metadata.AX, Version=6.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35

to replace with:

#
#    Microsoft.Dynamics.Framework.Reports, Version=6.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
#    Microsoft.Dynamics.Framework.Metadata.AX, Version=6.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35

Happy Daxing

Monday, April 14, 2014

Create sales order from sales quotation

Following code is used to create sales order from sales quotation:
static void createSalesOrderFromQuote(Args _args)
{
    SalesQuotationTable             salesQuotationTable = SalesQuotationTable::find("quotation Id");
    SalesQuotationEditLinesForm     editLinesForm;
    ParmId                          parmId;
    
    editLinesForm = SalesQuotationEditLinesForm::construct(DocumentStatus::Confirmation);
    
    parmId = editLinesForm.parmId();
    
    editLinesForm.initParmSalesQuotationTable(salesQuotationTable);    
    editLinesForm.parmTransDate(systemDateGet());
    editLinesForm.prePromptInit();
    editLinesForm.initParameters(NoYes::No, NoYes::No, NoYes::No, NoYes::No, NoYes::No, '', NoYes::No); 
    
    editLinesForm.run();
}

Happy Daxing !!!

Friday, February 28, 2014

Development patterns in Microsoft Dynamics AX 2012

To choose the appropriate programming model and tools for your business application development, you must first identify the basic development patterns that can be combined to create your application. Development patterns are the building blocks of any business application, and they fall into the following categories: 
  • Customization 
    • Alteration 
    • Extension 
    • Enhancement 
  • Integration 
  • External application module development 
    • Custom dedicated applications development 
    • Report development 
    • Enterprise Portal Web application development
Although real-life applications rarely fit neatly into a single development pattern, it is possible to identify large parts of an application that fit into one of these development patterns. It is important to understand these pattern categories, because each of the following programming models in Microsoft Dynamics AX is best suited for to one of these development patterns. The following sections provide definitions and an example of each development pattern.
 

1. Customization

Customization is the development pattern in which you create new functionality by altering, extending, or enhancing the functionality in the shipped product. 

Alterations to the base functionality are achieved by changing the metadata and source code of the base application. This is done through the alteration of artifacts such as X++ classes, tables, and Visual Studio projects in a higher layer, such as, for example, the ISV layer or the VAR layer. The unique layering feature of Microsoft Dynamics AX enables the system to synthesize the final application logic to be executed at run-time by aggregating the metadata and code modifications across all the layers. You should be aware that this layer-based customization capability is applicable to all artifacts stored in the Microsoft Dynamics AX model store, including the Visual Studio project artifacts.


Extensions to functionality are achieved by incrementally increasing base functionality by adding to the source code in the business logic of the base application. 


Enhancements to the application are achieved by adding new functionality originally unavailable in the base application. Typically, an enhancement is larger in scope than an alteration or extension. An example of customizations would be a scenario in which a new regulation from the government requires the existing sales tax computation business logic to reflect the inclusion of an additional “environmental sustainability tax” component. If the calculation behind this new component is straightforward and simple, you can simply alter the base tax computation logic in a higher layer and directly implement the required source code changes. However, if this tax module has business rules that are more complex, you should consider enhancing the base product with a new tax engine component that implements these requirements. 


2. Integration 

Integration is the development pattern that involves enabling existing applications (not written exclusively for Microsoft Dynamics AX) to interact and work with Microsoft Dynamics AX across process boundaries. An example of integration is when a value added reseller (VAR) integrates with an existing legacy Customer Relationship Management (CRM) system to synchronize the customer list between Microsoft Dynamics AX and the legacy CRM system.


3. External application module development 

The development pattern for application modules that run out-of-process of the AOS server or Microsoft Dynamics AX client processes. 

Custom dedicated application development is the development pattern that builds a new, custom external application/client that accesses functionality and data in the Microsoft Dynamics AX system. These applications can span multiple platforms and programming languages. 
An example of custom dedicate application development is creating a Windows Phone 7 application to view open sales orders in Microsoft Dynamics AX.

Report development is the development pattern that creates custom reports and customizes any out of the box reports for the Microsoft Dynamics AX platform. 

Enterprise Portal web application development is the development pattern that creates web applications specifically built on the Microsoft Dynamics AX Enterprise Portal development framework. 
An example of web application development is creating an Enterprise Portal-based employee self-service page to enter time-sheet data. 


Thursday, February 27, 2014

The Table.Field does not have a corresponding parm-method on the AxBC-class

If you add new field in a table and getting "The Table.Field does not have a corresponding parm-method on the AxBC-class" error message, you can run following Job to fix this issue. This job will add corresponding parm-method for newly added field in a table.

static void CreateAxBCParmMethodJob(Args _args)

    axGenerateAxBCClass axGenerateAxBCClass; 

    axGenerateAxBCClass = AxGenerateAxBCClass::newTableId(tablenum(CustTable));
    axGenerateAxBCClass.run(); 
 
}



Happy DAXing !!!!

Friday, January 24, 2014

Get Budget plan Ledger dimension from value

We can use following code to get Budget plan ledger dimension.

Container           offsetAccount;
RefRecId            ledgerDimension;

// offsetAccount = ['Account structure display value','Account structure', 'No of dimension', 'MainAccount','MainAccount value','Dimension 1', 'value', 'Dimension 2', 'value'];
offsetAccount = ['SCUS - Exp & Rev','SCUS - Exp & Rev', '3', 'MainAccount','1101','Fund', '1', 'Department', '0010'];

ledgerDimension = AxdDimensionUtil::getBudgetPlanningAccountId(offsetAccount);
info(strFmt("%1", ledgerDimension));