Thursday, 29 March 2018

Deactivate Business Process Flow from Existing Records in Dynamics CRM

Overview:
In Dynamics CRM, when you deactivate the Business process flow in the system, it continues to show the Business process flow ribbon for existing records, since they are already attached to the process. This post will discuss different approaches to deactivate the process from existing records

Following are the three approaches used to deactivate/remove business process flow from existing records in Dynamics CRM
  1. Delete the business process flow
  2. Use Javascript to hide the ribbon
  3. Use SDK to remove business process flow association


Delete the business process flow
One of the easiest approach to remove the business process flow is to completely delete the Business process flow, however, doing so will permanently delete the process. It is strongly recommended to create a copy of it or take backup before deleting it

Use Javascript to hide the ribbon

Following script can be used on Form OnLoad event to hide the Business Process Flow from a particular form. However, the record will still contain reference of the business process and will continue to show the ribbon on other forms of the entity

Xrm.Page.ui.process.setVisible(false);

Use SDK to remove business process flow association

When a business process flow is created, it also creates an entity that stores the details of each instance of the process. Deleting a record in the entity removes the instance details for the corresponding record. The CRM Developer SDK can be used to create a Console Application and delete such records. Following is the code snippet that achieves this purpose

            //Create Service proxy object with CRM Organization details
            IOrganizationService _service;
            OrganizationServiceProxy proxyService;

            ClientCredentials credentials = new ClientCredentials();
            credentials.SupportInteractive = false;

            credentials.UserName.UserName = UserName;
            credentials.UserName.Password = Password;

            Uri organizationUri = new Uri(organizationUURL);
            proxyService = new OrganizationServiceProxy(organizationUri, null, credentials, null);
            _service = (IOrganizationService)proxyService;

            //Fetch records from which Business process flow needs to be dissociated in an entity using Query expression
            QueryExpression query = new QueryExpression("contact");
            query.ColumnSet = new ColumnSet(true);
            
            EntityCollection entityCollection = _service.RetrieveMultiple(query);
            
            //Loop through individual records and deactivate Business process for each of them
            foreach (Entity item in entityCollection.Entities)
            {
                try
                {                    
                    //Retrieve all active instances of the Business process, specifying the Entity name and Entity GUID
                    RetrieveProcessInstancesRequest currentProcessInstanceReq = new RetrieveProcessInstancesRequest
                    {
                        EntityId = item.Id,
                        EntityLogicalName = "contact"
                    };
                    RetrieveProcessInstancesResponse currentProcessInstanceResp = (RetrieveProcessInstancesResponse)_service.Execute(currentProcessInstanceReq);

                    //If active process instances exists, find the Process name of the respective instance and use it to delete each active instance
                    if (currentProcessInstanceReq != null && currentProcessInstanceResp.Processes != null && currentProcessInstanceResp.Processes.Entities.Count > 0)
                    {
                        //Loop through individual process instance
                        foreach (var activeProcessInstance in currentProcessInstanceResp.Processes.Entities)
                        {
                            //Confirm if Process Id Exist, if yes, get the Process details
                            if (activeProcessInstance.Attributes.Contains("processid"))
                            {
                                //Get the Process record details from the Workflow entity
                                //This entity contains details of all business process that exist in the system
                                Entity workflow = _service.Retrieve("workflow", (activeProcessInstance["processid"] as EntityReference).Id, new ColumnSet(true));

                                //Confirm if the Process name exists
                                //The Process name is the name of the Business Process Flow entity which stores instance details for each record
                                if (workflow != null && workflow.Attributes.Contains("uniquename"))
                                {                                
                                    //delete the instance details from the Business Process Flow Entity
                                    _service.Delete(workflow["uniquename"].ToString(), activeProcessInstance.Id);
                                }                                
                            }                            
                        }
                    }                    
                }
                catch (Exception ex)
                {
                    //records exception details
                }
            }




Tuesday, 27 March 2018

Update Product Images in Microsoft Dynamics AX 2012

Introduction:
Microsoft Dynamics AX 2012 contains a helper class that can be used to attach images to a product. The images can either be a location on file system or hosted somewhere on web. To upload a product's image using X++ in Dynamics Ax 2012, you can use the following code snippet

Code Snippet:
    EcoResProductImageManagement        ecoResProductImageManagement;
    RefRecId    productRecId;

    //EcoResProductImageManagement class is helper class for image management
    ecoResProductImageManagement = EcoResProductImageManagement::construct();
   
    //Assign respective Document Type Id, URL incase the image location is Web or File, if the iamge is stored in file system
    ecoResProductImageManagement.parmDocuTypeId('URL');
       
    //The image location is provided by a url path or file system path and then define the actual location of the image file
    ecoResProductImageManagement.parmIsUrlPath(true);
    ecoResProductImageManagement.parmImage("http://sampleimage.com/300442.JPG", '', EcoResProductImageUsage::External, true);
   
    //Details of the product to which image needs to be assigned
    ecoResProductImageManagement.parmRefRecId(productRecId);
    ecoResProductImageManagement.parmRefTableId(tablenum(InventTable));
    ecoResProductImageManagement.parmDataAreaId(curext()); 
   
    //execute the process, it will assign the image to the provided product
    ecoResProductImageManagement.submit();

Thursday, 28 December 2017

Filter Option Sets in Dynamics CRM using Javascript

Introduction:
Suppose you have an Option set field named "optionSetField" that needs to show different values when field "optionSetFilterField" value is 1, and when field "optionSetFilterField" value is 2, it needs to show different values. This requirement can easily be achieved using JavaScript

Solution:
Create a new Web resource as shown below





In the Text Editor, paste the following JavaScript

function filterOptionSets() {
    //get option set control and its options
    var optionsetControl = Xrm.Page.ui.controls.get("optionSetField");
    var options = optionsetControl.getAttribute().getOptions();

    //get the field value on which bases the option set needs to be filtered
    var filterValue = Xrm.Page.getAttribute("optionSetFilterField").getValue();

    //get current selected value of option set
    var currentValue = Xrm.Page.getAttribute("optionSetField").getValue();

    //based on the filterValue, clear option set and then add the required options
    if (filterValue = 1) {
        optionsetControl.clearOptions();
        optionsetControl.addOption(options[0]);
        optionsetControl.addOption(options[1]);
        optionsetControl.addOption(options[2]);
    }
    else if (filterValue = 2) {
        optionsetControl.clearOptions();
        optionsetControl.addOption(options[3]);
        optionsetControl.addOption(options[6]);
    }
    else if (filterValue = 3) {
        optionsetControl.clearOptions();
        optionsetControl.addOption(options[4]);
        optionsetControl.addOption(options[5]);
        optionsetControl.addOption(options[7]);
    } 
   
    //if a value in option set field was already selected, re-set the previous value
    if (currentValue != null)
        Xrm.Page.getAttribute("optionSetField").setValue(currentValue);
}

//this method needs to be configured on "optionSetFilterField" field OnChange event
function onFieldChange() {
    Xrm.Page.getAttribute("optionSetField").setValue(null);
    filterOptionSets();
}

//this method needs to be configured on Form OnLoad event
function onFormLoad() {
    filterOptionSets();
}


Now, navigate to the Form Properties on the form where Option set control needs to be filtered and add the above created Web resource






Now set the Event Handlers as shown in the following images





Save the form and Publish the customization to start using the functionality!

Wednesday, 27 December 2017

Difference between CRM On-prem and Online Organization Service Authentication

Microsoft Dynamics CRM Organization service uses a slightly different method for authentication for CRM online version as compared to CRM on-premise. The difference between them is highlighted below:

//client credentials object
ClientCredentials credentials = new ClientCredentials();
credentials.SupportInteractive = false;

//when using CRM online
credentials.UserName.UserName = "test@test.com";
credentials.UserName.Password = "test123$";

//when using CRM on-premise
credentials.Windows.ClientCredential = new System.Net.NetworkCredential("test", "test123$", "test");

//create Organization service object
OrganizationServiceProxy proxyService = new OrganizationServiceProxy(new Uri(OrganizationURL), null, credentials, null);

Monday, 25 December 2017

Get Metadata in Operations 365

Introduction:
In Dynamics AX 2012, to get the tables, fields, forms, menu-items, you can query UtilElements table. However, starting Operations 365, this support is deprecated, even though the table is there, but it is empty. Instead of it, Microsoft has introduced a new class library, Microsoft.Dynamics.Ax.Xpp.MetadataSupport, which helps you to get Metadata objects.


This post will help you in getting all fields as well as table relations of SalesTable

Code Snippet:

public static void main(Args _args)
{
    //get SalesTable table name
    str tableName = tableStr(SalesTable);
    
    //Call the MetadataSupport method, GetTable to get table details
    AxTable axTable = Microsoft.Dynamics.Ax.Xpp.MetadataSupport::GetTable(tableName);  
        
    if(axTable != null)
    {                 
        info("****Fields****");
        
        //get list of fields and use an enumerator to loop through all fields      
        var axTableFieldList = axTable.Fields;        
        System.Collections.IEnumerator axTableFieldsEnumerator = axTableFieldList.GetEnumerator();

        while (axTableFieldsEnumerator.moveNext())
        {
            AxTableField axTableField = axTableFieldsEnumerator.get_Current();

            info(axTableField.Name);
        }
                
        info("****Relations****");
        
        //get all table relations as Key value pair
        var keyValueRelations  = Microsoft.Dynamics.Ax.Xpp.MetadataSupport::GetTableRelations(tableName);       
        var relationsEnumr = keyValueRelations.GetEnumerator();

        while (relationsEnumr.MoveNext())
        {
            //the relations are stored in Constraints object
            AxTableRelation tableRelation =  relationsEnumr.Current;
            var constraints = tableRelation.Constraints;
            var constraintsEnmr = constraints.GetEnumerator();

            while (constraintsEnmr.MoveNext())
            {
                AxTableRelationConstraintField constraintField =  constraintsEnmr.Current;
                
                info(tableRelation.RelatedTable);
                info(constraintField.Field);
            }            
        }
    }
}

Open attached file from DocuRef in browser in Dynamics Operations 365

Introduction:
To open an attach file on a browser tab in Dynamics Operations 365, you can use the following code snippet

Code Snippet:
    //get Public URL of the attach file from the DocuRef Buffer
    str displayUrl = DocumentManagement::getAttachmentPublicUrl(docuRef);
   
    //initialize browser object and navigate
    Browser br = new Browser();
    br.navigate(displayUrl);

Sunday, 24 December 2017

Attach File from a URL in Dynamics Operations 365

Introduction:
Suppose you have a File URL and you want to attach the file stored on that URL against a record in Dynamics Operation 365. Below snippet will help you in uploading the file to server and creating a new record in DocuRef table

Code Snippet:
    System.Net.WebClient webClient = new System.Net.WebClient();
    System.IO.Stream fileStream;
    System.Byte[] fileByteArr;
    DocuRef docuRef;  
    str fileURL = "www.myfilelocation.com/testfile.pdf"; //the URL of the file location
      
    //Download the file on client as byte array
    fileByteArr = webClient.DownloadData(fileURL);

    //convert byte array into a memory stream
    fileStream = new System.IO.MemoryStream(fileByteArr);

    //Use DocumentManagement class to attach the file and create a new record
    docuref = DocumentManagement::attachFile(tableId, recordId, companyId, docuTypeId, fileStream, fileName, mimeType, fileName);