You are currently browsing the tag archive for the ‘Dynamics CRM 2011’ tag.

Other than using Bing, you can easily integrate other geo-location sources. One of the most enterprise level solution is of course Esri Maps. They have a very good solution for mapping. They can also be pricy, so if you are looking for a solution on the cheap, and are not afraid of doing very little coding, read on.

Google Maps provides a very robust mapping API. You can find more details HERE.

To integrate this into your existing CRM, you will use an iFrame, and a custom HTML web resource. Your HTML will include a div tag with an id of “geolocation”. You can use another id, we’re using “geolocation” in the context of this example. Basically, your div will look like so:

<div id="geolocation" style="width: 800px; height: 500px;"></div>

Define the style width and height as needed to render properly within the iFrame at a standard screen resolution set at the enterprise level.

Pass the context to the iFrame, so you can retrieve the address fields. You will use these to build the address string you pass to retrieve the longitude and latitude.

With the new v.3 of the maps API, you do not need a API Key anymore, which makes it a lot easier to move your solution between various environments, and to have the solution internal only (no IFD).

When calling the API, if your CRM is set to use HTTPS, use the HTTPS Google API URL, otherwise use the HTTP address.

Add in the header of your HTML web resource the reference to the JS library:

<script src="http://maps.googleapis.com/maps/api/js?v=3.exp&sensor=false"></script>

In this example I am using HTTP.

In your HTML web resource, first function you want to call on page load is a function that takes the value of the address fields, and concatenates it all into a string. It will be along the lines of:

var _address = street + city + province + country + postalCode;

check and use only the fields where you have a value populated.

Once you have this address as a string, pass it to the first function that converts the address into latitude and longitude. This will look as follows:

ParseAddress: function(address) {
    var url = "
http://maps.googleapis.com/maps/api/geocode/json?" +
    "address=" + address +
    "&sensor=false"

    var xhr = base.createCORSRequest(‘GET’, url);
    if (!xhr) {
        alert(‘CORS not supported’);
        return;
    }

    // Response handlers.
    xhr.onload = function () {
        var text = xhr.responseText;
        var _start = text.indexOf("\"location\" :") + 12;
        var _location = text.substring(_start, text.indexOf("}", _start) + 1);

        geoCode.Initialize(_location);
    };

    xhr.onerror = function () {
        $("#message-area").append("Woops, there was an error retrieving server data.");
    };

    xhr.send();
},

This example is using CORS. CORS stands for “Cross-Origin Resource Sharing”, and for more details on CORS see the W3C documentation HERE.

This function makes a call to the Google API and on successful retrieval of data, calls another function called Initialize, passing the trimmed returned string that includes the location lat and lng.

The Initialize function takes this location, builds the map representation, sets the zoom on it, and drops a pin at the location provided by the coordinates:

Initialize: function (_location) {

    var jsonData = $.parseJSON(_location);
    var _bGeoLocation = new Array();
    _bGeoLocation = jsonData;
 

    var myOptions = {
        zoom: 16,
        center: new google.maps.LatLng(_bGeoLocation .lat, _bGeoLocation .lng),
        mapTypeId: google.maps.MapTypeId.ROADMAP
    }
    // Draw the map
    var mapObject = new google.maps.Map(document.getElementById(“geolocation”), myOptions);
    // Place the marker
    new google.maps.Marker({ map: mapObject, position: userLatLng });

}

Et voila, now you have a map with a pin on it pointing to the record’s address.

image

Enjoy!

Advertisements

Today my book has been released. As I mentioned in an earlier post, it has been available for pre-orders for a short while, and now it’s out.

The book is available from the publisher’s website at:

http://www.packtpub.com/microsoft-dynamics-crm-2011-scripting-cookbook/book

8826EN

A big thank you goes to PAKT for great guidance on the publishing process, to the editors involved in the process, the technical reviewers and to Mark for the initial kick in the butt that got me started on this project.

Enjoy!

So, one of these days I had a pretty interesting case. Just to give you an overview, on-premise environment, custom security roles, activity feeds installed.

The issues was as follows:

Navigating to any of the main entities, going on the Ribbon and adding a file that way worked just fine.

Creating a note, then opening it and trying to add an attachment resulted in the following error:

image

Pretty interesting, so I go first to Notes, and the group permissions are as follows:

image

Ok then, so the problem is not there. Just for kicks, I checked also the Append and Append To on the hosting entities (Case, Account, Contact). All looking good.

Last resort, let’s have a look at the logs then. Just to make your life easier, since tracing is not enabled by default on the server, there is nice free tool that allows you with a click of a button to enable/disable tracing, among other things. It is called Diagnostics Tool for Microsoft Dynamics CRM 2011 and can be found on Codeplex at

http://crmdiagtool2011.codeplex.com/

Once you’ve got a hold of the trace file, start looking for errors. It appears that this particular issue resulted in a message of: “Principal user … is missing prvReadmsdyn_PostAlbum privilege” message.

This is related to the Activity Feeds. Basically, what is requires is this: in the security role your user is in, go to the Custom Entities tab, and look for Profile Album entity. Make sure that Read is set to at least User.

image

Now try adding a file again to an existing note. Everything works as expected.

Enjoy!

So we all know about the standard options for hosting Dynamics CRM 2011: Cloud and On-Premise. Some of us might have also heard of 3rd party hosting, based on the cloud model. But there are also other alternatives.

On-Premise Partner Hosted

Otherwise known as colocation, in this model the environment is hosted by a 3rd party hosting provider. On the partner’s end, it will look very much like an On-Premise deployment, most likely a multi-tenant one. Possible models are determine by individual SLA’s. We could have some choice over items like uptime (choosing one of the available options offered), authentication model used, as well as integrations options. Do not assume that a 3rd party hosting company will not be willing to go over and beyond to offer something you can’t get with the standard Cloud offering from Microsoft. Some of the providers actually offer additional services to differentiate themselves. This could include include anything from integration alternatives, additional customization packages, as well as public portal templates.

One of the more complex scenarios, including redundancy and allowing both internal and external access to the environment (including mobile access) can be described by the following diagram. Note that the SharePoint environment can be located in either the hosting partner’s environment or on site.

OnPremPartnerHostedRedundant

A less complex model, non-redundant system, is depicted by the next diagram. Just as before, the SharePoint environment can be located either On-Premise or with the 3rd party hosting provider.

OnPremPartnerHostedNonRedundant

Alternatively, if only internal access is required, the environment can look as follows

OnPremPartnerHostedRedundantInternal

or

OnPremPartnerHostedNonRedundantInternal

Hybrid Hosted Model

Alternatively, with less separation, we can look at a hybrid hosting model. In such scenario, part of the environment is hosted by a 3rd party hosting provider, while still integrating and allowing certain components to function from within the internal network.

While this approach could be more cost-efficient, a good relation with the hosting provider is essential in such scenario. The following diagram depicts a possible scenario with redundancy

HybridRedundantInternetAccess

or with no redundancy

HybridNonRedundantInternetAccess

Any of these models can also be configured with no external access. One simple scenario can be depicted as

HybridNoInternetAccess

I hope this helps some of you looking for alternatives.

Enjoy!

One basic requirement received not too long ago prompted me to think about how to make this look pretty. I needed to show states/provinces by penetration in a nicely formatted dashboard.

The prep. work includes the creation of 3 map images for each state/province, using a set of defined colors, let’s name them Red/Yellow/Green. The percentages are irrelevant, you can set them up as required.

The work around the images is pretty important, as we will need to save them with transparency, and properly aligned so they overlap nicely.

Additionally, we will create a html resource, which is the actual page that we load the images onto.

Scripting is also pretty simplistic. First off, we have a process behind that calculates the state/province rating based on a formula. We will only define a custom field on the Account form that will hold the resulting value, as one of the three colors (options).

As part of the setup, and in this case it worked based on the business model, we have one Account for each state/province, with multiple child accounts.

Next, all we have to do on load, is define based on the rating value, which one of the three images we will load. Thus, the script could look something like this:

if(_rating == "Red") {
    _html += "<img src=’prov_red.gif’ />";
}
else if (_rating == "Yellow") {
    _html += "<img src=’prov_red.gif’ />";
}
else if (_rating == "Green") {
    _html += "<img src=’prov_red.gif’ />";
}

Additionally, to make it more dynamic, on the image we can define hot spots (yeah, do you remember the old days of static html?) over each state province. Here’s a link to remind you how that’s done:

image-map

Each URL of the hot spot areas defined will point to one Account on the system. We can either do this static, or dynamic by looking up the account by name or abbreviation (e.g. AB, IL, FL, etc.) I prefer abbreviation, as it is less typing.

The end result could look something like the following image:

image

Additionally, since we already have the resources in place, we can also enhance each account with a small image of the state/province, colored according to the penetration rating.

image

The possibilities are endless.

Enjoy!

When adding members to a marketing list, among other things pay attention to the number of members added. By default, there is a limit set at 50,000.

If working with an on prem. deployment, this can be adjusted in the MSCRM_CONFIG database, in the DeploymentProperties table, under AggregateQueryRecordLimit. This value also controls dashboard chart queries, so choose carefully how you modify it.

More details on MSDN at gg334634.

Obviously you can not modify this for CRM Online.

Please note, this limit does not apply to Dynamic Marketing Lists.

Enjoy!

For the sake of standardizing the format for all system postal codes, I have implemented the following script to re-format the user input of postal codes for both US and Canada. The purpose is to take the user’s input, and re-arrange it to follow a standard format.

The comments are self-explanatory.

// Function to format postal code
// for both Canadian and US postal codes
function FormatPostalCode(context)
{
  var oField = context.getEventSource().getValue();
  var sTmp;
 
  if(typeof(oField) != "undefined" && oField != null)
  {
    // check for US ZIP code
    if(oField.match(/^[0-9]{5}$/))
    {
        context.getEventSource().setValue(oField);
        return true;
    }

    // check for Canadian postal code
    sTmp = oField.toUpperCase();
    if (sTmp.match(/^[A-Z][0-9][A-Z][0-9][A-Z][0-9]$/))
    {
        sTmp = sTmp.substr(0,3) + " " + sTmp.substr(3,3);
        context.getEventSource().setValue(sTmp);
        return true;
    }
    if (sTmp.match(/^[A-Z][0-9][A-Z].[0-9][A-Z][0-9]$/))
    {
        context.getEventSource().setValue(sTmp);
        return true;
    }

    // alert("Incorrect ZIP/Postal Code format.");
    // postal code could be any other country, so leave as is
  }
}

Add the function in a Web Resource, and reference it on the postal code’s OnChange event. As the script uses the context, make sure you select the “Pass execution context as first parameter” option.

image

Enjoy!

Integrating CRM Accounts with LinkedIn is a very easy step. I have found numerous examples on doing this by using a Web Resource, but I don’t like the way it displays in a frame. It looks anything but professional.

So I decided there’s got to be a better way of doing this.

First off, I am using the jQuery library, so you will need to add that to your resources. It will make it so much easier to select elements on the page.

I have added an on load function to the account:

function Account_onLoad()
{
    var nvs_AccountName = Xrm.Page.getAttribute("name").getValue();
   
    $("#name").after(function() {
        var script = ‘<script src="
http://platform.linkedin.com/in.js" type="text/javascript"></script>’ +
        ‘<script type="IN/CompanyProfile" data-id="’ + nvs_AccountName +
        ‘" data-format="inline" data-related="false"></script>’;
        return script;
    });
}

I am looking for the Account name, as populated in the Name field, and retrieving the LinkedIn profile based on that. I have re-arranged the form to provide the space for the section that will be inserted right below the Account Name field:

image

In the Form Properties, add your references as below:

image

once you have all that set up, try opening an account, and you will get the following view:

image

No more iFrames, it just looks like it belongs there.

The beauty of this approach is also the fact that, if an account is not found on LinkedIn, no problem, the screen will look like so:

image

NOTE: Please be aware that you will get the following prompt:

image

Add LinkedIn to your browser’s trusted sites. In the Trusted Sites configuration, click on Custom level.

image

Set Display Mixed Content to Enable.

image

This setting can be pushed through a group policy. For more details see KB2625928.

Enjoy!

As you probably know, the Product has a non-sufficient form for most customers. Let’s have a look at the actual form, and see what’s missing:

image

Now, from what we see, the List Price is what is being used in the system calculations as the price to sell. The Standard Cost could be the MSRP, and the Current Cost is the price your company pays for the product, or the total cost to produce. This is minimalistic, so let’s look at a scenario:

Your company ABC Inc. buys products, and installs them for customers (let’s say Doors & Windows). How do we apply the form to this model?

First off, you have MSRP (Standard Cost). To that apply a standard discount percentage (needs to be added custom), to result in your Current Cost. To that you apply a percentage for S&H, and any other necessary fees, resulting in your List Price.

Since we do have the Description section, which includes the Vendor details, I think this was the scenario envisioned.

Otherwise, on to the manufacturing scenario:

Your company XYZ Inc. produces paper weights. Applying the form to this model:

Materials Cost + Labor Cost + Other expenses incurred = Current Cost

Current Cost + X Amount (expressed as % in many cases) = List Price

The Vendor becomes an interesting concept now. Either XYZ Inc. is the vendor, in which case the field becomes irrelevant, or you have multiple vendors/distributors, in which case you add the same product for each vendor, so you can track against vendors.

As I was saying, the Product is an entity where you should spend some time to map it correctly to the business model used.

Starting to customize a CRM without considering all the aspects could potentially force you in a corner when you get to the Product entity customization.

Enjoy!

One way or another, eventually you will get a request to change the default amount calculations as built in CRM. This could be either by adding a tax percentage (after all, all provinces or states use a fixed tax rate expressed as a percentage), or to handle some other kind of discount by a specific category of customers. Another common example is to apply tax at the Opportunity or Quote level rather than doing it for each product line added. After all, who wants to do the same task 10, 15 or even 20 times when you can do it just once?

If the approach is by using JScript, it may seem like a trivial task. Retrieve the values filled in by users, put your formulas in, an voila… almost perfect. Except, some fields do not retrain the value you pushed in. Even though the calculation on the screen looks correct, when you close and save your form, the totals are being re-calculated by the system, and your formulas are ignored.

In order to identify these cases, you can look at the MSDN documentation. Following is a sub-set that should help you with any calculations you need to override on the Opportunity, Quote and Invoice. The URLs are pointing to the original extended documentation on MSDN.

Opportunity

http://msdn.microsoft.com/en-us/library/gg328229.aspx

DisplayName

SchemaName

Type

IsValidForUpdate

Account

AccountId

Lookup

FALSE

Actual Revenue

ActualValue

Money

TRUE

Actual Revenue (Base)

ActualValue_Base

Money

FALSE

Contact

ContactId

Lookup

FALSE

Opportunity Discount Amount

DiscountAmount

Money

TRUE

Opportunity Discount Amount (Base)

DiscountAmount_Base

Money

FALSE

Opportunity Discount (%)

DiscountPercentage

Decimal

TRUE

Est. Revenue

EstimatedValue

Money

TRUE

Est. Revenue (Base)

EstimatedValue_Base

Money

FALSE

Exchange Rate

ExchangeRate

Decimal

FALSE

Freight Amount

FreightAmount

Money

TRUE

Freight Amount (Base)

FreightAmount_Base

Money

FALSE

Revenue

IsRevenueSystemCalculated

Boolean

TRUE

Opportunity

OpportunityId

Uniqueidentifier

FALSE

Price List

PriceLevelId

Lookup

TRUE

Pricing Error

PricingErrorCode

Picklist

TRUE

Total Amount

TotalAmount

Money

FALSE

Total Amount (Base)

TotalAmount_Base

Money

FALSE

Total Pre-Freight Amount

TotalAmountLessFreight

Money

FALSE

Total Pre-Freight Amount (Base)

TotalAmountLessFreight

_Base

Money

FALSE

Total Discount Amount

TotalDiscountAmount

Money

FALSE

Total Discount Amount (Base)

TotalDiscountAmount_Base

Money

FALSE

Total Detail Amount

TotalLineItemAmount

Money

FALSE

Total Detail Amount (Base)

TotalLineItemAmount_Base

Money

FALSE

Total Line Item Discount Amount

TotalLineItemDiscountAmount

Money

FALSE

Total Line Item Discount Amount (Base)

TotalLineItemDiscountAmount

_Base

Money

FALSE

Total Tax

TotalTax

Money

FALSE

Total Tax (Base)

TotalTax_Base

Money

FALSE

Currency

TransactionCurrencyId

Lookup

TRUE

NOTE: On the Opportunity, it may seem misleading, but the Discount (%) and Discount fields work independent of each other. By that I mean that you can add a Discount (%) and a Discount, and they will both be subtracted from Product Totals.

Quote

http://msdn.microsoft.com/en-us/library/gg309335.aspx

DisplayName

SchemaName

Type

IsValidForUpdate

Potential Customer

CustomerId

Customer

TRUE

Quote Discount Amount

DiscountAmount

Money

TRUE

Quote Discount Amount (Base)

DiscountAmount_Base

Money

FALSE

Quote Discount (%)

DiscountPercentage

Decimal

TRUE

Exchange Rate

ExchangeRate

Decimal

FALSE

Freight Amount

FreightAmount

Money

TRUE

Freight Amount (Base)

FreightAmount_Base

Money

FALSE

Name

Name

String

TRUE

Opportunity

OpportunityId

Lookup

TRUE

Payment Terms

PaymentTermsCode

Picklist

TRUE

Price List

PriceLevelId

Lookup

TRUE

Total Amount

TotalAmount

Money

FALSE

Total Amount (Base)

TotalAmount_Base

Money

FALSE

Total Pre-Freight Amount

TotalAmountLessFreight

Money

FALSE

Total Pre-Freight Amount (Base)

TotalAmountLessFreight_Base

Money

FALSE

Total Discount Amount

TotalDiscountAmount

Money

FALSE

Total Discount Amount (Base)

TotalDiscountAmount_Base

Money

FALSE

Total Detail Amount

TotalLineItemAmount

Money

FALSE

Total Detail Amount (Base)

TotalLineItemAmount_Base

Money

FALSE

Total Line Item Discount Amount

TotalLineItemDiscountAmount

Money

FALSE

Total Line Item Discount Amount (Base)

TotalLineItemDiscountAmount

_Base

Money

FALSE

Total Tax

TotalTax

Money

FALSE

Total Tax (Base)

TotalTax_Base

Money

FALSE

Currency

TransactionCurrencyId

Lookup

TRUE

 

Invoice

http://msdn.microsoft.com/en-us/library/gg309547.aspx

DisplayName

SchemaName

Type

IsValidForUpdate

Account

AccountId

Lookup

FALSE

Customer

CustomerId

Customer

TRUE

Invoice Discount Amount

DiscountAmount

Money

TRUE

Invoice Discount Amount (Base)

DiscountAmount_Base

Money

FALSE

Invoice Discount (%)

DiscountPercentage

Decimal

TRUE

Exchange Rate

ExchangeRate

Decimal

FALSE

Freight Amount

FreightAmount

Money

TRUE

Freight Amount (Base)

FreightAmount_Base

Money

FALSE

Invoice

InvoiceId

Uniqueidentifier

FALSE

Invoice ID

InvoiceNumber

String

FALSE

Payment Terms

PaymentTermsCode

Picklist

TRUE

Price List

PriceLevelId

Lookup

TRUE

Pricing Error

PricingErrorCode

Picklist

TRUE

Priority

PriorityCode

Picklist

TRUE

Total Amount

TotalAmount

Money

FALSE

Total Amount (Base)

TotalAmount_Base

Money

FALSE

Total Pre-Freight Amount

TotalAmountLessFreight

Money

FALSE

Total Pre-Freight Amount (Base)

TotalAmountLessFreight

_Base

Money

FALSE

Total Discount Amount

TotalDiscountAmount

Money

FALSE

Total Discount Amount (Base)

TotalDiscountAmount_Base

Money

FALSE

Total Detail Amount

TotalLineItemAmount

Money

FALSE

Total Detail Amount (Base)

TotalLineItemAmount_Base

Money

FALSE

Total Line Item Discount Amount

TotalLineItemDiscountAmount

Money

FALSE

Total Line Item Discount Amount (Base)

TotalLineItemDiscountAmount

_Base

Money

FALSE

Total Tax

TotalTax

Money

FALSE

Total Tax (Base)

TotalTax_Base

Money

FALSE

Currency

TransactionCurrencyId

Lookup

FALSE

 

Of course, you could write a plugin to do this, but if you need a quick and dirty solution, maybe for a presentation, or when going for a more agile approach, JScript is your friend.

Also, you will find that, for some fields, you will end-up hiding the original field, and creating your custom field to store the calculated values. When you do so, do not forget to hide the original and add your custom field in all the related views. The last thing you want is your invoice showing a price, but when you look at all the invoices for that month, to show a different amount in the view. Same goes for reporting, make sure that when you have a calculated field, you use that in the report.

Enjoy!

This is just a quick observation. You might be tempted, for the sake a clarity, to create an additional field next to the Contact phone number to capture a phone extension.

image

Even though it might seem like a very good idea, after all a lot of companies do still use phone extensions for each employee, when users will synchronize with Outlook, this field will of course not be mapped to anything in Outlook. Obviously, it’s a custom field, so it makes sense.

Anyway, the only way to store an extension and have it mapped between CRM and Outlook is to mark it in the phone number field. Try to use a convention across the company, so you can handle that in a formatting script.

I am still surprised at how many people still mark extensions with the following format:

(999) 999-9999 x999

or

(999) 999-9999 ext999

One note, for the sake of telephony, if you use a comma [,] instead of the [x], the cell phones will know to interpret that as a pause, and, once dialed, should be able to dial the number, followed by a pause and then the extension.

Make it easy for the mobile users to dial phone numbers!

Enjoy!

You might be reading this topic, and think it’s just scripting, and just like any other form. What’s different?

Now, most fields we can script against without a problem. But let’s focus on the Extended Amount field. There is a system calculation that takes place, and if you try to enable the field for editing on the form, you will realize that you can’t.

Funny enough though, assigning a value to the field in JavaScript “almost” works.

var calculatedValue = … ; // put your formula here

Xrm.Page.getAttribute(“extendedamount”).setValue(parseFloat(calculatedValue));

Don’t be fooled by the fact that the field value indeed updates, because that’s only on the screen. Once you save the Quote Product, the value automatically gets re-calculated based on the built-in formula, and your result is off.

The way to override that value is by writing a plugin, but that’s besides the point of this post. I was looking at alternatives using JS. In some circumstances you just can’t get the result you want without writing a plugin, but for easy things, you could basically take the following approach:

  • Create a new Unit Price field on the form.
  • Hide the default Unit Price field.
  • Do your calculations based on the custom Unit price field, update the unit price (adding discounts and other adjustments you want to make), and populate the new value in the default Unit Price field (the one you have hidden).
  • Using this new price, you can take advantage of the system calculation, and make sure the Extended Price gets updated.

image

For a Write In product, this is not a problem, but for an Existing product, this will only work if Override Price is selected. Otherwise, the calculated value is based on the price as it’s defined in the price list associated product.

Enjoy!

MVP Reconnect

Check out my course [Video]

Dynamics 365 Customer Engagement Administration

Dynamics 365 Customer Engagement Administration

Check out my course [Video]

Configuring and Extending Dynamics 365 Customer Engagement

Configuring and Extending Dynamics 365 Customer Engagement

Check out my course [Video]

Getting Started with Dynamics 365 Customer Engagement

Reviewed Book

Implementing Microsoft Dynamics 365 for Finance and Operations

Implementing Microsoft Dynamics 365 for Finance and Operations

Reviewed Book

Microsoft Dynamics 365 Extensions Cookbook

Microsoft Dynamics 365 Extensions Cookbook

Check out my Book

Microsoft Dynamics CRM 2016 Customization - Second Edition

Microsoft Dynamics CRM 2016 Customization - Second Edition

Check out my Book

Microsoft Dynamics CRM Customization Essentials

Microsoft Dynamics CRM Customization Essentials

Check out my Book

Microsoft Dynamics CRM 2011 Scripting Cookbook

Microsoft Dynamics CRM 2011 Scripting Cookbook

Reviewed Book

Microsoft Dynamics CRM 2011: Dashboards Cookbook

Microsoft Dynamics CRM 2011: Dashboards Cookbook

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 608 other followers

Follow Dynamics 365 Wizardry on WordPress.com