Canvas-Apps for Approvals in Dynamics 365-CE

There are scenarios where we need to configure approvals in Dynamics 365, for example, mark an account as a premium customer after approval or qualify leads after approval etc. We used dialog control to capture approval request and comments but, now dialog controls are depreciated and not advised for use in new projects.

As per Microsoft’s initial announcement

Dialogs are deprecated and are replaced by mobile task flows (available as of the December 2016 update), and business process flows. Both task flows and business process flows will continue to evolve to make the transition easier.

But either tasks flow or business process flow was not a perfect replacement for Dialog. Knowing this pain from users, Microsoft has now modified the announcement.

Dialogs are deprecated, and should be replaced by business process flows or canvas apps

Even though I knew canvas apps can be now embedded in model-driven apps, I hadn’t thought of this option until I came across this new announcement, so tried replicating my approval dialogs with a canvas app and it works fine. Pheww!!!! ūüôā

For testing purpose I replicated the dialog for creating approval request for Account entity.

  1. created a canvas app to create approval request.
This sample app changes account status to pending verification and captures the comments in one custom field.

2. Now we need to call this app from account form, obtain the app ID from app details section.

select app details to get the App GUID

3. I need to call this canvas app as a popup when user clicks a button. I created a custom button for account entity-> added a JavaScript as button action to call an HTML web-resource and embedded my canvas app in this html I-frame.

<html&gt;<head&gt;
<title&gt;Approval</title&gt;

<meta http-equiv="content-type" content="text/html; charset=UTF-8"&gt;
</head&gt;
<body style="padding: 0px; font-family: arial; overflow-wrap: break-word;" onload="LoadPowerApp()"&gt;
<center&gt;


<iframe id="Approval" width="800" height="600"&gt;</iframe&gt;



</center&gt;
<script id="myScript"&gt;
function LoadPowerApp()
{
var AccountID = window.parent.opener.Xrm.Page.data.entity.getId().slice(1, -1);
var url ="https://web.powerapps.com/webplayer/iframeapp?source=iframe&amp;appId=/providers/Microsoft.PowerApps/apps/56123673-f45c-4b96-b9e6-ece1b0a8069a&amp;ID="+AccountID;
document.getElementById("Approval").src=url;
}
</script&gt;


</body&gt;
</html&gt;

I know you have many questions now. "https://web.powerapps.com/webplayer/iframeapp?source=iframe&appId=/providers/Microsoft.PowerApps/apps/56123673-f45c-4b96-b9e6-ece1b0a8069a&ID="+AccountID;

This is the key and I will breakdown it into parts “https://web.powerapps.com/webplayer/iframeapp?source=iframe&appId=/providers/Microsoft.PowerApps/apps/APP GUID&CUSTOM PARAMETER NAME=”+PARAMETER VALUE

App GUID I explained in step 2, now regarding custom parameter, I deliberately didn’t mention it when we discussed the app creation and kept for this section. When we open this canvas from an account form(like we start a dialogue) the app needs the record GUID to update the account status

I have used form control in the canvas app and filtered the item using the ID Parameter.

4. Now try your button and you can see the magic.

You can download the sample APP from TDG Power Apps bank.

Please note this is a basic app I tried for testing purpose and needs many improvements to use in a live project. you are always welcome to discuss with on this app.

Hope this helps….. ūüôā

Advertisements

Plugin Input paramerts!!!

Whenever I get a  requirement for a new plugin, the first thing I will be doing is to analyze the information I derive from  IPluginExecutionContext interface and the input parameters. IPluginExecutionContext interface  Contains information that describes the run-time environment that the plug-in is executing in.
Chances to forget these values are high, hence I post these values here for a quick reference and will be helpful for someone who doesn’t have the time to search these details in the SDK ūüôā
Following are IpluginExecutinContext Properties.
Name Description
BusinessUnitId Gets the GUID of the business unit that the user making the
request, also known as the calling user, belongs to.(Inherited
from IExecutionContext.)
CorrelationId Gets the GUID for tracking plug-in or custom workflow
activity execution. (Inherited from IExecutionContext.)
Depth Gets the current depth of execution in the call
stack.(Inherited from IExecutionContext.)
InitiatingUserId Gets the GUID of the system user account under which the
current pipeline is executing.(Inherited from IExecutionContext.)
InputParameters Gets the parameters of the request message that triggered
the event that caused the plug-in to execute.(Inherited
from IExecutionContext.)
IsExecutingOffline Gets whether the plug-in is executing from the Microsoft
Dynamics 365 for Microsoft Office Outlook with Offline Access client
while it is offline. (Inherited from IExecutionContext.)
IsInTransaction Gets a value indicating if the plug-in is executing within
the database transaction.(Inherited from IExecutionContext.)
IsOfflinePlayback Gets a value indicating if the plug-in is executing as a
result of the Microsoft Dynamics 365 for Microsoft Office Outlook with
Offline Access client transitioning from offline to online and synchronizing
with the Microsoft Dynamics 365 server.(Inherited
from IExecutionContext.)
IsolationMode Gets a value indicating if the plug-in is executing in the
sandbox.(Inherited from IExecutionContext.)
MessageName Gets the name of the Web service message that is being
processed by the event execution pipeline.(Inherited
from IExecutionContext.)
Mode Gets the mode of plug-in execution.(Inherited
from IExecutionContext.)
OperationCreatedOn Gets the date and time that the related System
Job was created.(Inherited from IExecutionContext.)
OperationId Gets the GUID of the related System Job.(Inherited
from IExecutionContext.)
OrganizationId Gets the GUID of the organization that the entity belongs to
and the plug-in executes under.(Inherited from IExecutionContext.)
OrganizationName Gets the unique name of the organization that the entity
currently being processed belongs to and the plug-in executes
under.(Inherited from IExecutionContext.)
OutputParameters Gets the parameters of the response message after the core
platform operation has completed.(Inherited from IExecutionContext.)
OwningExtension Gets a reference to the
related SdkMessageProcessingingStep or ServiceEndpoint.(Inherited
from IExecutionContext.)
ParentContext Gets the execution context from the parent
pipeline operation.
PostEntityImages Gets the properties of the primary entity after the core
platform operation has been completed.(Inherited
from IExecutionContext.)
PreEntityImages Gets the properties of the primary entity before the core
platform operation has begins.(Inherited from IExecutionContext.)
PrimaryEntityId Gets the GUID of the primary entity for which the pipeline
is processing events.(Inherited from IExecutionContext.)
PrimaryEntityName Gets the name of the primary entity for which the pipeline
is processing events.(Inherited from IExecutionContext.)
RequestId Gets the GUID of the request being processed by the event
execution pipeline.(Inherited from IExecutionContext.)
SecondaryEntityName Gets the name of the secondary entity that has a
relationship with the primary entity.(Inherited from IExecutionContext.)
SharedVariables Gets the custom properties that are shared between
plug-ins.(Inherited from IExecutionContext.)
Stage Gets the stage in the execution pipeline that a
synchronous plug-in is registered for.
UserId Gets the GUID of the system user for whom the plug-in
invokes web service methods on behalf of.(Inherited
from IExecutionContext.)
Following  are input parameter keys for common plugin messages.

Create

create

Update

update

Assign

assign

Delete

delete

I have included only the common messages, as it could run out to be very lengthy if I include parameter details for all the messages. You can use the dubugger to see the keys for other messages.
Hope this helps ūüôā

 

Microsoft Dynamics CRM is behaving weird!!! (Common troubleshooting tips)

Sometimes your on-premise CRM system will show, some weird behaviors like workflows are not executing¬†/¬†plugins¬†are not working¬†/¬†data imports are failing¬†without any data issues etc…

I have faced certain issues¬†like the¬†entire system is not working¬†, system will ¬†show error messages without any logs, the only message it ¬†displays is “an error has¬†occurred”.

Following are the common issues you can check in such situations.

Ensure CRM Asynchronous services are running

From the server Administrative Tools select Services, and if the CRM Asynchronous services are not running ensure to start the services (if the Asynchronous  services are not running CRM will stop working).

Ensure CRM Sandbox services are running

From the server Administrative Tools select Services, and if the CRM Sandbox services are not running ,please start the services. If the Sandbox services are not running CRM will stop working or the normal system jobs will fail without showing any valid errors .

Check IIS

Ensure the following in IIS

CRM application pool and CRM  website are up. Try browsing the CRM website from IIS.

Make sure your SQL server and Reporting services are up

Ensure your local machine time is in sync with the server time 

This may sound funny but trust me this simple point can save your many productive hours.

Recently my system failed to ¬†perform certain operations and I wasn’t able to connect the Xrm tools too. When I debugged I got the keyword “past time” , then I identified that the server time was different from my local machine.

If you have tried all the above steps and still you are not able to identify the issue,please check the server event viewer this may give you some details regarding the failure.

Hope this helps!!!… ¬†ūüôā

 

 

 

 

 

On demand backups for CRM Online instance

With on demand backups, you can make your own backup before making some significant customization change or applying a version update.

CRM Online Administrator Center

About CRM managed on demand backups:

  • You can back up Production and Sandbox instances.
  • You can only restore to a Sandbox instance. To restore to a Production instance, first switch it to a Sandbox instance. See¬†Switch an instance.
  • Only CRM Online 2016 Update 1 or later versions are supported for backup.
  • On demand backups are retained for up to three days. Check your expiration date.
  • On demand backups are identified as created by someone other than System and by the presence of¬†Edit¬†|¬†Delete¬†|¬†Restore¬†in the details section. System backups only have¬†Restore.

Backup and restore CRM online instance

Configure SLA for Custom Entities

In previous releases, you could create SLAs only for case records. With CRM Online 2016 Update 1 and CRM 2016 SP1, you can now create enhanced SLAs for entities that are enabled for SLA. A system administrator or customizer can enable SLAs for the following entities and also for custom entities and custom activities:

  • Account
  • Contact
  • Order
  • Invoice
  • Quote
  • Opportunity
  • Lead
  • All activity entities like email, phone, and appointment except recurring appointment and its instances.

 

To enable SLA for custom entities you can select Enable for SLA option in the entity.

Untitled

once the entity is published you can configure SLA using the same steps we used for case, system will ask for the related entity at the time of SLA creation  .

Untitled

Please note that only enhanced SLA is available for custom entities , standard SLA can be used only with case entity.

To create see how to configure SLA please follow the link :Define SLA

To add a timer control to the form please follow : Add Timer

Hope this was helpful……..

 

 

WEB API- Part 5 : Get lookup text

Get lookup text using WEB API

Following code will fetch the originating lead lookup text and Id from an opportunity .

var req = new XMLHttpRequest();
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/opportunities?$select=_originatingleadid_value&$filter=_originatingleadid_value ne null", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");
req.onreadystatechange = function () {
    if (this.readyState === 4) {
        req.onreadystatechange = null;
        if (this.status === 200) {
            var results = JSON.parse(this.response);
            for (var i = 0; i < results.value.length; i++) {
                 var _originatingleadid_value = results.value[i]["_originatingleadid_value"];// Id
                 var _originatingleadid_value_formatted = results.value[i]["_originatingleadid_value@OData.Community.Display.V1.FormattedValue"];// text value
            }
        }
        else {
            alert(this.statusText);
        }
    }
};
req.send();

WEB API- Part 4 : Get option-set text

Get option-set text and value using Web API

Following code fetches lead source option-set value and text from lead entity.

var req = new XMLHttpRequest();
req.open("GET", Xrm.Page.context.getClientUrl() + "/api/data/v8.0/leads?$select=firstname,leadsourcecode&$filter=fullname eq 'Alex%20Eric'", true);
req.setRequestHeader("OData-MaxVersion", "4.0");
req.setRequestHeader("OData-Version", "4.0");
req.setRequestHeader("Accept", "application/json");
req.setRequestHeader("Content-Type", "application/json; charset=utf-8");
req.setRequestHeader("Prefer", "odata.include-annotations=\"OData.Community.Display.V1.FormattedValue\"");
req.onreadystatechange = function () {
    if (this.readyState === 4) {
        req.onreadystatechange = null;
        if (this.status === 200) {
            var results = JSON.parse(this.response);
            for (var i = 0; i < results.value.length; i++) {
                 var firstname = results.value[i]["firstname"];
                 var leadsourcecode = results.value[i]["leadsourcecode"];
                 var leadsourcecode_formatted = results.value[i]["leadsourcecode@OData.Community.Display.V1.FormattedValue"];
            }
        }
        else {
            alert(this.statusText);
        }
    }
};
req.send();

Please note the following code

" var leadsourcecode_formatted = results.value[i]["leadsourcecode@OData.Community.Display.V1.FormattedValue"];"

“fields name@OData.Community.Display.V1.FormattedValue” will give you the text value of the filed.

Note : Don’t forget to add req.setRequestHeader(“Prefer”, “odata.include-annotations=\”OData.Community.Display.V1.FormattedValue\””);
in the request header