Bug: Appointment organizer not being set

This bug was one the toughest for me to identify and implement a workaround as

  • It happens only one time in each session
  • Quick glance at the form during create does not provide any clues regarding the bug

The bug is this: When a new appointment is saved, the organizer field is not set even though it shows the field being populated during form load and save.

Here is how the appointment form looks when you create a new appointment:

Organizer

The organizer field appears to be populated, but it is not. This is the dataxml:

<appointment>
	<instancetypecode>0</instancetypecode>
	<prioritycode>1</prioritycode>
	<scheduledstart>2017-07-07T11:30:00</scheduledstart>
	<ownerid type="8" name="Natraj Yegnaraman">{40327227-A570-E611-80FA-005056A6A11C}</ownerid>
	<scheduleddurationminutes>30</scheduleddurationminutes>
	<isalldayevent>0</isalldayevent>
	<scheduledend>2017-07-07T12:00:00</scheduledend>
	<statuscode>5</statuscode>
	<transactioncurrencyid name="Australian Dollar" type="9105">{F0B901CE-7692-E211-82F1-0050569B4EC3}</transactioncurrencyid>
	<isbilled name="">false</isbilled>
	<ismapiprivate name="">false</ismapiprivate>
	<attachmenterrors name="">0</attachmenterrors>
	<isworkflowcreated name="">false</isworkflowcreated>
</appointment>

Compare this with the dataxml when the organizer field is really populated:

<appointment>
	<instancetypecode>0</instancetypecode>
	<organizer>
		<activityparty>
			<partyid type="8" name="Natraj Yegnaraman">{40327227-A570-E611-80FA-005056A6A11C}</partyid>
		</activityparty>
	</organizer>
	<prioritycode>1</prioritycode>
	<scheduledstart>2017-07-07T11:30:00</scheduledstart>
	<ownerid type="8" name="Natraj Yegnaraman">{40327227-A570-E611-80FA-005056A6A11C}</ownerid>
	<scheduleddurationminutes>30</scheduleddurationminutes>
	<isalldayevent>0</isalldayevent>
	<scheduledend>2017-07-07T12:00:00</scheduledend>
	<statuscode>5</statuscode>
	<transactioncurrencyid name="Australian Dollar" type="9105">{F0B901CE-7692-E211-82F1-0050569B4EC3}</transactioncurrencyid>
	<isbilled name="">false</isbilled>
	<ismapiprivate name="">false</ismapiprivate>
	<attachmenterrors name="">0</attachmenterrors>
	<isworkflowcreated name="">false</isworkflowcreated>
</appointment>

Also, notice that the Organizer field does not have a person icon next to the name, like the Owner field.

This is fix I implemented to workaround the issue:

	var organizer = Xrm.Page.getAttribute('organizer').getValue();
	if(Xrm.Page.ui.getFormType() === 1 && !organizer){
		Xrm.Page.getAttribute('organizer').setValue(Xrm.Page.getAttribute('ownerid').getValue());
	}

The script basically runs on form save provided it is a new record, and copies the Owner lookup value to Organizer lookup, if the Organizer lookup does not contain any value.

The reason the Organizer field is important is because, without this being set, that appointment won’t show up in the appointment creator’s Outlook  (even though they created it and they are also the owner).

This issue is happening in 8.1.0 and has been possibly fixed in 8.2.0.

Bug: Email replies with default signature

There appears to be a bug that affects this email replies when:

  • The email was created in CRM2015 and org was upgraded to CRM2016 OR the email was received from an external party AND
  • The user replies to the email from CRM2016/Dynamics 365

Email Signatures were added to CRM in 8.1. This feature enables an user to quickly setup their email signature(s). The user also has the capability to setup an email signature as default, so that it is automatically inserted into the email body. The bug is in the functionality.

When the user creates a new email, CRM always inserts div tag with id signature.

Email Signature New Email

Why does it insert this tag even though no signatures have been setup yet? It serves as a marker to know where to insert the signature in the reply: whether in the top or in the bottom.

When the user replies to an email which has a div#signature, then everything is fine. The signature is appended to the top of the email.

Email Signature div id signature exists

This is because when CRM sees that the user is trying to reply to an email which has a div#signature, it modifies the id to “oldsignature”, and appends a new div#signature to the DOM.

Email Signature oldsignature div.png

If the incoming email does not have div#signature in the DOM, CRM does not insert any div#signature to the reply at all. Even if you manually click, the “Insert Signature” button in the command bar, it inserts the signature to the bottom of the email which is not correct.

Email Signature in the bottom of reply.png

Resolution: Nothing at the moment, till MS fixes this issue.

Bug: Branched Business Process Flow

Branched Business Process Flow was introduced in CRM 2015. I encountered a bug in BPF today, which took a day and a half to figure out. I am posting the scenario so that it will be beneficial for others who experience the same error. The bug is this:

When a lead is qualified an exception is thrown when:

  1. BPF has branches AND
  2. BPF has condition stages AND
  3. Some condition stages have only one output i.e only Yes or No
  4. Data in the form could lead to a condition block that will result in a dead end

This is the BPF, I created to test this bug.

Test BPF.png

As you can see I have a condition called “Existing account is not null”, and a stage if is true, not no stage if it is false. With this BPF an exception is thrown when the lead is qualified and Existing account is null. Below is the full error in Dynamics 365 Online.

lead-qualify-error

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
	<s:Body>
		<s:Fault>
			<faultcode>s:Client</faultcode>
			<faultstring xml:lang="en-US">An unexpected error occurred.</faultstring>
			<detail>
				<OrganizationServiceFault xmlns="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
					<ActivityId>8cf5f7f2-d08f-4e2d-9374-daf02247e5d0</ActivityId>
					<ErrorCode>-2147220970</ErrorCode>
					<ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic"/>
					<Message>An unexpected error occurred.</Message>
					<Timestamp>2017-02-09T06:30:37.8410512Z</Timestamp>
					<ExceptionSource i:nil="true"/>
					<InnerFault>
						<ActivityId>8cf5f7f2-d08f-4e2d-9374-daf02247e5d0</ActivityId>
						<ErrorCode>-2147220970</ErrorCode>
						<ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic"/>
						<Message>System.ArgumentNullException: Value cannot be null.
Parameter name: g</Message>
						<Timestamp>2017-02-09T06:30:37.8410512Z</Timestamp>
						<ExceptionSource i:nil="true"/>
						<InnerFault i:nil="true"/>
						<OriginalException i:nil="true"/>
						<TraceText i:nil="true"/>
					</InnerFault>
					<OriginalException i:nil="true"/>
					<TraceText i:nil="true"/>
				</OrganizationServiceFault>
			</detail>
		</s:Fault>
	</s:Body>
</s:Envelope>

This is the exception detail in CRM 2015.

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
	<s:Body>
		<s:Fault>
			<faultcode>s:Client</faultcode>
			<faultstring xml:lang="en-AU">An unexpected error occurred.</faultstring>
			<detail>
				<OrganizationServiceFault xmlns="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
					<ErrorCode>-2147220970</ErrorCode>
					<ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
						<KeyValuePairOfstringanyType>
							<a:key>CallStack</a:key>
							<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">   at Microsoft.Crm.Extensibility.VersionedPluginProxyStepBase.Execute(PipelineExecutionContext context)
   at Microsoft.Crm.Extensibility.PipelineInstrumentationHelper.Execute(Boolean instrumentationEnabled, String stopwatchName, ExecuteWithInstrumentation action)
   at Microsoft.Crm.Extensibility.Pipeline.Execute(PipelineExecutionContext context)
   at Microsoft.Crm.Extensibility.MessageProcessor.&lt;&gt;c__DisplayClass1.&lt;RunStage&gt;b__0()
   at Microsoft.Crm.Extensibility.PipelineInstrumentationHelper.Execute(Boolean instrumentationEnabled, String stopwatchName, ExecuteWithInstrumentation action)
   at Microsoft.Crm.Extensibility.MessageProcessor.RunStage(PipelineExecutionContext context, Int32 pipelineStage)
   at Microsoft.Crm.Extensibility.MessageProcessor.Execute(PipelineExecutionContext context)
   at Microsoft.Crm.Extensibility.InternalMessageDispatcher.Execute(PipelineExecutionContext context)
   at Microsoft.Crm.Extensibility.ExternalMessageDispatcher.ExecuteInternal(IInProcessOrganizationServiceFactory serviceFactory, IPlatformMessageDispatcherFactory dispatcherFactory, String messageName, String requestName, Int32 primaryObjectTypeCode, Int32 secondaryObjectTypeCode, ParameterCollection fields, CorrelationToken correlationToken, CallerOriginToken originToken, UserAuth userAuth, Guid callerId, Guid transactionContextId, Int32 invocationSource, Nullable`1 requestId, Version endpointVersion)
   at Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.ExecuteRequestRequestWithInstrumentation(OrganizationRequest request, CorrelationToken correlationToken, CallerOriginToken callerOriginToken, WebServiceType serviceType, UserAuth userAuth, Guid targetUserId, OrganizationContext context, Boolean returnResponse, Boolean checkAdminMode, Object operation)
   at Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.ExecuteRequest(OrganizationRequest request, CorrelationToken correlationToken, CallerOriginToken callerOriginToken, WebServiceType serviceType, UserAuth userAuth, Guid targetUserId, OrganizationContext context, Boolean returnResponse, Boolean checkAdminMode)
   at Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.ExecuteRequest(OrganizationRequest request, CorrelationToken correlationToken, CallerOriginToken callerOriginToken, WebServiceType serviceType, Boolean checkAdminMode)
   at Microsoft.Crm.Extensibility.OrganizationSdkServiceInternal.Execute(OrganizationRequest request, CorrelationToken correlationToken, CallerOriginToken callerOriginToken, WebServiceType serviceType, Boolean checkAdminMode)</a:value>
						</KeyValuePairOfstringanyType>
					</ErrorDetails>
					<Message>An unexpected error occurred.</Message>
					<Timestamp>2017-02-08T05:33:31.5174084Z</Timestamp>
					<InnerFault>
						<ErrorCode>-2147220970</ErrorCode>
						<ErrorDetails xmlns:a="http://schemas.datacontract.org/2004/07/System.Collections.Generic">
							<KeyValuePairOfstringanyType>
								<a:key>CallStack</a:key>
								<a:value i:type="b:string" xmlns:b="http://www.w3.org/2001/XMLSchema">   at System.Guid..ctor(String g)
   at Microsoft.Crm.ObjectModel.BusinessProcess.WorkflowConditionNextStageEvaluator.GetNextStage(StageStep stage)
   at Microsoft.Crm.ObjectModel.BusinessProcess.ActivePathEnumerator.MoveNext()
   at Microsoft.Crm.ObjectModel.BusinessProcess.ActivePathCalculator.Calculate(String targetEntityName)
   at Microsoft.Crm.Common.ObjectModel.NextProcessStageInformationFinder.CalculateGlobalStageAndTraversedInformation(String firstEntityName, String nextEntityName)
   at Microsoft.Crm.Common.ObjectModel.LeadService.QualifyLead(BusinessEntityMoniker leadId, Boolean createAccount, Boolean createContact, Boolean createOpportunity, BusinessEntityMoniker opportunityCurrencyId, BusinessEntityMoniker opportunityCustomerId, BusinessEntityMoniker sourceCampaignId, Int32 statusCode, ExecutionContext context)</a:value>
							</KeyValuePairOfstringanyType>
						</ErrorDetails>
						<Message>System.ArgumentNullException: Value cannot be null.
Parameter name: g</Message>
						<Timestamp>2017-02-08T05:33:31.5174084Z</Timestamp>
						<InnerFault i:nil="true"/>
						<TraceText i:nil="true"/>
					</InnerFault>
					<TraceText i:nil="true"/>
				</OrganizationServiceFault>
			</detail>
		</s:Fault>
	</s:Body>
</s:Envelope>

The root cause for this seems to be GetNextStage. It seems the method can’t figure out what the next stage should be, when the BPF ends with a condition with only one branch. I haven’t tested other scenarios where this error could be triggered, but I was able to consistently reproduce the error on lead qualify.

Unrelated Note:

It seems business process has a client side caching mechanism. I had to clear cache every time after I made change to the BPF to test this issue.

Bug: Notes creation in custom entity

There currently seems to be an issue in Dynamics 365 Online, where you can see notes in area in the custom entity, but cannot create new notes. I could reproduce this issue in this version -> Version 1612 (8.2.0.773) (DB 8.2.0.764)

This is how the notes area looks in a custom entity. There is no header area to create new notes.

missing-header-in-notes

Compare this with the contact entity.

notes-area-contact-entity

As you can see, there is a textarea to create new notes, which seems to be missing in the custom entity. I looked into this using DevTools, and it appears to be display issue, as the DOM elements for creating the notes are still there. I quickly wrote this bookmarklet to temporarily show the create new notes text area in the form.

Sourcecode

(function () {
	let contentPanels = Array.from(document.querySelectorAll('iframe')).filter(function (d) {
			return d.style.visibility !== 'hidden'
		});
	if (contentPanels && contentPanels.length > 0) {
		let activityWall = contentPanels[0].contentDocument.querySelector('#notesWall div.header');
		if(activityWall && activityWall.style.display === 'none'){
			activityWall.style.display = ''
		}
	} else {
		alert('Entity form not detected');
	}
})();

Bookmarklet

javascript:(function(){let contentPanels=Array.from(document.querySelectorAll('iframe')).filter(function(d){return d.style.visibility!=='hidden'});if(contentPanels&&contentPanels.length>0){let activityWall=contentPanels[0].contentDocument.querySelector('#notesWall div.header');if(activityWall&&activityWall.style.display==='none'){activityWall.style.display=''}}else{alert('Entity form not detected');}})();void 0;

This is how the custom form will look, after you run the bookmarklet.

after-bookmarklet-header-restore-in-notes

I am not sure what sets the “display” property to none. When the DOM element is initially created, it is created without the “display” property set and then some event handler appears to be modifying this. I set a DOM breakpoint on “Attributes modifications”, but the breakpoint is not retained when I refresh the page, and hence I am unable to catch the “display: none” being set on “#notesWall div.header”. I spent some time to investigate this from the pretty-printed source, but could not figure it out.

Issues in debugging custom workflow assemblies

Three months ago, I wrote a post about profiling workflows with custom activity step -> Debugging custom workflow assemblies

There were couple of comments in the post, about it not working as expected due to

  1. Different version of CRM and/or
  2. Different version of Plugin Registration tool

I will now give you the steps to get this working.

  1. Install LINQPad -> https://www.linqpad.net
  2. Install CRM Driver for LINQPad -> https://crmlinqpad.codeplex.com/
  3. After creating a connection to your CRM instance, run this LINQ query
    XNamespace mxswa = "{clr-namespace:Microsoft.Xrm.Sdk.Workflow.Activities;assembly=Microsoft.Xrm.Sdk.Workflow, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35}";
    var query = from w in WorkflowSet.AsEnumerable()
    where
    w.Name == "[YOUR WORKFLOW NAME WITH CUSTOM STEP]" &&
    w.FormattedValues["type"] == "Definition"
    && w.IsCrmUIWorkflow.GetValueOrDefault()
    orderby w.ModifiedOn descending
    select new {
    w.Id,
    w.Name,
    WorkflowVersion = XElement.Parse(w.Xaml).Attributes().FirstOrDefault(a => a.Name.LocalName == "mxswa").Value.Split(';')[1],
    CustomSteps = from a in XElement.Parse(w.Xaml).Descendants($"{mxswa}ActivityReference")
    			   where !a.Attribute("AssemblyQualifiedName").Value.Contains("Microsoft.Crm")
    			  select new {
    			  CustomStepName = a.Attribute("DisplayName").Value,
    			  AssemblyName = a.Attribute("AssemblyQualifiedName").Value,
    			  HasArguments = a.Descendants($"{mxswa}ActivityReference.Arguments").Any()}
    };
    query.Where(c=>c.CustomSteps.Any()).Dump();
    
  4. You’ll get something like this. Note that the workflow in my case is “Blank Workflow with Custom Step”. Change this to the one you are trying to profile.Query

Issue 1: Cannot see the workflow step.

Blank Step

If you cannot see the workflow step, that means that there is a mismatch between the Microsoft.Xrm.Sdk.Workflow.dll version and/or your custom workflow step version. Go the folder with the Plugin Registration tool and check the version of the Microsoft.Xrm.Sdk.Workflow.dll version. The important thing is if the major version or minor version of the Microsoft.Xrm.Sdk.Workflow.dll assembly in Plugin Registration tool folder, is different from the one in the Workflow XAML, you will not see the step displayed.

CRM 2016 Plugin Reg Tool
CRM 2016 Plugin Registration Tool
CRM 2016 Update 1 Plugin Reg Tool.png
CRM 2016 Update 1 Plugin Registration Tool

Compare this with the results of the LINQ query and confirm that the major version and minor version match. Do this same for your custom workflow assembly as well.

Issue 2: NullReferenceException

There is a bug in the Plugin Registration Tool, that doesn’t allow you to profile workflow steps, that doesn’t have any argument. If you try to do this, you will get this exception.

Null Reference.png

The workaround for this is to add a dummy argument, to keep the plugin profiler happy.

Workflow

Hope this helps with your debugging efforts.

Bug: Entity primary field & realtime workflows

When you create a new entity, one of the fields that is generally left unchanged is the primary field for the entity. It is usually “[publisherprefix]_name”.

image

This field can be either required or left optional. This value of this field, is what shows up in the entity lookups. The value of this field is usually set automatically using JavaScript, Workflow or Plugin. If the value for this field is left null, it manifests a bug in the realtime workflow.

What is the bug?

If the value of the primary field is null on a parent entity, any child entity can’t use “if condition” on the parent relationship field. It would result in a “KeyNotFound” exception.

Replication steps

  1. Create an entity that will be the parent entity
  2. Create an entity that will be the child. Create a relationship to the parent entity image
  3. Make the primary field on the parent entity as optional image
  4. Create a new realtime workflow on the child entity, with a condition statement that refers the parent lookup field in the child entity image
  5. Create a parent record without the name field setimage
  6. Create a child record that has the parent lookup set the record created in the previous step. The parent lookup will be displayed as “no name” as the primary field for the parent entity is not set.image
  7. Execute the realtime workflow against the child record in the previous step. A “The given key was not present in the dictionary” exception will be displayedimage

The issue is happening both in CRM 2015 and CRMOnline on version 8.1.0.362. This issue seems to be only affecting realtime workflows and actions, and not background workflows.

EDIT (24/06/2016): Issue logged on Connect -> https://connect.microsoft.com/site687/feedback/details/2851391/realtime-workflow-action-crash-display-name-if-condition-keynotfoundexception

Bug: EntityReference, Create and latebinding

For big projects and large code sizes, it is recommended to use strongly typed classes when you are dealing with CRUD operations related to the CRM entities. You can use Early Bound Generator or “crmsvcutil” to do this. The advantages are compile type errors for mismatches types and intellisense. But if you are quickly testing out some code, latebinding is the easiest path, as it doesn’t involve the overhead of generating the classes. During this process, I discovered a bug.

Here is the code and I will explain the bug after that.

            CrmServiceClient crmSvc = new CrmServiceClient(ConfigurationManager.ConnectionStrings["CRMConnectionString"].ConnectionString);
            if (crmSvc.IsReady)
            {
                #region create contact with account entityreference

                var account = new Entity("account");
                account["name"] = "Test Account";
                account.Id = crmSvc.OrganizationServiceProxy.Create(account);
                Console.WriteLine($"Created account with id {account.Id}");

                Console.WriteLine("Creating contact with account as EntityReference");
                Console.WriteLine("----------------------------------------------------");

                var contact = new Entity("contact");
                contact["parentcustomerid"] = account.ToEntityReference();
                contact.Id = crmSvc.OrganizationServiceProxy.Create(contact);
                Console.WriteLine($"Created contact with id {contact.Id}");

                contact = crmSvc.OrganizationServiceProxy.Retrieve("contact", contact.Id, new ColumnSet("parentcustomerid"));
                Console.WriteLine($"Contact: Account Lookup {contact.GetAttributeValue<EntityReference>("parentcustomerid")?.Id}");

                #endregion

                #region update contact with account Guid

                Console.WriteLine("Updating contact with account as Guid");
                Console.WriteLine("----------------------------------------------------");
                try
                {
                    contact["parentcustomerid"] = account.Id;
                    crmSvc.OrganizationServiceProxy.Update(contact);
                    var updateContact = crmSvc.OrganizationServiceProxy.Retrieve("contact", contact.Id, new ColumnSet("parentcustomerid"));
                    Console.WriteLine($"Updated Contact: Account Lookup {updateContact.GetAttributeValue<EntityReference>("parentcustomerid")?.Id}");
                }
                catch (Exception e)
                {
                    Console.WriteLine($"Update Contact failed with Exception: {e.Message}");
                }

                crmSvc.OrganizationServiceProxy.Delete("contact", contact.Id);
                Console.WriteLine($"Deleted contact with id {contact.Id}");

                #endregion

                #region create contact with account Guid

                Console.WriteLine("\nCreating contact with account as Guid");
                Console.WriteLine("----------------------------------------------------");
                contact = new Entity("contact");
                contact["parentcustomerid"] = account.Id;
                //This should not succeed
                contact.Id = crmSvc.OrganizationServiceProxy.Create(contact);
                Console.WriteLine($"Created contact with id {contact.Id}");

                contact = crmSvc.OrganizationServiceProxy.Retrieve("contact", contact.Id, new ColumnSet("parentcustomerid"));
                Console.WriteLine($"Contact: Account Lookup {contact.GetAttributeValue<EntityReference>("parentcustomerid")?.Id}");

                crmSvc.OrganizationServiceProxy.Delete("contact", contact.Id);
                Console.WriteLine($"Deleted contact with id {contact.Id}");

                crmSvc.OrganizationServiceProxy.Delete("account", account.Id);
                Console.WriteLine($"\nDeleted account with id {account.Id}");

                #endregion

            }

Here is the result.

Guid Entityreference result

Even if you are using latebinding, CRM webservice respects type and doesn’t allow you to set an attribute to a type, that doesn’t match its definition, except in one case: You can set the EntityReference attribute to a Guid, and the code with execute without any exception. The good thing though is the fields is just set to null, and the Guid is ignored, but this a bug as you cannot do it with other types e.g. you can’t set an Optionset field to an int or a Currency field to a decimal.

I have logged this on Connect for verification. I experienced this behaviour in CRMOnline and CRM2015.

Connect issue -> https://connect.microsoft.com/site687/feedback/details/2740732/sdk-permits-setting-entityreference-attribute-to-guid-late-binding

Further reading:

MSDN: Create, retrieve, update, and delete (late bound)