Missing HR Menu Items In Dynamics

Patrick Roth - Click for blog homepageSo you think you know all about menus?  I thought I did too until I resolved a case today.

The customer was having an issue with some HR menus.

For some users (including sa) when logged into some companies, some of their HR menus wouldn’t show.  The ones the user notes are Compensation Management & Reconcile.

Normally four menu items appear.  In the customers’ case, the highlighted items were not there.

Now experience tells me that the reason menu items wouldn’t show is because the user doesn’t have security to the form for that company.

That surely sounds reasonable as it explains the lack of menu items for some users but only in specific companies.

A couple flaws in that theory are that this happens for the sa user who is Poweruser and therefore has access to everything.  The other flaw in that theory is that the user can make a shortcut to the window and it opens successfully in any company.

As every Dexterity developer knows; the CreateDefaultMenuStructure script in Dynamics is the script that kicks off menu creation.  All the menus are added initially without checking security so that the menus load as quickly as possible.

So the first step was to capture a script.log of the menus being added in the HR Code.

Partial Script.log

 'CreateHRUtilitiesMenu', 2, 179, 0, 1, 1
    'AddCommandToMenu()', 0, 0, 1568, 36, 1, 414, 22035, 22002, 1, 2, ""
        'LoadMenuItem', 0, 1568, 36, 414, 22035, 22002, 0, 0, 1
            'ApplySettingsDlg_Increment of form [80]', 0
    'AddCommandToMenu()', 0, 0, 1568, 36, 2, 414, 22035, 22003, 1, 2, ""
        'LoadMenuItem', 0, 1568, 36, 414, 22035, 22003, 0, 0, 2
            'ApplySettingsDlg_Increment of form [80]', 0
    'AddCommandToMenu()', 0, 0, 1568, 36, 3, 414, 22035, 22032, 1, 2, ""
        'LoadMenuItem', 0, 1568, 36, 414, 22035, 22032, 0, 0, 3
            'ApplySettingsDlg_Increment of form [80]', 0
    'AddCommandToMenu()', 0, 0, 1568, 36, 4, 414, 22035, 22039, 1, 2, ""
        'LoadMenuItem', 0, 1568, 36, 414, 22035, 22039, 0, 0, 4
            'ApplySettingsDlg_Increment of form [80]', 0

These are the four menu items on the HR Utilities menu and we can see the code is running that should be adding them to the menu.

Using the PSTL Menu Inquiry Utility, we confirmed that the menu items were on the menu, just hidden and disabled.

After the menus are finished loading, a CleanUpMenus script is run in the background to hide and disable the menus that the user doesn't have access to by calling Command_HideAndDisable for that command.

So logic suggested to me that the CleanUpMenus script is coming behind and for some reason decides the user doesn't have access to these forms and hides and disables the menu item.

Looking at the script.log, we see:

Partial Script.log

 'CommandList_CleanUp()', 0, 151
    'UserHasAccess() of form syCmdSecurityObj', 0, 414, 22035, 22002, ""
        'ResourceAccess', 414, "Archive_Applicant", 2, 0, ""
    'UserHasAccess() of form syCmdSecurityObj', 0, 414, 22035, 22003, "sa"
        'ResourceAccess', 414, "Archive_Employee", 2, 0, ""
    'UserHasAccess() of form syCmdSecurityObj', 0, 414, 22035, 22032, "sa"
        'ResourceAccess', 414, "HRP_Compensation", 2, 0, ""
    'UserHasAccess() of form syCmdSecurityObj', 0, 414, 22035, 22039, "sa"
        'ResourceAccess', 414, "HRP_Synch", 2, 0, ""

The CommandList_CleanUp() script loops through the Utilities command list and checks each item by calling the UserHasAccess() function which then calls ResourceAccess procedure (which is essentially the Security procedure).

One of these must be coming back as "false" but we cannot tell which one since you cannot see "out" or function return variables in a script.log.

In order to get this information, I wrote a small chunk to monitor the calls and if the result was denied to log it to a text file.

We ran it on the customer system and reviewed the results.  There were multiple "false" returns logged - but none for HR.  They were all for modules the customer didn't use so that would be expected.

Puzzling and unexpected.  If this isn't the cause then something that runs after the CleanUpMenus must be doing it.  So I addded a trigger to check the menu state for Disabled and Visible for these specific items to run after the CleanUpMenus script was finished running.  And while there isn't any reason to do so, I also put a trigger to run before the CleanUpMenus script ran.  Just an extra line of code, why not?

We deployed this newer version and I was again surprised to see that commands were being modified event before the CleanUpMenus script was ran.

Knowing now that the command was disabled earlier, it had to be disabled before it was even added to the menu?  But where?

Back to the script.log to see what might be going on.  Looking "up" in the script.log I found this:

Code Example:

 'Open_Command_HR_Form'
    'Command_HR_FORM_PRE on form Command_HR'
        'HideAllCommandLists of form Command_HR'
        'HR_Commands_Globalization of form Command_HR'
        <snip>
        'ApplyUserDataAccessSecurity of form Command_HR'
            'HR_RestrictedAccessToEmployeeData()', 0
                'GetPref', 1012, 0
                'SQLPath', 414, 10, 22006, ""
            'Command_HideAndDisable', hrAccrualPeriodData
            'Command_HideAndDisable', Months In Step Start
            'Command_HideAndDisable', [Not Found]
            'Command_HideAndDisable', Create Time
            'Command_HideAndDisable', [Not Found]
        'Command_ShowAndEnable', Company Name, 1

Right as the HR command form opens, HR is doing a little hide and seek with menu items.  Of particular interest is the "Command_HideAndDisable" procedure running.  That is the GP procedure that is typically used to hide and disable commands the user doesn't have access to.

The names are wrong and two show as [Not Found] but it looks like we found the issue.  But why do they get hidden?

The answer lies in the HR_RestrictedAccessToEmployeeData() function.

Here is the check to see if the Employee Filters for Division or Department are enabled or not.  We see the function calling the GetPref procedure and looking for item 1012 which happens to be the Division filter.  If enabled, then it checks the HR_Emp_Filters table to see if ANY item is unmarked.  If so, then access to this menu item is denied.

The HR code didn't check for the Department filter since the Division one failed and access had been denied to the Division menu item. 

Now that we have the reason, the last thing left is to look in the Human Resources Preferences window to verify this was the issue.

Pulling up the Human Resources Window, the Division (and Department) filters were marked.  And for the user and company we looked at, the Access was not marked for a few of the items.  Because of that, it appears that HR ended up hiding five HR menu items.  These two we were looking at and three that the user hadn't noticed.

So after a few hours of poking around script logs and source code, it looks like this was a "setup issue" in the end.  I ran this by a few of the Payroll/HR team and they weren't aware that this would happen so I didn't feel too bad that I didn't know it either.  But I guess we did all learn something in the end and figuring out the issue was interesting even if it did take longer than I expected.

Best Regards,

Patrick Roth
Dev Support