Tip #731: Hide and protect your custom CRM API

Protect yourself by coveringYesterday we illustrated how you can wrap some server-side functionality as a custom API to be consumed by the developers. One of the undesired side effects is that business can now poke their nose into the API when building workflow processes:

I can see your API in workflow

If your API is a smoking gun and you don’t want business to accidentally set it off then you can hide your API by introducing parameters that would stop custom action from appearing in the Action list. These “business unfriendly” parameter types include Picklist, Entity, or Entity Collection.

Let’s redefine our Normalize Customer action by declaring it to be global but adding a mandatory collection of contacts as a parameter:

Redefine custom action

Metadata definition will now look like:

<Action Name="georged_NormalizeCustomer">
  <Parameter Name="Customer" 
   Type="Collection(mscrm.crmbaseentity)" 
   Nullable="false"/>
</Action>

And we have to slightly change our plugin code:

 
var contacts = localContext.PluginExecutionContext
  .InputParameters["Customers"] as EntityCollection;
foreach (var contactParam in contacts.Entities)
{
// do your evil stuff
}

And then change the calling implementation (note that we didn’t have to change the main code, only implementation):

 
public static class Extensions
{
  public static void NormalizeCustomer(
       this IOrganizationService service, 
       Guid contactid)
  {
    var request = new OrganizationRequest
      ("georged_NormalizeCustomer");

//  old stuff	  
//  request.Parameters.Add("Target", 
//    new EntityReference("contact", contactid));

    var contacts = new EntityCollection() 
	{ EntityName = "contact" };
    contacts.Entities.Add(
	new Entity("contact", contactid));

    request.Parameters.Add("Customers", contacts);

    var response = service.Execute(request);
  }
}

Business now is safe:

Action is no longer available

Added bonus of this approach is that our API can now be called for the collection of the contact entities. Think about user selecting multiple records in the web client, and then pressing a button that would invoke a custom javascript sending a single message to the backend with the collection of the selected records. Then the plugin code (a.k.a. API implementation) can process these records in a batch request or transaction. All in a single round-trip.

Tip #730: Turn your CRM into API machine

Locked black boxWhen people ask me to describe the differences between custom workflow activities and custom actions, I always use the following definitions:

  • Custom workflow activities. A functional blackbox created by the developers to be consumed by the business
  • Custom activity. A functional blackbox created by the business to be consumed by the developers

There is more to the story, of course. As a developer, I always wanted to have the ability to extend server-side of CRM with custom API that I could call from all places code: javascript, custom utilities, plugins. For business users: nothing to see here, move along. Custom actions are the perfect mechanism to add this extensibility to CRM organization.

  1. Create custom action in CRM for the entity of your choice.Define custom action
  2. Don’t add any steps, simply activate the action.
  3. You now have a custom message. Don’t believe me? Open [your_org_url]/api/data/v8.1/$metadata and search for your action name. Hint: use Chrome and stock up some patience.
<Action Name="georged_NormalizeCustomer" IsBound="true">
   <Parameter Name="entity" Type="mscrm.contact" 
      Nullable="false"/>
</Action>
  1. Create a plugin using your action name as a message, entity contact, synchronous pre-operation.Register plugin
  2. Write some wicked code in plugin (I chose to swap first and last names without telling the business):
var orgSvc = localContext.OrganizationService;
var contactId = localContext.PluginExecutionContext
                .PrimaryEntityId;

var contact = orgSvc.Retrieve(
    "contact", contactId,
    new ColumnSet("firstname", "lastname"));

var temp = contact["firstname"];
contact["firstname"] = contact["lastname"];
contact["lastname"] = temp;

orgSvc.Update(contact);
  1. You now have a custom API that you can call wrap up and call like this:
public static class Extensions
{
  public static void NormalizeCustomer(
       this IOrganizationService service, 
       Guid contactid)
  {
    var request = new OrganizationRequest
      ("georged_NormalizeCustomer");

    request.Parameters.Add("Target", 
      new EntityReference("contact", contactid));

    var response = service.Execute(request);
  }
}
...
IOrganizationService svc = getityourself;
Guid contactid = whatever;

// call our "API"
svc.NormalizeCustomer(contactid);

Why is it better than simple client-side code? Because I can call this API from various places using various languages and then change the implementation on the backend (e.g. swap first and middle names instead) and I wouldn’t have to change anything on the caller side.

Tip #729: Mapping fields in N:N relationship

Nonmonogamy relationshipsNormal out-of-box CRM relationship mapping can be extremely beneficial, but it has a very big “gotcha”. They only work when creating a child entity from the parent form. So if the user is on the parent entity form, and adding a new entity via a grid, then the mapping will automatically be populated for the user. But what happens if you have an entity that is serving as a N:N entity (think Opportunity Products). Generally, a user would create an Opportunity Product from the Opportunity Form. This means if any mappings are setup from the Product to the Opportunity Product, they will not be applied.

There is no way for this to get applied natively, since the mapping takes place before the Opportunity Product is loaded, but the user doesn’t select a Product until after the Opportunity Product is loaded. Daryl “New MVP on the block” LaBar created a simple partial work around – a plugin that performs the mapping using what is defined in the OOB Field Mapping for the Relationship.

Glorious implementation details and the plugin download are available.

Generally speaking, it’s not restricted to N:N and would also be useful anywhere you would like to map a value over from a child form when selecting the parent. The only drawback is that the user will not see the mapped values on the form until after the initial save refresh occurs – and that can be arranged with a bit of javascript.

Image courtesy of https://www.xeromag.com/nonmonogamy.html

Tip #728: Install CRM for Outlook using SCCM

Complicated control panelWhen questions come all the way from Gustaf “Surströmming” Westerlund, some people including Gayan “Performance Wizard” Perera cannot keep still.

Question

According to Deployment and Administration Guide, it is not supported to install CRM for Outlook using System Center Configuration Manager for CRM Online or CRM 2016.

Gayan’s SCCM Ultimate Guide

We’ve used SCCM to deploy the Outlook client, it works well but some things we’ve had to do/watch out for:

  • If you’re deploying globally, make sure to copy the package to a share locally and install from that rather than pulling from the main server
  • Run a script on start up to kill Outlook, Skype for Business and any other Office application (if it’s set to start automatically) and notify the user that the installation may take awhile
  • Run the installer on start-up, this ensures a clean install
  • Use a batch script to start configwizard with the config.xml file and log output, you can then use this to re-try or stop
  • Might need to play with the switches if you’re upgrading (sometimes you need to remove and add back CRM organization

Usual Tipp Jarr’s disclaimer

Not [currently] supported, use at your own risk, contains small children between the flags, not to be consumed with alcohol while driving.

Tip #727: No currency in Field Service Mobile

Empty walletField Service in Dynamics CRM is massive – lots of entities, lots to configure, lots to learn. One of the nicest features on my books is a Resco-based mobile app, Field Service Mobile, fast and user-friendly.

When Field Service Mobile starts for the first time, it may display an error message:
Exception while fetching entity msdyn_agreement
Server Error: Code.Value=s:Sender| Reason.Text='msdyn_agreement' entity doesn't contain attribute with Name = 'transactioncurrencyid'.| Boring details.

Missing transactioncurrencyid

Looks like developers of the app had a different schema from the one deployed by the trial. The workaround is to create a dummy currency field in Agreements entity and then immediately delete it. That will create transactioncurrencyid and exchangerate system fields and leave them there permanently. Restart the app and you’re good to go.

Field Service Mobile

Tip #726: Dude, where is my option to export for update?

Question

I recently noticed that in version 2016, the option to export records for update (available in version 2015) has vanished from the Export To Excel dialog.

This SDK article describe the Import process as if this option is still available.

Can anyone clarify this feature status? Is there any other method to update existing records via the Import Wizard?

Export Excellence

Answer

And before you could even say “Cortana”, Feridun “Best Twitter Handle for CRM MVP” Kadir was on the case:

That is because the export always includes the columns needed for re-importing so the check-box was removed. Perform the export and review the [hidden] columns in the Excel file.

Tip #725: Rename the contract

Broken contractDid you ever come across the situation when “a friend of yours” tried linking a task (or any other activity record) to a contact only to stare at the screen in disbelief for 5 seconds:

Lookup contract by mistake

Where. Did. All. My. Contacts. Go?

Then you your friend exhaled in relief – “stupid contRacts” – and quickly selected the previous record in the dropdown.

Since 7 people in the world who dared to use contRacts have either gone insane since or regretted the decision many times over, here is the simple solution to save about 5 seconds x 10,000 = 13.89 hours of your time: go to customizations (no need to even create a solution) and rename contRact entity into zzzNobodyLovesYouContract (or just zzContract).
Lookup contact

Tip #724: Stuck Lightweight Outlook App

Lightweight feather stuck in a cobwebScenario: A user initiates the installation of the Lightweight Outlook App in Dynamics CRM 2016, and it never finishes installing.

If your Outlook App installation seems stuck after several hours or multiple days, the solution is to test and enable the mailbox for server side synchronization. After re-running the Test and Enable process, the lightweight Outlook App should successfully complete installation and be available to use from Outlook and OWA.

Tip #723: Using Related Data in Dynamics CRM Document Templates

DNA ReplicationHere we expand on the use of Document Templates in CRM 2016. We look at the different options available for including related data, talk about some of the limitations, and finally walk though how to create a template that uses data from different related records. (both parent and child)

YouTube player

Give us your feedback, all of it: good, bad, and ugly, I’m sure we can take it. Suggest new topics either in comments or by sending your ideas to jar@crmtipoftheday.com.

Don’t forget to subscribe to http://youtube.com/crmtipoftheday.

Tip #722: Add the number to your phone call records

Women Telephone Operators at WorkI cannot believe I’m writing an old tip from Gus “Niels Bohr” Gonzalez but it’s a suprisingly good one.

When you create a phone call record in CRM, all information is right there, including who to call and what number to dial.

CRM phone call record

Only one problem – CRM does not pop up reminders, that’s Outlook’s job. But when the record is synchonized to Outlook, it’s represented as a task and neither recipient nor the phone number are there.

Gus’s tip: create a workflow that adds the phone number for whoever you are calling to either the Subject or the Description of a phone call to make it easier for the users to see who they are calling.

So you create a phone call with the Subject: “Call to schedule 6-month appointment” and select “John Doe” on the regarding. A workflow runs and updates the Subject to “Call to schedule 6-month appointment (555-1234)”.

That change syncs to Outlook and now you can dial faster. You can also sync it to the description of the call if you believe the subject changing is too intrusive.

Bonus: If your system is configured so that you can dial the numbers using Skype or Skype for Business, prefix number with tel:

Outlook phone call record

That coveted sales call is now only one click away.