Tuesday 29 October 2013

Dynamics CRM 2013 - Custom Activity Issue

UPDATE: As of right now (01/04/2014) we still have this issue with many of our clients. Microsoft has requested that we create a ticket to fix everyone individually which I think is a bit ridiculous! So currently this issue has NOT been fixed :-(

With the upgrade of CRM 2011 to CRM 2013 the navigation didn't really suit what we wanted so we looked at doing an immediate update to improve the user experience.

We found by creating custom areas we could split our entities into a more usable style and by replacing some of our JavaScript with business rules meant we could provide a better experience to users on mobile. Where we did have issues though was with custom activities.

The upgrade to CRM 2013 appears to have a few bugs where custom activities are concerned. When a custom activity is created a field called "leftvoicemail" which is a Two Option type.
In CRM 2011 it looks like this
Dynamics CRM 2011 - Custom Activity


In CRM 2013 it looks like this

Dynamics CRM 2013 - Custom Activity

As you can see the field changes from "Managed" to "Unmanaged". This causes an error when you do an import as the following error is created:
"A managed solution cannot overwrite the OptionSet component with Id=xxxx-xxx-xxx-xxx-xxxx which has an unmanaged base instance.  The most likely scenario for this error is that an unmanaged solution has installed a new unmanaged OptionSet component on the target system, and now a managed solution from the same publisher is trying to install that same OptionSet component as managed.  This will cause an invalid layering of solutions on the target system and is not allowed."

To make matters worse you cannot delete this field as you will receive the error "Only custom fields can be deleted."

The only work around I found for this was to manually remove this field from the customizations.xml file.
For each custom activity in your solution you will need to find the following:
<attribute PhysicalName="LeftVoiceMail">

Delete everything including the above line and down to the matching </attribute> tag.


Another issue we had was with a relationship called <<custom entity name>>_mailbox_sendermailboxid. This occurred for each of the custom entities as well and again we had to remove this from the customizations.xml file.

Everything from <EntityRelationship Name="ixa_feedback_mailbox_sendermailboxid"> to </EntityRelationship>.

We have lodged a ticket with Microsoft in the hope they find a resolution as we would prefer not to have to do this hack for every update!


Monday 14 October 2013

Dynamics CRM 2013 - Business Rules

In Dynamics CRM 2013 a new feature that has been added is the Business Rules engine. This has two clear strengths compared to using JavaScript.

  1. Allows for "Non Developers" to create them due to the simple to use interface.
  2. Business Rules unlike JavaScript work with mobile forms.
There are going to be cases where the business rules won't suit and JavaScript will be required however in most cases a rule that is similar to JavaScript should be possible for minor functions.

For example:
On Address forms we wanted to default the country. The code looked like this

// If its a create form

if (Xrm.Page.ui.getFormType() == 1)
{
    Xrm.Page.ui.controls.get("country").getAttribute().setValue("My Country");
}

Business Rules do not appear to allow you to state what the form mode is. Which does reduce some of the logic that can be used. Below is a screenshot of a Condition.

Business Rules - Condition
A few things to notice about the above form. Firstly you can define the scope for your business rules in case you only want it to work on certain forms. Secondly the structure of a Condition is quite similar to the Advanced Find criteria. This means that staff who have experience with Advanced Find's can now write business rules. 

For my example I want to set the country attribute. The JavaScript was essentially defaulting the value so to achieve this I will say. Where country does not contain data as shown below:

Business Rules - Country Condition

Then I can then select an action from the Action menu shown below:

Business Rules - Action Menu

In this case we want to set the country value to "My Country". Which uses the "Set field value" option.

Business Rules - Set field value
Once I have created my action I can enter my description of my rule and my rule name. Save and close the Business Rule and Activate it.

When you go to the form you will see that My Country will magically appear. If you blank the field then move to another field it will appear again.

Business Rules - Rule in action
Is the above solution perfect? No. If a client would like to leave the field blank then they won't be able to. Ideally we would be able to (as the JavaScript does) only do this on form load or only set it when a user has populated the address. NOTE: If Street 1 is mandatory as above I recommend adding a second condition to only populate when Street 1 contains data.

As of the current release its clear to see the benefits of the Business Rules and the limitations. However its a fantastic starting point long time users of CRM will agree!

A few things of note:
  • Conditions appear to currently be AND only. It is not possible to group or OR Conditions.
  • As stated above Business Rules are currently based only on a field. You cannot state "On Save", "On Create", "On Load" etc.
  • Something from my personal wish list is filtering an Option Set based on criteria. However this might be a fair way into the future!

Thursday 10 October 2013

Dynamics CRM 2013 - Mail Merge

While browsing through the features of Dynamics CRM 2013 one thing that we couldn't find was the Mail Merge functionality.

UPDATE:
The Mail Merge functionality is still there!! You can still Mail Merge from a Marketing List as mentioned below however if you want you can do it on the Advanced Find results pane as shown below.



If you read Scott Durow's blog here you will see how to retrieve the button.
http://www.develop1.net/public/post/How-to-restore-a-hidden-button-on-the-CRM-2013-Command-Bar.aspx

However I found it strange that this functionality would just disappear when it seems to be such a key component of a CRM system.

A colleague then showed me the button is still within CRM its just not on the account and contact entities like it used to be.

To create a mail merge you now need to make sure you have a marketing list. You then have the option of "Mail Merge on Marketing List". I would think that this is a good idea as it restricts the creation of Mail Merges however as we've just discovered this I haven't had a chance to think of the negatives that might exist.

Thursday 3 October 2013

Dynamics CRM 2013 - What you need to know

With the release of Dynamics CRM 2013 (Orion) brings a re-imagined experience for users. Whether or not this is a better experience remains to be seen however there is significant changes compared to CRM 2011. I'll try and briefly go through these changes.

New User Interface

As per the screen shots below there are significant differences between the two interfaces. Gone is the left hand side navigation and the ribbon has been replaced (more on that soon). Microsoft has also reduced the colour and replaced the icons with more basic designs.

Dynamics CRM 2011 - Dashboard

Dynamics CRM 2013 - Dashboard

Bye Bye Ribbon

The Microsoft ribbon and left hand navigation have been removed from Dynamics 2013 and replaced with a command bar. The command bar now handles all interaction with entities and settings. It's a conscious decision to reduce immediately available links in place of more real estate for the record/dashboard. Time will tell whether or not this is a good decision.
Dynamics CRM 2011 - Navigation


Dynamics CRM 2013 - Navigation

Form Changes

If your clients are used to CRM 2011 - Polaris then some of the changes they may be familiar with. The following are some of the changes for 2013.
  • Forms auto save
  • Bing Map integration
  • Social Window
  • Reduced Pop Ups

Dynamics CRM 2011 - Account Form

Dynamics CRM 2013 - Account Form

Form Auto Save

When using the new forms these forms will now auto save. There is no save button in the top left any more. Instead there is a little Save button in the bottom right. When a change is made a status will appear next to the save button stating there are "unsaved changes" then this will change to "saving" and then the status will disappear indicating the change has been made. Changes are also saved when a form is closed.

Bing Map Integration

No longer will developers need to build their own third party map integration.  Map integration is now provided out of the box.

Social Window

With Microsoft's purchase of Yammer (think Facebook for companies) it was only a matter of time before it was included in their products. It allows for the social integrations between team members to display within CRM. Activities such as Emails, and Phone calls also appear in this window.

As the window takes up ALOT of real estate it will be interesting going forward to see how much value this actually provides to clients.

Reduced Pop Ups

While there are some pop ups still within Dynamics CRM they have been greatly reduced. If required you can pop out a record into its own window however by default most open in the one window which for regular CRM users will be a big relief!


Tablet and Phone Integration

Dynamics CRM 2013 has been redesigned from the ground up to allow for it to render correctly on various desktop screen sizes as well as tablets such as iPad. The tablets all use the desktop version which is just re-rendered to suit the screen size.

The phone versions use a mobile client which is great for doing a quick look up of a client details. The editing functionality is a bit clunky so I still wouldn't use it too much for editing purposes.

It appears that Dynamics CRM 2013 has finally sorted out the issues of the past as it successfully works on iPads, iPhones, and Android!

iPad Screenshots








Android Screenshots



Process driven forms

Microsoft rolled out some process driven forms in Dynamics CRM 2011 Polaris however in CRM 2013 it is better designed and better implemented. In the past, forms with a lot of information would be difficult for clients to understand and it made it difficult for applications etc to be filled out in an orderly fashion.

With process driven forms you can change the information available to users and change the information that is required at each step of the way. This will hopefully reduce some of the javascript required in the past.

Dynamics CRM 2013 - Process Driven Lead



Business Rules

One of the best new parts of Dynamics CRM 2013 is the business rules engine, it replaces what in the past had to be accomplished by javascript. Business rules are presented in a simple to use english format which means that clients can do their own business rules if required. These rules also work in the mobile version where javascript does not.

Dynamics CRM 2013 - Business Rules Engine


There is alot more that is new or different in Dynamics CRM 2013. As I discover more I will update this post (or probably create a new one)

0x8004F016 - A managed solution cannot overwrite the LocalizedLabel component with Id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX which has an unmanaged base instance. The most likely scenario for this error is that an unmanaged solution has installed a new unmanaged LocalizedLabel component on the target system, and now a managed solution from the same publisher is trying to install that same LocalizedLabel component as managed. This will cause an invalid layering of solutions on the target system and is not allowed.

We had a client who had a very old version of our CRM customizations managed solution. When we attempted to update we received the following error.

ErrorCode:
0x8004F016

ErrorText:
A managed solution cannot overwrite the LocalizedLabel component with Id=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX which has an unmanaged base instance.  The most likely scenario for this error is that an unmanaged solution has installed a new unmanaged LocalizedLabel component on the target system, and now a managed solution from the same publisher is trying to install that same LocalizedLabel component as managed.  This will cause an invalid layering of solutions on the target system and is not allowed.

Checking the internet the responses seemed to be based around deleting data from the SQL database. This is not recommended for two reasons. 1) Its not microsoft supported  2) It doesn't work for CRM Online.

The item that was failing was:
ItemType       
Name Localized Name
Form Incident Case
Investigating the forms related to Case I found that the name of forms had been changed i suspect by an unmanaged customization.  By changing the name back to the correct name I could then import my managed solution correctly.

Wednesday 2 October 2013

Dynamics CRM 2011 - Filter a lookup

Filtering a lookup in CRM 2011 is quite straight forward. Below is a sample that I used to filter one lookup based on the value of another.



function filterSecondLookup()
{
    try {
        var lookupObject = Xrm.Page.getAttribute("lookup_one").getValue();

        if (lookupObject != null)
        {

            lookup_one_name = $('').text(lookupObject[0].name).html();  // text of lookup
            lookup_one_id = lookupObject[0].id; // Guid of lookup

            var viewId = '{1ACB2B35-B07C-44D1-868D-258DEEAB88E2}'; //Random GUID
            var entityName = 'account';
            var viewDisplayName = 'Filtered Lookup Name';

            var fetchXml = "" +
                              "" +
                                "" +
                                "" +
                                "" +
                                "" +
                                "" +
                                "" +
                                "" +
                                  "" +
                                    "" +
                                  "" +
                                "" +
                              "" +
                            "";

            var layoutXml = "" +
                          "" +
                            "" +
                            "" +
                            "" +
                            "" +
                          "" +
                        "";

            Xrm.Page.getControl('lookup_two').addCustomView(viewId, entityName, viewDisplayName, fetchXml, layoutXml, true);
        }
        else
        {
            Xrm.Page.getControl("lookup_two").setDefaultView(window.defaultSiteViewId );
        }
    }
    catch (ex)
    {
        alert("Error in filterSecondLookup: " + ex);
    }
}

Monday 30 September 2013

Dynamics CRM 2013 - Orion has been released!

The new version of Dynamics CRM 2013 has been released today (October 1st 2013) for CRM Online customers. It is vastly different to CRM 2011. Download your 30 day trial here http://www.microsoft.com/en-au/dynamics/crm-free-trial-overview.aspx

Thursday 25 July 2013

Dynamics CRM 2011 - PHP and SOAP Calls

I put the first little section regarding CRM 2011, PHP, and SOAP here ->  http://crmtroubleshoot.blogspot.com.au/2013/07/dynamics-crm-2011-php-and-soap-using.html

I went into some basic information about authentication so in this second part I thought I would give people the SOAP calls for talking to CRM 2011.

NOTE: I am not suggesting the following are the only ways to connect using SOAP to CRM and I know its not a complete list (Delete for example) however I hope this assists you in your investigation it is not intended to do all the work for you! ;-)

SOAP CALLS


CREATE
Creates a single CRM record


    
      
  
      
      
      
      00000000-0000-0000-0000-000000000000
      
      
    



RETRIEVE
Returns a single CRM record based on the GUID.


  
  
  
 false
 
   
 
  



RETRIEVE ALL ENTITIES
This returns all all entities. I don't really recommend this because it is incredibly slow!


 
   
    
  EntityFilters
  Entity
    
    
  RetrieveAsIfPublished
  true
    
   
   
   RetrieveAllEntities
 



RETRIEVE MULTIPLE
Returns a collection of CRM Records based on the Criteria list passed


 
  
    false
    
   
    
  
  
  false
  
  
  
  
    
    
    
    false
  
 



UPDATE
Updates a single record based on the GUID


 
  
   
  
  
  
  
  
  
 



WHO AM I
Returns the User Id, Business Unit Id, and Organization Id for the logged in user


 
  
  
  WhoAmI
 



CHILD ITEMS


COLUMN
This should be repeated for each column required for the Retrieve Requests


 



ATTRIBUTES
This should be repeated for each attribute required for a Create or Update Request. There is various types for attributes.

EntityReference

 
 
  
  
  
  


OptionSetValue

 
 
  
 


Money

 
 
  
 


String

        
 


Boolean

        
 


Integer

        
 


Date Time

        
 




CRITERIA
Used to filter the query results


 
 
  
  
   
   
   
    
    
   
  
 



ORDER
Used to define the sort order of the result set


 
 
  
  
 



Tuesday 16 July 2013

Dynamics CRM 2011 - PHP and SOAP using Office 365

NOTE: If you are using this code I found a small issue!!
Microsoft appears to have changed the SSL version they are using. Due to this the following function needs to be modified!
 public static function GetSOAPResponse($url, $request) {
    // Set up headers.
    $headers = array(
      "POST " . "/Organization.svc" . " HTTP/1.1",
      "Host: yourorganisation.api.crm5.dynamics.com",
      'Connection: Keep-Alive',
      "Content-type: application/soap+xml; charset=UTF-8",
      "Content-length: " . strlen($request),
    );

    $cURLHandle = curl_init();
    curl_setopt($cURLHandle, CURLOPT_URL, $url);
    curl_setopt($cURLHandle, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($cURLHandle, CURLOPT_TIMEOUT, 60);
    curl_setopt($cURLHandle, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($cURLHandle, CURLOPT_FOLLOWLOCATION, TRUE);
    //COMMENT OUT THIS LINE!!curl_setopt($cURLHandle, CURLOPT_SSLVERSION, 3);
    curl_setopt($cURLHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_setopt($cURLHandle, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($cURLHandle, CURLOPT_POST, 1);
    curl_setopt($cURLHandle, CURLOPT_POSTFIELDS, $request);
    //COMMENT OUT THIS LINE!!$response = curl_exec($cURLHandle);
    // ADD THE FOLLOWING FOUR LINES INSTEAD (and a try/catch wouldn't hurt!)
    if( ! $response = curl_exec($cURLHandle)) 
    { 
        trigger_error(curl_error($cURLHandle)); 
    }
    curl_close($cURLHandle);

    return $response;
  }


Now onto the post!!
I recently found myself in a position where I needed to connect to CRM Online from PHP.  As I didn't have anywhere to put my code except for the client side creating my own wrapper for the CRM Web services didn't seem like a great option.

I found a few examples of CRM, PHP, and SOAP however many seemed to have some issue along the way (In particular around Office 365 authentication).

So firstly we will look at Office 365 authentication. To connect you need to request so tokens from the CRM Web Service as follows:

class Authentication
{
 public $username;
 public $password;
 public $keyIdentifier;
    public $securityToken0;
    public $securityToken1;

 public function Authentication($_username, $_password)
 {
  $this->username = $_username;
  $this->password = $_password;
  $this->AuthenticateUser();
 }
 
 function BuildOCPSoap()
 {
  /*
  Select the right region for your CRM
  crmna:dynamics.com - North America
  crmemea:dynamics.com - Europe, the Middle East and Africa
  crmapac:dynamics.com - Asia Pacific
  */
  $region = 'crmapac:dynamics.com';

  $OCPRequest = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
       <s:Header>
      <a:Action s:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>
      <a:MessageID>urn:uuid:%s</a:MessageID>
      <a:ReplyTo>
        <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>
      </a:ReplyTo>
      <a:To s:mustUnderstand="1">%s</a:To>
      <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <u:Timestamp u:Id="_0">
       <u:Created>%sZ</u:Created>
       <u:Expires>%sZ</u:Expires>
        </u:Timestamp>
        <o:UsernameToken u:Id="uuid-cdb639e6-f9b0-4c01-b454-0fe244de73af-1">
       <o:Username>%s</o:Username>
       <o:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">%s</o:Password>
        </o:UsernameToken>
      </o:Security>
       </s:Header>
       <s:Body>
      <t:RequestSecurityToken xmlns:t="http://schemas.xmlsoap.org/ws/2005/02/trust">
        <wsp:AppliesTo xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
       <a:EndpointReference>
         <a:Address>'. $region .'</a:Address>
       </a:EndpointReference>
        </wsp:AppliesTo>
        <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>
      </t:RequestSecurityToken>
       </s:Body>
     </s:Envelope>';
  
  $OCPRequest = sprintf($OCPRequest, gen_uuid(), 'https://login.microsoftonline.com/RST2.srf',  getCurrentTime(), getNextDayTime(), $this->username, $this->password);
  return $OCPRequest;
 }
 
 public static function GetSOAPResponse($url, $request) {
    // Set up headers.
    $headers = array(
      "POST " . "/Organization.svc" . " HTTP/1.1",
      "Host: yourorganisation.api.crm5.dynamics.com",
      'Connection: Keep-Alive',
      "Content-type: application/soap+xml; charset=UTF-8",
      "Content-length: " . strlen($request),
    );

    $cURLHandle = curl_init();
    curl_setopt($cURLHandle, CURLOPT_URL, $url);
    curl_setopt($cURLHandle, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($cURLHandle, CURLOPT_TIMEOUT, 60);
    curl_setopt($cURLHandle, CURLOPT_SSL_VERIFYPEER, FALSE);
    curl_setopt($cURLHandle, CURLOPT_FOLLOWLOCATION, TRUE);
    curl_setopt($cURLHandle, CURLOPT_SSLVERSION, 3);
    curl_setopt($cURLHandle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    curl_setopt($cURLHandle, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($cURLHandle, CURLOPT_POST, 1);
    curl_setopt($cURLHandle, CURLOPT_POSTFIELDS, $request);
    $response = curl_exec($cURLHandle);
    curl_close($cURLHandle);

    return $response;
  }
  
  public function AuthenticateUser()
 {
  $SOAPresult = GetSOAPResponse('https://login.microsoftonline.com/RST2.srf', $this->BuildOCPSoap());
  $responsedom = new DomDocument();
        $responsedom->loadXML($SOAPresult);
  
        $cipherValues = $responsedom->getElementsbyTagName("CipherValue");
       
        if( isset ($cipherValues) && $cipherValues->length>0){
            $this->securityToken0 =  $cipherValues->item(0)->textContent;
   $this->securityToken1 =  $cipherValues->item(1)->textContent;
   $this->keyIdentifier = $responsedom->getElementsbyTagName("KeyIdentifier")->item(0)->textContent;
        }else{
            return null;
        }
 }
      


gen_uuid returns a random guid, getCurrentTime gets the current date/time, getNextDayTime gets tomorrow (or any expiry date/time you wish)

We then send this to the authentication srf to get the required tokens

You should now have  the required tokens which you then place in your header for any requests as shown below:


public static function getHeader($_action, $_authentication) {

  $header = '
  <s:Header>
   <a:Action s:mustUnderstand="1">http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/'.$_action .'</a:Action>
   <a:MessageID>
    urn:uuid:'.self::gen_uuid().'
   </a:MessageID>
   <a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo>
   <a:To s:mustUnderstand="1">
    https://yourorganisation.api.crm5.dynamics.com/XRMServices/2011/Organization.svc
   </a:To>
   <o:Security s:mustUnderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
   <u:Timestamp u:Id="_0">
    <u:Created>'.self::getCurrentTime().'Z</u:Created>
    <u:Expires>'.self::getNextDayTime().'Z</u:Expires>
   </u:Timestamp>
   <EncryptedData Id="Assertion0" Type="http://www.w3.org/2001/04/xmlenc#Element" xmlns="http://www.w3.org/2001/04/xmlenc#">
    <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc"></EncryptionMethod>
    <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
     <EncryptedKey>
      <EncryptionMethod Algorithm="http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p"></EncryptionMethod>
      <ds:KeyInfo Id="keyinfo">
       <wsse:SecurityTokenReference xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
        <wsse:KeyIdentifier EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509SubjectKeyIdentifier">
         '.$_authentication->keyIdentifier.'
        </wsse:KeyIdentifier>
       </wsse:SecurityTokenReference>
      </ds:KeyInfo>
      <CipherData>
       <CipherValue>
        '.$_authentication->securityToken0.'
       </CipherValue>
      </CipherData>
     </EncryptedKey>
    </ds:KeyInfo>
    <CipherData>
     <CipherValue>
      '.$_authentication->securityToken1.'
     </CipherValue>
    </CipherData>
   </EncryptedData>
   </o:Security>
  </s:Header>';

  return $header;

 }

public static function WhoAmIRequest($request){

 $authentication = new Authentication('Username', 'Password');
 $xml = '
 <s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" xmlns:a="http://www.w3.org/2005/08/addressing" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
  'getHeader('Execute', $authentication).'
  <s:Body>
   <Execute xmlns="http://schemas.microsoft.com/xrm/2011/Contracts/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <request i:type="b:WhoAmIRequest" xmlns:a="http://schemas.microsoft.com/xrm/2011/Contracts" xmlns:b="http://schemas.microsoft.com/crm/2011/Contracts">
     <a:Parameters xmlns:c="http://schemas.datacontract.org/2004/07/System.Collections.Generic" />
     <a:RequestId i:nil="true" />
     <a:RequestName>WhoAmI</a:RequestName>
    </request>
   </Execute>
  </s:Body>
 </s:Envelope>'; 

 return self::GetSOAPResponse('https://yourorganisation.api.crm5.dynamics.com/XRMServices/2011/Organization.svc', $xml);
}

This code is slightly modified obviously from what I am using however I have the above working. The above is not intended to be the entire solution it is just expected that it might assist you in your own work.

Thursday 11 July 2013

CRM2011 - "A currency is required if a value exists in a money field. Select a currency and try again."

I was writing a javascript function and during testing kept getting "A currency is required if a value exists in a money field. Select a currency and try again." while attempting to enter data into the field.

Basically what had happened was a currency field was added to an existing CRM entity.  CRM doesn't like this for some weird reason.

After googling alot of people suggested that you write javascript to set the currency onload.  This to me seemed like a bit of a hack which i'd prefer to avoid.  Instead do the following:

Step 1 - Go to File -> Options
Then in the first tab General go down to "Select a default currency". Set it to the appropriate value.
NOTE: This is a user setting so this needs to be repeated for all users however HOPEFULLY this is already set.

Step 2 - Add the "Currency" field to the form for the entity you are having issues with. Save and Publish

Step 3 - Select all existing records for the entity and "Edit"

Step 4 - Click on the currency lookup and select the required currency. Save the changes

You will now find the required currency symbol appears in the field and you can happily enter data into the field!

Monday 17 June 2013

CRM 2011 OData XMLHttpRequest Maximum Length

I had a requirement where I wanted to know if a user had a particular system role.  I created a function which returned all User Roles with a particular name (System Administrator for example) and compared this to what gets returned from Xrm.Page.context.getUserRoles().

This worked in the Development and Testing environments so it was pushed to Production.  When in Production the function failed to recognise the correct Roles which made no sense.

After wasting several hours of my life I discovered that the issue was related to the relationship between Business Units and Roles.

The Production environment had 60+ business entities where Development and Testing had much less.  The 60+ business units meant that CRM created 60+ versions of the Security Roles so searching for "System Administrator" returned over 60 records.

The page length for XMLHttpRequest OData requests through the CRM Web Service is fixed at 50.

Rather than repeat the solution here I suggest you look at Lakshman Reddy's blog which is what lead me to my fix.

http://lakshmanindian.wordpress.com/2012/12/07/retrieving-more-than-50-records-using-odata-in-crm-2011/

The only issue I had was the process of returning all those Roles slowed down my CRM page.  So I instead reversed my query for example.

Original
Return all Roles with 'System Administrator' name then compare with User Roles.

Updated
Return all Roles where Guid is in User Roles

Dynamics CRM 2011 Authentication Types

Connecting to CRM 2011 (in particular CRM Online) can be tricky due to the different authentication methods on offer. If you are allowing users to connect to CRM using different authentication types then you need to be aware of the different requirements


  • Windows Live Id (WLID)
    • Device Registration
    • Username
    • Password
  • Office 365 - (Online Commerce Platform) (OCP)
    • Username
    • Password
    • Security Token using Username/Password
  • Active Directory
    • Domain
    • Username
    • Password
  • Federated
    • Username
    • Password

Dynamics CRM 2011 - "An error occurred when verifying security for the message"

If you attempt to connect to the Dynamics CRM 2011 Web Services using SOAP you may come across this message "An error occurred when verifying security for the message".

In most cases this will be an issue of the server and client times not being close enough so they web server rejects the request.

However I was receiving this message for a completely different reason.

I was using the "CRMOnlineSOAPRequests" example written in C# as a base reference point however I wanted to write my code in PHP so I googled and found a couple of good examples.

Both the C# code and my PHP code returned the required tokens when authenticating however the second token was always 6764 characters in length while the PHP second token was 6796 in length.

I then discovered that I was attempting to connect to the wrong region for my authentication!

If you are receiving this error then ensure that you have selected the correct URN for your region.

  • crmna:dynamics.com - North America
  • crmemea:dynamics.com - Europe, the Middle East and Africa
  • crmapac:dynamics.com - Asia Pacific