D365 Set status and statusreason from custom ribbon button using JavaScript on UCI

We have seen including myself this request quite a lot from early days on CRM 2011 ‘Set Status, Statusreason by clicking the ribbon button’. I started to love the new way of getting this done so quickly in latest UCI by just calling the Xrm.WebApi.updateRecord function to update the record. All we need before start is JavaScript Webresource, Ribbon button on the entity form. See below scenario to click a custom button on Account form to change the status and status reason and refresh the form.

  1. Javascript webresouce with a function as written below

function ClickCustomButton(primaryControl) {
var formContext = primaryControl;
setStatusStatusReason(formContext, “account”, formContext.data.entity.getId(), 1, 614020000); // these need to be changed as per your context
}

function setStatusStatusReason(executionContext, entityLogicalName, recordId, statecode, statuscode) {

var id = recordId.replace(“{“, “”).replace(“}”, “”);
var data = { “statecode”: statecode, “statuscode”: statuscode };

Xrm.WebApi.updateRecord(entityLogicalName, id, data).then(
function success(result) { executionContext.data.refresh(true); },
function (error) { executionContext.data.refresh(true); }
);
}

2.¬† Access Scott’s tool Ribbonworkbench plugin from Xrmtoolbox and access Account entity from the solution you intend to apply these changes.

  • Add Ribbon button on account form if you don’t have one
  • Add Command definition with JavaScript Action
  • Select the webresource and function name ‘ClickCustomButton’ from step 1
  • Set Crm parameter -> PrimaryControl

 

Publish! that should do the magic, no more long code as we done in olden days!

To test go to the account form and click the custom button from step 2, upon click it should change the status and status reason of account.

 

Note: If you don’t know how to add ribbon button on ribbon workbench click this link before jumping to this blog. If you face any status reason transitions error, then go to the status reason field customizations and edit the ‘Status Reason Transitions’ or disable it.

 

D365 CE UCI JavaScript: alert() throwing error, replace with Xrm.Navigation.openAlertDialog

Geeks,
JavaScript on new interface UCI is throwing error when we use standard alert() method on BPF field OnChange event.
alert(“message”); //Old code that is failing
sometimes it takes you to infinite loop after you click Ok to come out of context.
Easy fix as suggested by Microsoft to start use below code on UCI and .then() for call back functions is optional
Xrm.Navigation.openAlertDialog({ confirmButtonLabel: "Ok", text: "message" }).then(
    function success(result) {
        console.log("Alert dialog closed");
    },
    function (error) {
        console.log(error.message);
    }
);// new code working
Version: D365 CE online v 9.1 wave 2
Further read Click here

CRM Visual studio project Unsupported error: Project types may not be installed

 

Case study:

When we download the CRM projects from the GIT or TFS repository with old version like VS 2013/2015 version and try to open in latest version like 2017/2019

 

Issue replication: Open the downloaded project from GIT repo in VS 2017/2019, then we hit with this error.

VS unsupported project types

Though the migration scripts run behind, to upgrade most of the code to work in latest version, but still we facing below error from migration script

The application which this project type is based on was not found. Please try this link for further information: http://go.microsoft.com/fwlink/?LinkID=299083&projecttype=089D5B2A-9C2F-4B6D-9F7B-CFD25BB0B7F4

Resolution:

Make a copy of the existing project that is corrupted, Just open the .csproj file for the project that is corrupted in any editor like notepad/notepad++ and find the tag <ProjectTypeGuids>. Replace the <ProjectTypeGuids> entire line with below and save.

<ProjectTypeGuids>{349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>

Now try to open the project in Visual studio latest version, that should have resolved the issue. Thanks Xrmpedia for sharing

Basics CRUDE JavaScript V9.x webapi Unified Interface

Hello,

Thanks for visiting, some goodies for you.

Recently I have met CRM MVP Guido Preite  came to know the beauty of webapi on JavaScirpt. Smart code and quick development by XRM.WebApi wrapper for server side calls on clientside. Amazing part is, response is quite fast.

 

function CreateRecord() {

var data =

{

“name”: “Sample Account”,

“primarycontactid”:

{

“firstname”: “John”,

“lastname”: “Smith”

},

“opportunity_customer_accounts”:

[

{

“name”: “Opportunity associated to Sample Account”,

“Opportunity_Tasks”:

[

{ “subject”: “Task associated to opportunity” }

]

}

]

}

 

// create account record

Xrm.WebApi.createRecord(“account”, data).then(

function success(result) {

alert(“Account created with ID: ” + result.id);

// perform operations on record creation

},

function (error) {

alert(error.message);

// handle error conditions

}

);

}

 

function deleteRecord() {

var id = “94ab9061-781d-e911-a981-00224800c940”;

Xrm.WebApi.deleteRecord(“account”, id).then(

function success(result) {

alert(“Account deleted”);

// perform operations on record deletion

},

function (error) {

alert(error.message);

// handle error conditions

}

);

}

 

function updateRecord() {

var id = “c8714c94-8a1d-e911-a983-00224800c5df”;

// define the data to update a record

var data =

{

“name”: “Updated Sample Account “,

“creditonhold”: true,

“address1_latitude”: 47.639583,

“description”: “This is the updated description of the sample account”,

“revenue”: 6000000,

“accountcategorycode”: 2

}

// update the record

Xrm.WebApi.updateRecord(“account”, id, data).then(

function success(result) {

alert(“Account updated”);

// perform operations on record update

},

function (error) {

alert(error.message);

// handle error conditions

});

}

function retrieveRecord() {

var id = “c8714c94-8a1d-e911-a983-00224800c5df”;

Xrm.WebApi.retrieveRecord(“account”, id, “?$select=name&$expand=primarycontactid($select=contactid,fullname)”).then(

function success(result) {

alert(“Retrieved values: Name: ” + result.name + “, Primary Contact ID: ” + result.primarycontactid.contactid + “, Primary Contact Name: ” + result.primarycontactid.fullname);

// perform operations on record retrieval

},

function (error) {

alert(error.message);

// handle error conditions

}

);

}

 

Further read // JavaScript source code works for v9.1 https://docs.microsoft.com/en-us/dynamics365/customer-engagement/developer/clientapi/reference/xrm-webapi

ping me if you have any queries, hope this helped you.

Thank you!

Unit testing in Data migration different to Unit testing on Application

Hi folks,

From application driven programming background, I always knew Unit Testing is proxy tests we do on the application, purely by developers with some sample data to validate whether the application is working as expected. If we come with the same concept of unit testing on Data migration it’s bonkers! #Mind your software language

Unit testing on Data migration is part of testing data migration, where we test the object count; comparing source and target i.e. schema test. So it’s nothing to do with Data, nor the sample data we pass to test the scripts whether it gives expected output or not.

Further read from experts

https://technet.microsoft.com/en-us/library/bb497068.aspx

Plugin to upload file in SharePoint integrated with CRM 2016

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Sdk.Messages;
using System.Runtime.Serialization;
using System.Security;
using System.ServiceModel;
using Microsoft.SharePoint.Client;
using Microsoft.Xrm.Sdk.Metadata;
using Microsoft.Xrm.Sdk.Query;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace PreAnnotationPlugin
{
public class PreAnnotationCreate : IPlugin
{

public void Execute(IServiceProvider serviceProvider)
{

//Please make sure CRM and sharepoint integration Oob is completed
//This is plugin code to trigger when new annotation record has attachment created, then it uploads the file to sharepoint, even can create custom folder
//The below code reads the filename and attachment, converts to bytes, pass the data to Sharepoint and uploads via Json rest End point
//Env: CRM 2016 onpremise, Sharepoint 2013 onpremise, gac register on CRM server Microsoft.SharePoint.Client.dll, microsoft.sharepoint.client.runtime.dll, Newtonsoft.Json.dll
//make sure the Sharepoint link “http://siteurl&#8221; is working from CRM server locally

Guid contactId;
string SiteUrl = “http://siteurl&#8221;;
string spCRMDomain = “domain”;
string spCRMUsername = “username”;
string spCRMPassword = “pass”;
Guid regardingObjId = Guid.Empty;
Guid supplierid = Guid.Empty;
string supplierName = string.Empty;
Guid certificateGuid = Guid.Empty;
Guid spDocLocDefaultCertId = Guid.Empty;
string spsiteName = string.Empty;
string folderpath = “account”; //Default
string regardingObjLogicalName = string.Empty;
string CertificateName = string.Empty;
Guid spsiteId = Guid.Empty;
Guid spDocLocAccId = Guid.Empty;
Guid spDocLocCertId = Guid.Empty;
string ctfoldername = “”;
string filename = string.Empty;
string documentBody = string.Empty;

IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof (IPluginExecutionContext));
IOrganizationServiceFactory serviceFactory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof (IOrganizationServiceFactory));
IOrganizationService service = serviceFactory.CreateOrganizationService(context.UserId);
ITracingService tracing = (ITracingService)serviceProvider.GetService(typeof(ITracingService));

if (context.InputParameters.Contains(“Target”) && context.InputParameters[“Target”] is Entity)
{
try
{

Entity postMessageImage = (Entity)context.PostEntityImages[“PostImage”];

//use post image and read the values of filename an document body

tracing.Trace(“Entered the PrenotePluginstep1”);
Entity entity = (Entity) context.InputParameters[“Target”];

//convert the document to bytes
byte[] documentBytes = Convert.FromBase64String(documentBody);

//Get the root spspite that is default
string spSiteFetchXml = string.Format(@”

“, SiteUrl);

EntityCollection SpSiteDefaultist = service.RetrieveMultiple(new FetchExpression(spSiteFetchXml));

if (SpSiteDefaultist.Entities.Count > 0)
{

//Default spsiteId for http://siteurl
spsiteId = SpSiteDefaultist[0].Id;

//Get the related Certificate record
if (postMessageImage.Attributes.Contains(“objectid”))
{
regardingObjId = ((EntityReference)(postMessageImage.Attributes[“objectid”])).Id;
}

if (postMessageImage.Attributes.Contains(“objectid”))
{
regardingObjLogicalName = ((EntityReference)postMessageImage.Attributes[“objectid”]).LogicalName;

if (regardingObjLogicalName == “new_customentity”)
{

string customEntityFetchXml = string.Format(@”

“, regardingObjId);

EntityCollection customEntityList =
service.RetrieveMultiple(new FetchExpression(customEntityFetchXml));

if (customEntityList.Entities.Count > 0)
{
//get the values from the querylist
}
}
}

string customEntityrecordUrl = string.Format(“{0}_{1}”, customEntityRecordName, cusotmEntityRecordId).Replace(“-“, “”).Replace(“/”, “”).Replace(“:”, “”).Replace(“.”, “”).Replace(” “, “”).ToUpper();

//Get Default Sharepoint document location for custom record
string splocationCertfetchXml = string.Format(@”

“, regardingObjId);

EntityCollection splocCertlist = service.RetrieveMultiple(new FetchExpression(splocationCertfetchXml));

if (splocCertlist.Entities.Count > 0)
{
//found the location
spDocLocCertId = splocCertlist[0].Id;
}
else
{//create the docloc for new_customEntity record

string certificateUrl = string.Format(“{0}_{1}”, customEntityRecordName, cusotmEntityRecordId).Replace(“-“, “”).Replace(“/”, “”).Replace(“:”, “”).Replace(“.”, “”).Replace(” “, “”).ToUpper();

//GetDefaultSplocation for new_customEntity record
string splocationaccountfetchXml = string.Format(@”

“, supplierid);

EntityCollection splocAccountlist = service.RetrieveMultiple(new FetchExpression(splocationaccountfetchXml));

if (splocAccountlist.Entities.Count > 0)
{
//if found get the folder name or library name
spDocLocAccId = splocAccountlist[0].Id;

//create the Sharepoint folder for the custom entity

using (ClientContext clientContext = new ClientContext(SiteUrl))
{
SecureString passWord = new SecureString();

foreach (char c in spCRMPassword.ToCharArray()) passWord.AppendChar(c);

clientContext.Credentials = new NetworkCredential(spCRMUsername, spCRMPassword, spCRMDomain); //new SharePointOnlineCredentials(crmusername, passWord);
var folder = CreateSharePointFolder(SiteUrl, spCRMUsername, spCRMPassword, (string)splocAccountlist[0].Attributes[“relativeurl”], certificateUrl);

}
}
else
{
//Create the SharePoint folder for account and new_customentity entity

string accountURL = string.Format(“{0}_{1}”, accountName, accountId).Replace(“-“, “”).Replace(“/”, “”).Replace(“:”, “”).Replace(“.”, “”).Replace(” “, “”).ToUpper();

using (clientcontext clientcontext = new clientcontext(siteurl))
{
securestring password = new securestring();

foreach (char c in spcrmpassword.tochararray()) password.appendchar(c);

clientcontext.credentials = new networkcredential(spcrmusername, spcrmpassword, spcrmdomain); //new sharepointonlinecredentials(crmusername, password);
var folder = createfolder(clientcontext.web, “organisation”, accounturl);
folderpath = folder.name;
folder = createfolder(clientcontext.web, folderpath, certificateurl);
ctfoldername = folder.name;

}
}
}
}

bool filestatus = UploadUsingRest(“http://siteurl&#8221;, “new_customentity”, documentBytes, filename);

tracing.Trace(“After UploadUsingRest”);

// throw new Exception();

}
catch (InvalidPluginExecutionException exception)
{

tracing.Trace(“Error exception” + exception.message);
throw new InvalidPluginExecutionException(exception.Message);
}

}
}

public static bool UploadUsingRest(string siteurl, string libraryName, byte[] documentBytes, string fileName)
{

// tracing.Trace(“Entered the PrenotePluginstep1”);
bool status = false;
// byte[] binary = System.IO.File.ReadAllBytes(filePath);
// string fileName = System.IO.Path.GetFileName(filePath);
string result = string.Empty;
//Url to upload file
string resourceUrl = siteurl + “/_api/web/GetFolderByServerRelativeUrl(‘” + libraryName + “‘)/Files/add(url='” + fileName + “‘,overwrite=true)”;
HttpWebRequest wreq = HttpWebRequest.Create(resourceUrl) as HttpWebRequest;
wreq.UseDefaultCredentials = false;
//credential who has edit access on document library
NetworkCredential credentials = new System.Net.NetworkCredential(“username”, “passWord”, “domain”);
wreq.Credentials = credentials;

//Get formdigest value from site
string formDigest = GetFormDigestValue(siteurl, credentials);
wreq.Headers.Add(“X-RequestDigest”, formDigest);
wreq.Method = “POST”;
wreq.Timeout = 1000000; //timeout should be large in order to upload file which are of large size
wreq.Accept = “application/json; odata=verbose”;
wreq.ContentLength = documentBytes.Length;
try
{
using (System.IO.Stream requestStream = wreq.GetRequestStream())
{
requestStream.Write(documentBytes, 0, documentBytes.Length);
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);
}

try
{
WebResponse wresp = wreq.GetResponse();
using (System.IO.StreamReader sr = new System.IO.StreamReader(wresp.GetResponseStream()))
{
result = sr.ReadToEnd();
status = true;
return status;
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);

}
}

private static string GetFormDigestValue(string siteurl, NetworkCredential credentials)
{
string newFormDigest = “”;
HttpWebRequest endpointRequest = (HttpWebRequest)HttpWebRequest.Create(siteurl + “/_api/contextinfo”);
endpointRequest.Method = “POST”;
endpointRequest.ContentLength = 0;
endpointRequest.Credentials = credentials;
endpointRequest.Accept = “application/json;odata=verbose”;

try
{
HttpWebResponse endpointResponse = (HttpWebResponse)endpointRequest.GetResponse();
}
catch (Exception ex)
{
throw new Exception(ex.Message);

}

try
{
WebResponse webResp = endpointRequest.GetResponse();
Stream webStream = webResp.GetResponseStream();
StreamReader responseReader = new StreamReader(webStream);
string response = responseReader.ReadToEnd();
var j = JObject.Parse(response);
var jObj = (JObject)JsonConvert.DeserializeObject(response);
foreach (var item in jObj[“d”].Children())
{
newFormDigest = item.First()[“FormDigestValue”].ToString();
}
responseReader.Close();

}
catch (Exception ex)
{

throw new Exception(ex.Message);

}

return newFormDigest;
}

private static Folder CreateFolder(Web web, string listTitle, string fullFolderUrl)
{
if (string.IsNullOrEmpty(fullFolderUrl))
throw new ArgumentNullException(“fullFolderUrl”);

var list = web.Lists.GetByTitle(listTitle);
if (list == null)
{
return null;
}
var curFolder = list.RootFolder.Folders.Add(fullFolderUrl.ToString());
web.Context.Load(curFolder);
web.Context.ExecuteQuery();
return curFolder;
}

public static string CreateSharePointFolder(string sharepointsite, string crmusername, string crmpassword, string mainfolder, string subfolder)
{
string status = string.Empty;

if (string.IsNullOrEmpty(sharepointsite) || string.IsNullOrEmpty(crmusername) || string.IsNullOrEmpty(crmpassword) || string.IsNullOrEmpty(mainfolder) || string.IsNullOrEmpty(subfolder))
return string.Empty;

try
{
using (ClientContext clientContext = new ClientContext(sharepointsite))
{
SecureString passWord = new SecureString();

foreach (char c in crmpassword.ToCharArray()) passWord.AppendChar(c);

clientContext.Credentials = new NetworkCredential(“username”, “password”, “domain”); //new SharePointOnlineCredentials(crmusername, passWord);
var folder = CreateFolder(clientContext.Web, mainfolder, subfolder);
status = folder != null ? folder.Name : string.Empty;
}
}
catch (Exception ex)
{
throw new Exception(ex.Message);

}
return status;

return null;
}

}
}

Create folder in SharePoint 2013 programmatically with CRM 2016 integrated

Hello there,

Below is the console app, add references ‘Microsoft.SharePoint.Client.dll’, ‘Microsoft.SharePoint.Client.Runtime.dll’. Code can be used in CRM 2016 plugin, which I tried and it is working.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;
using System.Runtime.Serialization;
using System.Security;
using System.ServiceModel.Dispatcher;
using Microsoft.Xrm.Sdk;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.SharePoint.Client;

namespace SampleSP1
{
class Program
{
static void Main(string[] args)
{

CreateSharePointFolder(“http://siteurl&#8221;, “username”, “password”, “account”, “Fourth Coffee”);

}

public static string CreateSharePointFolder(string sharepointsite, string crmusername, string crmpassword, string mainfolder, string subfolder)
{
string status = string.Empty;

if (string.IsNullOrEmpty(sharepointsite) || string.IsNullOrEmpty(crmusername) || string.IsNullOrEmpty(crmpassword) || string.IsNullOrEmpty(mainfolder) || string.IsNullOrEmpty(subfolder))
return string.Empty;

try
{
using (ClientContext clientContext = new ClientContext(sharepointsite))
{
SecureString passWord = new SecureString();

foreach (char c in crmpassword.ToCharArray()) passWord.AppendChar(c);

clientContext.Credentials = new NetworkCredential( crmusername, crmpassword, domain); //new SharePointOnlineCredentials(crmusername, passWord);
var folder = CreateFolder(clientContext.Web, mainfolder, subfolder);
status = folder != null ? folder.Name : string.Empty;
}
}
catch (Exception ex)
{
return ex.Message;
}
return status;

return null;
}

private static Folder CreateFolder(Web web, string listTitle, string fullFolderUrl)
{
if (string.IsNullOrEmpty(fullFolderUrl))
throw new ArgumentNullException(“fullFolderUrl”);

var list = web.Lists.GetByTitle(listTitle);
if (list == null)
{
return null;
}
var curFolder = list.RootFolder.Folders.Add(fullFolderUrl.ToString());
web.Context.Load(curFolder);
web.Context.ExecuteQuery();
return curFolder;
}
}
}

Cheers