Implicit Shares in Microsoft CRM 2011

April 24, 2013 7 comments

Microsoft CRM has a security model feature which is not well documented but can be of use when designing the security for your CRM implementation.   The concept is that with CRM configured a certain way you can ensure that child records created in CRM are automatically shared to the owner of the parent record.    A common place you might want to do this is with customer records (Contacts and Accounts) – where you might like any child records added to these records by other users to be always visible to the owner of that Customer record.   CRM utilizes Shares for this but these are hidden shares that you will not see in the CRM UI.   They do exist in the PrincipalObjectAccess (POA) table though.   Because Sharing is used, users are able to see records above and beyond what their security role permits.  i.e. A user’s security role may grant them Read access to only the Activity records they own but if another user adds an Activity to one of their Contacts and implicit sharing is enabled then the user will be able to see this Activity, even if they are not the owner of the record.

There are some peculiarities related to implicit shares that you need to understand if you want this to be a key part of your security design.  

Firstly, the settings that drive this behavior are the Cascade settings on your entity relationships:

image

To have an implicit share created when a child record is added to a parent you need to have the Reparent option set to the Cascade.   This cascade is triggered when the lookup field on the child record changes from one parent to another.   This trigger also fires when a child record is first created under a parent record as the lookup field goes from NULL to a non-NULL value.    Now, there is a caveat here, the share will only be created if the owner of the child record differs from the owner of the parent record.   But then if the records share the same owner there is no need to share access.

Here’s how this looks.  I have these Cascade settings between Contact and Task:

image

I have a Contact owned by CRM Admin and another user, Ken Mallit has created a Task against this Contact:

image

On the Task record there is no visible Share:

image

But in the PrincipalObjectAccess table you will see a Share has been created against the Task, sharing access to the CRM Admin user:

image

And if I log in as CRM Admin I can see the Task owned by Ken, even when my security role only grants user-level read access to Tasks.

Now, if Ken moves the Task from the original Contact to a different Contact (re-parenting the record) that too fires the Re-parent event and the implicit share is updated.   After this change I see 2 records in the POA table:

image

The original share sharing the Task to CRM Admin is still there but the InheritedAccessRightsMark value is now 0.  This is what CRM does, it zero’s out the permissions shared rather than deleting the record.  I believe I heard from a Microsoft colleague that a maintenance job purges these eventually.   We also see a new share, sharing the Task to Jill Frank who is the owner of the Contact I moved the Task to.   This sharing change means CRM Admin loses visibility of this Task (which is good as the Task no longer relates to one of their Contacts) and Jill gains visibility of the Task.

Let’s now look at what happens during an Assign event.  Currently, we have a Task owned by Ken that is parented by a Contact owned by Jill.  An implicit share provides Jill access to the Task.   If we now reassign the Contact record from Jill to Mark let’s see what happens to the implicit share:

image

You will see that no new Share has been created for Mark.  And the existing Share for Jill has been zeroed out (InheritedAccessRightsMark value is now 0).   This caught me by surprise when I first hit this.  This feels like a functional gap to me.   Now there is another Cascade setting we can fiddle with here to alter the behavior, but you may not like what it does.   The above tests were done with the Cascade Assign setting set to None.   If we flick  that over to Cascade All and then repeat the test we see the same result with the implicit shares but the reassignment of the Contact will cascade down and the Task record will get reassigned as well.   This will give Mark access to the Task attached to his Contact (he will now be the Owner of the Task) but Ken, the original Owner of the Task, will lose access. Which may well be an undesirable behavior.  I found this behavior quite problematic on my last project as I was using Teams to manage access to Contacts and whenever the Team changed on the Contact I had to make sure Tasks were not reassigned but still needed access to the Task to be adjusted to the new Team.  End up creating a custom “Assigned To” field and some other bits and pieces to address that.

Hope this helps someone out there. Implicit shares are an interesting feature.  It feels a little incomplete to me.  It is important you are aware of it so understand why users see what they see.  But if you want to build it into your security design you need to be very careful.

About these ads

Cannot Delete Plug-ins in CRM 2011

April 24, 2013 1 comment

I recently had the less than enjoyable experience of hitting a new issue during a late night Production deployment.   This was perhaps the 6th deployment to this environment but the first time I had seen this problem.   The problem surfaced as an error during the deletion of the SDK Message Processing Steps of an existing Plug-in solution.  I typically delete all existing Steps and then delete the Plug-in Assembly before importing the new Solution zip file to ensure no retired Steps remain in the destination environment.   The CRM UI gave little information, just “an error has occurred”.  The CRM trace gave more details, but these didn’t immediately suggest the root cause:

“Error while normalizing non-case sensitive AD credentials for use on a case sensitive SQL installation”

“Unable to obtain DNS hostname of Active Directory domain controller with ntdsa object name”

“Action failed for assembly ‘XXXX.WMCRM.Plugins, Version=1.0.0.0, Culture=neutral, PublicKeyToken=4f1f52667a84f9b5′: Assembly must be registered in isolation., ErrorCode: –2147220906”

“MessageProcessor fail to process message ‘Delete’ for ‘sdkmessageprocessingstep’.”

I started to wonder whether my deployment user account had been demoted from being a Deployment Administrator so I tried to launch the CRM Deployment Manager application.  It failed to launch.  It repeated the error from the trace:

“Unable to obtain DNS hostname of Active Directory domain controller with ntdsa object name”

My googling efforts landing me on this post:  http://social.microsoft.com/Forums/en-US/crmdeployment/thread/a8abdbc7-fa4c-4f3d-8d9e-809e4a9dc3b8/  which didn’t match my scenario but one of the solution steps resonated with the error message I was seeing:

Add the Preferred Domain Controller  value to MS CRM registry entries: HKLM\Software\Microsoft\MSCRM

Step 1. Right Click and click on NewString value as "PreferredDc"

Step 2. Add the value to PreferredDc is YourDomainControllerName you can find this in your AD by typing the command in your cmd prompt echo %logonserver%

And those steps worked for me.  I ran echo %logonserver% to determine the Domain Controller my server was connected to and added a registry key to tell CRM that was my Preferred Domain Controller.  Immediately (no reboot required) Deployment Manager was working again and I was able to delete my SDK Message Processing Steps.

So it seems like the CRM Deployment Service caches a Domain Controller and the one cached was no longer working correctly.  I was able to ping it, so it was up, but it wasn’t able to perform the function requested of it. 

We removed the registry key when finished.  I don’t think we want to tie the CRM deployment service to a particular DC.  I am hoping to learn more about this from Microsoft to determine steps to avoid these steps in the future.   Caching a Domain Controller in an Enterprise setting where Domain Controllers come and go seems like a product deficiency.   Neither an IISReset or a server reboot helped us.

Hope this helps someone out there.

Tags:

Duplicate Record Button using Jscript in Microsoft CRM 2011

July 31, 2012 11 comments

As detailed here, Rollup Pack 8 for Microsoft CRM 2011 gave us a couple of shiny new jscript toys:

Xrm.Utility.openEntityForm(name,id,parameters)

and

Xrm.Utility.openWebResource(webResourceName,webResourceData,width, height)

In this post I will show you how to add a “Duplicate Record” button to a CRM form, using the new openEntityForm function.

First, lets add the button.  Download Erik Pool’s ribbon editor from CodePlex.  If you are still editing ribbon xml manually and not using this tool then you are mad.   Run the tool, connect to your CRM organisation and open the ribbon you wish to edit.  I am going to add my button to the Case form, adding to the last Group of buttons on the right hand side.  I click in that Button Group and then click New Button and name my button:

image

My button appears small by default so I change the Template Alias to Large.  I need an icon for my button and the one I see on the “Copy Selected” button looks good so I can click on that button and copy and paste the image properties from that button over to my new button:

image

I tidy the Label and Tooltip and then click on the Action tab and enter the name of my jscript library and function, which we will create next:

image

Finally, I save the change and then check my button appears in CRM:

image

I want to demonstrate copying a range of data types so I add a few custom fields to my Case form:

image

Ok, now we can write the jscript function.  There are 3 steps to this:

1. Collect field values from the source record

2. Define the values you want to populate into the destination record

3. Pop the form, passing across the values to be populated

I’ve pasted just the first part below.  Most of this is just me using CRM’s getValue() functon, but you will see I do some null value checks on the lookup fields (to avoid errors) and I also need to extract multiple values from each lookup field.  You will also note for the date field I call another function so that the value returned by the getValue() call gets converted into the MM/DD/YYYY format that the next step requires:

function DuplicateCase() {
    //get values from the Form
    var CaseId = Xrm.Page.data.entity.getId();
    var CaseTitle = Xrm.Page.data.entity.attributes.get("title").getValue();
    if (Xrm.Page.data.entity.attributes.get("customerid").getValue() != null) {
        var CustomerId = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].id;
        var CustomerName = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].name;
        var CustomerType = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].entityType;
    }
    if (Xrm.Page.data.entity.attributes.get("subjectid").getValue() != null) {
        var SubjectId = Xrm.Page.data.entity.attributes.get("subjectid").getValue()[0].id;
        var SubjectName = Xrm.Page.data.entity.attributes.get("subjectid").getValue()[0].name;
    }
    var CaseOriginCode = Xrm.Page.data.entity.attributes.get("caseorigincode").getValue();
    var CaseTypeCode = Xrm.Page.data.entity.attributes.get("casetypecode").getValue();
    var CaseDate = FormatDate("new_dateofincident");  // wants "MM/DD/YYYY" (this might be environment specific though)
    var CaseUrgent = Xrm.Page.data.entity.attributes.get("new_urgent").getValue();
    var CaseClaimAmount = Xrm.Page.data.entity.attributes.get("new_claimamount").getValue();
    if (Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue() != null) {
        var CurrencyId = Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue()[0].id;
        var CurrencyName = Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue()[0].name;
    }
    if (Xrm.Page.data.entity.attributes.get("ownerid").getValue() != null) {
        var OwnerId = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].id;
        var OwnerName = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].name;
        var OwnerType = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].entityType;
    }
    var CaseDescription = Xrm.Page.data.entity.attributes.get("description").getValue();

Here’s the second section of the function, here I am basically placing each of the values I extracted during step 1 into a parameter object (testing for and excluding null values as I go):

 

    //define default values for new Incident record
    var parameters = {};
    if (CaseTitle != null) {
        parameters["title"] = CaseTitle + " - COPY";
    }
    if (CustomerId != null && CustomerName != null) {
        parameters["customerid"] = CustomerId;
        parameters["customeridname"] = CustomerName;
        parameters["customeridtype"] = CustomerType;
    }
    if (SubjectId != null && SubjectName != null) {
        parameters["subjectid"] = SubjectId;
        parameters["subjectidname"] = SubjectName;
    }
    if (CaseOriginCode != null) {
        parameters["caseorigincode"] = CaseOriginCode;
    }
    if (CaseTypeCode != null) {
        parameters["casetypecode"] = CaseTypeCode;
    }
    if (CaseDate != null) {
        parameters["new_dateofincident"] = CaseDate;
    }
    if (CaseUrgent != null) {
        parameters["new_urgent"] = CaseUrgent;
    }
    if (CaseClaimAmount != null) {
        parameters["new_claimamount"] = CaseClaimAmount;
    }
    if (CurrencyId != null && CurrencyName != null) {
        parameters["transactioncurrencyid"] = CurrencyId;
        parameters["transactioncurrencyidname"] = CurrencyName;
    }
    if (OwnerId != null && OwnerName != null) {
        parameters["ownerid"] = OwnerId;
        parameters["owneridname"] = OwnerName;
        parameters["owneridtype"] = OwnerType;
    }
    if (CaseDescription != null) {
        parameters["description"] = CaseDescription;
    }
    if (CaseId != null && CaseTitle != null) {
        parameters["new_parentcase"] = CaseId;
        parameters["new_parentcasename"] = CaseTitle;
    }

And the last bit is simply:

    //pop incident form with default values
    Xrm.Utility.openEntityForm("incident", null, parameters);
}

Here’s how it looks in action.  Here’s my source record:

image

And here’s what pops up when I click the button:

image

To use this yourself in your unique scenarios you will obviously need to edit the getValue() and parameters lines to match your fields.  I’ve covered off the main data types and added a little bit of robustness to help guide you on this.   The openEntityForm utility certainly helps out here.

Here’s the jscript function in full and the missing FormatDate function:

function DuplicateCase() {
    //get values from the Form
    var CaseId = Xrm.Page.data.entity.getId();
    var CaseTitle = Xrm.Page.data.entity.attributes.get("title").getValue();
    if (Xrm.Page.data.entity.attributes.get("customerid").getValue() != null) {
        var CustomerId = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].id;
        var CustomerName = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].name;
        var CustomerType = Xrm.Page.data.entity.attributes.get("customerid").getValue()[0].entityType;
    }
    if (Xrm.Page.data.entity.attributes.get("subjectid").getValue() != null) {
        var SubjectId = Xrm.Page.data.entity.attributes.get("subjectid").getValue()[0].id;
        var SubjectName = Xrm.Page.data.entity.attributes.get("subjectid").getValue()[0].name;
    }
    var CaseOriginCode = Xrm.Page.data.entity.attributes.get("caseorigincode").getValue();
    var CaseTypeCode = Xrm.Page.data.entity.attributes.get("casetypecode").getValue();
    var CaseDate = FormatDate("new_dateofincident");  // wants "MM/DD/YYYY" (this might be environment specific though)
    var CaseUrgent = Xrm.Page.data.entity.attributes.get("new_urgent").getValue();
    var CaseClaimAmount = Xrm.Page.data.entity.attributes.get("new_claimamount").getValue();
    if (Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue() != null) {
        var CurrencyId = Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue()[0].id;
        var CurrencyName = Xrm.Page.data.entity.attributes.get("transactioncurrencyid").getValue()[0].name;
    }
    if (Xrm.Page.data.entity.attributes.get("ownerid").getValue() != null) {
        var OwnerId = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].id;
        var OwnerName = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].name;
        var OwnerType = Xrm.Page.data.entity.attributes.get("ownerid").getValue()[0].entityType;
    }
    var CaseDescription = Xrm.Page.data.entity.attributes.get("description").getValue();

    //define default values for new Incident record
    var parameters = {};
    if (CaseTitle != null) {
        parameters["title"] = CaseTitle + " - COPY";
    }
    if (CustomerId != null && CustomerName != null) {
        parameters["customerid"] = CustomerId;
        parameters["customeridname"] = CustomerName;
        parameters["customeridtype"] = CustomerType;
    }
    if (SubjectId != null && SubjectName != null) {
        parameters["subjectid"] = SubjectId;
        parameters["subjectidname"] = SubjectName;
    }
    if (CaseOriginCode != null) {
        parameters["caseorigincode"] = CaseOriginCode;
    }
    if (CaseTypeCode != null) {
        parameters["casetypecode"] = CaseTypeCode;
    }
    if (CaseDate != null) {
        parameters["new_dateofincident"] = CaseDate;
    }
    if (CaseUrgent != null) {
        parameters["new_urgent"] = CaseUrgent;
    }
    if (CaseClaimAmount != null) {
        parameters["new_claimamount"] = CaseClaimAmount;
    }
    if (CurrencyId != null && CurrencyName != null) {
        parameters["transactioncurrencyid"] = CurrencyId;
        parameters["transactioncurrencyidname"] = CurrencyName;
    }
    if (OwnerId != null && OwnerName != null) {
        parameters["ownerid"] = OwnerId;
        parameters["owneridname"] = OwnerName;
        parameters["owneridtype"] = OwnerType;
    }
    if (CaseDescription != null) {
        parameters["description"] = CaseDescription;
    }
    if (CaseId != null && CaseTitle != null) {
        parameters["new_parentcase"] = CaseId;
        parameters["new_parentcasename"] = CaseTitle;
    }

    //pop incident form with default values
    Xrm.Utility.openEntityForm("incident", null, parameters);
}

// This function takes the fieldname of a date field as input and returns the value of that field in MM/DD/YYYY format
// Note: the day, month and year variables are numbers
function FormatDate(fieldname) {
    var d = Xrm.Page.data.entity.attributes.get(fieldname).getValue();  
    if (d != null) {
        var curr_date = d.getDate();
        var curr_month = d.getMonth();
        curr_month++;  // getMonth() considers Jan month 0, need to add 1
        var curr_year = d.getFullYear();
        return curr_month + "/" + curr_date + "/" + curr_year;
    }
    else return null;
}

Shrinking VirtualBox VDI Files

July 25, 2012 7 comments
Hi, if like me you use VirtualBox for your Microsoft CRM environments you no doubt have at least one crazy large VirtualBox VDI file taking up space on your hard drive.

Today, I thought I would try and trim mine back. I started with a 90GB VDI file and got it down to 65GB.

Here’s what I did:

1. Ran Disk Defragmenter within the VM and defrag’ed the disk. 

2. Ran TreeSize Free within the VM to check for large files/folders that I could trim (didn’t find anything, but did notice the winsxs folder was huge, so did some research on that…)

3. Ran the DISM command line below to purge the winsxs folder (removes data that resides on your disk to enable rollback of service packs, makes your service packs permanent – do a bit of research on this before you decide to do this)

Here’s what I ran:

clip_image002

This reduced the size of C drive inside my VM by 5GB. I think the results you get will depend upon the initial level of your OS and the number of updates you have installed over top.  There seems to be different clean up utilities like this for different OS’s and Service Packs.   I’m no expert on this though.  I ran this over a Windows 2008 R2 SP1 Enterprise Edition install. 

4. Downloaded sdelete to the C drive of my VM and then ran from a command prompt the following command (from c:\ ) sdelete -c -z C:\

This will write 0’s over any unused space on your VM hard disk.  This makes it crystal clear that that unused space can be purged.

5. Shutdown my VM.

6. Opened a command prompt on my host machine and changed directory over to the VirtualBox program folder (C:\Program Files\Oracle\VirtualBox).  Ran the following command to ask VirtualBox to compact my VM’s VDI file:

VBoxManage modifyhd –compact "D:\VirtualBox\CRM 2011 on SQL 2008 DEV Image\Hard Disk\CRM2011 DEV Image (64-BIT Win2008R2  SQL2008R2).vdi"

That’s it.   Oh, and the VM still launched afterwards Smile

Global Search Add-ons for Microsoft CRM 2011

July 24, 2012 7 comments

In this post I provide a quick run down on the Global Search add-ons available for Microsoft CRM 2011.   Here are the candidates:

    I will cover the Installation experience, Setup options and the end user search experience…

 

Global Search by Akvelon

Installation

Global Search is provided as an executable that you install.  Once quirk of the installation process is the license activation screen launches in the context of your default Organisation and you cannot select an alternative Organisation.  Checking the instructions, you need to manually navigate to a different URL to activate the product for a different Organisation.  This is a bit lame, most other ISV vendors do a better of job of this.  Akvelon’s installation instructions are of a high standard though.

Oh and uglier still, I notice the URL refers to the ISV folder on the CRM Server – eek!  That is not supposed to be used anymore with CRM 2011, these guys haven’t updated their installer or architecture for CRM 2011 to the current standard.  The product will work, but this architectural decision leaves me with some doubt about this vendor.  Hopefully, plans exist to update this.

image

There is a second installer you need to run if you wish to allow searching of Attachments, it installs some supporting technologies for you.  Looks like Attachment search is enabled through a crawler executable that indexes the contents of attachment files every 15 minutes.

 
Setup

Before anyone can use Global Search they must be assigned a Global Search security role.  Three custom CRM Security Roles are provided:

image

The Administrator role grants access to global settings, the Experienced Users role grants users access to personal search settings and the the 3rd role grants access to just the Search tool.

To configure global settings you navigate to Akvelon’s configuration screen, which has been added to the CRM Settings menu:

image

Before attachment search will work you need to enable it via the Attachment Search configuration button.  A configuration screen will pop:

image

Similarly you can enable SharePoint search and Email indexing (enabling email indexing improves email search performance):

image  image

By this point you should get the picture, these guys have built out a fairly extensive search solution.  I wonder, do CRM users really need this much functionality?   I haven’t come across this before, Microsoft CRM mostly contains structured data.  However, once you start talking using CRM heavily for tracking emails and attachments decent search does become more important.  Out of the box, Microsoft CRM has always suffered from poor email search, it has limited CRM’s ability to act as a centralised email repository.  The unspoken reality is even if you are tracking emails religiously into CRM you really need to store your emails locally as well for this reason.  Interesting.

Anyway, from the settings screen I can enable additional entities for search (including custom entities):

image

And I can configure search fields and the view definition of the search results:

image

 
User Experience

The user manual suggests End users assigned  the minimal permission security role should see a search screen like the below:

image

This doesn’t seem to be the case for me, I see the search screen with the personal search settings visible as I should if I had the ‘Experienced Users’ role.  Perhaps this is because I have the OOTB Systems Administrator role – not sure.   This pic from the user manual matches what I see:

image

Putting personal search settings to the side, this is the search user experience users get…

If you search for “Happy” the search engine will check the CRM entities enabled for search to see if the fields enabled for search contain the text “Happy”.  The results are summarised per entity:

image

You can expand the search results to see individual records (which you can then double-click to open):

image

The column headings are sortable and if the search results for any particular entity exceeds 5 records then filtering becomes enabled and anything you type in under the column headings instantly filters the results. This makes for a nice user experience:

image

If an entity has been enabled to show related entities in the search results you will be able to drill through to related entities like you can see with the Opportunity Notes below:

image

You can use a wildcard to search for e.g. “Hap&y”.  You can type in 2 words such as “Happy Gilmour” and any record that contains both “Happy” and “Gilmour” in its search fields will be returned, even if those words are in separate fields.

When searching you can tick the Attachments option to have the search extended to include CRM attachments:

image

image

The personal search settings that can be configured are much the same as the global settings.  The user experience is the same:

image

 

Conclusions for Akvelon
  • Lots of functionality
  • Strong personalisation features
  • Seems robust
  • Fast searches (albeit on my small sample database)
  • Easy to use
  • User interface looks very CRM 4.0, but it gets away with it
  • Presentation of search results could look better, it looks a bit messy
  • Installation into ISV folder is not a good look, but then again, not sure it really matters
  • Because indexing is used, recently edited items may get missed by the search

 

PowerGlobalSearch by Power Objects

I’m going to describe the User Experience before Installation & Setup this time as the Setup explanation will make more sense this way (and installation is dead simple)…

User Experience

End users access PowerGlobalSearch from the CRM menu, with the application appearing in the main application pane just like a CRM view.   The look and feel is very Microsoft CRM 2011, they’ve done a great job with that.

Your search string is entered in the top right hand corner, which was counter intuitive for me, but obviously not a big stumbling block.  Search results are rendered in the main part of the screen and a preview pane appears on the right showing a preview form of the currently selected record:

image

First thing I noticed was the search is not doing a ‘contains’ it is doing a ‘begins with’.  So my search for “Happy” only brought up a subset of my records.  Switching to “*happy” returned more results:

image

Search results are listed without any grouping, you get one big list, one set of shared columns.

You can re-sort the search results by clicking on any column header and the Status dropdown allows you to easily expand your search to include inactive/completed records:

image

Above that Status dropdown you can click use the Filter dropdown to deselect entities, to filter the search results:

image

No other filtering is available, Akvelon offers more functionality here.

It is interesting that Power Objects have adopted this consolidated view approach where all records from the different entities are all displayed in the same column set.  It makes for a much cleaner UI but it means only a finite number of fields can be displayed in the search results.  Akvelon is more configurable here, but I do prefer Power Object’s cleaner lines.

One feature that Power Objects has over Akvelon is the shortcut buttons along the top of the search results, these allow you to quickly create new records in relation to a record in the search results:

image

(not sure how much this would be used in reality though)

Installation & Setup

Power Objects provide their add-on as a managed solution so installation only requires a quick import. After install you need to open the imported solution to access the configuration screen for license entry and setup:

image

On the Setup screen we can configure which CRM Entities should be searched against (supports custom entities too).  You define which fields should be searched against and you also define which 2 fields from that entity should appear in the Name and Detail columns of the search results:

image

e.g.:

image

Also configurable is the reading pane, this is configured through a drag and drop UI that seemed quite cool at first but then started to feel a little buggy to me (e.g.: once I defined the reading pane for my SMS entity the SMS search results stopped showing up):

image

Conclusions for Power Objects
  • Less functionality then Akvelon, but has a Reading pane and presents search results in a different way, which might appeal
  • Fast searches (albeit on my small sample database)
  • Easy to use
  • User interface looks very Microsoft CRM 2011, nice and modern, very clean
  • Simple installation and minimal footprint on your CRM server
  • Buggy configuration (for me at least)
  • Silverlight app, won’t run on 64-bit browsers or 64-bit version of Outlook Client (this is an issue for any Silverlight based ISV add-on) – and what is the future of Silverlight?
  • What’s up with Power Objects comic book style website?!?

 

PowerSearch by MSCRMAddons.com

Installation & Setup

PowerSearch is provided as an executable that you run to install their product.  Installation process is straightforward and at the end you will see 2 new managed solutions in your CRM system, a new configuration screen under CRM’s Settings menu and the Power Search utility added to your Workplace menu:

image

The Power Search configuration screen takes a while to load (the solution in general seems to be slower than the first two reviewed here).  The configuration screen presents entities configured for search in a list view:

image

You can add new entities (including custom entities) and remove unwanted entities.  The experience of adding a new entity is not particularly nice, with a small cramped screen and some less than intuitive fields for “Sort Order” and “Collaps on Rows” as you can see below:

image

You select which fields should be searched against, which columns should be displayed in the search results and which status fields should be considered as ‘Active’.

There is some sort of Favourites screen but the UI didn’t render large enough for me to be able to save any changes.  The presentation standard of this screen is not great.  In general, I wasn’t enjoying myself here, the experience was a definite step down after the first 2 products, but configuration is a one time thing so perhaps it doesn’t matter a whole lot.

User Experience

Searching is a slow experience and I found the the search results screen messy:

image

Conclusions for MSCRMAddons
  • User experience not as nice as competitors, didn’t perform as well during my (limited) testing

    Overall Conclusions

    Well you have 2 good products to pick here from Akvelon and Power Objects.  I think the critical decision factors will be price and functionality.

    I prefer the user experience with Power Objects clearly Akvelon packs in a lot more functionality – but do you need it?

    I priced both products recently for a 500 user deal and Akvelon’s purchase price was significantly higher then Power Objects’.  However, when I took into account annual costs over 3-5 years the gap closed significantly.  You will need to do your own price comparison for your user size to see which prices out better.

    Both products can easily be installed under a trial license.  Please use the Comments below to share your experiences and opinions.

    Metro UI coming to Microsoft Dynamics CRM

    July 16, 2012 1 comment

    Exciting first looks of future Microsoft CRM screens from Worldwide Partner Conference 2012 courtesy of MSDynamicsWorld.com:

    (you need to register for free at the above site to read their article) 

    Dynamics CRM Metro app Sales Home

    Microsoft are suggesting we might start to see some of these screens by March 2013 (in their 2012 Winter release).

    Dynamics CRM Metro App Team management

    One more screen:

    Dynamics CRM Metro app Opportunity

    Exciting stuff!

     

    Smile

    Tags: ,

    Service Contracts & Service Scheduling in Microsoft CRM 2011

    July 13, 2012 4 comments

    Another cheat sheet, this time covering Microsoft CRM’s rarely used features of Service Contracts and Service Scheduling…

    Service Contracts

    Contract Templates are defined under Settings \ Templates (where Mail Merge Templates are also maintained)

    On the Contract Template you indicate whether the Contract provides for a fixed number of Cases, a fixed number of Hours or covers a number of days.   And I guess that provides fairly good cover.  If I think of support contracts my employer offers they tend to fall into one of those 3 categories.

    You can also define the hours and days of the week that the Contract covers (that’s what the little green dots are about in the screenshot below) – but settings these limits seems to have no effect:

    image

    Contract Templates cannot be edited once created, if a change is required you need to create new Contract Templates.  You don’t seem to be able to delete or deactivate unwanted Contract Templates once they have been used at least once.

    When you go to create a Service Contracts CRM acts slightly differently from normal, it pops a dialog and asks you to select a Contract Template, you chose one and only then does the Service Contract form appear.  So, the Contract Template is not just a data entry shortcut, it drives the behaviour of Service Contracts.

    On the Service Contract you specify the Customer the Contract is for and you enter the start and end date of the contract.  This date range is validated against later when you try and charge Cases to the Contract:

    image

    The price of the Contract and the level of service provided for that price is defined one level down, on the Contract Line.  The only real important field on the Contract Line is the “Total Cases/Minutes” field.  This is what the Customer is buying from you – whether it be 20 Cases, or 3000 minutes (50 hours).  It is this amount that will be get deprecated when you charge Cases to the Contract in the future:

    image

    There’s a bunch of other fields on the Contract Line, you can specify a Product and enter pricing details.  And of course you can create multiple Contract lines under a Contract if you want. 

    To activate the Contract so that Cases can be charged to it you need to click the “Invoice Contract” button.  It doesn’t create an Invoice record, it just changes the status of the Contract to Invoiced.

    When creating a Case you can charge the Case to the Contract so long as the current date falls within the Contract’s Start and End dates and the Contract status is Invoiced:

    image

    Invoiced Contracts can be put on hold via the “Hold Contract” button, this temporarily prevents Cases from being charged to the Contract (I guess you might use this if payment lapsed).

    The “Allotments Remaining” balance on the Contract Line is deducted only when the Case is resolved.  There’s a field in the Case Resolution window where you can record the number of minutes to be charged to the Contract (for Contracts of that type):

    image

    I think that about covers it for Service Contracts.  If your requirements don’t fit within these parameters then I recommend a cautious review of the suitability of this out of the box design.  You may find adopting some custom entities and/or building out your own business logic extensions might be a better approach.  Like all CRM scenarios you need to measure the gap and look at the unique characteristics of the CRM entities and screens and decide whether those characteristics are of benefit or are in the way.

    UPDATE:  Just noticed that cancelled Contracts can be Renewed via a “Renew Contract” button.   This will create a duplicate Contract with a Status of Draft.   

    Here is more details on Contract Statuses, listed in the order of the Contract life cycle:

    Draft – after a contract is created, it is automatically assigned a Draft status. This indicates the contract is still under development. Draft contracts can be edited but they cannot be linked to a case.

    Invoiced – changing the contract status from Draft to Invoiced indicates your organization and the customer have agreed to the contract terms. A contract must have at least one contract line to be invoiced. A contract with a status value of Invoiced cannot be edited or deleted, it can only be cancelled or put on Hold.

    Active – once a contract is invoiced, it remains in an Invoiced state until the contract start date is reached. At that point, the contract’s status will be automatically updated to Active. You can link cases to a contract once the contract is in an Active state. A contract with a
    status value of Active cannot be edited or deleted, but new Contract Lines can be added (to allow extensions to the Contract).

    On Hold – an active contract can be placed on hold. A contract in an On Hold state cannot be edited, cancelled, or deleted, and it cannot be associated with cases. It can be reactivated by releasing the hold.

    Cancelled – an active contract can also be cancelled. When a contract has a status of Cancelled, it cannot be associated with cases. A cancelled contract can be renewed.

    Expired – a contract remains active until it reaches its end date, at which time its status is changed to Expired. Expired contracts can be renewed.

     

    Service Scheduling

    Setup steps for the Service Scheduling module need to be completed under Settings: Business Management. 

    Start by defining your Resource Groups.  Create a Resource Group for each types of Resource that you will need to schedule – e.g. if you are scheduling surgeries you might have the following Resource Groups: Doctors, Nurses, Surgeries and Anaesthetists:

    image

    Next identify each Resource under those Resource Groups.  Resources can be CRM Users, Teams, or Facilities/Equipment records.  Continuing with the Surgery example I would add My CRM Users that are Doctors to my Doctors Resource Group and then do the same for Nurses and Anaesthetists.  Finally, I would create new Facilities/Equipment records for each Surgery and add those to the Surgeries Resource Group:

    image

    Next define the geographic locations where your Resources can be utilised.  You create these as Sites.  Under each Site you select the Resources that are valid for that Site.   Later when scheduling you will take a customer request for a Service and the scheduling engine can optionally apply a Site constraint to ensure only Resources listed for the Site are scheduled. 

    image

    Next step is to define the Services that you will be selling to your Customers.   Stay in the Business Management area of CRM and go to Services, add a New record.  Give the Service a name, indicate the duration the Service should be scheduled for – e.g. 1 hour, 2 hours, etc. and set when the first appointment can be scheduled each day:

    image

     

    Next you need to define the resource requirements for the Service.  CRM has a unique UI for this bit, here’s an example where I say for the “Tummy Tuck” Service we need an Anaesthetist, a Doctor, a Surgery and 2 Nurses:

    image

    I’ve specified Resource Groups here but you can specify specific Resources instead if required – e.g. for the Tummy Tuck maybe you have to use Surgery A, or the Doctor has to be Ben Hanks.  

    You will see that Sites are mentioned as well, this is where you can limit the pool of resources by their Site associations (to prevent you scheduling resources from distant locations).

    Next, we need to define when our Resources are available.  Call up your first User and click on the Work Hours link.  A calendar will appear.  By default the Work Hours defined for Users equates to 24x7x365 availability – i.e. no constraint is imposed.  The same is true for Equipment/Facilities records.  This might be ok for Equipment/Facilities records but is unlikely to be ok for Users.  So, click on one of the days and then click the Delete button to remove the default work hours definition.  Click the Setup button and then select either the Weekly Schedule or Daily Schedule.    Then define the working hours for that resource:

    image

    There are options here to vary the working hours by day or have them the same each day.  You indicate which days of the week the User works and whether they should be considered unavailable on days where we have indicate the business is closed (e.g. on public holidays).  The Set Work hours link takes you to a sub-screen where you can define start and end times and any breaks:

    image

    You can also define any restrictions the Resource has in regards to performing certain Services at certain times.  e.g. perhaps our User here refuses to perform Tummy Tuck surgeries in the morning:

    image

    Last step is to define those business closures.  There’s a screen for that under Business Management, it’s pretty obvious how you create records there:

    image

    Ok, that’s everything setup.  CRM can now start assisting us with the scheduling of Services.  We will give CRM requests like “Mr Smith wants a Tummy Tuck in Auckland one morning next week” and CRM will use all of the setup configuration we have fed it to determine which applicable resources are available.  HOWEVER, CRM does not go as far as auto-scheduling.  You still need to pick from CRM’s search results the resource you want.

    image

    This scheduling is done from the Service Calendar.   On the Calendar you can chose to view a Day, Week or Month at a time, the view won’t typically fit on the screen, scrollbars allow you to scroll left to right to see the entire time period:

    image

    The “Type” dropdown above the calendar view allows you to choose the record type to display down the calendar.

    To schedule a Service you can click the Schedule button on the Ribbon and CRM will pop first a Service Activity form and then pop over top of that the Schedule Service Activity screen.   The Schedule Service Activity Screen is essentially an availability search screen that helps you assign Resources to the Service Activity record.

    Select the Customer, select the Service they are requesting, optionally select the Site where the Service is to be performed and then enter the customers scheduling preferences – e.g. they might want the Service scheduled for one afternoon next week:

    image

    You will notice that I’ve changed scenarios with my screenshot.   I got to this point with my Surgery example and hit the limitations of the Service Scheduling module (which I admit I had forgotten).  What I found was the grid at the bottom listed out all of available timeslots for all of the resources that met the requirements of my Service, however CRM doesn’t bundle these for you – it doesn’t say “hey next Thursday at 2pm you could go with Bob, Adan, Jill, Nina and Surgery A”.   It doesn’t even allow any useful sorting abilities to allow you to do this yourself.  And worse still, you cannot actually select more than one Resource on this screen.  The CRM scheduling screen just doesn’t support multiple-resource bookings.  So I changed my scenario to something a lot simpler. 

    My new scenario is Air Conditioning Servicing.  I offer a single Service: “A/C Service” and this Service requires 1 Technician for 1 hour.  Here I enable a prioritisation feature I didn’t mention earlier which will see the search results filtered based on who has the least number of appointments scheduled:

    image

    With this scenario the Schedule Service Activity screen works ok.  I scan the search results and click on line 3 to book Bob in for 1pm on the 8th July.  I click the Schedule button at the bottom of the form and the screen closes, passing that selection back to the Service Activity form that has been sitting in behind the search screen:

    image

    I enter a Subject and then Save & Close, I now see that hour of Bob’s time blocked out on the Service Calendar:

    image

    If later I need to reschedule this Appointment I can select the calendar item and then click the Reschedule button on the Ribbon.  This will pop the Service Activity record and the Schedule Service Activity screen over top of it and automatically execute a search against Bob’s availability to give me new slots to pick from:

    image

    To see all the jobs scheduled for a certain timeframe you can switch the Service Calendar from showing Resources to show Service Activities instead:

    image

    To tick these Service Activities off as they are Completed you can use the Change Status button on the Ribbon (these are Activity records so they shouldn’t remain open):

    image

    You can manually schedule/reschedule appointments in which case CRM becomes more of a validation engine then an availability search engine.  You will see alerts like the below which you can chose to ignore:

    image

    The Service Calendar supports Service  Activity and Appointment records.  I think Microsoft’s idea is that Appointments are periods of blocked time around which Service Activities can be scheduled – so they might be internal meetings / training or personal appointments.  When scheduling availability is validated against both of these calendar item types.  The Conflicts button on the Ribbon will highlight any scheduling conflicts with a red border:

    image

    I think that about covers it for Service Scheduling.  In summary, it doesn’t handle multi-resource bookings well.  It does an ok job with simple single-resource scheduling offering some good search abilities.  A couple things I haven’t mentioned is the Service Calendar needs to be manually refreshed when you change an existing calendar item, that’s not ideal.  A big concern to me as an implementer is neither the Service Calendar or the Schedule Service Activity screen can be customised.   This leaves me very uncomfortable, I normally have customisation options I can discuss with a customer – here the only answers are “Sorry, I can’t change anything” or “Sorry, we would have to build a custom version of this screen to give you that”.

    I think what you get with CRM’s Service Scheduling module is one of three things:

    1. It is a simple service scheduling tool that you can adopt and use as is (if you can fit your requirements into its functional breadth), or
    2. It is a sample application indicative of what you could build using CRM as a rapid development platform.  It can be used as a pilot or during design workshops to help you establish your requirements and design the solution that your business needs, or
    3. It is a validation engine for service scheduling.  You setup the rules and then schedule manually or via your own custom user interface and it will let you know when you break the availability rules.

    UPDATE 1:  I just spotted an almost useful tip in the Microsoft Coursewear on Service  Scheduling – you can access the Service Calendar directly via a URL like… 

    https://adventureworkscycle.crm.dynamics.com/sm/home_apptbook.aspx

    … which makes better use of the available screen real estate:

    image

    (but this is suitable only as a read-only view as without the ribbon appearing you cannot schedule new activities)

    UPDATE 2:  Something else I missed, Customers can have Service Preferences defined on their Customer record – e.g. Preferred User, Preferred Day:

    image

    These preferences appear in the form assistant and are considered by the search engine.  I suspect the “Least Busy” / “Most Busy” setting on the Service is given a higher priority by the scheduling engine then the customer preferences but the preferences are still part of its algorithm:

    image

    UPDATE 3: I forgot to mention Capacity.  You can define the Capacities of each Resource (Users and Facilities/Equipment records).  You enter this against their Work Hours (you have to click through to the work hours detail and then click the Show Capacity button):

    image

    This is where you can indicate that a particular Conference Room has a Capacity of 50 seats.  Then when you define your Services you can define what Capacity the Service requires – e.g. a “Room Rental (small)” might require a Capacity of 40:

    image

    Then when requesting available resources/times these requirements and constraints will be taken into account.

     

    I’m keen to hear where you have found CRM’s Service Scheduling module of use.  Please use the Comments feature below.

    Follow

    Get every new post delivered to your Inbox.

    Join 372 other followers