Pages

Wednesday, November 13, 2013

AX Retail 2012 R2: Create New POS Form with Editable Quantity



Last blog, I explained about how to create new Retail POS transactions.  Now, I will show creating new POS form.  

Noted:  This customization is based on AX 2012 R2 + CU6 


Example from Tender Declaration 

With the requirement to have a new POS form for entering planned order quantity, It's been long time since I was .NET developer.  And because there's very short time, the only one way is, copying features from existing form. ;-)  

So, I found out that Tender Declaration is a good one having grid and editable quantity. 



This form is in "Retail SDK\POS Plug-ins\Services\Dialog\WinFormsTouch".    

Tip: How did I find this form?  Easily, search for "TenderDeclaration" in all files of entire solution.  That's the way I always use when develop POS.    



Noted:  I just mention this for guys who just get to know Retail POS development.  Not only install Retail POS SDK from AX Setup, you need to have DevExpress Winforms license. And once you first buy, you won't see the WinForms installer for MS Dynamics AX 2012 R2 (v2011.2.111) in the Download Manager, you need to request from the DevExpress Support Center.  


  

Where should the new form be?

Since only small form, I will simply create it in BlankOperations project.  You may decide to have a new project if there're too many objects and better have its default namespace under "Microsoft.Dynamics.Retail.Pos".  

Under BlankOperations, add new item "DevExpress Form v11.2"



In behind code, set the form to inherit from "LSRetailPosis.POSProcesses.frmTouchBase", to have a blank form as Retail POS pattern. 

namespace Microsoft.Dynamics.Retail.Pos.BlankOperations.WinFormsTouch
{
    public partial class POSTestForm : LSRetailPosis.POSProcesses.frmTouchBase
    {
        .....

Before design grid, you better drag TableLayoutPanel to form first, then you can have all object's size showing as percentage.  Set the Dock properties; Header = Top, TableLayoutPanel = Fill.     



Drag XtraGrid control to TableLayoutPanel, set position in the table by Column, ColumnSpan, Row, RowSpan as you want.  


     
I prefer to design grid columns by "Run Designer".  Reference field name is case-sensitive from Grid DataSource field.  




Grid DataSource

If grid is only for viewing, Grid DataSource can be a simple DataTable (System.Data.DataTable).   But this form is different because needs to be filled the quantity.  So, we will bind Grid DataSource using ViewModel sealed class.  See example in frmTenderCount.cs --> TenderViewModel: -

    /// <summary>
    /// Model of tender row for grid to bind to
    /// </summary>
    sealed internal class TenderViewModel
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="TenderViewModel"/> class.
        /// </summary>
        public TenderViewModel()
        {
            this.Enabled = true;
        }

        /// <summary>
        /// Gets or sets the type of the tender operation.
        /// </summary>
        /// <value>
        /// The type of the tender operation.
        /// </value>
        public PosisOperations TenderOperationType
        {
            get;
            set;
        }
        ....... 

Declare variable for Grid DataSource as list of ViewModel.  Load data from DataTable to the ViewModel and add to the list.  Then, bind the ViewModel list to GridControl. 

    private System.Data.DataTable yourDataTable;
    System.Collections.Generic.List<YourViewModel> gridSource;

    private void loadData()
        {
            try
            {
                yourDataTable = <<Function to get DataTable>>
                gridSource = new System.Collections.Generic.List<YourViewModel>();

                foreach (DataRow row in yourDataTable.Rows)
                {
                    YourViewModel viewRow = new YourViewModel()
                    {
                        ITEMID = (string)row["ITEMID"],
                        ITEMNAME = (string)row["ITEMNAME"],
                        COLOR = (string)row["COLOR"],
                        CONFIG = (string)row["CONFIG"],
                        ORDEREDQTY = (decimal)row["ORDEREDQTY"]),
                        UNITID = (string)row["UNITID"]
                    };
                    gridSource.Add(viewRow); 
                }


                grResult.DataSource = gridSource;
                ...... 


Entering quantity in Grid

Because Retail POS is touchscreen based.  It's not convenience to input data using keyboard.  We need to draw Quantity cell using event handler "CustomDrawCell".  See example: -

        private void gvTenders_CustomDrawCell(object sender, DevExpress.XtraGrid.Views.Base.RowCellCustomDrawEventArgs e)
        {
            string column = e.Column.FieldName;

            if (column == COLQTY || column == COLTOTAL)
            {
                // Determine the tender type
                PosisOperations operationTenderType = gridSource[e.RowHandle].TenderOperationType;

                // Draw the calculator icon in the quantity column if the tender type is not cash or currency
                bool drawIcon = (column == COLQTY) && (operationTenderType != PosisOperations.PayCash) && (operationTenderType != PosisOperations.PayCurrency);

                e.Appearance.FillRectangle(e.Cache, e.Bounds);
                DrawButton(e.Cache, e.Bounds, gridTenders.LookAndFeel.ActiveLookAndFeel.ActiveStyle, e.Appearance, GetButtonState(e.RowHandle, column), e.DisplayText, drawIcon);

                e.Handled = true;
            }

        }

Create event handler; KeyUp, MouseDown for controlling grid column click: -

        private void gvTenders_KeyUp(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Space)
            {
                switch (gvTenders.FocusedColumn.FieldName)
                {
                    case COLQTY:
                        OnQtyButtonClick(gvTenders.FocusedRowHandle);
                        break;
                    case COLTOTAL:
                        OnTotalButtonClick(gvTenders.FocusedRowHandle);
                        break;
                    default:
                        break;
                }
            }

        }

        private void gvTenders_MouseUp(object sender, MouseEventArgs e)
        {
            if (QtyPressedRowHandle != GridControl.InvalidRowHandle)
            {
                OnQtyButtonClick(QtyPressedRowHandle);
                QtyPressedRowHandle = GridControl.InvalidRowHandle;
            }

            if (TotalPressedRowHandle != GridControl.InvalidRowHandle)
            {
                OnTotalButtonClick(TotalPressedRowHandle);
                TotalPressedRowHandle = GridControl.InvalidRowHandle;
            }
        }


Input the quantity using frmInputNumpad dialog.  Then, update inputted quantity to Grid DataSource (ViewModel).  You can also modify the dialog, e.g. Integer+Positive --> set dialog EntryTypes properties as NumpadEntryTypes.IntegerPositive. 



        private void OnTotalButtonClick(int rowHandle)
        {
            if (!gridSource[rowHandle].Enabled)
            {
                return;
            }

            using (frmInputNumpad inputDialog = new frmInputNumpad())
            {
                inputDialog.EntryTypes = NumpadEntryTypes.Price;
                inputDialog.PromptText = LSRetailPosis.ApplicationLocalizer.Language.Translate(1443);
                inputDialog.CurrencyCode = gridSource[rowHandle].Currency;

                // The input dialog should allow negative values if it is a tender declaration
                inputDialog.AllowNegativeValues = (this.transaction.TransactionType == TenderCountTransaction.TypeOfTransaction.TenderDeclaration);
                inputDialog.EntryTypes = NumpadEntryTypes.IntegerPositive;

                LSRetailPosis.POSProcesses.POSFormsManager.ShowPOSForm(inputDialog);

                if (inputDialog.DialogResult == DialogResult.OK)
                {
                    decimal subTotal;
                    if (decimal.TryParse(inputDialog.InputText, out subTotal))
                    {
                        gridSource[rowHandle].Total = subTotal;
                        UpdateTotalAmount();

                        //Reseting the data source of denomination grid since user has altered the total amount
                        denominationDataSources[rowHandle] = null;
                    }
                }
            }
        }

Save Data from ViewModel list

When save data from ViewModel List to Store DB, we will loop it to ViewModel variable using "FindAll".  
                    
          List<TenderViewModel> cashTenders = gridSource.FindAll(tender => tender.TenderOperationType == PosisOperations.PayCash);

          foreach (TenderViewModel cashCount in cashTenders)
          {
              .....

View form using Blank Operation button

Create a Blank Operation button for calling the form.  Giving Operation number as you want.      



        /// <summary>

        /// Displays an alert message according operation id passed.

        /// </summary>

        /// <param name="operationInfo"></param>

        /// <param name="posTransaction"></param>        

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Grandfather")]

        public void BlankOperation(IBlankOperationInfo operationInfo, IPosTransaction posTransaction)
        {
            // This country check can be removed when customizing the BlankOperations service.
            if (Functions.CountryRegion == SupportedCountryRegion.BR || Functions.CountryRegion == SupportedCountryRegion.HU)
            {
                return;
            }


            switch ((operationInfo.OperationId).ToUpperInvariant().Replace(" ", string.Empty))
            {
                #region Planned Order
                case "CREATEORDER":  //Create Planned Order
                    CreateOrder(operationInfo, true, Application);
                    break;
                default:
                    //default, just echo the operation number and parameter value 
                    break;
            }
        }

        private void CreateOrder(IBlankOperationInfo operationInfo, bool allowBlank, IApplication application)
        {
            using (WinFormsTouch.POSFormTest searchDialog = new WinFormsTouch.POSFormTest())
            {
                bool inputValid;
                do
                {
                    inputValid = true;

                    searchDialog.Application = application; 
                    application.ApplicationFramework.POSShowForm(searchDialog);

                    // Quit if cancel is pressed...
                    if (searchDialog.DialogResult == System.Windows.Forms.DialogResult.Cancel && allowBlank)
                    {
                        return;
                    }
                    else if (searchDialog.DialogResult == System.Windows.Forms.DialogResult.OK)
                    {
                        .....
                    }
                    else if (searchDialog.DialogResult == DialogResult.Cancel && !allowBlank)
                    {
                        inputValid = false;
                    }
                } while (!inputValid);
            }
        }

After compile BlankOperations.dll and replace it in Retail POS services directory (C:\Program Files (x86)\Microsoft Dynamics AX\60\Retail POS\Services).  Run Retail POS and click the new Blank Operation button.  You will see the new form with grid and editable quantity. 

  

11 comments:

  1. Thanks for giving this information. You have made it so easy to understand.

    ReplyDelete
  2. Really the best informative information about POS Form, its very helpful.

    Pos Solutions
    Cash Registers

    ReplyDelete
  3. Its good and informative post, gives a lot of knowledge. retail epos i would like to see more from you....

    ReplyDelete
  4. Thanks for great information you write it very clean. epos I am very lucky to get this tips from you.

    ReplyDelete
  5. Thanks for sharing this useful information epos regarding There is a very good information on your blog.

    ReplyDelete
  6. Fantastic website. epos system Lots of useful information here. thank you in your effort!

    ReplyDelete
  7. Thanks for sharing bioinformatic stuff and really enjoyed to read it. i saw a website related on Free POS, Ipod POS and hope that your reader will enjoyed to read it.

    ReplyDelete
  8. Boost Your Sales With Our Takeaway EPOS Software.Fast EPOS System for Takeaway Businesses takeaway epos
    takeaway epos software
    epos software for takeaway
    takeaway epos

    ReplyDelete
  9. This is such an amazing article, the blogger have so much knowledge about this topic kindly write more about on point of sale system in Pakistanand point of sale in Pakistan

    ReplyDelete
  10. This is the Great Post. epos Thanks for this wonderful post share with us...

    ReplyDelete