You are currently browsing the tag archive for the ‘CRM Online 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!

I’ve run into this situation recently, and I would think this is a bug.

Let’s say you are a System Customizer, and you have a read-only form and an edit form for an entity, say Account. Now you try to create a Workflow for Accounts, and when you get to add an Update Record step, and click on the Set Properties button, you are presented with the read-only form. Annoying  enough.

The reason for this behavior is because Dynamics CRM remembers the last form type you have used. If the last time you accessed an Account you’ve used the read-only form, you are presented in the Workflow with that same form.

To avoid that, go into an Account, and change the form to the edit form. Save and go back to your Workflow. Now you should be getting the proper edit form.

Hopefully we see a fix for this soon.

Enjoy!

Every now and then, if your scripts are performing form changes, and you try to navigate without saving, you will be prompted to save. But what if you want to see exactly what was changed on a large form?

The following function can show you the modified fields:

function checkIsDirty()
{
    var _mesage = "";
    Xrm.Page.data.entity.attributes.forEach(
        function(attribute, index)
        {
            if(attribute.getIsDirty() == true)
            {
                message += attribute.getName() + "\n";
            }
        }
    );
    alert("These fields have been modified: \n" + message);
}

You could put this on a ribbon button, and allow the form user to check at any time what was changed since the last save. Alternatively, you could highlight the fields with a different background color rather than bring up an alert message.

Enjoy!

The following form types are available in Dynamics CRM 2011:

Value Form Type
0 Undefined
1 Create
2 Update
3 Read Only
4 Disabled
5 Quick Create (Deprecated)
6 Bulk Edit
11 Read Optimized

 

To get the Form Type in JScript, use the following syntax:

var _formType = Xrm.Page.ui.getFormType();

I’ve been doing this for so many times, I thought I’d share it with the new CRM customizers out there, as it’s a neat way of getting something done without delving too deep into development and plugins.

This approach uses JScript to capture the Save event on a form, and to divert it on a different course of action.

First off, let’s begin with the basics. A condition on the form will triggers this action. In most cases I want to allows the form save, but in special circumstances I want to block it and perform a different action. We’ll be doing this on the Contact form.

So let’s go ahead and add a Two Options field on the form. I named it “new_isspecialcustomer”.

The following function, associated with the OnSave event, checks for the value on my newly created field, and decides on the action.

function StopSave(context)

{

var _isSpecialSelection = null;

var _isSpecial = Xrm.Page.getAttribute("new_isspecialcustomer");

if(_isSpecial != null)

{

_isSpecialSelection = _isSpecial.getValue();

}

if(_isSpecialSelection == false)

{

alert("You cannot save your record while the Customer is not a friend!");

context.getEventArgs().preventDefault();

}

}

Good. I assume you know by now how to create a web resource, add the JScript function to it, etc…

So, the preventDefault() stops the Save. What the user sees is an alert like this:

clip_image002

and the form remains opened and unsaved.

As presented previously in another blog post (this one to be more precise), you can write a function as such to start a workflow:

function launchWorkflow(dialogID, typeName, recordId)
{
  var serverUri = Mscrm.CrmUri.create(‘/cs/dialog/rundialog.aspx’);

  window.showModalDialog(serverUri + ‘?DialogId=’ + dialogID + ‘&EntityName=’ + typeName +
    ‘&ObjectId=’ + recordId, null, ‘width=615,height=480,resizable=1,status=1,scrollbars=1);

  // Reload form
  window.location.reload(true);
}

Now it’s starting to take shape. Now all we have to do is call this function from our previous one. Where you prevent the save, add a call to launchWorkflow. The parameters are as follows:

· GUID of the Workflow or Dialog

· The type name of the entity

· The ID of the record

And voila! Now you hijacked the save, performed your own actions, and didn’t have to fire up Visual Studio to write a plugin.

Just as a note, it’s probably more efficient to do this in a plugin, but I use this approach for a lot of quick demos I put together in a relatively short time. Gives a new meaning to the “Quick and Dirty” phrase.

Enjoy!

MVP Reconnect

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

Join 617 other followers

Follow Dynamics 365 Wizardry on WordPress.com