Referencing User Security Groups in UI Customization

I am sure there is a simple answer here, but I am still getting into the UI customization layer of Epicor and I have a lot to learn.

I am attempting to make certain fields/rows read only based on user security groups. I can create a row rule that disables the row, but I need to reference the GroupList from the UserFile based on the CallContextClientData value CurrentUserID for the rule.

Any help pointing me in the right direction on this would be much appreciated.

Michael

No need to do this, you can just use Field Security to easily set access to certain fields based on group.

Chris, thanks for chiming in.

Though I like the concept of field level security, my experience tells me that this approach may do more harm than good. I have found that there can be unintended consequences with field level security. Here is an example:

After upgrading to 10.1 we made the Labor Rate value on the Employee, Labor Detail and Job Operation records visible only to certain security groups so that floor personnel could not see the labor rate of other workers. What we found its that doing so caused the Burden Rate to set to 0 on labor transaction for any user with the restriction. It appears that the business object has the same restriction as the user. In this case, the labor rate reads as 0 to the user and to the business object, which multiplies it by the burden percentage and gets 0 in return. I certainly did not expect this result, but I suppose it makes sense. I did report it to Epicor Support–I believe they indicated it is working as designed.

In this case, I would be adding the field level security to many fields on several tables. What happens, for instance, if a checkbox field that the business object sets to true on record creation finds that it is read only? Ugh!

Also, using field level security would make the fields read only across the entire application. Though that may be OK, I imagine there may be exceptions.

I think this leaves me where I started–row rules that reference user security groups.

Michael

1 Like

One option that some use is to create a customization for your read-only group and just remove the fields from that form.

Another option is to create a BPM testing a field change from any to any and prevent those if the user belongs to a group. The advantage here is you’re protecting from all user interfaces (Client, REST, etc.)

Mark W.

Mark, I appreciate your thoughts on this.

I am trying to avoid the multiple customization route–that was plan A.

My target application is Part Entry. We have decided that the Engineering group should be responsible for maintaining everything in Part Entry except what is in/under the Sites tab. The Sites tab would be the domain of the planners. So, I could have a customization without the Site tab for the Engineers–that works. Then, I suppose that I could eliminate all tabs except the Part Detail tab and the Site tab and disable the Part row for the Planner version. The thing I am not crazy about is that we loose context–it’s like having a tracker with much of the data missing. Engineers would not have access to Site settings and Planners would not have access to say, revision, information. Not a huge deal–they could have the part tracker open at the same time to see what is missing–but a bit clunky. I realize that I may just have to give up my aesthetic bias on this.

The BPM idea is also interesting, though it would involve over a hundred fields over 6 or 7 tables. Probably doable, though performance may prove to be an issue. And though the universality provided by BPMs might be a benefit, there might be exceptions such as certain updatable dashboards with some fields. I would need to give this another look–probably worthwhile.

The Row Rule method just seems more elegant. There are roughly 8 different rows that that the Engineers would be able to edit and 3 or so for the Planners. There is a Row Action that disables the entire row when the Row Rule produces a true result. We have access to the CurrentUserID and one would think that it shouldn’t be too difficult to query the database and get the value of the GroupList to see if it contains the relevant security groups.

Thanks again for your input.

Michael

Here’s my code for looking up whether a user belongs to a security group in form customisation:

private bool UserBelongsToProdSupGroup()
	{
	
	    EpiDataView edv = (EpiDataView) this.oTrans.EpiDataViews["CallContextClientData"];
	    string userID = edv.dataView[0]["CurrentUserId"].ToString();
	
	    try
	    {   
	        UserFileAdapter adapterUserFile = new UserFileAdapter(this.oTrans);
	
	        adapterUserFile.BOConnect();
	        bool result = adapterUserFile.GetByID(userID); 
			
	        if (result)
	        {
	            if (adapterUserFile.UserFileData.UserFile[0]["GroupList"].ToString().IndexOf("_PR_Sup") != -1)
	            {
		            //MessageBox.Show("User belongs to ProdSup Group");    
					return true; 
	            }
	        }
	        
	        adapterUserFile.Dispose();
	    }
	    catch (System.Exception ex)
	    {
	        ExceptionBox.Show(ex);
	    }
		
		return false;

	}
7 Likes

Hi Chris,

I have just started working on Epicor, can you please tell me how we can use field level security for giving access of fields to particular group of users.

Have a look at the user guides: Sign In

image

Mark,

Thanks so much for your reply–it is pointing me in the right direction and I very much appreciate it. I do need a bit more assistance as I am a novice with C# and Epicor (as you will see).

Remember that I am attempting to apply the rules to multiple rows. To begin with, I have applied the rule to two rows as follows:

private void CreateRowRule_Part()
{
	// Description: Part
	// **** begin autogenerated code ****
	RuleAction disabledPart_RowAction = RuleAction.AddRowSettings(this.oTrans, "Part", false, SettingStyle.Disabled);
	RuleAction[] ruleActions = new RuleAction[] {
			disabledPart_RowAction};

	// Create RowRule and add to the EpiDataView.
	RowRule rrCreateRowRule_Part = new RowRule("Part.PartNum", new RowRuleConditionDelegate2(this.Engineer_CustomRuleCondition), "Part.PartNum", ruleActions);
	((EpiDataView)(this.oTrans.EpiDataViews["Part"])).AddRowRule(rrCreateRowRule_Part);
	// **** end autogenerated code ****
}

private void CreateRowRule_PartRev()
{
	// Description: PartRev
	// **** begin autogenerated code ****
	RuleAction disabledPartRev_RowAction = RuleAction.AddRowSettings(this.oTrans, "PartRev", false, SettingStyle.Disabled);
	RuleAction[] ruleActions = new RuleAction[] {
			disabledPartRev_RowAction};

	// Create RowRule and add to the EpiDataView.
	RowRule rrCreateRowRule_PartRev = new RowRule("Part.PartNum", new RowRuleConditionDelegate2(this.Engineer_CustomRuleCondition), "Part.PartNum", ruleActions);
	((EpiDataView)(this.oTrans.EpiDataViews["PartRev"])).AddRowRule(rrCreateRowRule_PartRev);
	// **** end autogenerated code ****
}

private bool Engineer_CustomRuleCondition(Ice.Lib.ExtendedProps.RowRuleDelegateArgs args)
{

    EpiDataView edv = (EpiDataView) this.oTrans.EpiDataViews["CallContextClientData"];
    string userID = edv.dataView[0]["CurrentUserId"].ToString();

    try
    {   
        UserFileAdapter adapterUserFile = new UserFileAdapter(this.oTrans);

        adapterUserFile.BOConnect();
        bool result = adapterUserFile.GetByID(userID); 
		
        if (result)
        {
            if (adapterUserFile.UserFileData.UserFile[0]["GroupList"].ToString().IndexOf("Engineering 1") != -1)
            {
	            MessageBox.Show("User belongs to Engineering 1 Group");    
				return false; 
            }
        }
        
        adapterUserFile.Dispose();
    }
    catch (System.Exception ex)
    {
        ExceptionBox.Show(ex);
    }
	
	MessageBox.Show("User does NOT belong to Engineering 1 Group");
	return true;

}

While this works as needed, it seems to be a bit sloppy and inefficient.

I say its sloppy, because in creating my row rules I reference the Part.PartNum value to be equal to itself as part of the rule. I don’t really need that as I can rely solely on the return value of the delegate. [I am following the example created by the Rule Wizard, which requires at least one conditional value.]

It is inefficient as the MessageBox appears 4 times for every Part Row and 2 or three times (I lost count) for every PartRev row. I just need it to evaluate once per Part row–actually, once per session, but it is my understanding that row rules fire on row change.

Finally, rather than have the Delegate and the Row Rule use a boolean value, is it possible to use a string? I’d have the Delegate produce a concatenated string based on one or more security groups i.e. “~Engineer~Planner” then have the row rules evaluate the existence of substrings in the returned value.

Any help on this would also be appreciated.

Michael