CRM 2011 – Displaying images for a contact

A quick blog post to point you in the direction of a couple of exellent tools to display images in for a contact (or other entities).

The timing of this was a day too late (for me) because I had written a basic way to display images yesterday and then today I find the solution today and it works better than mine because you can have multiple images and it resizes the images in a neat way, working out the aspect ratio from the available space.

Both solutions use the method of uploading picture files as a note for the contact and then display these.

For the html, jquery method then go to this blog and you can download the solution file

http://mscrmblogger.com/2012/03/31/imagebrowser-webresource/

You will need to add the image img_browser.html as a webresource onto your page.  This is very useful because I think (I haven’t tried it) use this to display images on other entities.

The MSCRM blogger team have also created a silverlight version, well actually they created that first, you can get that here

http://crmattachmentimage.codeplex.com/

A great contribution to the CRM community and will save everyone writing the same solution over and over.

CRM 2011 – displaying similar quote products in a subgrid by injecting FetchXML

I had a requirement this week where a customer was adding products to a quote and when they selected a product they then want to see previous quotes which featured the same product and the price and quantity it sold for.  Basically the sales person wanted to see what the product had been sold for before to make sure the price they were quoting wasn’t too radical.

The idea I came up with was to have a grid on the Quote Product form, which displayed quote products of the same type as the quote product selected.

This would mean I would have to dynamically change a view or show items in the grid.

I created a view showing the fields of the Quote Product and then specified a product I was searching for.

I found this excellent blog post

CRM 2011 – Change Subgrid View Java Script

This blog post explains how you can load in new fetchXML into a subgrid, you can inject the FetchXML on the form onload, which enables you to get some variables from the form and inject them into FetchXML which then is used to update the SubGrid.

So on my Quote Product form I checked to see if a product had been selected on the form OnLoad, on this particular form the subgrid doesn’t load until you have selected a product, unit and a quantity, which then reloads the form.

The code itself which I have taken and changed from the blog post referenced above, is quite clever.  The function is called and if the SubGrid has not been loaded, it calls the function again after a small time delay.  This ensures the SubGrid is loaded before you try and inject the fetchXML into the view.

The FetchXML is created, I got the fetchXML by doing an advanced find on my previously created view, there is a button which allows you to retrieve the FetchXM, which is very useful.

function UpdateSubGrid() {
var quoteGrid = document.getElementById("Quote_Products");
var productId = Xrm.Page.getAttribute("productid").getValue();
if (productId != null){
//If this method is called from the form OnLoad, make sure that the grid is loaded before proceeding
 if (quoteGrid == null) {
 //The subgrid hasn't loaded, wait 1 second and then try again
 setTimeout('UpdateSubGrid()', 1000);
 return;
 }

var fetchXml = ""
+ " <entity name='quotedetail'>"
+ " <attribute name='productid' />"
+ " <attribute name='productdescription' />"
+ " <attribute name='priceperunit' />"
+ " <attribute name='quantity' />"
+ " <attribute name='extendedamount' />"
+ " <attribute name='salesrepid' />"
+ " <attribute name='uomid' />"
+ " <attribute name='manualdiscountamount' />"
+ " <attribute name='baseamount' />"
+ " <attribute name='new_quoteid' />"
+ " <attribute name='quotedetailid' />"
+ " <order attribute='productid' descending='false' />"
+ " <filter type='and'>"
+ " <condition attribute='productid' operator='eq' uiname='1 Function Easy Clean Mini Kit' "
+ " uitype='product' value='" + productId[0].id +"' />"
+ " </filter>"
+ " <link-entity name='quote' from='quoteid' to='quoteid' visible='true' link-type='outer' alias='a_ff5a49bd001a45f7a14dd99a28f37f91'>"
+ " <attribute name='customerid' />"
+ " <attribute name='name' />"
+ " <attribute name='quotenumber' />"
+ " <attribute name='quoteid' />"
+ " </link-entity>"
+ " <link-entity name='product' from='productid' to='productid' visible='false' link-type='outer' alias='a_e0b689bded554f07ab0b241a932d482e'>"
+ " <attribute name='productnumber' />"
+ " </link-entity>"
+ " </entity>"
+ "</fetch>";
 // alert(fetchXml);
 //Inject the new fetchXml
 quoteGrid.control.setParameter("fetchXml", fetchXml);
 //Force the subgrid to refresh
 quoteGrid.control.refresh();
}

CRM 2011 – Setting a user lookup with the logged in user with Javascript

Sometimes you need to log the current user in a User Lookup.

Xrm.Page.context.getUserId()

if you wanted to set a user lookup field you would need to do this


var setUservalue = new Array();
 setUservalue[0] = new Object();
 setUservalue[0].id = Xrm.Page.context.getUserId();
 setUservalue[0].entityType = 'systemuser';
//setUservalue[0].name = retrievedUser.FullName;

Xrm.Page.getAttribute("meta_aurarrangedby").setValue(setUservalue)

meta_aurarrangedby is a user lookup field

This will set the correct guid but the user name will be blank until the page is reloaded if you want to add the user name then you need to retrieve the user name using the guid the example code below will do this using an OData query but you will have to add the json2.js file to the entity so it can be called from your javascript.

function Getinfo() {
 var context;
 var serverUrl;
 var UserID;
 var ODataPath;
 context = Xrm.Page.context;
 serverUrl = context.getServerUrl();
 UserID = context.getUserId();
 ODataPath = serverUrl + "/XRMServices/2011/OrganizationData.svc";
 var retrieveUserReq = new XMLHttpRequest();
 retrieveUserReq.open("GET", ODataPath + "/SystemUserSet(guid'" + UserID + "')", true);
 retrieveUserReq.setRequestHeader("Accept", "application/json");
 retrieveUserReq.setRequestHeader("Content-Type", "application/json; charset=utf-8");
 retrieveUserReq.onreadystatechange = function () {
 retrieveUserReqCallBack(this);
 };
 retrieveUserReq.send();

}

function retrieveUserReqCallBack(retrieveUserReq) {
if (retrieveUserReq.readyState == 4 /* complete */) {

if (retrieveUserReq.status == 200) {
 var retrievedUser = this.parent.JSON.parse(retrieveUserReq.responseText).d;
 if (retrievedUser.FullName != null)

var setUservalue = new Array();
 setUservalue[0] = new Object();
 setUservalue[0].id = Xrm.Page.context.getUserId();
 setUservalue[0].entityType = 'systemuser';
 setUservalue[0].name = retrievedUser.FullName;

Xrm.Page.getAttribute("meta_aurarrangedby").setValue(setUservalue)
 }

else {

 }
 }
}

The Xrm.Page.context has quite a few useful functions the functions are listed on this page

The getServerUrl is very useful as is the getUserRoles

Xrm.Page.context provides access to the following functions:

  • getAuthenticationHeader: A deprecated method that returns the encoded SOAP header necessary to use Microsoft Dynamics CRM Web service calls using JScript.
  • getCurrentTheme Returns a string representing the current Microsoft Office Outlook theme chosen by the user.
  • getOrgLcid: Returns the LCID value that represents the Microsoft Dynamics CRM Language Pack that is the base language for the organization.
  • getOrgUniqueName: Returns the unique text value of the organizations name.
  • getQueryStringParameters: Returns an array of key value pairs representing the query string arguments that were passed to the page.
  • getServerUrl: Returns the base server URL. When a user is working offline with Microsoft Dynamics CRM for Microsoft Office Outlook, the URL is to the local Microsoft Dynamics CRM Web services.
  • getUserId: Returns the GUID value of the SystemUser.id value for the current user.
  • getUserLcid: Returns the LCID value that represents the Microsoft Dynamics CRM Language Pack that is the user selected as their preferred language.
  • getUserRoles: Returns an array of strings representing the GUID values of each of the security roles that the user is associated with.
  • isOutlookClient: Returns a Boolean value indicating if the user is using Microsoft Dynamics CRM for Microsoft Office Outlook.
  • isOutlookOnline: Returns a Boolean value indicating whether the user is connected to the Microsoft Dynamics CRM server while using Microsoft Dynamics CRM for Microsoft Office Outlook with Offline Access. When this function returns false, the user is working offline without a connection to the server. They are interacting with an instance of Microsoft Dynamics CRM running on their local computer.
  • prependOrgName: Prepends the organization name to the specified path.

CRM 2011 – defaulting the Customer lookup to contacts on the case form

There are a few lookups in CRM call Customer lookups and can be either Accounts or Contacts.

I have to say I often find these annoying.  Often on the Case form I have to add an account lookup so the user can specify an account and a contact.  The problem is when I use the Customer lookup it defaults to account lookup.

The Customer record is locked onto the form so you can’t get rid of it (you can hide it and assign it a value if you wanted but that’s a bit messy and extra fields etc.)

So on this form I added an account lookup and then used the customer lookup to specify a contact and I wanted a way to default the lookup to be a contact lookup.

I found some code shown below on the blog MSCRM Bing’d which is written by CRM MVP Rhett Clinton.

document.getElementById("customerid").setAttribute("defaulttype", "2");
<pre>Xrm.Page.getControl("customerid").setDefaultView("a2d479c5-53e3-4c69-addd-802327e67a0d");

To get this solution to work I had to go and get the guid of the contact view and in this case I wanted the Active Contacts and replaced the SetDefaultView guid with my guid and then I put the code into the onload event.

Then when I clicked on the customer lookup it defaults to contacts and the view I specified in the guid.

The only downside is if you search in the box without pressing the lookup button it does still search contacts and accounts.

So a big thanks to Rhett for helping resolve a problem which had annoyed me for quite a while.


CRM 2011 – adding validation to the Resolve Case button

I had a scenario where the customer wanted the helpdesk user to fill in some call analysis fields when a case was resolved.

Initially I made a new status called Completed, I created some new fields for the users to fill in regarding customer satisfaction, if they resolved the case within the time period and some other fields.

When the user changed the status to completed, some JavaScript made those fields required so the user had to fill them.

This worked fine unless the user press the Resolve Case button and this just closed the case without them having to fill in call analysis fields.

I initially looked into changing the case resolution form.  The case resolution form pops up when you press the case resolved button and you have to fill in a few fields, the perfect place for some call analysis.  Perfect except you can’t modify it, Microsoft don’t let you modify this Activity.  I have no idea why but there you go.

I briefly thought about adding some rules to enable the case resolution button only when you clicked on the status completed.

I then saw some Javascript which did the job.  It’s quite clever and I learnt some thing new in CRM.

The solution to my problem was some javascript which is triggered on the OnSave event on the Case form, it checks what type of save has happened and if it was triggered from pressing the Case Resolution it validated the form.

The answer although was shown on many forums originated from the blog post below.  The blog post has a link to the javascript file you can download so I would definitely go there to get it.  Also this is a great site for CRM developers with lots of other great blog posts like how to trigger a workflow using javascript

The original Javascript and example is taken from this blog post but has now been created as a wiki page blog post below, you need to look at the original article because it explains the whole solution ands walks you through it which I am not going to do.

http://social.technet.microsoft.com/wiki/contents/articles/4122.dynamics-crm-2011-dynamics-crm-2011-perform-jscript-validations-on-entity-form-before-execution-of-special-events.aspx

// Use the following function on Form Save Event,
// CRM will pass the execution context in function parameter prmContext

function FrmOnSave(prmContext) {
 // Local variable to store value indicating how the save event was initiated by the user.
 var wod_SaveMode, wod_SaveEventVal;

// Change the Save Event Value as per required Save Event
 wod_SaveEventVal = <Value>;

if (prmContext != null && prmContext.getEventArgs() != null) {

wod_SaveMode = prmContext.getEventArgs().getSaveMode();

// 1 will pass on Recalculate button click
 if (wod_SaveMode == wod_SaveEventVal) {
 // Write your validation code here

alert("Write your validation code here");

// Use the code line below only if validation is failed then abort function save event
 prmContext.getEventArgs().preventDefault();

}
 }
}

You can download the code from the blog link to a skydrive, click here for that

The code above is a neat solution to the problem.  In the OnSave event it gets the code to pass variables, this then passes the context.  Using the context you can then get the save mode prmContext.getEventArgs().getSaveMode()  this gives you a number.  If the number is 5 then you know the save has been triggered by pressing the resolve case button.

This was interesting because I didn’t know that save events had different numbers depending on where they were triggered.  The blog post has a list of all the save events

Entity
Event Mode
Value
Activities
Save as Completed
58
Activities
Close Activity Note 2
5
Activities
To Opportunity Note 2
5
Activities
To Case Note 2
5
Activities
To Lead Note 2
5
All
Save Note 2
1
All
Save and Close
2
All
Deactivate
5
All
Reactivate
6
Article
Submit
10
Article
Approve
12
Article
Reject
11
Article
Unpublish
13
Campaign Activity
Close Campaign Activity
5
Campaign Activity
Distribute Campaign Activity
4
Campaign Response
Convert Campaign Response (Create new lead or Create new record for a customer)
54
Campaign Response
Convert Campaign Response (Convert Existing Lead -> Qualify)
16
Campaign Response
Convert Campaign Response (Convert Existing Lead-> Disqualify)
15
Campaign Response
Close Response
5
Case
Resolve Case
5
Case
Cancel Case
40
Case
Reactivate Case
6
Contract
Invoice Contract
38
Contract
Copy Contract
39
Contract
Recalculate Note 2
1
Contract
Hold Contract
31
Contract
Renew Contract
34
Contract
Cancel Contract
33
E-mail
Send
7
Goal
Recalculate
66
Goal
Close Goal
5
Invoice
Invoice Paid
57
Invoice
Cancel Invoice
27
Invoice
Recalculate
1
Invoice
Get Products
44
Invoice
Lock Pricing
52
Lead
Qualify
16
Lead
Disqualify
15
Opportunity
Close as Won Note 2
5
Opportunity
Close as Lost Note 2
5
Opportunity
Recalculate Opportunity Note 2
1
Order
Create Invoice
19
Order
Fullfill Order
56
Order
Cancel Order
26
Order
Recalculate Note 2
1
Order
Get Product
43
Order
Lock Pricing
50
Product
Convert to Kit
35
Product
Convert to Product
36
Queue
Approve E-mail Note 1
4
Queue
Reject E-mail Note 1
4
Quote
Recalculate Note 2
1
Quote
Get Products
28
Quote
Activate Quote
29
Quote
Create Order
17
Quote
Revise
24
Quote
Close Quote
25
User
Approve E-mail Note 1 & Note 2
4
User
Reject E-mail Note 1 & Note 2
4
User
Change Business Unit Note 1
4
User or Team Owned Entities
Assign
47

 

Although I was capturing the save event, when I changed some fields to be required it didn’t seem to stop the page from saving, so I had to add some Javascript which stopped the Case form from saving and popped up a javascript message warning the user to fill in some Call analysis fields.  I added some validation which checks to see if certain fields are null, if they are then I stopped the form from saving.

prmContext.getEventArgs().preventDefault();
alert(“You must enter Call Analysis information”);

 

There are probably other ways to do this but it shows you the power of CRM 2011 that you have quite a few options

CRM 2011 – Tool – JavaScript Model Generator

I like to see what useful tools are being created  and there have been some fantastic ones, my favourite is the sitemapeditor

I have been writing some Javascript recently and it’s always a bit of a hassle having to look up all of the  names of the fields you want to use.

In CRM 4 there use to be an excellent tool which created some intellisense for you but I haven’t found anything which is really useful for CRM 2011.

so it is with some interest I saw a tweet from CRM MVP Donna Edwards about a Javascript model generator.  The project is available on codeplex and you can download it using the link below

http://crmjsmodelgenerator.codeplex.com/

Now it took me a while to work out how to get this working, these are the instructions

How to generate it:

NOTE – Make sure your javascript file changes are saved before generating the model as your unsaved changes will get overwritten.

  1. Edit the provided sample.jsconfig file.
  2. Update CrmServer to use the correct server and org.
  3. Set the JsDirectory to be the folder where your script files are located.
  4. Add an Entity node for the entity form that you will be working on
    • EntityName is required and needs to be the schema name
    • FileName is optional and it will try to use the EntityName + “.js” to find the javascript file in the JsDirectory. Use this if the file name is different than the EntityName
  5. Run the JSModelGenerator.exe

<JsConfig CrmServer=”http://server/org&#8221;
JsDirectory=”C:\Source\Project\Scripts”>
<Entities>
<Entity EntityName=”contact” FileName=”contact.js” />
</Entities>
</JsConfig>

I changed the config file but then I was running it and nothing was really happening.  What you have to do is copy specify where the downloaded javascript files are on your computer and then it will update those files and add in the intellisense code to the file.

I quite like this solution because I can now quickly find all the fields on a form and it’s quick and easy to run