AX for Retail 2012: Adding manager approval to custom operations

Edit (2013-10-08):  The code snippet for 2012 R2 is slightly different.  See the update below.

There are a number of places in AX for Retail POS where manager approval is needed to perform a task.  You may have noticed one of these when trying to exit the application.  You can tell that it’s a manager login by the slightly different appearance:

 

1   2

         

As a developer, there may be other areas in the application that you may want to ask for manager approval.  This could be your own procedure (a blank operation) or maybe an existing procedure that is in a customizable plug-in.

Here is one example of functionality that doesn’t have manager approval but could probably use it:  maximum discount.  Suppose you have your non-managers set up with a maximum line discount of 5%:

3

 

If the user attempts to add a larger discount, this message is shown (as expected):

4

 

However, there is no great option at this point if we want to give the customer a larger discount.  If a manager has a higher limit, the workaround is to suspend the transaction, have the manager log in and resume the transaction, add the discount, re-suspend the transaction, and then have the cashier resume the transaction with the new discount applied.  It would be better if the POS could prompt the user for manager credentials to override.

The logic that enforces the maximum discount just happens to be in the Discount service (plug-in) which means that it is customizable.  There are four methods that enforce the various discounts:  AuthorizeLineDiscountAmount(), AuthorizeLineDiscountPercent(), AuthorizeTotalDiscountAmount(), and AuthorizeTotalDiscountPercent().  The logic is similar in each;  I’ll use AuthorizeLineDiscountPercent() for this example.

The code snippet blow shows the existing logic.  Note how the percentage entered is compared to the max percentage for the user; if it is greater, the message is given and the authorization fails:

  1: if (discountItem.Percentage > 100m || discountItem.Percentage > maximumDiscountPct)
  2: {
  3:     string message = string.Format(LSRetailPosis.ApplicationLocalizer.Language.Translate(3183), maximumDiscountPct.ToString("n2")); 
  4:     //The discount percentage is to high. The limit is xxx %.
  5:     using (frmMessage dialog = new frmMessage(message, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information))
  6:     {
  7:         this.Application.ApplicationFramework.POSShowForm(dialog);
  8:     }
  9: 
 10:     returnValue = false;
 11: }
 12:

 

Instead of just failing, we could alternatively call the manager access logon window instead.  Since this window gets called frequently in the product, it is designed to be simple to call.  We can add this into the existing logic pretty easily:

2012 R1 version:

  1: if (discountItem.Percentage > 100m || discountItem.Percentage > maximumDiscountPct)
  2: {
  3:     using (LSRetailPosis.POSProcesses.WinFormsTouch.frmManagerAccess frmManAcc 
  4:                 = new LSRetailPosis.POSProcesses.WinFormsTouch.frmManagerAccess(0))
  5:     {
  6:         LSRetailPosis.POSProcesses.POSFormsManager.ShowPOSForm(frmManAcc);
  7: 
  8:         if (frmManAcc.DialogResult == DialogResult.OK)
  9:         {
 10:             // Manager auth was successful
 11:             returnValue = true;
 12:         }
 13:         else
 14:         {
 15:             // Failed.  Use original logic
 16:             string message = string.Format(LSRetailPosis.ApplicationLocalizer.Language.Translate(3183), maximumDiscountPct.ToString("n2")); 
 17:             //The discount percentage is to high. The limit is xxx %.
 18:             using (frmMessage dialog = new frmMessage(message, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information))
 19:             {
 20:                 this.Application.ApplicationFramework.POSShowForm(dialog);
 21:             }
 22:             returnValue = false;
 23:         }
 24:     }
 25: }
 26:

Note how the frmManagerAccess object does most of the work for us.  We create an instance of the object with a zero for the operation:  instead of asking about a specific operation (void transaction, X-report printing, etc.) we’re just asking if the user has the Manager privileges permission.  After that we send the object to the ShowPOSForm() method and then check the resulting DialogResult property.  If the manager logon was successful and the resulting user indeed had manager privileges, the DialogResult is “OK” and we can go ahead an authorize the larger discount.  Otherwise, we just show the original message and disallow the discount.

Note that this does not actually check the manager’s discount limit.  A limitation of the frmManagerAccess object is that it does not expose the operator ID that was used to log on so there is no way of knowing which manager approved the discount.  But there may be other ways for you to fine-tune the limits.

2012 R2 Version:

using Microsoft.Dynamics.Retail.Notification.Contracts;
using Microsoft.Practices.Prism.Interactivity.InteractionRequest;
...
else if (lineDiscountItem.Percentage > 100m || lineDiscountItem.Percentage > maximumDiscountPct)
{

    ManagerAccessConfirmation managerAccessInteraction = new ManagerAccessConfirmation();
    InteractionRequestedEventArgs request = new InteractionRequestedEventArgs(managerAccessInteraction, () => { });
    Application.Services.Interaction.InteractionRequest(request);

    if (managerAccessInteraction.Confirmed)
    {
        // Manager auth was successful
        returnValue = true;
    }
    else
    {
        string message = string.Format(LSRetailPosis.ApplicationLocalizer.Language.Translate(3183), maximumDiscountPct.ToString("n2")); //The discount percentage is to high. The limit is xxx %.
        using (frmMessage dialog = new frmMessage(message, System.Windows.Forms.MessageBoxButtons.OK, System.Windows.Forms.MessageBoxIcon.Information))
        {
            this.Application.ApplicationFramework.POSShowForm(dialog);
        }

        returnValue = false;
    }

}

You will also need to add references to the Microsoft.Dynamics.Retail.Notification.Contracts and Microsoft.Practices.Prism.Interactivity assemblies.

Note that in 2012 R2 the Manager Access Form is now in the InteractionDefaults service so you can pretty it up a bit if you need to.

There are probably many other places that you could add a manager challenge in your customizations.  Feel free to let the community know your ideas by using the comments section below.

Note:  This article was written with AX for Retail 2012 in mind but I believe a similar technique would work in 2009 as well.