Archive for December, 2012

In Dynamics CRM 2011 Rollup 5, Microsoft introduced multi-series charts. Basically this means you can now graph 2 or more fields side by side so you can compare these values visually. One example of this would be Opportunities Est. Revenue vs. Actual Revenue, grouped by Owner. Using this example, we would have one Category (Horizontal) axis; Owner, and 2 Series (Vertical) axis; Est. Revenue and Actual Revenue. When the chart is previewed, the sum of both values for each user will be displayed side by side, to see which users are under or over estimating.

We can see in the image above the chart showing Est. Revenue vs. Actual Revenue, grouping by Owner. In the chart designer, we have defined the revenue fields as Legend Entries (Series), and the Owner as the Horizontal (Category) Axis.

You may have noticed in the first image, that there were 2 different scales, one for each of the series. In this case, the Est. Revenue scale goes up to 300,000 and the Actual Revenue scale to 200,000. This means the values being compared are not a direct comparison, as you can see in the first column the Actual Revenue which appears more than the Est. Revenue is actually less.

Fortunately we can modify the chart XML to allow the chart to use the same scale for both values. First you need to export the chart, which you can do in one of the following ways:

FOR PERSONAL CHARTS:

Expand and view the chart you want to export from an entity grid view. On the Charts tab of the ribbon, click the ‘Export Chart’ button. NOTE: To re-import the chart use the ‘Import Chart’ button above.

FOR SYSTEM CHARTS:

Select the chart from the Customization area/Solution for that entity. Click ‘More Actions’, and then ‘Export Chart’. NOTE: To re-import the chart use the ‘Import Chart’ option above.

When you export the chart, an xml file will be downloaded. Open this file (in notepad is fine). Within the <presentationdescription> tag, you will see a <series> tag containing another <series> tag for each of your series defined in the chart. On the second series, you will see an attribute called ‘YAxisType’. Remove this attribute so that it looks like the <series> tag above it.

<presentationdescription>
    <Chart Palette="None" PaletteCustomColors="55,118,193; 197,56,52; 149,189,66; 117,82,160; 49,171,204; 255,136,35; 97,142,206; 209,98,96; 168,203,104; 142,116,178; 93,186,215; 255,155,83">
      <Series>
        <Series ChartType="Column" IsValueShownAsLabel="True" Font="{0}, 9.5px" LabelForeColor="59, 59, 59" CustomProperties="PointWidth=0.75, MaxPixelPointWidth=40">
        <Series ChartType="Column" IsValueShownAsLabel="True" Font="{0}, 9.5px" LabelForeColor="59, 59, 59" CustomProperties="PointWidth=0.75, MaxPixelPointWidth=40" YAxisType="Secondary">
      </Series>

Save the XML file, and then import the file using the same method you used to export it. Browse to the XML file, and then click next. You will be prompted to Replace the existing chart, or to Keep Both. Choose ‘Replace’.

The chart will be updated, and you can now check the chart again to view the changes (make sure to publish system charts). You will notice the chart now uses only one axis, and the values are compared accurately.

The drilldown functionality will still be available, so users can click on any of the bars to view the records that make up that section. You can also still modify the chart using the CRM chart designer without effecting the modified XML.

Advertisements

this blog post I will be discussing a few ‘best practices’ when using Dynamics CRM 2011 Workflow Processes, and in particular when using the ‘Wait Condition’. I will assume that you’re familiar with the basics of how to use workflow wait conditions, and so I will be focusing purely on a few of the tricks that I’ve picked up over the years while using workflows, so that you can use them in the most effective manner possible.

1. TIMEOUT VS. WAIT

While a timeout is technically a wait condition, it appears differently once saved. Unlike a standard wait condition, a timeouts wait condition cannot later be modified, unless it is to simply change the date it is waiting until (as in, it must remain a ‘timeout’).

A timeout also cannot have multiple conditions defined within the same timeout; for example waiting until a date is reached and the status is complete (meaning the condition won’t be met until both of these are true).

So when should you use a Timeout and when should you use a Wait?

A ‘Timeout’ should be used when waiting until a date. For example, if you need to wait until 1 day before the due date of an appointment to send a reminder email, you need to use a Timeout. Timeouts can also be used to wait a static number of days. For example waiting 7 days duration before performing some other logic.

A standard ‘Wait’ can be used to wait for any other conditions. For example, if you have an opportunity pipeline workflow which creates an activity, you can use a wait condition to wait until the activity is completed before continuing onto the next phase.

Avoid using Process Execution Time in wait conditions, as these conditions will never be met and the workflow will continue waiting forever. Instead, you should always timeout until a specific date using the form assistant or a static date.

2. ADD STEPS AFTER THE WAIT CONDITION INSTEAD OF INSIDE

Wait conditions with no steps defined inside the actual wait condition will still wait until the condition is met before processing any steps after the wait condition.

By using this approach, it means there aren’t as many nested conditions, which makes it a lot easier to make changes to the workflow later on. When you delete a wait condition, it also deletes any steps defined within the wait condition, which means you would have to recreate any of the steps defined within the wait condition. It also means you can add your steps in first, and then add in the wait condition later.

If you have a parallel wait condition, where one of the two branches end the workflow, you can add the steps to end the workflow inside the wait, and then the steps for the other wait can be added outside (after) the whole wait condition. This will never be hit when the other wait condition is met, as the workflow will have been ended.

3. WHAT IF THE DATE CHANGES OR THE SERVER GOES DOWN DURING A TIMEOUT?

If you’re using a Timeout to wait until a date, and that date changes, the timeout will automatically adjust and wait until the new date. Even if that date is changed to be in the past, the timeout will readjust to the new date, and will fire instantly.

If the server goes down, or the async service stops, any workflows that are ‘waiting’ will be resumed when the server/async service comes back up. If the server/async is down at the exact time as when a wait condition is waiting until, the workflow will continue again when the server/async comes back up, and it will process the wait conditions that were due during the down time.

4. TRIGGER WORKFLOWS ON CREATE WHEN USING A WAIT CONDITION

A Workflow triggered on Create will only ever run once for each record, whereas a workflow triggered by other means (such as field change) will fire a new instance of the workflow each time. If you are performing wait conditions, or sending emails for example, you don’t want these being sent twice for the same record.

If a date (such as an end date) won’t be entered right away, and you only want to start waiting when this date is entered, you can still trigger the workflow on create, and just timeout until this date. Even if there is no value, the workflow will wait until there is a value, and until that value is reached. If the workflow was triggered on change of the end date, you could potentially have multiple instances of the workflow running if the date were to change after it was initially set.

There are a few issues with this approach however. The workflows could potentially be waiting a long time before the date is set and reached, or the date could never be set at all, in which case the workflow would continue to wait forever. There are 2 ways to avoid these issues:

• Add a parallel wait branch to the wait condition, so you can wait a static length (such as 3 months after the created on). If the end date is not set and reached by that time the workflow will end, and optionally send someone an email or perform some other action.

• Trigger the workflow on change of the end date, but create a new hidden date field on the entity to prevent multiple instances of the workflow running at once.

In the workflow, before the wait condition, set the hidden date field to equal the end date. Add a parallel wait condition to wait until the end date does not equal the hidden date field. If this condition is met, end the workflow (as another instance of the workflow will now be running). This will ensure only 1 instance of the workflow is running per record.

5. CHECK THE DATE IS IN THE FUTURE BEFORE WAITING

If historic records are being created/updated, you may not want your workflow to continue processing. This would require a simple check condition before the wait condition to check Process Execution time is BEFORE the date you are waiting for. If the date is not in the future, the workflow would not continue.

This would mean that if your workflow was running on change of the date field, and it was changed to a date/time before today, the new instance of the workflow would be ended as the date is not in the future. Assuming you had ended any earlier workflows, this would mean the steps defined after the wait condition would not fire for that record.

This check would be completely optional and dependant of your particular requirements, as you may need the workflow to fire even on records where the date is set in the past.

NOTE: Without the check condition, a Timeout waiting until the End Time will be met straight away, even though the date has passed.

 

6. MAKE SURE THE DATE HAS A VALUE BEFORE WAITING

If you’re waiting until a date, make sure the date field contains data. If the field does not contain data, the workflow will error. This includes where the field initially has no value, and also where the field had a value, but was cleared before the wait condition was met.

To get around this, you can simply make the date field required, so that you know there will always be a value. Otherwise, you could add an extra check/wait condition into your workflow to make sure there is a value, or to wait until there is a value before waiting until the date.

You can also put logic in place to prevent the field value being cleared once it is set initially, so that the workflow won’t error while waiting for the date, but you can still add initial validation if the date won’t be entered right away.

In Dynamics CRM 2011, when you create an Invoice using Existing Products as the line items, these line items use the price from the related Product. If the price on the product changes, the Invoices that include the product will automatically be updated to include the new price. Note that this behaviour only occurs if the pricing on the Invoice has not been ‘locked’ – which means the prices are fixed (or confirmed), and so any changes to the product catalog will not modify this invoice.

This functionality of updating invoice products also applies to both Quotes and Orders, in that the line items using existing products will be automatically updated if the price list items change. Like invoice, these also have methods of ‘locking’ the pricing, which differs slightly for each.

QUOTE

While a Quote is in a draft state (as in, has not been ‘activated’), changes to a products price will automatically update the line items. Once the Quote has become ‘active’, changes to the product prices will no longer effect the quote line items.

If the Quote is ‘revised’, the status changes from Active back to Draft, and the quote line items will be updated to the latest product prices. Any product price changes will affect the line items again while the quote is draft.


ORDER

An Order that is created from a Quote will automatically be ‘locked’, however one that is created manually from scratch will start out unlocked. As with the quote, any product price changes while the order is not locked will automatically update the line items.

To lock the pricing on an Order, click the ‘Lock Pricing’ button on the ribbon. Once the pricing has been locked, it can be unlocked again by clicking the ‘Use Current Pricing’ button. Similar to the Quote, when you use current pricing on an Order, the prices for all related line items will be updated to the current product pricing.

In the Totals section of the Order, you will see a check box called ‘Prices Locked’, which indicates whether the Order pricing is currently locked or not.


INVOICE

When an Invoice is created from an Order, the ‘Prices Locked’ setting will NOT be mapped across. If the Order pricing was locking, the new Invoice pricing will not be locked by default.

The pricing can be toggled using the ribbon (just like the Order), there is a button to ‘Lock Pricing’ when the invoice is not locked, and a ‘Use Current Pricing’ button when the invoice pricing is locked. Like the Order, this will update all line items to the new prices.

Once an Invoice has been completed or paid, the prices will no longer be affected by product price changes.


While this feature can be quite useful if your product prices are constantly changing, it can be potentially dangerous as it can mess up your data if you forget to lock pricing on your existing quotes/orders/invoices. If you are ever modifying product prices, just be aware of this, and how it may affect your existing records.

If you need to check what rollup your CRM is on, the easiest way to do this is to open your CRM server and check ‘Programs and Features’, then ‘View Installed Updates’ from the left hand side. This will display the installed rollups for existing CRM components.


Of course, if you’re not using CRM on-premise, you won’t have access to the server. Luckily, there are other ways to find your version number using outlook or the web client.

To find the version number from outlook (2010):

  1. Click the file menu in outlook
  2. Select CRM from the menu
  3. Click About Microsoft Dynamics CRM

To find the version number from the web client:

  1. Click the file menu in the web client
  2. Select Help from the menu
  3. Click About Microsoft Dynamics CRM

The following window will appear, showing the build number for the CRM 2011 Server, and the CRM 2011 Outlook Client (if installed).


The following table outlines the rollup that matches each version number, including the release dates for each rollup, prerequisites, and a link to the KB article for more information:

Update Version Date Published KB Article Prerequisite
RC 5.0.9688.53 14 Dec 2010
RTM 5.0.9688.583 16 Feb 2011 KB2461082
Rollup 1 5.0.9688.1045 4 Apr 2011 KB2466084 RTM
Rollup 2 05.00.9688.1155 6 Jun 2011 KB2466086 RTM
Rollup 3 05.00.9688.1244 26 Jul 2011 KB2547347 RTM
Rollup 4 05.00.9688.1450 19 Sep 2011 KB2556167 RTM
Rollup 5 05.00.9688.1533 20 Oct 2011 KB2567454 RTM
Rollup 6 05.00.9689.1985
05.00.9690.1992
12 Jan 2012
20 Jan 2012
KB2600640 RTM
Rollup 7 05.00.9690.2165 23 Mar 2012 KB2600643 Rollup 6
Rollup 8 05.00.9690.2243 3 May 2012 KB2600644 Rollup 6
Rollup 9 Cancelled Rollup 6
Rollup 10 05.00.9690.2720 16 Aug 2012 KB2710577 Rollup 6
Rollup 10 v2 05.00.9690.2740 4 Oct 2012 KB2710577 Rollup 6
Rollup 11 05.00.9690.2835 11 Oct 2012 KB2739504 Rollup 6
Rollup 11 v2 05.00.9690.2838 (Client)
05.00.9690.2839 (Server)
15 Oct 2012 (Client)
22 Oct 2012 (Server)
KB2739504 Rollup 6

 

According to this table, the system in my screenshot above is on Rollup 11 v2 for both the Server and Outlook Client.

We built a custom Silverlight screen for one of our customers, which automatically opens when a record is opened. For some users however, the Silverlight screen would not open. The form loads fine, but the Silverlight just wouldn’t pop up. We tried reproducing this on our own computers, but could not achieve the same result. Even after checking the IE Pop-up Blocker, we could not produce the same issue.

After some investigation, we discovered that the Google Toolbar in IE has a pop-up blocker of its own that is preventing the Silverlight screen from opening. As you can see in the screenshot above, it mentions at the top that there was a Pop-up blocked by Google Toolbar, however this message is not displayed for long, and so it can be difficult to notice. There is another message at the bottom of the page: “Pop-ups were blocked on this page. Press the “Ctrl key when clicking to allow pop-ups.” – however doing this doesn’t actually seem to help.

Once we confirmed it was the Google toolbar, it was fairly simple to disable the pop-up blocker.

  1. Open IE.
  2. On the Google Toolbar, click ‘More’.
  3. Select Pop-up Blocker, and then click Pop-up Blocker again from the menu.

  1. Note: If Pop-up Blocker is not available under the ‘More’ menu, it is probably already being displayed on the toolbar.
  2. The pop-up blocker icon should appear on the toolbar, and should look like the icon below.

  1. Note: If the icon looks like this:  (meaning pop-ups are being blocked), click the icon to allow pop-ups.

When we open our record now, the Silverlight screen pops up correctly.

If there are still problems we can disable the Google toolbar completely by clicking the ‘X’ on the left side of the Google toolbar.

If you ever have the same problem where CRM screens are not opening for some users, check their add-ins/toolbars for IE, as there are other toolbars (such as yahoo) which have built in pop-up blockers as well.

Microsoft Dynamics CRM has encountered an error. Please tell Microsoft about this problem. Send Error Report, or Don’t Send.

If you receive a lot of errors like this while using Dynamics CRM 2011, you may already know that you can change your personal options to automatically send these error reports to Microsoft, or to never send this. This will mean those errors will not pop up on your screen anymore as they will automatically be handled. Most of the time however, we have several users who are all going to experience this same issue, and so we don’t want to have to go through each users login to change their personal options! Fortunately, there is a way to globally define this setting. This means you can decide to send or not send errors to Microsoft for all users, so that this type of error message doesn’t bother another user ever again!

To define this global setting, navigate to Settings, Administration, and then click on Privacy Preferences.

A dialog box will appear to specify your organizations privacy settings. Click on the ‘Error Reporting’ tab, and then select to specify the error notification preferences for all users. You can then choose whether to automatically send or not send error reports. The recommended setting here is to automatically send errors, so that Microsoft can fix these in future rollups.

Once you click ok, the changes will be applied. If you (or any other users) try and set their personal options now, the ‘Privacy’ tab will be removed, as it is now managed at the organization level. If any of those errors ever occur again in the future for any users, they will not be annoyed by a pop up message.

The following plugin can be registered on post-create of the Product entity, so that each time a Product is created in your Dynamics CRM 2011 system, a Price List Item is automatically generated for that product.

The Price List Item will include the List Price defined on the Product. If you only have one Price List in your system, the Price List Item will be linked to that Price List. If you have multiple Price Lists, the first Price List created in your system will be used.

The Default Unit defined on the Product will be used as the Unit on the Price List Item. If no List Price is defined on the Product, the Price List Item will still be created, but the price will be $0.00, which you can modify later.

The full plugin is as below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

namespace Tester.Plugin
{
    /// 
    /// When a Product is created, if there are Price Lists in the system automatically create a Price List Item for each
    /// plugin steps: product - create - post - sync
    /// 
    public class Plugin : IPlugin
    {
        public void Execute(IServiceProvider serviceProvider)
        {
            IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));
            IOrganizationServiceFactory factory = (IOrganizationServiceFactory)serviceProvider.GetService(typeof(IOrganizationServiceFactory));
            IOrganizationService service = factory.CreateOrganizationService(context.UserId);

            Entity target = context.InputParameters["Target"] as Entity;
            CreatePriceListItem(target, service);
        }

        private void CreatePriceListItem(Entity product, IOrganizationService service)
        {
            Money listPrice = new Money(0); //get the list price, or set to 0 if not set
            if (product.Attributes.Contains("price"))
            {
                listPrice = (Money)product.Attributes["price"];
            }

            EntityReference defaultUnit = (EntityReference)product.Attributes["defaultuomid"];
            
            QueryExpression query = new QueryExpression { EntityName = "pricelevel" };
            query.Criteria.AddCondition("statecode", ConditionOperator.Equal, 0);
            query.ColumnSet = new ColumnSet("pricelevelid");
            query.Orders.Add(new OrderExpression("createdon", OrderType.Ascending));

            EntityCollection priceLists = service.RetrieveMultiple(query);
            if (priceLists.Entities.Count > 0)
            {
                priceLists.Entities.ToList().ForEach(priceList =>
                {
                    Entity priceListItem = new Entity("productpricelevel");
                    priceListItem["amount"] = listPrice;
                    priceListItem["uomid"] = defaultUnit;
                    priceListItem["productid"] = new EntityReference("product", product.Id);
                    priceListItem["pricelevelid"] = new EntityReference("pricelevel", priceList.Id);

                    service.Create(priceListItem); //create the price list item
                });
            }
        }
    }
}