Friday, January 30, 2015

How to send an email via workflow without using the Send Email step (use create step instead)

You might already wonder: why the heck wouldn’t I just use the awesome “Send Email” step available in the workflow designer?! This post explains why but how to easily work around it.

We recently came across a scenario in which we wanted to automate some emails via workflows. However, we also needed to add some attachments to those emails based on some logic. Unfortunately in the current CRM workflow designer you cannot have any logic to add dynamic attachments to your email when you use the “Send Email” step of a workflow definition. However, we knew we could develop a simple custom workflow activity that could add attachments to the email. The problem was: If you use a Send Email step, the email is sent immediately and you have no way modify the email with attachments.

So the easy work-around is to first use the “Create” step in the workflow designer to first create an instance of the email an populate all the fields as desired. Then you can add additional steps that take actions on those emails, for example, a custom step that adds attachments to it. Because the records from the “Create” steps are available in all subsequent steps in the workflow, you can always reference it back and update it as many times as you want.

Once you have manipulated your email and added the relevant attachments with a custom step you are finally ready to send it, and this is the tricky part. How can you add a step to send an existing draft email? Well, certainly you could develop another custom workflow activity to do so. However, the solutions turns out to be much easier: All you need to do is set the status of the email to “Pending Send” and set the No. of Delivery Attempts to 0. Once you do this, then the email router will be ready to deliver the email (of course this is assuming you have configured your email router!).

So with a simple “Change Status” step and a subsequent “Update” step you can force the workflow to send the email you created!

image

image

Wednesday, November 19, 2014

Avoiding form reload when switching CRM forms based on a field

Quite often we define different forms for a given entity and we do a form switch based on a field rather than based on the security role of the user. The problem we observe with this approach is that there is often a double form load because when the wrong form loads then record re-loads using the appropriate form. This post offers a solution for avoiding the double form load in this scenario.

Say you have different types of opportunity which are identified by an “Opportunity Type” field and each opportunity type has its own form and BPF. The traditional approach would be to have a JavaScript on load of the form which will navigate to the appropriate form. The issue is that CRM web application loads by default the form that was last used by the given user. So if the user was looking at an opportunity of type “New Sale” and then opens an opportunity of type “New Service Contract” then the service contract opportunity will first load using the “new sale” form and after that form is loaded it will open the correct “service contract” form. The effect is bad user experience because the form loads twice so it doubles the form load time.

I have come across a solution that prevents this double form loading from happening which was suggested by a colleague. The solution is based on the premise that we can change which is the last viewed form for a given user on the fly when the user is retrieving a record. So you can intercept the retrieve operation from a plugin and then look at the type of opportunity that the user is retrieving and then update the user’s last viewed form to be the correct form for the opportunity type that is just getting retrieved. This way, the opportunity is always loaded in the correct form without a form switch.

The solution is a two-step process for the retrieve plugin:

1. In the pre-operation you must make sure to include the “opportunity type” field in the ColumnSet so that it is available later on for you to decide which is the appropriate form for the record.

2. In the post-operation you will have the “opportunity type” value and then you can update the user’s last viewed form to the correct form for the given opportunity type. To do so you simply need to modify the UserEntityUISettings entity for the given user and set the lastviewedformxml field.

Here is some sample code to use from your Retrieve plugin:

on Retrieve plugin
  1. var pluginContext = (IPluginExecutionContext)context;
  2. if (pluginContext.IsPreOperationStage())
  3. {
  4.     var columns = (ColumnSet)pluginContext.InputParameters["ColumnSet"];
  5.     if (!columns.Columns.Contains(OpportunityTypeAttributeName))
  6.         columns.AddColumn(OpportunityTypeAttributeName);
  7. }
  8. else if (pluginContext.IsPostOperationStage())
  9. {
  10.     var currentEntity = context.GetEntityFromContext();
  11.     if (currentEntity == null)
  12.         return;
  13.  
  14.     SetForm(currentEntity.ToEntity<Opportunity>(), service, context.UserId, tracingService);
  15. }

 

Set the correct form
  1. private void SetForm(Opportunity opp, IOrganizationService service, Guid userId)
  2. {
  3.     var query = new QueryExpression(UserEntityUISettings.EntityLogicalName);
  4.     query.Criteria.AddCondition("ownerid", ConditionOperator.Equal, userId);
  5.     query.Criteria.AddCondition("objecttypecode", ConditionOperator.Equal, Opportunity.EntityTypeCode);
  6.     query.ColumnSet = new ColumnSet("lastviewedformxml");
  7.     var settings = service.RetrieveMultiple(query).Entities;
  8.  
  9.     // Some users such as SYSTEM have no UserEntityUISettings, so skip.
  10.     if (settings == null || settings.Count != 1 || opp.pwc_OpportunityType == null) return;
  11.  
  12.     var setting = settings[0].ToEntity<UserEntityUISettings>();
  13.     string formToUse;
  14.     switch ((pwc_contractedengineservice)opp.pwc_OpportunityType.Value)
  15.     {
  16.         case pwc_contractedengineservice.NewSale:
  17.             formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", AdHocEngineServiceSaleFormId);
  18.             break;
  19.         case pwc_contractedengineservice.ServiceContract:
  20.             formToUse = String.Format("<MRUForm><Form Type=\"Main\" Id=\"{0}\" /></MRUForm>", ContractedEngineServiceSaleFormId);
  21.             break;
  22.         default:
  23.             return;
  24.     }
  25.     if (!formToUse.Equals(setting.LastViewedFormXml, StringComparison.InvariantCultureIgnoreCase))
  26.     {
  27.         // Only update if the last viewed form is not the one required for the given opportunity type
  28.         var s = new UserEntityUISettings { Id = setting.Id, LastViewedFormXml = formToUse };
  29.         service.Update(s);
  30.     }
  31. }

 

Regarding the supportability of this approach it seems to be a grey area. Technically you are doing operations via the SDK and using regular plugins which seems like a supported approach. The only problem is that the lastviewedformxml field of the UserEntityUISettings which is not documented in the SDK (although the entity seems valid for update).

A few things to consider is that if you are able to make this work with simple security roles then it would be simpler to configure the forms using security roles and you would not need this work-around. Consider as well whether you really need multiple forms or you can have other approaches such as JavaScript to hide/show sections depending on the type of record. I have posted some guidance regarding the different options available: http://gonzaloruizcrm.blogspot.ca/2014/07/different-entity-flavours-new-entity.html

Tuesday, October 14, 2014

Error when importing CRM solution 0x8004F658: The label {}, id: {} already exists. Supply unique labelid values.

I usually don’t post on CRM bugs but this one made me lose so much time that I figured I might share my experience.

We are in the middle of a CRM 2013 upgrade project and while transporting our main solution with all the entities we keep getting this error message: The label '{0}', id: '{1}' already exists. Supply unique labelid values while importing entity forms. I first thought that somehow we messed up the labels since we have a bilingual English and French system. However, after looking at the LocalizedLabel tables I could not find any duplicates of what the error message was talking about.

Then I decided to do a search in all the system forms in the target environment and check where the labelid occurred. I never really found a duplicate but I noiticed that in some cases the label already existed in the target environment in the formXML. This was as expected but to get over the problem I deleted the problematic forms in the target environment (since they will get overwritten by the new solution anyway). Some forms were the system forms so I could not really delete them, therefore I just stripped them down completely to remove all tabs, sections and fields as much as possible to make sure the custom labels would have been deleted.

Now I publish all the customizations and try importing again. Half of the failures are gone but I still get the same error message on some entity forms. I find it weird since the “duplicate” labels are nowhere to be found. I also validated that in my custoizations.xml the labelid was not duplicated, no, each GUID appeared only once.

With no time to call Microsoft support I had to find a work-around. So here’s what I do (not proud of the solution but it got me out of trouble). I open the customizations.xml file and locate the labelid which solution import is complaining about. And I simply generate a new GUID and replace it:

image

In some other cases there was not even a “labelid” attribute on the tab so I just added it with a new Guid. This happened in some tab, section and cell nodes, it was all over the place for some reason.

So after assigning a new Guid to all these sections I was able to import successfully. I never really had the time to go deeper into the root cause, but after talking to a colleague we suspect that the issue stems from the fact that we used the “Merge Forms” button that is available in CRM 2013 to help you upgrade your forms. The issue only occurred for the entities for which we used that feature so therefore our suspicion.

image

I will never be sure but I will try to avoid using that button until I have time to get back to the root cause analysis of this issue, lot of time wasted.

Monday, September 29, 2014

What’s up with CRM not working in latest version of Chrome?

You might have noticed that some features of CRM are no longer working after updating to Chrome 37. This posts provides some insight into work-arounds and possible solutions

All of the sudden seems like the CRM 2011 application is broken for Chrome for some features such as editing workflow send email steps. This is because CRM relies on a web API called showModalDialog() which Google Chrome is no longer supporting as of version 37, you can read more about it here. Note that it was deprecated since version 35 and Firefox has also deprecated that API.

So what can we do about it? Well, the easiest work around for now is to use Internet Explorer while you can find a more permanent solution. Microsoft has also published another temporary work-around so that you can continue to use Chrome: KB3000002. However, there are three bad news with that workaround. The first is that it requires each user to apply the work-around. The second is that users must have a highly privileged account on their work station in order to be able to apply the workaround, and we know that in the enterprise world, very few users will be given enough privileges or access to make the changes suggested by Microsoft. Finally the third bad news is that the work-around is only valid until May 2015!

Microsoft had quite a job to do in order to fix the entire CRM application to remove all the usage of showModalDialog(). This will probably take some time before we are able to apply a patch at the server level which will automatically fix the problem for all users. Perhaps before that happens there will be an easier work-around solution that Microsoft can come up with but don’t get your hopes up.

I guess this article is no good news for those who refuse to give IE a try, it’s one of the consequences of multi-browser support and Microsoft having little control on when other browsers drop support for API's that CRM relies on.

Wednesday, September 17, 2014

Should I use CRM personal views or system views?

When customizing Dynamics CRM the question often arises on whether to use System Views or Personal Views. They both have pros and cons that I will explore in this post.

Let’s first look at what is different. I had previously posted an article about the differences between personal and system views, here is a summary:

 

Personal

System

Ownership

Can be owned by a user or team

Owned by the organization

Visibility

By default it is only visible to the user who creates it and users/teams with whom it was shared

By default it is visible to all users.

Privileges

Can be protected using the standard privilege depths for the entity (none, user, BU, BU and child BU, Organization). This can allow you to make a chart/dashboard accessible to some users but not all and be able to select which users can see which charts/dashboards.

User access can only be configured to all or none (if a user has access to a system chart/view then the user will have access to ALL system charts/views.

Sharing

Can be shared with a specific user or team. For example the CEO might want to share a chart only with a VP.

Cannot share or unshare system views/dashboards/charts since they are all visible at the organization level (by everyone).

Solutions

Cannot be included in a solution. This is a show stopper if you need to move personal views/dashboards/charts across deployments and organizations. You would need to copy them manually. For charts, you can export the XML and import as a system chart.

System views/dashboards are solution aware and are fully supported to be transported in solutions.

CUD operations (Create, update, delete)

Most users will have access to create their own personal views, dashboards and charts.

Only high privileged users and system administrators should have access to CUD operations on system views, charts and dashboards.

 

The problem with system views is that:

  1. Requires IT to create/update and deploy the views
  2. Cannot define which users see the view, all users will see all system views.
  3. Can very quickly clutter the view selector with numerous views making usability a challenge when the users have too many views to choose from.

The problem with personal views is that:

  1. Often leads to excessive sharing, if every user creates views and shares them with the team then volume of views will grow very fast making it hard for users to find the views they actually use.N
  2. Once a view has been shared with you, you cannot “reject” it if you don’t want it. You would have to ask the view owner to un-share it with you.

 

We will explore more in details what best practices can be leveraged to reduce these problems:

  • If the view is only required for a small subset of users it is better to leverage shared personal views
  • If the view is to be changed often by business users then it is easier as personal view.
  • If the view is a default view that everyone needs and does not change often it is better as system view.
  • If your entity already has 10+ system views, you should consider whether you really need to add more system views or if you can manage at the personal view level.
  • If different users need to see different information (e.g. service vs. marketing user) for the same entity then you can leverage personal views shared with a team (service or marketing team)
  • There should be small number of users who are trained to create, maintain and share personal views. You should avoid everyone sharing their own views with everyone else.
  • All users should be trained to create their own personal views but be mindful before sharing it.
  • Before disabling a user in CRM please ask the user to delete, assign or un-share all personal views.
  • When sharing a view, make sure that you also share the “share” privilege so that way you give everyone the chance to opt-out to your view or share with other users:

image

 

What happens to personal views if user leaves the company (disabled user)

If a user has created views and the user has shared these views with multiple users then it can be a problem when the view owner leaves the company and the user is disabled because the shared views continue to be active and all users with whom the views were shared will continue to see those views. However, at this point it is not possible to delete or update the views that were created by a disabled user. If you find yourself in this situation you will have to open the disabled user and click on “Reassign Records” so you can reassign the personal view to a new owner. (Note: This will reassign all the records in the system, not just the system views).

image

 

How to reject personal views

If another user shared with you a view that you don’t want to see, you have 2 options:

  1. If the user who shared the view was kind enough to share with you the “share” privilege then you can easily opt-out to the view by removing yourself or your team from the sharing list.
  2. However, if you are less lucky and the view owner only shared “Read” privileges with you then you will have find out who is the owner of the view and ask them to remove you. To find the view owner go to Advanced Find, select the entity from the dropdown and click “Saved Views” button. Now you can find out who is the owner of the view that you don’t want and you can ask them to remove you.

 

How to assign a personal view

If you created a personal view and you no longer want to maintain it, you can assign it to another user by opening the list of your saved views and clicking “Assign Saved Views”

image

 

 

How can IT identify which personal views were shared and with whom

There is no easy way to find all the shared views in CRM, you can create a custom report in CRM or simply run the following query in your database:

 

select userquery.Name AS 'View Name', userquery.OwnerIdName as 'View Owner', SYSTEMUSER.FullName 'Shared with user', TEAM.Name as 'Shared with Team'

FROM principalobjectaccess

JOIN userquery on objectid = userquery.userqueryid

left outer JOIN SYSTEMUSER on principalid = SYSTEMUSER.SystemUserId

left outer JOIN TEAM on principalid = TEAM.TeamId

WHERE objecttypecode = 4230

 

This will give you a list of all the views, the view owner and the users/teams with whom each view is shared. You can use the SQL statement above to create a CRM report that is available to CRM users from the CRM application.

Monday, September 15, 2014

CRM Auto-numbering: What happens when you reach the maximum number

I’ve often been asked this question about the out-of-the box auto-numbering feature in CRM: What happens to my auto-number when I get over 99’999 cases in CRM?

Let’s look at how auto-numbering works out of the box. Each of the supported entities (contracts, cases, articles, quotes, orders, invoices and campaigns) have the following configuration:

image

Prefix: This is a 1-3 character prefix that you can use to identify which entity the number references. In the example above if you see INV-01000-AS7F you know that this number references an invoice because of the “INV” prefix. This prefix is configurable.

Number: This is a sequential number that will be incremented with each new record. You see this number holds between 4 and 5 digits. Hence the question of what happens if you have more records than the number of digits can support.

Suffix: This is a system-generated random number that is supposed to be unique. I t’s very obscure why there is a need for a suffix, unfortunately it is not configurable and you cannot remove it. All you can do is specify the length between 4 and 6 characters.

 

Initially I thought that when your number goes over 99999 then it will simply change to 100000. However, I could not find any documentation that specifies how this works behind the scenes or what to expect. So I had no choice but to confirm my theory by testing:

image

This is good news. so I even went ahead and tested what happens if you have 1 million cases and found the same result:

image

Therefore there is nothing to worry about (except the lack of documentation).

Monday, July 21, 2014

Mesa de Expertos CRM – Verano 2014

Este 22 de Julio no te pierdas la mesa de expertos sobre la importación de datos a CRM. Estaremos cubriendo 30 tips en 30 minutos, no me había imaginado la cantidad de consejos valiosos que existen sobre la importación de datos.

Como siempre, el evento es organizado por La Comunidad CRM y tiene como panelistas a varios MVPs de habla hispana:

  • Gus Gonzalez (MVP, Zero2Ten)
  • Damián Sinay (MVP, Remoting Coders)
  • Ramón Tebar (MVP, MetroBank)
  • Demian Raschkovan (MVP, Infoaván)
  • Gonzalo Ruiz (MVP, Avanade)
  • Pablo Peralta (MVP, Dynamix UruIT y CRMGamified)
  • Atilio Rosas (MVP, Consultor autónomo)
  • Jimmy Larrauri (Microsoft)

Para todos los detalles y para registrarte, has click aquí.