Starting Dynamics AX for Retail (POS Development)

One of the most challenging on dynamics AX is to develop POS on AX Retail module.

The Dynamics AX for Retail POS application was designed base on .Net Application using C# language so it’s little bit odd for regular AX technical who always develop using  X++, so since the POS is not using standard AX MorpX so we need to prepare to develop POS.

  1. Visual Studio for development tools.
  2. Dev Express License (Optional for better touch GUI)
  3. Retail SDK (POS Source Code)

Let’s  jump   into number 3. I assume you already have visual studio on dev server.

You can found Retail SDK on Dynamics AX Installation.

Installation

After installation finished you can found the source code  on document folder.

Source Code

For very first time lets  focus at the two folder on the POS Plug-ins, there is Services and Triggers because almost all POS function source code is on that folder.

Services: Services are actually .Net assemblies. POS implements many of the features as services using interfaces and can be modified using Visual Studio. POS loads these services at run time by calling the interfaces. So whenever you modify or extend the standard service keep the assembly name same as original so that POS can recognize it and call it at run time.

Triggers: Triggers are called before and after the operations. There are two types of triggers, Pre-triggers and Post-triggers. Pre-triggers provide a way of validation before a certain operation is executed. Post-triggers are used to respond to an operation after it has finished. You can modify the triggers same way as services.

*I use Dynamics AX 2012 R3 CU8 on this tutorial, also work for AX 2012 Feature Pack, AX 2012 R2, and AX 2o12 R3.

Create Custom Script

So first lets open the Triggers folder and double click Triggers.sln file, you will show all the triggers source code now expand on visual studio, lets pick our first function to modify.

For this tutorial we will add some function when POS starting, so lets’s open ApplicationTriggers Project and open  ApplicationTriggers.cs , at this class you will found the some of methods, this methods will be calling when POS staring, for example  ApplicationStart, ApplicationStop, PreLogon, PostLogon and etc.

Try to add some code at ApplicationStart method, i assume this method will be calling when POS application starting, lets add some simple script at here, let’s try add some dialog box.

 public void ApplicationStart()
{
     //Our Custom Script
     //<begin>
     MessageBox.Show("POS aplication starting....");
     //<end>

     string source = "IApplicationTriggers.ApplicationStart";
     string value = "Application has started";
     LSRetailPosis.ApplicationLog.Log(source, value, LSRetailPosis.LogTraceLevel.Debug);
     LSRetailPosis.ApplicationLog.WriteAuditEntry(source, value);
     ...
     ...
}

To make the script running on the POS we need to build the project in to dll and copy the dll on POS folder.

Try to run your POS, and the message box will be showing when POS starting.

Deploy Custom Script/dll

You should copy all  custom *.dll files on the Extensions folder at Retail POS Folder. Don’t replace the original ones.

We can using two different Extensions Folder on POS .

1. Retail POS\Extensions

Source Code2. Retail POS\Services\Extensions

Source Code

Always use the first one for custom Triggers *.dll, and second one for Services *.dll to make it easy to remember and maintenance.

Lets Starting To Debug POS Custom Code.

For debug the code, we can change some properties on the visual studio project,  use our first sample project, right click on that project and chose Properties.

First open Build  Tab on Properties UI and select the Output Path because this is Triggers so i  select “C:\Program Files (x86)\Microsoft Dynamics AX\60\Retail POS\Extensions\” Folder.

Source Code

And than open Debug tab, at the Start Action group select Start external program and select your POS.exe file.

Source Code

just put your break point on your custom script, and run the project using F5 or Start Button. Lets see the magic happen.

*Note: You can copy all POS Folder to your own folder to make the  original POS Files safety.

Advertisements

set WaterMark on textbox c# AX POS

extension method :

public static class TextBoxWatermarkExtensionMethod
{
     private const uint ECM_FIRST = 0x1500;
     private const uint EM_SETCUEBANNER = ECM_FIRST + 1;

     [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
     private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, uint wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);

     public static void SetWatermark(this TextBox textBox, string watermarkText)
     {
        SendMessage(textBox.Handle, EM_SETCUEBANNER, 0, watermarkText);
     }
}

how to use it example on form constructor :

public TIDFrmIDSKirimUang()
{
     InitializeComponent();

     TextBoxWatermarkExtensionMethod.SetWatermark(txtTNoHP,"0815xxxxxxx");
     TextBoxWatermarkExtensionMethod.SetWatermark(txtNoHP,"0815xxxxxxx");
}

reference :
http://vidmar.net/weblog/archive/2008/11/05/watermarked-textbox-in-windows-forms-on-.net.aspx

Convert int to color C# example code

protected void Calendar1_DayRender(object sender, DayRenderEventArgs e)
    {
        if (e.Day.IsSelected == true)
        {
            int color = SelectedDates[e.Day.Date];

            byte[] values = BitConverter.GetBytes(color);
            if (!BitConverter.IsLittleEndian) Array.Reverse(values);

            Color myColor = Color.FromArgb(values[0], values[1], values[2]);
            e.Cell.BackColor = myColor;
        }
    }

reference :
http://stackoverflow.com/questions/6131438/mapping-an-integer-to-an-rgb-color-in-c-sharp

Visualize with ax data to ASP calendar COLOR highlight example code on Enterprise Portal C#

i’ll explain the code below on another day .
when we remove the highlights on calender with clicks, it will remove the data on ax
and do the same when we add highlights on calendar.

Preview :
Capture

on design

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="HRSUploadTimeSheet.ascx.cs" Inherits="HRSUploadTimeSheet" %>
<dynamics:AxDataSource ID="hrsScheduleRequestWorkerTmpDS" runat="server" 
    DataSetName="HRSScheduleRequestWorkerTmpDS" 
    ProviderView="HRSScheduleRequestWorkerTmp">
</dynamics:AxDataSource>
<dynamics:AxDataSource ID="HRSJmgProfileDayDS" runat="server" 
    DataSetName="HRSJmgProfileDayDS" 
    ProviderView="JmgProfileDay">
</dynamics:AxDataSource>

<dynamics:AxForm ID="AxForm1" runat="server">
    <dynamics:AxMultiColumn ID="AxMultiColumn1" runat="server">
        <dynamics:AxColumn ID="AxColumn1" runat="server">
            <dynamics:AxGridView ID="AxGridView1" runat="server" BodyHeight="" 
                DataKeyNames="ProfileId" DataMember="HRSScheduleRequestWorkerTmp" 
                DataSetCachingKey="09d6aa10-4163-4e45-9074-8449211996ae" 
                DataSourceID="hrsScheduleRequestWorkerTmpDS" EnableModelValidation="True" 
                AllowPaging="False" 
                onselectedindexchanged="AxGridView1_SelectedIndexChanged" 
                ShowFilter="False">
                <Columns>
                    <asp:TemplateField ConvertEmptyStringToNull="False" 
                        HeaderText="<%$ AxLabel:@SYS303656 %>" SortExpression="PersonnelNumber">
                        <ItemTemplate>
                            <asp:Label ID="PersonnelNumber" runat="server" Text='<%# Bind("PersonnelNumber") %>'></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <dynamics:AxBoundField DataField="Name" DataSet="HRSScheduleRequestWorkerTmpDS" 
                        DataSetView="HRSScheduleRequestWorkerTmp" SortExpression="Name">
                    </dynamics:AxBoundField>
                    <asp:TemplateField ConvertEmptyStringToNull="False" 
                        HeaderText="<%$ AxLabel:@SYS7607 %>" SortExpression="ProfileId">
                        <ItemTemplate>
                            <asp:Label ID="ProfileId" runat="server" Text='<%# Bind("ProfileId") %>'></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                </Columns>
            </dynamics:AxGridView>
        </dynamics:AxColumn>

         <dynamics:AxColumn ID="AxColumn2" runat="server">
            <asp:Calendar ID="Calendar1" runat="server" 
                                    OnPreRender="Calendar1_PreRender" 
                                    OnSelectionChanged="Calendar1_SelectionChanged" 
                                    ondayrender="Calendar1_DayRender" Height="226px" 
                    Width="427px">
            </asp:Calendar>
            <dynamics:AxGridView ID="AxGridView2" runat="server" BodyHeight="" 
                DataKeyNames="RecId" DataMember="JmgProfileDay" 
                DataSetCachingKey="a1fd011a-2d33-4680-a45d-c70b2dcaf863" 
                DataSourceID="HRSJmgProfileDayDS" EnableModelValidation="True"
                ondatabound="AxGridView2_databound" ShowFilter="False" AllowPaging="False"
                >
                <Columns>
                    <asp:TemplateField ConvertEmptyStringToNull="False" 
                        HeaderText="<%$ AxLabel:@SYS67221 %>" SortExpression="Color">
                        <ItemTemplate>
                            <asp:Label ID="Color" runat="server" Text='<%# Bind("Color") %>' Visible="false"></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <asp:TemplateField ConvertEmptyStringToNull="False" 
                        HeaderText="<%$ AxLabel:@SYS7607 %>" SortExpression="Profile">
                        <ItemTemplate>
                            <asp:Label ID="Profile" runat="server" Text='<%# Bind("Profile") %>'></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <asp:TemplateField ConvertEmptyStringToNull="False" HeaderText="Profile Key" 
                        SortExpression="HRSProfleKey">
                        <ItemTemplate>
                            <asp:Label ID="ProfileKey" runat="server" Text='<%# Bind("HRSProfleKey") %>'></asp:Label>
                        </ItemTemplate>
                    </asp:TemplateField>
                    <dynamics:AxBoundField DataField="StartWorkTime" DataSet="HRSJmgProfileDayDS" 
                        DataSetView="JmgProfileDay" SortExpression="StartWorkTime">
                    </dynamics:AxBoundField>
                    <dynamics:AxBoundField DataField="EndWorkTime" DataSet="HRSJmgProfileDayDS" 
                        DataSetView="JmgProfileDay" SortExpression="EndWorkTime">
                    </dynamics:AxBoundField>
                </Columns>
            </dynamics:AxGridView>

        </dynamics:AxColumn>
    </dynamics:AxMultiColumn>
</dynamics:AxForm>

.cs file

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.DataVisualization.Charting;
using System.Web.UI.WebControls;
using Microsoft.Dynamics.Framework.Portal.UI.WebControls;
using Microsoft.Dynamics.Framework.Portal.UI.WebControls.WebParts;
using System.IO;
using System.Data.OleDb;
using System.Data;
using Proxy = Microsoft.Dynamics.Framework.BusinessConnector.Proxy;
using Microsoft.Dynamics.Framework.BusinessConnector.Session;
using System.Drawing;
using Microsoft.Dynamics.AX.Framework.Portal.Data;
using Microsoft.Dynamics.Framework.BusinessConnector.Adapter;


public partial class HRSUploadTimeSheet : System.Web.UI.UserControl
{
    private string profileId { get; set; }
    private string scheduleRequestId { get; set; }
    private string personnelNumber { get; set; }
    private Boolean canEdit { get; set; }

    Dictionary<DateTime, int> dict = new Dictionary<DateTime, int>();

    public Dictionary<DateTime, int> SelectedDates
    {
        get
        {
            if (ViewState["Dates"] == null)
            {
                dict.Add(DateTime.MaxValue.AddDays(-2), 0);
                ViewState["Dates"] = dict;
            }

            return (Dictionary<DateTime, int>)ViewState["Dates"];
        }
        set
        {
            ViewState["Dates"] = value;
        }
    }

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

    protected void Page_Load(object sender, EventArgs e)
    {
        IAxaptaRecordAdapter currentRecord = AxBaseWebPart.GetWebpart(this).ExternalRecord;
        if (currentRecord != null)
        {
            string status = currentRecord.GetField("HRSRequestStatus").ToString();
            if (status != "0")
            {
                canEdit = false;
            }
            else
            {
                canEdit = true;
            }
            scheduleRequestId = currentRecord.GetField("ScheduleReqestId").ToString();
        }
        if (!IsPostBack)
        {
            getProfileIdAndPersonnelNumber(true);
            getCalendarDataFromAX();
        }
      
    }

    #region grid
    protected void AxGridView2_databound(object sender, EventArgs e)
    {
        for (int i = 0; i <= AxGridView2.Rows.Count - 1; i++)
        {
            Label colorTxt = (Label)AxGridView2.Rows[i].FindControl("Color");
            Color myColor = Color.FromArgb(Convert.ToInt32(colorTxt.Text));
            AxGridView2.Rows[i].Cells[0].BackColor = myColor;
        }

        getProfileIdAndPersonnelNumber(true);
        getCalendarDataFromAX();
        FilterRows();
   
    }

    protected void AxGridView1_SelectedIndexChanged(object sender, EventArgs e)
    {
        getProfileIdAndPersonnelNumber();
        getCalendarDataFromAX();
        FilterRows();

    }

    protected void getProfileIdAndPersonnelNumber(Boolean isInit = false)
    {
        Label profileIdLbl;
        Label PersonnelNumberLbl;

        if (isInit)
        {
            profileIdLbl = (Label)AxGridView1.Rows[0].FindControl("ProfileId");
            PersonnelNumberLbl = (Label)AxGridView1.Rows[0].FindControl("PersonnelNumber");
        }
        else
        {
            profileIdLbl = (Label)AxGridView1.Rows[AxGridView1.SelectedIndex].FindControl("ProfileId");
            PersonnelNumberLbl = (Label)AxGridView1.Rows[AxGridView1.SelectedIndex].FindControl("PersonnelNumber");
        }
        profileId = profileIdLbl.Text;
        personnelNumber = PersonnelNumberLbl.Text;
    }

    protected void getCalendarDataFromAX()
    {
        SelectedDates.Clear();
        SelectedDates.Add(DateTime.MaxValue.AddDays(-2), 0);

        DateTime keyDate = System.DateTime.Now;
        int valueInt = 0;
        Microsoft.Dynamics.AX.ManagedInterop.Container axContainer;

        axContainer = (Microsoft.Dynamics.AX.ManagedInterop.Container)this.AxSession.AxaptaAdapter.CallStaticRecordMethod("HRSScheduleRequestWorkerTmp", "getDataScheduleByWorker", personnelNumber, scheduleRequestId);

        for (int i = 1; i <= axContainer.Count; i++)
        {
            if (i % 2 != 0)
            {
                keyDate = Convert.ToDateTime(axContainer.get_Item(i));
            }
            else
            {
                valueInt = Convert.ToInt32(axContainer.get_Item(i));
                if (SelectedDates.ContainsKey(keyDate))
                {
                    SelectedDates.Remove(keyDate);
                }

                SelectedDates.Add(keyDate, valueInt);

            }
        }

        ViewState["Dates"] = SelectedDates;
    }

    protected void ShowRows()
    {
        // Display all the rows
        foreach (GridViewRow row in AxGridView2.Rows)
        {
            row.Visible = true;
        }
    }

    protected void FilterRows()
    {
        ShowRows();
        string colProfileId;
        // Loop through the rows and apply the filter
        int i = 0;
        foreach (GridViewRow row1 in AxGridView2.Rows)
        {
            Label lblProfile = (Label)AxGridView2.Rows[i].FindControl("Profile");
            // Get the filter column values
            colProfileId = lblProfile.Text;

            // If the name, visit and discharge fields are not equal to "All" and not equal to the filter value then the row is hidden.
            if (colProfileId != profileId)
            {
                row1.Visible = false;
            }
            i++;
        }

    }

    #endregion

    #region calendar
    protected void Calendar1_PreRender(object sender, EventArgs e)
    {
        // Reset Selected Dates 
        Calendar1.SelectedDates.Clear();
        // Select previously Selected Dates 

        foreach (var dt in SelectedDates)
        {
            Calendar1.SelectedDates.Add(dt.Key);
            Color myColor = Color.FromArgb(Convert.ToInt32(dt.Value));
            Calendar1.SelectedDayStyle.BackColor = myColor;
        }

    }

    protected void Calendar1_SelectionChanged(object sender, EventArgs e)
    {
        if (canEdit)
        {
            getProfileIdAndPersonnelNumber();
            //Check if selected Date is in the saved list 
            // Remove the Selected Date from the saved list 
            if (SelectedDates.ContainsKey(Calendar1.SelectedDate))
            {
                SelectedDates.Remove(Calendar1.SelectedDate);
                //delete record in ax , call custom table static method
                this.AxSession.AxaptaAdapter.CallStaticRecordMethod("HRSScheduleRequestWorkerTmp", "deleteScheduleRequestWorker", personnelNumber, scheduleRequestId, Calendar1.SelectedDate.Date);
            }

            else
            {
                Label colorTxt = (Label)AxGridView2.Rows[AxGridView2.SelectedIndex].FindControl("Color");
                Label profileKey = (Label)AxGridView2.Rows[AxGridView2.SelectedIndex].FindControl("ProfileKey");
                SelectedDates.Add(Calendar1.SelectedDate, Convert.ToInt32(colorTxt.Text));
                //insert to ax , call custom table static method
                this.AxSession.AxaptaAdapter.CallStaticRecordMethod("HRSScheduleRequestWorkerTmp", "insertScheduleRequestWorker", personnelNumber, scheduleRequestId, Calendar1.SelectedDate.Date, profileId, profileKey.Text);
            }

            ViewState["Dates"] = SelectedDates;
        }
    }

    protected void Calendar1_DayRender(object sender, DayRenderEventArgs e)
    {
        if (e.Day.IsSelected == true)
        {
            int color = SelectedDates[e.Day.Date];
            Color myColor = Color.FromArgb(color);
            e.Cell.BackColor = myColor;
        }
    }

    #endregion
}

get return container from AX and iterate(loop) items example code EP (Enterprise Portal) C#

protected void getCalendarDataFromAX()
    {
        SelectedDates.Clear();

        DateTime keyDate = System.DateTime.Now;
        int     valueInt = 0;
        Microsoft.Dynamics.AX.ManagedInterop.Container axContainer;

        axContainer = (Microsoft.Dynamics.AX.ManagedInterop.Container)this.AxSession.AxaptaAdapter.CallStaticRecordMethod("HRSScheduleRequestWorkerTmp", "getDataScheduleByWorker", personnelNumber, "SCH-000001");

        for (int i = 1; i <= axContainer.Count; i++)
        {
            if (i % 2 != 0)
            {
                keyDate = Convert.ToDateTime(axContainer.get_Item(i));
            }
            else
            {
                valueInt = Convert.ToInt32(axContainer.get_Item(i));
                if (SelectedDates.ContainsKey(keyDate))
                {
                    SelectedDates.Remove(keyDate);
                }
            
                SelectedDates.Add(keyDate, valueInt);
              
            }
        }

        ViewState["Dates"] = SelectedDates;
    }

Add new custom webparts (Data Grid) to Enterprise Portal Quick Launch sidebar tutorial

This is step by step to create custom webparts and added it to Enterprise Portal (EP)
1. at Visual Studio, add new EP VS Project
Capture

2. Right click at the solution project, and click add new item, select Microsoft Dynamics Ax -> EP User Control with form and grid.
Capture

3. Go to AOT, add new data set , then add datasource, link it to your table , do compile and do INCREMENTAL CIL.
Capture

4. Go back to VS, refresh schema for the datasource, and select your newly created datasource
Capture

5. Make sure your column etc are inside axForm, fill the properties DataKeyNames,DataMember and DataSourcesID
Capture

6. at gridview, select datasource, and edit colomn, select field you want to show on the grid
Capture

7. right click on VS solution project, and choose “Add your_webParts to AOT”
Capture

8. go back to AOT, on Web -> Web Content -> Managed , find your webcontrol, right click on it, and Deploy to EP, then choose which module this webcontrol belongs to. After you deploy it , AX will create an URL Menu Item automatically at (Web -> Web Menu Items -> URLs) and Page Definition (Web -> Web Files -> Page Definition
Capture

9. If your menu is on submenu of the module, example Home\EmployeeServices\ManageTime , just change it on properties “Web Module” at Page Definition. and change the url on on web url properties
Capture
Capture

10. The next step is we want to add newly web parts to EP Quick Launch. Find the page you want to customize on “Web Modules” on AOT, on the properties, look at value of properties “Quick Launch”
Capture

10. Go to Web -> Web Menus , find the Quick Launch, and drag and drop the newly add url menu item to Quick Launch Menu, then deploy the EP from System Admin -> Setup -> Enterprise Portal -> Deployment
Capture

11. go to Web -> Web Modules . just right click and deploy on the module you customize.
Capture

12. Last, restart IIS for refreshing the EP with newest customize, with command prompt see post :
https://hellodax.com/2015/05/22/restart-iis-command-prompt/
Capture

13. Preview
Capture

Init field value from datasource , show on Enterprise Portal AXBoundField

on AOT dataset method init() :
Capture

X++ code on init method example:

public void init()
{
    super();
    DNLLeaveRequest.DNLEmplRecId  = DNLEmployeeTable::findByUserId().RecId;
    DNLLeaveRequest.LeaveDateFrom = today();
    DNLLeaveRequest.LeaveDateTo   = today();
}

or you can code it on Tables\initvalue()

XML code on visual studio for EP pages :

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="TIDDNLLeaveRequestEP.ascx.cs" Inherits="TIDDNLLeaveRequestEP" %>
<dynamics:AxDataSource ID="AxDataSource1" runat="server" 
    DataSetName="TIDLeaveRequestDS" ProviderView="DNLLeaveRequest" 
    oninitializeddatasetrun="AxDataSource1_InitializedDataSetRun">
</dynamics:AxDataSource>
<dynamics:AxForm ID="AxForm1" runat="server" DataSourceID="AxDataSource1" 
    DataKeyNames="RecId,Added,AdvanceUsed,BlankLeave,carryused,ContactPhone,createdBy,createdDateTime,dataAreaId,DNLEmplRecId,DNLLeaveTypeRecId,IdApproval,IdPersonSubstitute,LeaveDateFrom,LeaveDateTo,modifiedBy,modifiedDateTime,Remarks,RequestDate,State,StatusFlag,SubmittedBy,SubmittedDateTime,TableId,TotalLeaveDays,TravellingLocation,getEmplName**,GetEntitlement**,getExpiration**,getLeaveDesc**,mostRecentComment**,requiredActionDueDate**,titleFields**" 
    DataMember="DNLLeaveRequest_Current" AutoGenerateInsertButton="True" 
    DefaultMode="Insert" UpdateOnPostBack="true" oninit="AxForm1_Init"  >
    <dynamics:AxMultiColumn ID="AxMultiColumn1" runat="server">
    
 
    
 
        <dynamics:AxColumn ID="AxColumn2" runat="server">
            <dynamics:AxGroup ID="groupLeaveRequest" runat="server" FormID="AxForm1" 
                Caption="Leave Request" CaptionAlign="Top" HorizontalAlign="Left" 
                Width="300px">
                <Fields>
                    <dynamics:AxReferenceBoundField DataField="DNLEmplRecId" 
                        DataSet="TIDLeaveRequestDS" DataSetView="DNLLeaveRequest" 
                        SortExpression="DNLEmplRecId" Mandatory="True">
                    </dynamics:AxReferenceBoundField>
                    <dynamics:AxReferenceBoundField AutoPostBack="True" 
                        DataField="DNLLeaveTypeRecId" DataSet="TIDLeaveRequestDS" 
                        DataSetView="DNLLeaveRequest" SortExpression="DNLLeaveTypeRecId" 
                        Mandatory="True">
                    </dynamics:AxReferenceBoundField>
                    <dynamics:AxBoundField AutoPostBack="True" DataField="LeaveDateFrom" 
                        DataSet="TIDLeaveRequestDS" 
                        DataSetView="DNLLeaveRequest" SortExpression="LeaveDateFrom">
                    </dynamics:AxBoundField>
                    <dynamics:AxBoundField AutoPostBack="True" DataField="LeaveDateTo" 
                        DataSet="TIDLeaveRequestDS" 
                        DataSetView="DNLLeaveRequest" SortExpression="LeaveDateTo" 
                        Mandatory="True" >
                    </dynamics:AxBoundField>
                    <dynamics:AxBoundField AutoPostBack="True" DataField="Remarks" 
                        DataSet="TIDLeaveRequestDS" DataSetView="DNLLeaveRequest" 
                        SortExpression="Remarks">
                    </dynamics:AxBoundField>
                    <dynamics:AxBoundField AutoPostBack="True" DataField="TotalLeaveDays" 
                        DataSet="TIDLeaveRequestDS" DataSetView="DNLLeaveRequest" 
                        SortExpression="TotalLeaveDays">
                    </dynamics:AxBoundField>
                    <dynamics:AxBoundField DataField="GetEntitlement**" DataSet="TIDLeaveRequestDS" 
                        DataSetView="DNLLeaveRequest" ReadOnly="True">
                    </dynamics:AxBoundField>
                </Fields>
            </dynamics:AxGroup>
        </dynamics:AxColumn>
    </dynamics:AxMultiColumn>
</dynamics:AxForm>

C# code on visual studio for EP pages

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.DataVisualization.Charting;
using System.Web.UI.WebControls;
using Microsoft.Dynamics.Framework.Portal.UI.WebControls;
using Microsoft.Dynamics.Framework.Portal.UI.WebControls.WebParts;
using Microsoft.Dynamics.AX.Framework.Portal.Data;
using Microsoft.Dynamics.AX.Framework.Services.Client;
using Microsoft.Dynamics.Framework.BusinessConnector.Session;
using Microsoft.Dynamics.Framework.BusinessConnector.Adapter;
using Microsoft.Dynamics.Framework.Portal;
using Microsoft.Dynamics.Framework.BusinessConnector.Proxy;

public partial class TIDDNLLeaveRequestEP : System.Web.UI.UserControl
{
    DateTime dateFrom;
    DateTime dateTo;
    Int64 recIdLeaveType;
   
    

    #region customMethod
    private ISession AxSession
    {
        get 
        {
            AxBaseWebPart webPart = AxBaseWebPart.GetWebpart(this);
            return webPart == null ? null : webPart.Session;
        }
    }
    static AxBoundField GetField(DataControlFieldCollection fields, string name)
    {
        foreach (DataControlField field in fields)
        {
            // Is this the field being searched for?
            AxBoundField boundField = field as AxBoundField;
            if (boundField != null && String.Compare(boundField.DataField, name, true) == 0)
            {
                return boundField;
            }

        }
        // Nothing found, so return null.
        return null;
    }

    private DataSetViewRow CurrentRow
    {
        get
        {
            try
            {
                DataSetView dataSetView;
                dataSetView = this.AxDataSource1.GetDataSet().DataSetViews["DNLLeaveRequest"];
                return (dataSetView == null) ? null : dataSetView.GetCurrent();
            }
            catch (System.Exception)
            {
                return null;
            }
        }
    }

    #endregion customMethod

    protected void AxForm1_Init(object sender, EventArgs e)
    {
        //this.loadFirst();
        this.AxForm1.DefaultMode = DetailsViewMode.Insert;
        this.AxForm1.AutoGenerateInsertButton = true;

        AxBoundField axbfNoNPK;
        axbfNoNPK = GetField(groupLeaveRequest.Fields, "DNLEmplRecId");
        axbfNoNPK.ReadOnly = true; 
    }


    protected void Page_Load(object sender, EventArgs e)
    {

    }
 
   
    protected void AxDataSource1_InitializedDataSetRun(object sender, AxDataSourceDataSetEventArgs e)
    {
        e.DataSet.Init();
    }
}

preview :
Capture