2
votes

I'd like to use a plugin to fill the case entity form data. I'm new to working with c# and plugins, but I get the sense that this is more reliable than using javascript form scripting. If this is a bad assumption, please tell me.

My goal is to 1) get and check the security role of the user, 2) based on security role, auto-fill the "Customer" field with a generic, hard-coded value.

The "Customer" field on the case form is a "System Required" field. I currently have a plugin registered on Create of incident, pre-validation & synchronous. When I try to save without manually filling out the "Customer" field, I am unable to save and the UI points me to the fact that the field is not filled out yet.

Am I trying to do something that is not possible? Or should I just be using javascript and/or business rules for something like this?

4
So do you want the field to be populated as soon as the form is opened, or populated upon saving the form?Equalsk
It should populate as soon as the form is opened.Slappy White

4 Answers

1
votes
  1. Plugin is expensive. According to your need, a OnLoad JavaScript function is more appropriate.
  2. Regarding pre-validation stage, the validation is actually check against things like user privileges; field requirement level is checked on client side. So even a pre-validation plugin won't help user skip a required field.
  3. If plugin is your preference, you may set a default value to this field, and populate the correct value in the plugin.
1
votes

In order to do this through a plugin you may need to use RetrieveMultiple request for the roles that the user could have and then loop thru the results to assign the customer field value based on the role...

For example if you have your a fetchxml to get the roles you could use it in your plugin (you could use a QueryExpression as well instead of the fetchxml)...

string FetchXML =  
    @"<fetch mapping='logical' count='50' version='1.0'>
        <entity name='systemuser'>
            <attribute name='fullname' />
        <link-entity name='systemuserroles' from='systemuserid' to='systemuserid'>
        <link-entity name='role' from='roleid' to='roleid'>
            <attribute name='name' />
        </link-entity>
        </link-entity>
        </entity>
    </fetch>";

EntityCollection result = svcClient.OrganizationServiceProxy.RetrieveMultiple(new Microsoft.Xrm.Sdk.Query.FetchExpression(FetchXML));

// and then loop thru result
if (result != null)
{
    foreach (var item in result.Entities) {
        // ... do your stuff here
        // item.roleName is just an idea.. I haven't tested the code yet..
        Entity case = new Entity("case");

        if (item.roleName == "role 01") 
                contact.Attributes["customer_field"] = "hard-code value 01";
        if (item.roleName == "role 02") 
                contact.Attributes["customer_field"] = "hard-code value 02";

         ...
         ...
    }
}

For that particular scenario I think you can achieve it by using just javascript. If you decide to go that way here's an idea...

Attach a javascript function ( Ex. autoFillCustomer() ) to the onload event of the form that will validate the role name and based on the result it will set the hard-coded desired value.

function autoFillCustomer(){
        var roles = getAllUserRolesNames();
        var customerField = Xrm.Page.getAttribute("customer_field");

        for (var i = 0; i < roles.length; i++) {
            var roleName = roles[i];

            switch (roleName) {
                case "Role Name 01":                
                    if ( customerField!= null )
                        customerFieldsetValue("hard-coded avlue 01");

                    break;

                case "Role Name 021":                
                    if ( customerField!= null )
                        customerFieldsetValue("hard-coded avlue 02");

                    break;

                default;            
                    if ( customerField!= null )
                        customerFieldsetValue("default value if needed");
            }
        }
    }

And then.. on the other hand you may have another js webresource with the implementation of getAllUserRolesName() and add it to your form as well. (this is because you can use it on new future scenarios)

// Display Name: Get All User Roles Names
// Description:  Returns an array containing all the roles and teams currently assigned to a user
//               This will contain the name and not the guid which is what the standard getUserRoles() function does.
function getAllUserRolesNames() {
    var guid = "[A-z0-9]{8}-[A-z0-9]{4}-[A-z0-9]{4}-[A-z0-9]{4}-[A-z0-9]{12}";
    var serverUrl = Xrm.Page.context.getClientUrl();
    var userId = Xrm.Page.context.getUserId();
    userId = userId.match(guid);
    var teamQuery = "TeamMembershipSet?$select=TeamId&$filter=SystemUserId eq guid'" + userId + "'";
    var teamRoleQuery = "TeamRolesSet?$select=RoleId&$filter=";
    var roleQuery = "RoleSet?$select=Name&$filter=";
    var teams = makeRequest(serverUrl, teamQuery, 0);
    teamRoleQuery = composeQuery(teamRoleQuery, "TeamId", teams);
    var teamRoles = makeRequest(serverUrl, teamRoleQuery, 1);
    userRoles = Xrm.Page.context.getUserRoles();
    if (userRoles != null) {
        for (var i = 0; i < userRoles.length; i++) {
            teamRoles.push(userRoles[i].match(guid));
        }
    }
    roleQuery = composeQuery(roleQuery, "RoleId", teamRoles);
    var roles = makeRequest(serverUrl, roleQuery, 2);
    return roles;
}
function makeRequest(serverUrl, query, type) {
    var oDataEndpointUrl = serverUrl + "/XRMServices/2011/OrganizationData.svc/";
    oDataEndpointUrl += query;
    var service = GetRequestObject();
    if (service != null) {
        service.open("GET", oDataEndpointUrl, false);
        service.setRequestHeader("X-Requested-With", "XMLHttpRequest");
        service.setRequestHeader("Accept", "application/json, text/javascript, */*");
        service.send(null);
        var retrieved = $.parseJSON(service.responseText).d;
        var results = new Array();
        switch (type) {
            case 0:
            for (var i = 0; i < retrieved.results.length; i++) {
                results.push(retrieved.results[i].TeamId);
            }
            break;
            case 1:
            for (var i = 0; i < retrieved.results.length; i++) {
                results.push(retrieved.results[i].RoleId);
            }
            break;
            case 2:
            for (var i = 0; i < retrieved.results.length; i++) {
                if (results.indexOf(retrieved.results[i].Name) == -1) {
                    results.push(retrieved.results[i].Name);
                }
            }
            break;
        }
        return results;
    }
    return null;
}

function GetRequestObject() {
    if (window.XMLHttpRequest) {
        return new window.XMLHttpRequest;
    } else {
        try {
            return new ActiveXObject("MSXML2.XMLHTTP.3.0");
        } catch (ex) {
            return null;
        }
    }
}

function composeQuery(queryBase, attribute, items) {
    if (items != null) {
        for (var i = 0; i < items.length; i++) {
            if (i == 0) {
                queryBase += attribute + " eq (guid'" + items[i] + "')";
            } else {
                queryBase += " or " + attribute + " eq (guid'" + items[i] + "')";
            }
        }
    }
    return queryBase;
}
1
votes

EDIT

Arun has pointed out in the comments that the original question was about the Case (incident) entity and the Customer field for this entity is a mandatory field that cannot be modified to be optional.

This means my answer below isn't possible for the OP, but I'm leaving it here in case it helps anyone working on a different entity


Even though you've set the plugin to execute on pre-validation, this is still on the Create message pipeline, meaning that you have to hit the save button first. However you've set the field to be required which is why you're getting the validation message

If you want to persist with this approach I would make the following changes:

  1. Change the Customer field so that it is not mandatory
  2. Change the Customer field so that it is read-only
  3. Register your plugin as you have described (Create; pre-validate; synchronous) to update the InputParameters of the Incident based on your business requirements (i.e. based on the user's security role)

Plugins in this stage (pre-validate) means that the create message will be intercepted before it hits the CRM database and inject your changes into the data.

The advantage to doing it this way (using a plugin instead of javascript as suggested by other users) is that it will be applied to all CRM Forms

The disadvantage of this approach is that the user only sees the empty customer field until they click save. If they press Save and Close then they might not realise that the customer has been set at all. You can workaround this by moving this field to a section called "auto-completed" or "system-generated" so the user knows this is filled out automatically

0
votes

Plugin is the better choice. Set a dummy customer while creating incident and in pre create you can identify the actual customer & overwrite the dummy value.

http://mobile.crmsoftwareblog.com/2016/09/service-case-customer-field-not-required-dynamics-crm-2016/