Showing posts with label msdyn365. Show all posts
Showing posts with label msdyn365. Show all posts

July 15, 2018

Say No to local Option sets (but pay attention in PowerApps)

The use of global option sets in Dynamics is normally considered a best practice, the obvious advantage is the ability to reuse the same list of options in multiple fields inside the same entity or a different one.
Dynamics still allows the creation of local option sets but from version v9 there is another difference between Local and Global option sets: the External Value property:

This property is available only on Global option sets, when you create a local option this new property is missing:

External Value is intended for Virtual Entities, I understand it's not an everyday situation to deal with it, but this gives you another reason to avoid local option sets.

Today we live in the world of CDS and PowerApps, but the choice between local and global option sets is also inside the Canvas mode. If before the creation of fields was handled generally by System Administrators (in what we call today model-driven apps), today a larger set of user can face this dilemma.
Microsoft published a detailed page regarding option sets inside PowerApps here:
Create an Option set
So make your users aware of the "Option sets" inside the navigation pane:
Although I agree with the warning written by Microsoft

"Local option sets can only be used by the entity and field they are created against, and cannot be reused on other entities. This approach is only recommended for advanced users that a specific need for a local option set."

teach your users that the options inside a global option set will be available in all the fields using that option set, so if you add or remove options this will affect also other fields created by different users based on the existing option set.

November 9, 2017

Catch the Revise Quote message inside a Plugin

A user in Dynamics CRM/365 can revise a Quote, it's a process where the old quote is closed and a new quote is created in draft state and ready to be updated.
Dynamics doesn't support a specific message that we can intercept (in order to register a plugin step) when a quote is revised, however one of the possible ways is to deal with the Create message.

When quote are revised the integer (Whole Number) field revisionnumber is increased, this field is managed by the platform as it is not valid for Create or for Update according to its Metadata.
So when a new quote is created the revision number is 0, and the revised quotes will have 1,2,3...
Fetching the quote with the previous revision number is not a clever method to retrieve the revised quote. For example if we have an original quote (0) in Closed state and a revised quote (1) in Closed state too (because we cancelled), we are able to revise the original quote (0) and the new quote will have 2 as revision number.

My friend Daryl LaBar (@ddlabar) suggested to check the ParentContext property inside the plugin in order to access to the ReviseQuote message that initialized the create message.
In the end I wrote a plugin (registered on the Create message, Pre-Operation, Synchronous) to check the Parent Context and get the reference of the revised quote. Here the code:
public void Execute(IServiceProvider serviceProvider)
{
  try
  {
    IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

    if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
    {
      Entity currentQuote = (Entity)context.InputParameters["Target"];
      if (currentQuote.LogicalName != "quote") { return; }
      // check the revision number first
      int revisionNumber = currentQuote.GetAttributeValue<int>("revisionnumber");
      if (revisionNumber > 0)
      {
        IPluginExecutionContext parentContext = context.ParentContext;
        // check if the Parent Context contains the QuoteId parameter of the ReviseQuote message
        if (parentContext.InputParameters.Contains("QuoteId") && parentContext.InputParameters["QuoteId"] is Guid)
        {
          Guid revisedQuoteId = (Guid)parentContext.InputParameters["QuoteId"];
          IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
          IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);

          Entity revisedQuote = service.Retrieve("quote", revisedQuoteId, new ColumnSet(true));
          
          // rest of the code

        }
      }
    }
  }
  catch (Exception ex)
  {
    throw new InvalidPluginExecutionException(ex.Message);
  }
}

May 24, 2017

Xrm.Page.context.getVersion is now a supported method, use it!

Recently the MSDN page regarding the Client-side context methods of Dynamics 2016/365 has been updated and thanks to the Dynamics team a new method is inside the documentation: Xrm.Page.context.getVersion.

Some developers are already using this method in their code, personally I didn't because I had not the necessity to differentiate between the 8.x endpoints.

The method returns a string containing the full version in the Major.Minor.Build.Revision format, an example is "8.2.1.185".

In the previous CRM versions if we want to get the version from the client we had two ways:
  • call the RetrieveVersionRequest message using the SOAP endpoint (example)
  • check if a specific CRM function exists (example)
I used often the second way because I always considered doing a server call (often synchronous) not a good choice for the script performance.

Why we want to know the current CRM version? Because some functions are available only from a specific version, like the one to execute a workflow: the Microsoft.Dynamics.CRM.ExecuteWorkflow introduced with the 8.2 release.

But for the Web API endpoint we need to provide only Major.Minor and not the full version, therefore a transformation is necessary.

The following code maybe will be considered exaggerated by someone but creating a robust piece of code will reduce the necessity to change the script in the future:
function getCurrentVersion(context) {
    if (context.getVersion == undefined) { return ""; }
    var versionArray = context.getVersion().split(".");
    if (versionArray.length < 2) { return ""; }
    return versionArray[0] + "." + versionArray[1]; 
}
The result from the "8.2.1.125" is "8.2" ready to be inserted inside your Web API url.

Few considerations:
  • If the version cannot be retrieved I decided to return an empty string, but you can return null, throw an exception, call another method, set a default value, etc etc...
  • the function requires the context, in this way you can use in the form scripts (like an onload event) or inside a WebResource
  • I decided to split the string to an array using the dot (.) I think this is the most robust way compared to doing a substring or expecting a second dot in the string

April 4, 2017

GetAttributeValue<object>, why not?

Today I wrote a small piece of code in order to replicate the exact same data from a CRM instance to another keeping the same ID, it was an exercise because I had to copy only a couple of entities, otherwise I could use one of the several tools made for this kind of operation (like KingswaySoft).

While I was writing the code I arrived to the point where I need to map the fields, so I started to write the usual GetAttributeValue<string>, GetAttributeValue<EntityReference>, ...
And I asked myself:
"source and target attributes are always of the same type, what if I use object for the Generic?"
"something like newEntity["name"] = oldEntity.GetAttributeValue<object>("name"); works?"

With a bit of surprise I found that actually works, so in the end I wrote this piece of code.
Some notes:
  • It uses CrmServiceClient, because there are two instances (source and target) one of them should contains the option RequireNewInstance = true;
  • The UpsertRequest works also when the entity has the ID defined, and not only when the entity is defined using an alternate key syntax
  • As suggested by Tinus Smith in his comment, if the purpose is to do an exact copy, it's not necessary to create the entityTarget and copy its values with GetAttributeValue<object>, instead the entitySource can be used directly inside the Target property of the UpdateRequest:
    UpsertResponse response = (UpsertResponse)target.Execute(new UpsertRequest { Target = entitySource });
MigrateEntity(source, target, "new_entity", new List { "new_name", "new_date", "new_lookupid" });

private static void MigrateEntity(CrmServiceClient source, CrmServiceClient target,
                                  string entityName, List<string> columns)
{
    QueryExpression querySource = new QueryExpression(entityName);
    querySource.ColumnSet = new ColumnSet(columns.ToArray());
    EntityCollection collSource = source.RetrieveMultiple(querySource);
    foreach (Entity entitySource in collSource.Entities)
    {
        try
        {
            Entity entityTarget = new Entity(entityName);
            entityTarget.Id = entitySource.Id;
            foreach (string column in columns)
            {
                entityTarget[column] = entitySource.GetAttributeValue<object>(column);
            }
            UpsertResponse response = (UpsertResponse)target.Execute(new UpsertRequest { Target = entityTarget });
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

January 5, 2017

MVP again for 2017, transition to the new Dynamics era

January 1st I received my third Microsoft MVP Award for my contributions to the Dynamics community, the category is still "Business Solutions" as Microsoft did not make new changes to the MVP Award structure.
It's an honor for me to be part of this group, to "celebrate" the event I will use this post to write some of my thoughts about the MVP/Dynamics world.

Currently there are a bit less than 100 MVP awarded for the Dynamics CRM competency (I don't have the exact number) but between my Twitter feed and the Dynamics Community forums I can easily write a list of another 100 great people that works with Dynamics CRM and are not MVP. They write blog posts, create useful tools, tweet relevant news and their contributions are valuable to everybody in the community. The fact that they don't have an award doesn't make their job, their opinions, their experiences less important, in the same way my contributions have not an higher value because I am an MVP, I can consider the award a small proof of my competences, but it doesn't define totally me or what I do.
The community is central, without users sharing questions/answers/thoughts, no matter if the topics are basic or advanced, the MVP program will not have reason to exist.

During the last year I had the chance to participate also to some "offline" events: April in Rome I joined the Western Europe MVP & Community Day 2016, October in Milan I joined the Microsoft Future Decoded and November in Seattle the annual MVP Summit.
All these three events were amazing, I had finally the opportunity to meet in person fantastic people and I hope to meet them soon again. If you want to get an idea you can read Leon Tribe post about the summit https://thatcrmblog.wordpress.com/2016/11/19/post-summit-debrief/

Regarding my contributions in 2016, I think the most important is the Dependent OptionSet Generator, it's a small tool but I know people using it and they find it useful, when you create a tool the goal is to simplify a process, not everybody sends spaceships to Mars (I am looking at you Elon Musk), and the saved time can be used for something else or spent outside work.
Another contribution that I am proud is that I got back the Microsoft Community Contributor (MCC) badge, it's assigned every 6 months, and in the last year my number of answers were not enough to get it.
What I failed regarding my contributions in the last year? Definitely blogging, I wrote only few posts, maybe 2017 will inspire me to write more.

2016 is also the year the Dynamics ecosystem changed and it's now toward a new direction. The "Dynamics 365" momentum now can look like just a rebranding, but it's definitely more, inside and outside of what was formerly known as Dynamics CRM. The platform is HUGE with all the available solutions like Field Service, Project Service and the Portals, it's TERRIFIC considering the integrations with PowerBI, Azure and AX, it's a GREAT future for the Dynamics platform and for the people works with it (the MVP summit was held during the US elections, so now I unintentionally use some words).

If you arrived at this point of my unexpected long post, I wish you Happy New Year and I hope you will reach your goals for 2017!