Customizations.xml Advanced Hacks

For those who attended my session about customizations.xml advances hacks, here is everything I promessed you during the session.

The presentation

Here’s the PowerPoint with the relevant information.

Enable / Disable the Jumpbar on subgrids

First example was to enable or disable the jumpbar on subgrids. In order to do so, you need to add or set the <EnableJumpBar> property in the parameters of the subgrid as shown below.

<cell showlabel="false" locklevel="0" id="{a64dbfbd-4d31-4b01-99a8-d6019536c1a5}" rowspan="1" colspan="1">
  <labels>
    <label description="New SG control 1573914400892" languagecode="1033" />
  </labels>
  <control id="lbs_contacts" classid="{E7A81278-8635-4D9E-8D4D-59480B391C5B}" indicationOfSubgrid="true">
    <parameters>
      <TargetEntityType>contact</TargetEntityType>
      <ViewId>{00000000-0000-0000-00AA-000010001004}</ViewId>
      <ViewIds>{00000000-0000-0000-00AA-000010001003},{00000000-0000-0000-00AA-000010001004}</ViewIds>
      <EnableViewPicker>false</EnableViewPicker>
      <RelationshipName>lbs_Event_Contact_Contact</RelationshipName>
      <EnableJumpBar>false</EnableJumpBar>
    </parameters>
  </control>
</cell>

Hide columns on your view

To hide columns on your view, you can set the ‘ishidden’ attribute on the column of the view as shown below.

One of the attendees on the session in Brussels also marked the ‘disableSorting’ attribute that makes sure users cannot sort the view on that column.

<layoutxml>
  <grid name="opportunityproducts" jump="" select="1" icon="1" preview="1">
    <row name="opportunityproduct" id="opportunityproductid">
      <cell name="productid" width="200" />
      <cell name="productdescription" width="200" />
      <cell name="priceperunit" width="100" />
      <cell name="quantity" width="75" />
      <cell name="extendedamount" width="100" />
      <cell name="opportunityproductname" ishidden="1" imageproviderfunctionname="Sales.ProductTypeImage.getProductTypeImageUrl" imageproviderwebresource="$webresource:Sales/ClientCommon/Sales_ClientCommon.js" />
      <cell name="isproductoverridden" />
      <cell name="producttypecode" ishidden="1" disableSorting="1" />
      <cell name="baseamount" width="100" />
    </row>
  </grid>
</layoutxml>

Embed PowerBI Report in a Model Driven form

Microsoft has documented this feature well and you’re able to see all information on this page. Down here is the code that needs to be added to the section under the </labels> tag. To filter on the current record, please add the highlighted line. Also don’t forget to set the height of the section by adding rowspan=”10″ to the <cell> tag.

<control id="unfilteredreport" classid="{8C54228C-1B25-4909-A12A-F2B968BB0D62}">
  <parameters>
    <PowerBIGroupId>00000000-0000-0000-0000-000000000000</PowerBIGroupId>
    <PowerBIReportId>91649484-1601-4345-8c12-4dfea5d69399</PowerBIReportId>
    <TileUrl>https://app.powerbi.com/reportEmbed?reportId=91649484-1601-4345-8c12-4dfea5d69399</TileUrl>
    <PowerBIFilter>{"Filter": "[{\"$schema\":\"basic\",\"target\":{\"table\":\"Account\",\"column\":\"accountid\"},\"operator\":\"Eq\",\"values\":[$a],\"filterType\":1}]", "Alias": {"$a": "accountid"}}</PowerBIFilter>
  </parameters>
</control>

Creating custom dialogs

In the presentation I told you about custom dialogs in CRM. These are perfectly to build and it’s all explained very well by Bob Guidinger. You can find everything you need to know on these two posts: https://bguidinger.com/blog/category/dialog-boxes

Force users to use the Unified Interface

You can’t have missed it. All Dynamics applications that are based on the PowerPlatform will be using the same interface: the Unified Interface. Microsoft has announced deprecation of the the ‘old’ web client (see this article for more information), so there’s no way back. On October 1, 2020, the legacy web client will no longer be available.

Have you already migrated all your users to the Unified Interface? GOOD! I guess you probably face the same problem as I do than. When users log on, they are still routed to the legacy web client! They keep on working in this new client instead of the new… As of October 2019 we’re able to force users to use the Unified Interface (whoop whoop!).

How to activate this force?

First, login to your Dynamics 365 CE environment and open the PowerPlatform Admin Center. Now go to the environments tab and select the environment where you want to activate the force. Go to Settings > Behavior > Interface settings and then turn on Use Unified Interface only.

Frequently Asked Questions about this change

Is there any downtime while applying this change?

No, this change applies immediately, upon reloading the page. If it doesn’t, clear cache and retry.

I want to use Unified Interface for all the apps, but still want Dynamics 365 – custom to open in the legacy web client. Is that possible?

You can achieve this by setting all apps to be Unified Interface apps. Note that this won’t change the home page experience to Unified Interface. Unified Interface Only mode is a prerequisite to get the new home page experience.

What app will be opened when the force is activated?

When the user has access to only one app, they will be routed to that app without any problems. When they have access to multiple apps, they will be routed to https://home.dynamics.com and will have to choose on of the apps.

Please give us the Outlook Add-In in PowerApps

UPDATE: Ok, in the meantime a couple of people showed me the feature. Apparently it’s available after all! I was looking for server-side sync as I always thought the Outlook Add-In only works with server-side sync. However, it turns out the Outlook Add-In works without server-side sync and is available in CDS. Here’s the link: https://powerapps.microsoft.com/en-us/blog/introducing-the-powerapp-that-brings-common-data-service-into-outlook/

As a Dynamics 365 Solution Architect, I’ve done a ton of xRM projects. Using the good-old Dynamics CRM platform as a app-builder instead of using the Sales and Customer Service functionality in there. So you probably can’t imagine my enthusiasm when Microsoft brought us the PowerPlatform. It’s basically what I needed: a flexible platform to build business applications without Sales or Customer Service functionality. Thank you Microsoft! 🙂

Now that I’ve done the first couple of projects on the new PowerPlatform I’m totally happy. However, there is one feature from the Dynamics 365 platform that I really miss in almost every PowerApp: Server-Side Synchronization with the Outlook Add-In.

The ability to track e-mail is an ability that is needed in a lot of business scenario’s. Not only in Sales and Customer Service scenario’s. Currently it’s only available with the Sales and Customer Service apps. I encourage Microsoft to make it available in all PowerApp scenario’s. Please, Microsoft, give us the Outlook Add-In in PowerApps!

The loophole for now

So if you ran into the same issue and you’re looking for a solution to use the Outlook Add-In in a PowerApp, here’s what I do. For those users who need an Outlook Add-In, I provide them with an additional Team Member license on top of the PowerApp license. Please make sure to create a Dynamics 365 environment instead of a regular CDS environment. The regular CDS environment won’t contain the proper entities to use Server-Side Synchronization.

Dynamics 365 and PowerPlatform API Limits explianed

Together with a lot of other Dynamics 365 / Power Platform professionals, I was very surprised with the announcement of Microsoft about API limits. Now that the dust has settled and the documentation has cleared some first questions, we’re able to review the decision.

My first thought about the API limits were: there goes one of our unique selling points against Salesforce. But the more I think of it and the more I talk to Microsoft product managers about it, it starts to make sense. Both from a technical point of view and strategy wise.

What is an API call?

First, let’s dive a little bit into the changes made lately. You can’t have missed the huge strategy change within the Dynamics 365 (CE) landscape lately. It simply comes down to this. The PowerPlatform – as a platform to create business applications on – becomes the base of it all. The Sales and Customer Service functionality (formally known as Dynamics CRM) are applications developed by Microsoft on top of that PowerPlatform.

At the heart of the PowerPlatform is the Common Data Service. A database where both your business data and your application business logic (metadata) is stored. The amount of usage of the Common Data Service defines the resources needed. Every time that a user or an application requests data from the Common Data Service or pushes data towards it counts as an API call.

Do I need to worry about the limits?

No, I don’t think so. If you assign an appropriate license to the user. The API limits that come with the licenses are based on normal usage. Are you a heavy user? I still don’t think that should be an issue.

So I don’t need to do anything?

Well, with this change it’s more imported then ever to really think about your solution design. If you create forms that have an enormous amount of subgrids on it, that’ll consume more API calls. If you don’t need it as immediate information, please put it one click away. Also think about your custom coding! Inefficient JavaScript or plugins can lead to a significant increase of API calls.

Why tell me why

Let’s face it: all of us had experienced performance issues with environments in the past. When a ticket is logged with Support, it often has to do with another customer having extreme data loads, pushing the underlying servers to the max. When this happens, it comes down to performance degradations of other environments running on the same server.

Microsoft has been searching for a way to manage these problems, but the only way they can manage is if they know what’s coming. At this moment, there is no reason why a customer would tell Microsoft it’s going to do a large data load or anything else that takes a lot of resources.

By limiting the API usage and letting customers buy API call packages, Microsoft pushes consultants and developers to think about the usage of resources and providing Microsoft the information it needs to allocate enough resources.

So everything is fine?

From a technical point of view: yes. From a commercial point of view: absolutely not!

Not having API limits was one of the unique selling points against Salesforce. For companies using a lot of data processing or having a lot of integrations, the choice between Microsoft and Salesforce is now less self-evident. Next, the price for extra API calls seems pretty high to me.

At this point, the limits are user based. This means individual users getting punished for using the application. That doesn’t make sense. The reason for Microsoft is environment-wide and should not be applicable to customers, but to the whole environment.

Please, Microsoft, make the API limits an environmental limit and not a user-based limit!

Creating a custom Status for your (custom) entity

It’s been always a mystery to me why we’re not able to change the status field on an entity. We just get to change the status reason field, but sometimes you just want some more. Microsoft knows it, cause they use multiple status fields on for instance the Lead and Opportunity entity.

Let face it: most of the time it’s just fine to have Activate and Inactive as statuses on your entity. (Now that we’re talking about statuses… Isn’t it just weird that Active has the value 0 and Inactive the value 1??!) But there are those situations that you just need more. For instance one of my customers log their medical consults in an entity. In this case it would have been great to have Active, Processed and Invoiced as statuses. And you know what: it’s possible!

Now before you get too excited about this I need to temper your enthusiasm a bit. Although it can be done, it’s unsupported customization. When you read my solution and you read this article, you’ll understand why it’s unsupported. Since it’s unsupported, I would strongly advice you to do this only for custom entities and not for oob entites. So far for the disclaimer 🙂

In order to add or change the statuses you need to change the customization xml file. Now let’s go through the steps. (I expect you to have some experience with customizing Dynamics 365 CE. If not, please contact your Microsoft partner to execute these steps.)

Create the solution and get the right file

Create a temporary solution with just what you need. In this case, just add the status and status reason fields from the entity you wish to change. Although you just want to change the status field, you’ll also need the status reason field. Later on it’ll come clear why that is needed.
Export the solution and open the zip file. Now get one file out – the customization.xml file – and store it somewhere to edit.

Edit the customization xml

Now comes the editing of the file. Please be careful here, cause you might break something here.
In the file, locate this part

<states>
  <state value="0" defaultstatus="1" invariantname="Active">
    <labels>
      <label description="Active" languagecode="1033" />
    </labels>
  </state>
  <state value="1" defaultstatus="2" invariantname="Inactive">
    <labels>
      <label description="Inactive" languagecode="1033" />
    </labels>
  </state>              
</states>

As you can see, both default statuses (Active and Inactive) are there. Notice the defaultstatus tag that is in there. This is important and indicates the default status reason for that status. Since we don’t have a status reason for it yet, we will just fill in a random number now that we’ll use later to create the status reason with.

To create the new status just copy everything between a <state> and </state> tag and change the value, defaultstatus, invariantname and desciption. It should look like this. In this example I use Invoiced with vaue 10000. I would always use a high number as you never know what Microsoft comes up with in the future.

<states>
  <state value="0" defaultstatus="1" invariantname="Active">
    <labels>
      <label description="Active" languagecode="1033" />
    </labels>
  </state>
  <state value="1" defaultstatus="2" invariantname="Inactive">
    <labels>
      <label description="Inactive" languagecode="1033" />
    </labels>
  </state>
  <state value="10000" defaultstatus="20000" invariantname="Invoiced">
    <labels>
      <label description="Invoiced" languagecode="1033" />
    </labels>
  </state>                  
</states>

Now we need to do the same for the status reason field. Please keep in mind the value for the status reason should be equal to the defaultstatus value that we’ve used before. The same goes for the state.

<statuses>
  <status value="1" state="0">
    <labels>
      <label description="Active" languagecode="1033" />
    </labels>
  </status>
  <status value="2" state="1">
    <labels>
      <label description="Inactive" languagecode="1033" />
    </labels>
  </status>                    
  <status value="20000" state="10000">
    <labels>
      <label description="Invoiced" languagecode="1033" />
    </labels>                    
  </status>
</statuses>

Import the file and you’re done! 🙂

Now that you’ve changed the cusomization xml file you should put the customization xml file back into the solution zip. Notice that it overwrites the existing customization xml file. Import the solution zip back into your environment and you should have the new status.

Things to take into consideration

Again, this customization is not supported. Always be careful with these unsupported customization and question yourself if you can’t do it any other way.

Since it’s unsupported, the user interface doesn’t take your new status into account. There is no out of the box option to set a record to your new status. You can use workflow or you’ll have to create a button on the command bar to set your status.

Last but not least: as for as I could see, when a record is in the new status, its forms are always in read-only state. The Active status is the only status where you can edit the data on the form.

Nested groups not supported in Dynamics 365 CE

So at my current CE project, we want to work with guest user access in CE. This company uses an external company to process incoming e-mail and phone calls. They would like the users from the external company to directly login to their CE instance and do their jobs. This can be done perfectly by the new guest user feature in CE.

Now from a maintenance perspective in Azure Active Directory, you’d like these specific guest users to be in a separate group, so you have them grouped together and can maintain them all together. That would mean you have two security groups with users that need to access the same CE instance. Unfortunately, you can only have one security group setup for accessing one CE instance… :-(.

But wait, what about nested security groups! I created a third security group and made two other security groups member of the third. But when I added the third security group to the CE instance, nobody could login anymore… There was the gap: nested security groups are simply not supported. So we ended up with one security group that had all users in it. Not particularly Azure Active Directory best practice, but it worked 🙂

Also want support for multiple or nested security groups? Good! Vote for my idea here.

Activation Failed status on Data Export Profile

I’ve setup the Dynamics 365 Data Export Service numerous of times and I’ve made a lot of companies happy with this service. Having your Dynamics 365 CE data in a separate database has advantages for reporting, but also for data quality checks.

In my current project, we’ve done a data migration from a previous custom application to Dynamics 365 CE. This customer had a huge amount of data and we ended up with a Dynamics 365 CE database of 150 GB of data. After the migration we wanted to do some checks if all data had been migrated correctly. We used the Data Export Service to make that possible.

When we created the Data Export Profile we simply checked the ‘Select all’ option, since we wanted to do quality checks on ‘all’ data in the database.

Unfortunately, we got an error immediately when we finished the wizard!

After some digging, we found the issue. There are some entities in Dynamics 365 CE that are available for selection in the entity list (since they are track change enabled) but seem to break the Data Export Profile. The entities I found so far are:

  • mobileofflineprofile
  • mobileofflineprofileitem
  • mobileofflineprofileitemassociation
  • offlinecommanddefinition

Simply uncheck these entities and you are good to go!

WebApi batch request

The WebApi in Dynamics CRM has the option to send batch requests. This reduces the number of calls from client to server and when e.g. you need to delete a lot of records from JavaScript it saves time processing these deletes.

Unfortunately, creating a batch request is not as straight forward as doing a POST call for example. The MSDN documentation is a good starting point, but then I still had some trouble making it work. I’ve created a question on stackoverflow and the Dynamics Community. As I needed the functionality I kept trying different options and reading the documentation and I finally figured it out.

It’s very important that you build the content in the batch request as documented with MSDN. Also note that when you do a delete batch request you still need to send an empty object. With a single delete call to the api you don’t have to explicitly do this. Another things that’s a bit hard creating a batch request, the errors that are returned are not existent. You just get an empty result and you have to figure out yourself what you have done wrong.

This is the request if you’re using jQuery to do the call.

$.ajax(
{
    method: 'POST',
    url: 'http://crm/api/data/v8.0/$batch',
    headers: {
        'Content-Type': 'multipart/mixed;boundary=batch_' + batchId,
        'Accept': 'application/json'
    },
    data: payload
});

And here’s the way to build the payload:

var data = [];
data.push('--batch_123456');
data.push('Content-Type: multipart/mixed;boundary=changeset_BBB456');
data.push('');
data.push('--changeset_BBB456');
data.push('Content-Type:application/http');
data.push('Content-Transfer-Encoding:binary');
data.push('Content-ID:1');
data.push('');
data.push('POST http://tenanturl/api/data/v8.0/accounts HTTP/1.1');
data.push('Content-Type:application/json;type=entry');
data.push('');
data.push('{ "name": "batch acount 2"}');
data.push('--changeset_BBB456');
data.push('Content-Type:application/http');
data.push('Content-Transfer-Encoding:binary');
data.push('Content-ID:1');
data.push('');
data.push('DELETE http://tenanturl/api/data/v8.1/accounts(52eb1677-427b-e611-80bb-0050568a6c2d) HTTP/1.1');
data.push('Content-Type:application/json;type=entry');
data.push('');
data.push('{}');
data.push('--changeset_BBB456--');
data.push('--batch_123456--');

var payload = data.join('\r\n'); 

Easy contact form to CRM lead using Microsoft Flow

Many companies are looking for an easy solution to process a contact form on their website to a lead in CRM. In this post I’ll provide an easy, self service solution that requires no code.

Tools

To build this solution we are assuming you use the following tools.

  • You have Dynamics 365 for Customer Engagement
  • Microsoft Flow is enabled in your Office 365 tenant
  • You are ok with using Cognitoform or any other form tool or custom code that can post the form data to an url
  • You already use WordPress and Contact Form 7

I’ll be using a generic approach where you parse the json data from a webhook call. The tool above also has a built in connector to Microsoft Flow. That would even be a better option, but not all platforms support Flow. This approach is very easy to implement for a developer building your website.

Setup

The first thing we need to create is the basic Microsoft Flow. Go to Microsoft Flow and log in with your Office 365 credentials. Create a new flow and start with a blank canvas. The first step is adding a trigger. The trigger you are looking for is the Request trigger. Select it and don’t fill out anything just yet. Next, add the Dynamics 365 connector and configure it to create a lead in your CRM instance. For now just use some dummy values. Save your flow and notice that you’ll get a request url in the Request trigger. Copy that so we can use it in the Cognitoform setup.

It’s pretty easy to setup a test account with Cognitoformand and create your first form. It took me less than 5 minutes. After you are done configuring your fields, you want to go to the submission settings on the bottom of the page. Add the Flow url in the “Post Json data to a website”:


Test your form and return to your flow and you’ll likely see one run has failed. That’s ok for now. Click this failed run and look for the request body of the trigger. Copy that:

Edit your flow and click the Request trigger. Click “Use sample payload to generate schema” and paste in the json from the previous step. Now you have defined the schema from the request and you can use those schema values to create the lead. Click the “Create record” step and use the defined values form the previous step to enter the values for your CRM fields.

Now you’re all setup. Fill out a form a see that a lead is created in CRM.

New: the Scribe Online Toolbox

Six years ago, I started working with Scribe and since then I use some Scribe component in almost every project I do. With Scribe Online as my no. 1 integration platform I’ve been able to take most of my integration requirements to a success.

When you work with Scribe Online a lot, you notice some features are missing. Features that don’t affect the integration itself, but that make your life as a Consultant or Developer easier. Well here is www.scribeonlinetoolbox.com. Together we should make life easier!

Let’s make a wishlist:

  • Bulk enable/disable Solutions – one of the connecting application goes into acquaintance mode. Your Solution tries to connect every 5 minutes, but since the application is down, email start to run. Would it be easy to just enable or disable a couple Solutions with one click?
  • Bulk enable/disable Maps – exactly the same as above, but now for Maps 🙂
  • Easy transfer Maps from one Solution or Org to another – most customers have separate Orgs for their dev, test, acceptance and production. It would be great to have the possibility to just transfer a Map from one Org to another without the need to download and upload the json file, modifying the name, validating etc.
  • Documentation in one click – a nice presented Word file with your block-structure and mappings in place? (ps. I know about the Google Docs tool, but let’s face it… it’s Google Docs….)

I’m sure you can think of more tools we might need ;-). For now, I started with the first one and will continue to work on the other tools. Please let me know if you have any ideas or what you think of the Scribe Online Toolbox :-).