Showing posts with label CRM. Show all posts
Showing posts with label CRM. Show all posts

Saturday, June 4, 2016

Clone a Solution - Microsoft Dynamics CRM 2016

Microsoft Dynamics CRM 2016 introduces a new feature Solution Cloning "Clone a Solution" - It creates a copy of solution with new version in which all patches released from base solution are merged.

You can only clone an unmanaged solution. If you clone an unmanaged base solution, it will create a new solution with higher version(Major & Minor) than base solution. And all the patches released from base solution will be merged into the cloned solution.

A cloned solution can be exported both Managed and Unmanaged

Import cloned solution in target system:

Import feature of clone solution is a bit different from normal solution, when you import a clone solution you will see a message - "This solution package contains an update for a solution that is already installed"

Current Version: 1.0.0.0
Version contained in the update: 1.1.0.0



Click on Next




Here you get option "Stage for Upgrade". If you select this option a holding solution will be created. It means you will see both base solution and upgraded solution. But the upgrade will not take place, to apply upgrade you need to click on "Apply Solution Upgrade" on Solution explorer form.

Maintain Customizations: Selecting this option will maintain any unmanaged customizations performed on components but also implies that some of the updates included in this solution will not take effect. 

Example: Suppose you have customized a field in default system solution and change its length from 50 to 100. If in case the cloned solution contains the same field with length 150. In this case maintain customization will retain the unmanaged customization i.e. field length will remain 100.

Overwrite Customizations: Selecting this option will overwrite any unmanaged customizations previously performed on components included in this solution. All updates included in this solution will take effect. From above example- In this case the field length will be updated to 150.

Clone a Patch: Refer Post

Thursday, June 2, 2016

Solution - Clone a Patch in Microsoft Dynamics CRM 2016

To perform and release customization in CRM from one system to another, usually we create "Solution", once done with customization(s) we export it either managed or unmanaged and deploy it to production.

Suppose a scenario where we have created a Solution (Solution-A), in that we have done some changes [added a new field say "new_field" length 50 for a entity] and deployed it to production.

Due to some reason now the requirement is to change the field length from 50 to 100. In earlier version to achieve it, either we directly update the field length in production or create a new solution (Solution-B) with all the changes and deploy it on production. 

To overcome this situation Microsoft Dynamics CRM 2016 introduces new feature "Clone a Patch". In which you can release fixes for the solution(s). In this scenario we can create Patch "Clone Patch" for Solution-A.

Benefits:
A. You can only create a patch Solution from a base Solution.
B. Patch solution can not be further patched.
C. There could be N no of patches from base Solution.
D. You can not install a patch if base solution is not available.
E. Patch solution version must have higher build and revision numbers and same major and minor version than base solution version.
F. Patches are only supported in CRM organizations of version 8.0 or later.
G. You can not update base solution, if it is patched.

Delete a Patch or Base Solution:

In the above example we have created two solutions (Solution-A and Solution-B). B is the patch for A and higher in version. To delete a base solution (Solution-A) 

Unmanaged : Then Solution-B must be deleted first then Solution-A

Managed: Solution-A can be deleted directly, Microsoft dynamics will remove all the patches in order from higher to lower version.

Thursday, April 21, 2016

Impersonate or Assign user in Microsoft Dynamics CRM


Impersonation is someone else working on your behalf to accomplish your task. In most integration scenarios with external applications non-interactive user account is used to impersonate an interactive user. 

Non-interactive account only works when "Act on behalf" privilege is assigned. Also it will execute within the security role boundary of Interactive user. So an interactive user have access to case entity create - user or team level and on other hand non-interactive have access  at Organization level. Then only user and team level access will be granted to non-interactive. Impersonation is best where an operation shall execute in the interactive user security role context.

The above approach does not guarantee that an operation will always get complete, suppose a scenario where case-create role revoked from the interactive user, then non-interactive user acting on behalf of interactive user will not be able to perform create operation.

If the requirement is that always a case get created in dynamics CRM, then we can configure non-interactive user account and grant organization level access to create a case and assign etc, once case get created  it can be assigned to any interactive user. The difference you see here is created by user will non-interactive & owner will be interactive when you assign. On the other hand if you impersonate then created by and owner both will be interactive.



Sunday, April 17, 2016

Plugin Error to Entity Microsoft Dynamics CRM

Plugin(s) are used to apply custom business logic within organization service's event execution pipeline. In event of any exception the execution of plugin code terminates along with core operation. If you have enabled the exception logging of plugin exception (Settings>>Administration>>System Setting>>Customization>>Enable logging to Plugin Trace log >> Exception), plugin exception will be logged in Plugin Trace log.


Plugin Trace Log is a centralized view for all the plugin exceptions. Tracing of plugin exception to custom entity is not possible without customization and configuration. In this post we will see an approach to log plugin exception(s) to custom entity.



Requirement is to write a plugin on contact create, that plugin will validate "First Name", if first name is left blank then contact should not get created and plugin will throw error and terminates the core operation. And this specific plugin error needs to logged into "ContactPluginError" entity.




In above process flow chat, a recursive workflow will be running in background that will be triggered in every X minutes, it will create a custom entity record recursively, on that custom entity there will be a post operation registered plugin which will read & delete error detail from Plug-in trace log and move it to custom entity.

A. Enable Plug-in exception tracing

Plugin errors will be logged to Plug-in trace log when enabled from system settings. 



B. Custom Entity "ContactPluginError"

Create custom entity to log plug-in error. This entity will appear in Setting area.



Add fields to store exception details.




Customize "ContactPluginError" form to display error details.


Save and Publish customizations.


C. Custom Entity "RecursiveMovePluginError"

RecursiveMovePluginError entity will be created by workflow, this entity will have a plugin registered on create which will read data from plugin trace log entity and move it to custom entity. 


Add a field to store the type (plugin name) whose error log needs to moved from plugin trace log to ContactPluginError entity.



Customize RecursiveMovePluginError form and add field type.



Save and Publish Customization.

D. Create Workflow

Create background on demand workflow, that needs to be first triggered manually, once triggered it will automatically executes every 2 minutes and create RecursiveMovePluginError entity record.



Set below properties on Create step of RecursiveMovePluginError record.


Save and activate the workflow.

D. Plugin

Create and register "TracePlugin" plugin that will be move exception from Plugin trace log to custom entity.





Register plugin Post operation - Async



E. Execute and Trigger Process

Create Contact record without first name, an exception will the thrown by plugin

System plug-in trace log will be having an entry for this plugin error.



Configure and Trigger workflow job to move errors to ContactPluginError entity. Create a record for RecursiveMovePluginError and specify plugin type that needs to be monitored and moved.


Trigger workflow





Now all the plugin error thrown by plugin type "ContactDataValidation.FirstNamePlugin" will be moved from plugin trace log to custom entity "ContactPluginError".





Summary:

"Plugintracelog" entity does not expose "Create" message to register any plugin. Therefore when any record is created in "Plugintracelog"  entity there is no way to directly fire any plugin and perform any action on that.

Recursive workflows are auto triggered, so whenever a new record "RecursiveMovePluginError " is created, registered plugin "TracePlugin"  on "RecursiveMovePluginError" entity create message will also execute, its task is to read all the matched types "ContactDataValidation" plugin logs and move it to custom entity "ContactPluginError". Since I have configured workflow with a timeout condition of 2 minutes, so 2 minutes delay is expected in move operation from "PluginTracelog" entity to  "ContactPluginError" entity.


For Reference:

Solution File:




Plugin Code:


Wednesday, March 30, 2016

Web API for Microsoft Dynamics CRM Online or internet facing deployments

What is Web API?
Web API is implementation of OData (Open Data Protocol) which is an OASIS standard that defines the best practice for building for building and consuming RESTful APIs.
Web API Authentication
- Javascript Web Resources: You don't need to specify authentication pattern if call is made from the CRM application.
- CRM On-Premise: When you use the Web API for on-premises deployments you must include the user’s network credentials.
- CRM Online or IFD: When you use the Web API for CRM Online or an on-premises Internet-facing deployment (IFD) you must use OAuth.

Steps to register your IFD or CUSTOM CRM application in Azure Active Directory 
Step 1: Login to Windows Azure portal
Step 2: Select your organization and click on applications


It will list down all the application configured in selected organization.

 

Step 3: To Add your CRM application click on Add button.


Step 4: Add an application my organization is developing
Step 5: Enter name of your application

  


*Note: Web application or WEB API is selected for web application and for desktop, console it should be native client application. Since our sample is native client application, hence selected “Native” option
Step 6: Click Next and add redirect URI


Step 7: Once application is added, we need to configure it


Step 8: Add Microsoft dynamics crm permission to this application





Step 9: Assign Delegate Permission to Microsoft Dynamics CRM

 



Step 10: Save the Changes


Step 11: Copy Client ID, that we will use in native client application


Console Application: This sample demonstration will use OAuth authentication to connect CRM Services and your application MyCRM registered in Azure active directory.

 

static void Main(string[] args)
        {
          
            // TODO Substitute your correct CRM root service address,
            string resource = "https://XXXX.api.crm.dynamics.com/";

            // TODO Substitute your app registration values that can be obtained after you
            // register the app in Active Directory on the Microsoft Azure portal.
            string clientId = "db329e3d-4d6d-4ef2-9195-bfad1daed689";
            string redirectUrl = "https://birlasoft22.api.crm.dynamics.com/";


            // Authenticate the registered application with Azure Active Directory.
            AuthenticationContext authContext =
                new AuthenticationContext("https://login.windows.net/common", false);
           
            AuthenticationResult result = authContext.AcquireToken(resource, clientId, new
                                                                   Uri(redirectUrl));
            RunAsync(result).Wait();
        }

        static async Task RunAsync(AuthenticationResult result)
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri("https://XXXX.api.crm.dynamics.com/");
                client.Timeout = new TimeSpan(0, 2, 0);  // 2 minutes
                client.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue("Bearer", result.AccessToken);
          
                HttpResponseMessage response = await client.GetAsync("api/data/v8.0/accounts");

                if (response.IsSuccessStatusCode)
                {
                    string s = await response.Content.ReadAsStringAsync();
                    Console.Write(s);
                }

                Console.ReadKey();
            }

        }

Make sure to add nuGet package “Active Directory Authentication Library” in your project.


Output
: Since we have not passed credentials in code, login window opens, here enter your credentials.


Once you sign-in JSON response will be returned.

 

Helpful Links:
https://azure.microsoft.com/en-us/documentation/articles/mobile-services-windows-store-dotnet-adal-sso-authentication/
https://azure.microsoft.com/en-us/documentation/articles/web-sites-dotnet-get-started/
https://msdn.microsoft.com/en-us/library/gg327838.aspx
https://msdn.microsoft.com/en-us/library/mt595798.aspx
https://msdn.microsoft.com/en-us/library/mt595799.aspx

Sunday, March 20, 2016

Round Robin Assignment Microsoft Dynamics CRM

What is Round Robin?

It is rotation of tasks through a group. A task could be a lead, case etc. Round Robin term frequently used when to assign tasks to team members. Leads and cases are commonly assigned through “Round Robin” implementation, but it can used for other entities too.

In this blog, I have discussed Round Robin assignment for leads in Microsoft Dynamics CRM, but the concept is same for other entities too.

In this example, we assign newly generated lead to a member of specific team (SalesTeam), instead of doing it manually we have done it through round robin. Here "SalesTeam" can have variable members, and team size  can change any time (member(s) can be added or deleted).

All this is achieved through a custom entity "counter", workflow and custom activity.

Custom Entity: "counter" entity will have integer value in a field "currentcounter" and 1:N relationship between "counter" and "lead" entity. This "currentcounter" will be pointer to a member of "SalesTeam" to whom new lead should get assigned. When "currentcounter" values reaches to threshold i.e. Count of Member of "SalesTeam", it will get reset. 

Custom Activity: It provides a user(member) entity record which belongs to "SalesTeam" based on "currentcounter" value of "counter" entity and also provides next "currentcounter" value calculated based on no of members in team ("SalesTeam").

Workflow: Assign lead to a user and update "currentcounter" value in "counter" entity by the value returned from custom activity.

Lead
Sales Team Member
CurentCounter
Remarks


1
“counter” and “lead” has 1:N relationship, a record has added in “counter” entity with “currentcounter” value set as 1, which is pointer to first member of “SalesTeam”
Lead1
USER1
2
Condition: CountofMembers(SalesTeam)!=currentcounter Action: INCREMENT currentcounter by 1
Lead2
USER2
3
Condition: CountofMembers(SalesTeam)==currentcounter Action: RESET currentcounter to 1
Lead3
USER3
1
Condition: CountofMembers(SalesTeam)!=currentcounter Action: INCREMENT currentcounter by 1
Lead4
USER1
2
Condition: CountofMembers(SalesTeam)!=currentcounter Action: INCREMENT currentcounter by 1
Lead5
USER2
3
One more member added to team (user4)
Condition: CountofMembers(SalesTeam)!=currentcounter Action: INCREMENT currentcounter by 1
Lead6
USER3
4
Condition: CountofMembers(SalesTeam)==currentcounter Action: RESET currentcounter to 1
Lead7
USER4
1
Condition: CountofMembers(SalesTeam)!=currentcounter Action: INCREMENT currentcounter by 1
Lead8
USER1
2
Two members are deleted from team (User2 and User3)
Condition: CountofMembers(SalesTeam)==currentcounter Action: RESET currentcounter to 1


Initially system will have one record inserted for counter entity and have value "1" for "currentcounter" field. Based on team size the "currentcounter" value will be updated.

*Note: Team name "SalesTeam" is hard coded  and case sensitive in this example. In case you are implementing it for a different team, change its name accordingly.

Implementation Round Robin

A. Customization

Create Custom Entity “Counter”



Add Field “currentcounter” to Custom Entity “counter”



Create 1:N relationship between “counter” and “lead” Entity



1.       Customize “lead” Entity Form




Add relational lookup to the lead entity form and change its visibility setting from field properties.



Save and Publish your changes, and add an entity record for counter entity




B. SDK - Custom Activity

Create custom activity and register it in Plug-in Registration Tool.


using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Workflow;
using System;
using System.Activities;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace POC.AssignCounter
{
    public sealed partial class GetUseronCounter : CodeActivity
    {

        protected override void Execute(CodeActivityContext executionContext)
        {
            try
            {
                IWorkflowContext context = executionContext.GetExtension<IWorkflowContext>();

                IOrganizationServiceFactory serviceFactory =
                  executionContext.GetExtension<IOrganizationServiceFactory>();

                IOrganizationService service =
                    serviceFactory.CreateOrganizationService(context.UserId);

                //Get entity from InArgument
                Entity counter = service.Retrieve("new_counter",
                   this.InputEntity.Get(executionContext).Id, new ColumnSet("new_currentcounter"));
                             
                if (counter.Contains("new_currentcounter"))
                {
                    //Fetch xml to get users of a team. Here one user will be returned per page and page position will be
                    //decided on current counter value.
                    string xmlquery = "<fetch version=\"1.0\"  mapping=\"logical\" distinct=\"true\" page=\"" + (int)counter["new_currentcounter"] + "\" count=\"1\" returntotalrecordcount=\"true\">" +
                                        "<entity name=\"systemuser\">" +
                                        "<attribute name=\"systemuserid\" />" +
                                        "<order attribute=\"fullname\" descending=\"false\" />" +
                                        "<link-entity name=\"teammembership\" from=\"systemuserid\" to=\"systemuserid\" visible=\"false\" intersect=\"true\">" +
                                        "<link-entity name=\"team\" from=\"teamid\" to=\"teamid\" alias=\"aa\">" +
                                        "<filter type=\"and\">" +
                                        "<condition attribute=\"name\" operator=\"eq\" value=\"SalesTeam\" />" +
                                        "</filter>" +
                                        "</link-entity>" +
                                        "</link-entity>" +
                                        "</entity>" +
                                        "</fetch>";

                    FetchExpression query = new FetchExpression(xmlquery);
                    EntityCollection users = service.RetrieveMultiple(query);

                    //Set out argument OutputEntity- first user entity of resultset.
                   this.OutputEntity.Set(executionContext, new EntityReference("systemuser", new Guid(users[0].Attributes["systemuserid"].ToString())));

                    int newcounter = 1;
                    //Counter value should not exceed to total member count of team, in case if it exceeds
                    //that means user has been deleted from team.
                    if ((int)counter["new_currentcounter"] >= users.TotalRecordCount)
                    {
                        //Set default value 1
                        newcounter = 1;                                          
                    }
                    else
                    {
                        //Increment by 1 current counter value
                        newcounter = (int)counter["new_currentcounter"] + 1;
                    }

                    //Set out argument OutputCounter- new counter value, that will be further
                    //used by workflow to update counter entity.
                    this.OutputCounter.Set(executionContext, newcounter);

                }            

            }
            catch (Exception ex)
            {
                throw new InvalidPluginExecutionException("GetUseronCounter ERROR>>>>>: " + ex.StackTrace.ToString(), ex);
            }
        }

        [RequiredArgument]
        [Input("Counter")]
        [ReferenceTarget("new_counter")]
        public InArgument<EntityReference> InputEntity { get; set; }

        [Output("user")]
        [ReferenceTarget("systemuser")]
        public OutArgument<EntityReference> OutputEntity { get; set; }

        [Output("NewCounter")]   
        public OutArgument<int> OutputCounter { get; set; }

    }
}


C. Workflow Creation
     

Create a synchronous workflow 




     When a new lead is created in system, this real time workflow will get triggered. And all the steps defined will execute in order.

Step 1: Set a counter value for newly created lead. A lead can be created from web forms or through SDK calls. We don't want user to select counter entity every time whenever a new lead is created in system. Also there is a relationship between counter and leads, therefore a counter record needs to be set for lead and round robin implementation.




Step 2: Custom activity will be called, it takes the counter entity assigned to the lead.







The above activity will be returning two parameter, one will be the user to whom leads should be assigned and the other will the next "currentcounter" value

Step 3
: Update the "counter" entity's  "currentcounter" field value by the updated value returned from Step2.




Step 4: Assign lead to the returned system user from custom workflow activity Step 2. 






Step 5
: Stop the workflow with "Success"


Step 6
: Save and Activate workflow.

Test - Implementation Round Robin

Create Sales Team and add members




Create leads in system it will be auto assigned to Sale’s team members.





You can add or delete members to team, round robin will assign leads to a member of team.