To enable the new lead experience, navigate to App Settings >> Lead + opportunity management in the Sales Hub App.
On enabling it, we get the option to specify whether the Account, Contact, and Opportunity records are created automatically or the user (seller) has the option to specify which record to create.
We also see the option to Copilot summarizing the case once a lead is qualified.
For opportunity, if we select Seller, we get an additional option to modify the Opportunity form by adding or removing fields and also the option to allow a maximum of 5 new opportunities during lead qualification.
Clicking on the Add or remove fields link opens the Opportunity qualify lead form for customization.
Let us see it in action.
On clicking the Qualify command, we can see the new Qualify lead dialog opened in the side pane.
It gives us the option to create a new Account, Contact, and/or Opportunity record.
For Account and Contact, we can specify an existing record or select none if we do not want the Account and Contact record to be created.
For opportunity, we can click on edit icon to update the details of the opportunity record to be created. It will open the Opportunity qualify lead form.
Similarly clicking on + New opportunity allows us to specify a value for the new opportunity record.
As we had selected the option of a maximum of 5 opportunities, we got the below message on trying to specify the 6th opportunity record.
“You can’t add more than 5 opportunities.”
Let us click on Qualify.
We can see below details, the link to the records generated, and the summary.
Clicking on Finish opens the 1st opportunity record. We can also see the summary added to it.
On setting all the options to Automatic,
we can see the below details in the Qualify lead dialog, all as read-only information.
Using the SuppressCallbackRegistrationExpanderJob optional parameter in our Request object, we can bypass the execution of the flows registered against that particular event/trigger.
We have a flow registered on the update event of the lead record.
Now let us create a lead record through SDK.
We can see our flow triggered.
Now let us add the optional parameter SuppressCallbackRegistrationExpanderJob and check
This time as expected our lead record was created but the flow was not triggered.
Below is our sample record for a custom table custom_contract for which we will be creating the folder in SharePoint and associated it with this record using the SharePoint Document Location record. We’d use the same naming pattern that CRM uses i.e. Name + ”_” + ”GUID”
Below we have passed the details of the record along with CrmServiceClient and SharePoint’s client context.
The below code will first check if there is already a SharePoint Document Location created by CRM using ends with GUID condition. If it doesn’t exist, we are using the name field plus GUID of the record to create the folder in SharePoint and then associate using SharePoint Document Location record. Also we have used Regex to make sure we replace the special characters with “-“ similar to what CRM does. We are also checking if the Folder exists with that name in SharePoint before creating one.
Below is the GUID / record we have specified while creating the SharePoint Document Location record, the SharePoint Document location configured for that particular table.
The sample code –
var authManager = new AuthenticationManager(applicationId, certPath, password, domain);
ClientContext clientContext = authManager.GetContext(siteUrl);
var serviceClient = new CrmServiceClient(connection);
if (serviceClient.IsReady == true)
{
var myCRMRecord = serviceClient.Retrieve("custom_contract",
new Guid("f655304e-045b-ef11-bfe2-000d3a9b4e06"), new ColumnSet("custom_name"));
CheckIfShareDocLocationExistElseCreateandAssociate(myCRMRecord, serviceClient, clientContext);
}
private static void CheckIfShareDocLocationExistElseCreateandAssociate(Entity myCrmRecord, CrmServiceClient serviceClient, ClientContext clientContext)
{
var querySharePointDocumentLocation = new QueryExpression("sharepointdocumentlocation");
querySharePointDocumentLocation.ColumnSet.AddColumns("name", "relativeurl", "parentsiteorlocation", "regardingobjectid");
querySharePointDocumentLocation.Criteria.AddCondition("regardingobjectid", ConditionOperator.Equal, myCrmRecord.Id);
querySharePointDocumentLocation.Criteria.AddCondition("relativeurl", ConditionOperator.EndsWith, myCrmRecord.Id);
var result = serviceClient.RetrieveMultiple(querySharePointDocumentLocation);
if (result.Entities.Count == 0)
{
var folderName = myCrmRecord.Attributes["custom_name"].ToString();
// this makes sure we replace the special characters with - similar to what CRM does when autocreating the record.
string regexPattern = @"[~#%&*:<>?/\\{|}.""-]";
string replacement = "-";
folderName = Regex.Replace(folderName, regexPattern, replacement) + "_" + myCrmRecord.Id.ToString().ToUpper();
var folderRelativeURL = "custom_contract/" + folderName;
Web currentWeb = clientContext.Web;
var folderExists = currentWeb.DoesFolderExists(folderRelativeURL);
if (!folderExists)
{
var list = clientContext.Web.Lists.GetByTitle("Documents");
list.RootFolder.Folders.Add(folderRelativeURL);
clientContext.ExecuteQuery();
}
var documentLocation = new Entity("sharepointdocumentlocation");
documentLocation["name"] = "Documents on Default Site 1";
documentLocation["relativeurl"] = folderName;
// specifying the GUID of the SharePoint Document Location configured for that table
documentLocation["parentsiteorlocation"] = new EntityReference("sharepointdocumentlocation", new Guid("cf97e6d8-0b4e-ef11-a317-6045bdd74f46"));
documentLocation["regardingobjectid"] = new EntityReference("custom_contract", myCrmRecord.Id);
serviceClient.Create(documentLocation);
}
}
While trying to load all the files in a particular folder for processing, we got the below error
The attempted operation is prohibited because it exceeds the list view threshold – Microsoft.SharePoint.SPQueryThrottledException
The folder we were targeting contained 25K files in it. (the default threshold – 5000 items)
So we updated our code to make use of CamlQuery to retrieve the files in batch along with the listitemcollectionpostion object to loop and process through all the files.
Below is the sample code for reference –
var applicationId = "d7eaeeb7-ef0a-474d-9b94-567013576c14";
var password = "password";
var domain = "xyz.onmicrosoft.com";
var siteUrl = "https://xyz.sharepoint.com/sites/MyTeamSite";
var certPath = @"C:\SharePointApp\MyTestCertificate.pfx";
var folderRelativeUrl = "xyz/correspondences";
var authManager = new AuthenticationManager(applicationId, certPath, password, domain);
ClientContext clientContext = authManager.GetContext(siteUrl);
var folder = clientContext.Web.GetFolderByServerRelativeUrl(folderRelativeUrl);
clientContext.Load(folder);
// replaced it with usage of CamlQuery
//context.Load(folder.Files);
//context.ExecuteQuery();
ListItemCollectionPosition position = null;
do
{
CamlQuery camlQuery = new CamlQuery
{
ViewXml = $@"<View Scope='RecursiveAll'>
<Query>
<OrderBy>
<FieldRef Name='ID' Ascending='TRUE'/>
</OrderBy>
</Query>
<RowLimit>500</RowLimit>
</View>",
ListItemCollectionPosition = position
};
var listItems = folder.ListItemAllFields.ParentList.GetItems(camlQuery);
clientContext.Load(listItems);
clientContext.ExecuteQuery();
position = listItems.ListItemCollectionPosition;
foreach (ListItem listItem in listItems)
{
// To process only the files (not folder)
if (listItem.FileSystemObjectType == FileSystemObjectType.File)
{
var file = listItem.File;
clientContext.Load(file);
clientContext.ExecuteQuery();
ProcessFiles(file);
}
}
} while (position != null);
We were using the below code to create a sharepointdoucmentlocation record through a C# Console App.
private void CreateAssociateSharePointDocumentLocation(string folderName, Guid recordGuid, ServiceClient serviceClient)
{
var documentLocation = new Entity("sharepointdocumentlocation");
documentLocation["name"] = "Documents on Default Site 1";
documentLocation["relativeurl"] = folderName;
documentLocation["parentsiteorlocation"] = new EntityReference("sharepointdocumentlocation", new Guid(parentSiteorLocation));
documentLocation["regardingobjectid"] = new EntityReference("schemanametable", recordGuid);
serviceClient.Create(documentLocation);
}
For folderName we were using the below format, similar to what CRM does, when someone opens the document tab for the record, to create the sharepointdocument location record.
{name} + “_” + {GUID}
However, while creating one particular record we got the below error which was because the name field had “&” in it.
The relative URL contains invalid characters. Please use a different name. Valid relative url names cannot end with the following strings: .aspx, .ashx, .asmx, .svc, cannot begin or end with a dot, cannot contain consecutive dots, and cannot contain any of the following characters: ~ ” # % & * : ? / \ { | }.
As the error message suggests we need to either remove/replace the special characters in the name field before creating the folder in SharePoint and associating it with the SharePoint Document Location record in the CRM.