Knowledge found and lost while working with Microsoft Dynamics CRM
RSS icon Home icon
  • Free Utility Released: Export JavaScript from CRM

    Posted on June 30th, 2009 mitch Print Print 1 comment

    Part of an upcoming commercial utility I am building exports custom JavaScript found in a CRM installation.  I needed that functionality for a current customer project so I decided to go ahead and create a separate utility that performs that one function.

    Here is the user interface:

    image

     

    JavaScript Extraction Rules

    The JavaScript found within each entity will be extracted using the following rules:

    • Only custom JavaScript is extracted. Built-in CRM JavaScript is ignored.
    • JavaScript for the Form’s OnLoad and OnSave events will be exported.
    • JavaScript for each Atttribute’s OnChange event will be exported.
    • Only active JavaScript is exported. If you don’t have the Active checkbox on the event checked, it is ignored.
    • If the JavaScript is Active but there is no JavaScript in the event, it is ignored.

     

    Output Folder

    A sub-folder will be created using the name of the CRM Organization in the folder where the utility is located.

     

    Output Contents

    Each event will be written to a separate file with a .js extension. The naming convention is:

    OnLoad

    <entity>_onload.js

    OnChange

    <entity>_<attribute>_onchange.js

    Here is what the new Demo CRM VPC installation’s output looks like:

    image

     

    Download it from the Free Utilities page.

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    102 views
  • The Dangers of Repurposing Existing CRM Entities

    Posted on June 11th, 2009 mitch Print Print No comments

    Since Dynamics CRM can be so easily customized, one of the traps that CRM developers and architects occasionally find themselves in is using an existing CRM entity for a different purpose.

    An example would be using the Case Entity (incident) to track product fulfillment requests.

    Usually, when such a decision is made, the entity being repurposed is not being used and as far as the designers can see, it will never be used.  So, they customize it to fit their needs and, in some cases, completely altering the visual and structural layout of the entity to where you can’t actually determine how it looked and worked before.

    Then, as is more the case than not, 6 to 12 months into the future, when Dynamics CRM is running your business, you find yet another need that can be filled by using parts of the system that were previously unused.

    The only problem is, you’ve repurposed that part ( the Case Entity, for example ) and it is so customized and heavily used that it would be almost impossible to move the customizations and data to a new entity and revert the original Entity back to it’s original design and purpose.

    Repurpose or Create

    If you are starting down the repurposing path, you need to ask yourself why you are doing so.  What is is about the Entity that makes you think:

    1. You will never use it
    2. It has some design feature that can’t be duplicated in a custom entity

    There are indeed occasions where a standard CRM Entity has a feature that you can’t duplicate on a custom Entity – like the Customer data type ( can be Account OR contact ).  But most of the time, you’ll spend as much time removing or hiding the existing functionality as you would have in creating a new Entity from scratch. 

    Sometimes even more time is spent customizing the standard Entity and it would be to just create the whole thing from scratch.

    Conclusion

    When your requirements call for a new custom Entity, start from scratch, don’t automatically look for an existing Entity that is not used and change it.  Microsoft spent a lot of time and effort into the design of Dynamics CRM and the relationships between the Entities is pretty well defined and understood.

    Have your design add value to CRM, not change CRM in ways that will cause issues later.

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    167 views
  • JavaScript: Working with Dates Returned from Web Service Calls

    Posted on June 10th, 2009 mitch Print Print 2 comments

    Revised Thursday, July 11th.

    I’ve been doing a lot of work recently using JavaScript to retrieve data from CRM via web service calls.  One of the issues I ran into is the date format returned by CRM is in Coordinated Universal Time ( UTC ) which is not the same format most programming languages use.  This leads to conversion issues or programming exceptions.

    To work around any issues, I created a JavaScript function that allows me to parse the UTC date and time string and return a JavaScript Date value.

    At this point, the function is only concerned with the date, but if you need the time, you can modify the function to add those additional parameters to the new Date() line.

    For more information on the JavaScript Date object, please visit the following link.

    function ConvertDateFromUTC(startDate){
    // UTC time formatted string
    //2009-03-25T09:30:00-00:00 // 1 2 //1234567890123456789012345 
     
    var year = parseInt(startDate.substr(0, 4),10);
    var month = parseInt(startDate.substr(5, 2),10);
    var day = parseInt(startDate.substr(8, 2),10);
    var hour = parseInt(startDate.substr(11, 2),10);
    var min = parseInt(startDate.substr(14, 2),10); 
     
    return new Date(year, month - 1, day);
    }

    July 11th: Corrected issues as pointed out by Moti, in the comments.

     

    Making It Work

    In this example, I’m using the excellent CrmService library released by Ascentium, to retrieve values from a contact.

    var oService = new Ascentium_CrmService();
     
    var asCols = ["fullname", 
                  "address1_postalcode", 
                  "address1_city", 
                  "address1_stateorprovince", 
                  "birthdate"];
     
    var beRetrievedContact = 
           oService.Retrieve("contact", 
           crmForm.all.customerid.DataValue[0].id, 
           asCols);
     
    var birthDateString = beRetrievedContact.attributes["birthdate"].value;
     
    var birthDate = new Date(ConvertDateFromUTC(birthDateString));

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    226 views
  • Retrieving State and Status Options via the MetadataService

    Posted on June 9th, 2009 mitch Print Print No comments

    As you probably know, the State and Status options for a CRM entity are linked together.  You can’t change the State options or values but you can change the Status options ( which are generally shown in a picklist ).

    On a recent project, I needed to simulate the Cancel Opportunity dialog through a custom user interface.  Rather than build a hard-coded list of reasons for the cancelation, I decided to use the CRM MetadataService to dynamically pull the data using the following method ( in C# ):

    public SortedList RetrieveStatusList(
                          string entityName,
                          int statusValue)
    {
        SortedList list = new SortedList();
    
        AttributeMetadata attMetaData;
        RetrieveAttributeRequest request =
            new RetrieveAttributeRequest();
        request.EntityLogicalName = entityName;
        request.LogicalName = "statuscode";
        RetrieveAttributeResponse response;
    
        response =
            (RetrieveAttributeResponse)metadataService.Execute(request);
        attMetaData = response.AttributeMetadata;
    
        StatusAttributeMetadata status =
            (StatusAttributeMetadata)attMetaData;
    
        foreach (StatusOption o in status.Options)
        {
            if (o.State.Value == statusValue)
            {
                list.Add(o.Label.UserLocLabel.Label,
                         o.Value.Value.ToString());
            }
        }
    
        return list;
    }

    The method takes two parameters:

    1. The entity name
    2. The state code value

    I am using this method to populate a ASP.NET dropdown list box using the following code:

    DropDownList1.DataSource = RetrieveStatusList("opportunity", 2);
    DropDownList1.DataTextField = "Key";
    DropDownList1.DataValueField = "Value";
    DropDownList1.DataBind();

    In the case of the Opportunity, we have the following State code values:

    Name Value
    Lost 2
    Open 0
    Won 1

     

    Since we are canceling an Opportunity, we use the Lost state, which has a value of 2.

     

    How This Works

    The key to making sense of this process is the StatusOption object.  It contains data for both the Status Reason codes and the State with which are they associated.

    Since we are looking for a specific state, we check the State for the Option and if it matches our expected value, we add it to the list.

    Customization, Development, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    131 views
  • Using the Form OnSave Event

    Posted on May 11th, 2009 mitch Print Print No comments

    While working on a customer project recently, we added logic to prevent the user from saving should certain form conditions exist.  During testing, we found that Save events were coming from a variety of unexpected places.

    Take for instance the Opportunity Entity.  There are several related entities connected to Opportunity:

    image

    Clicking on the Orders link on the Opportunity allows you to  review or created new associated Orders.

    When you click the New Order button on the Orders pane, one of the first actions it will perform is to save the parent Opportunity with an internal call that looks something like this:

    crmForm.SubmitCrmForm(21, true, true, false);

     

    How This Causes Problems

    This system-originated save caused a problem in our solution because we had actually removed the Save and Save and Close buttons until our approval processes had been completed.  This “save from nowhere” broke our validation processes.

     

    Making Your Save Process Solid

    If you review the OnSave event documentation on MSDN ( or or the SDK help file ), you’ll see that you can actually determine from where the save originated.

    The property event.Mode will contain the origination point for the save activity.  In our case, we only wanted the OnSave event to fire if the event.Mode was 1 ( Save ), or 2 ( Save and Close ).  The code looks like this:

    if (event.Mode == 1 || event.Mode == 2)
    {
    }

     

    Finally, if it turns out that you need to watch for a specific save event, the first parameter of the crmForm.SubmitCrmForm will contain value that will be used to set the event.Mode.

     

    Stopping the Save

    As you will see in the SDK documentation, you can actually stop the save from occurring, using the following code:

    event.returnValue = false;

    This is useful should you need to inspect the form state and only allow the process to complete if all checks pass.

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    273 views
  • Changing a Checkbox Attribute’s Functionality

    Posted on April 20th, 2009 mitch Print Print 2 comments

    Each CRM Form field has an OnChange event associated with it that allows you ( the developer ) to execute JavaScript when the user changes the value of the attribute.  This event is fired when you change the attribute’s value and you leave the field – by clicking or using the Tab key.

    In certain instances, I have had bit attributes on the CRM Form which are formatted to display as a Checkbox.

    I would like the JavaScript added to the OnChange event to be executed immediately upon the user clicking the checkbox and changing the value. NOT when the user leaves the field.

    Using the following code, you can add that functionality:

    function ClickMe()
    {
      crmForm.all.new_checkboxfield.FireOnChange();
    }
     
    crmForm.all.new_checkboxfield.attachEvent('onclick',ClickMe, false);

    How It Works

    In the Form’s OnLoad event:

    1)I create a small JavaScript function called ClickMe that does nothing more than call the OnChange event for the bit attribute we’re working with.

    2) I use the JavaScript function attachEvent to attach the ClickMe function to the onclick event of the attribute.

    Once this code is published, any time the user clicks the checkbox, it will execute the OnChange code for the attribute.

    If you need to add this functionality to additional attributes, just duplicate the above code.

    Customization, Dynamics CRM, Unsupported
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    571 views
  • Custom Messages and Moving Customizations Between Organizations

    Posted on March 28th, 2009 mitch Print Print No comments

    This week I was working with a customer who was using CRM 4.0 and had a fairly advanced set of customizations dating all the way back to CRM 3.0.

    I wanted to do some prototyping on an idea I had so I created a second Organization on the development server with the idea of exporting customizations from the main development organization into the new test organization.  That was the plan anyway, but it didn’t work out like that.

    Here is one of the errors I received when performing the import:

    Failure: opportunity: The custom message must contain the same number of substitution parameters as the system message. Enter a message with the correct number of substitution parameters.

    Wow, that’s nice.  Now since I am a developer, I’m pretty sure I know what it’s talking about, and sure enough, I think I found the culprit:

    image

    Notice that the Default Display String has ‘{0}’ in it?  That’s the “substitutable parameter.”  Notice that the Custom Display String doesn’t have it?

     

    What is a Substitutable Parameter?

    Within the .NET programming language C#, you have the ability to leave a “placeholder” or “substitutable parameter” inside of a string.  You later replace that parameter with a real value using the method String.Format.  The parameters start with zero (0) and increase in numeric value from there. 

    So, the command:

    String.Format(“this is a {0}.”, “test”);

    would produce the result:

    this is a test.

     

    So what went wrong?

    Well, there are actually two issues:

    1. The developer who created this customization didn’t make the Custom Display String function exactly the same the Default Display String.  This would have possibly caused an error at some point in the future.
    2. It appears that the upgrade process from CRM 3.0 to 4.0 did not perform any such checks while the import process does.  This is why the import of the Opportunity Entity failed.

     

    How do you fix it?

    There are two ways:

    1. Delete the customized message string.
    2. Correct the issue with the substitutable parameter not existing in the custom message string.

     

    Conclusion

    Unfortunately, it is quite common to see small issues like this when attempting to move an upgraded CRM system to a fresh install.  There are many issues corrected by hotfixes and added the the various hotfix rollups, but other issues will require you correct the problem manually.

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    247 views
  • Using built-in CRM functions when writing SQL Reports

    Posted on February 20th, 2009 mitch Print Print 1 comment

    If you've ever worked with CRM's Advanced Find you know what an amazing number of built-in date query parameters there are.  It turns out, that those query parameters have corresponding user-defined SQL functions that you can use in your own queries for operations such as custom reports.

    Here is a list of some of the more common date functions:

    fn_BeginOfDay
    fn_BeginOfHour
    fn_BeginOfLastMonth
    fn_BeginOfLastSevenDay
    fn_BeginOfLastWeek
    fn_BeginOfLastXDay
    fn_BeginOfLastXHour
    fn_BeginOfLastXWeek
    fn_BeginOfLastYear
    fn_BeginOfMonth
    fn_BeginOfNextMonth
    fn_BeginOfNextWeek
    fn_BeginOfNextYear
    fn_BeginOfThisMonth
    fn_BeginOfThisWeek
    fn_BeginOfThisYear
    fn_BeginOfToday
    fn_BeginOfTomorrow
    fn_BeginOfWeek
    fn_BeginOfYear
    fn_BeginOfYesterday
    fn_EndOfLastMonth
    fn_EndOfLastWeek
    fn_EndOfLastYear
    fn_EndOfNextMonth
    fn_EndOfNextSevenDay
    fn_EndOfNextWeek
    fn_EndOfNextXDay
    fn_EndOfNextXHour
    fn_EndOfNextXWeek
    fn_EndOfNextYear
    fn_EndOfThisMonth
    fn_EndOfThisWeek
    fn_EndOfThisYear
    fn_EndOfToday
    fn_EndOfTomorrow
    fn_EndOfYesterday
    fn_FirstDayOfMonth
    fn_LastXMonth
    fn_LastXYear
    fn_LocalTimeToUTC
    fn_NextXMonth
    fn_NextXYear
    fn_NTDayOfWeek

    If you would like to see these functions in action, copy the following script into SQL Management Studio and run it ( after connecting to the CRM database ):

     

    select 'fn_BeginOfDay' as "Function",  dbo.fn_BeginOfDay(GetDate()) as "Value"
    union
    select 'fn_BeginOfHour',  dbo.fn_BeginOfHour(GetDate())
    union
    select 'fn_BeginOfLastMonth',  dbo.fn_BeginOfLastMonth(GetDate())
    union
    select 'fn_BeginOfLastSevenDay',  dbo.fn_BeginOfLastSevenDay(GetDate())
    union
    select 'fn_BeginOfLastWeek',  dbo.fn_BeginOfLastWeek(GetDate())
    union
    select 'fn_BeginOfLastXDay',  dbo.fn_BeginOfLastXDay(GetDate(), 1)
    union
    select 'fn_BeginOfLastXHour',  dbo.fn_BeginOfLastXHour(GetDate(), 1)
    union
    select 'fn_BeginOfLastXWeek',  dbo.fn_BeginOfLastXWeek(GetDate(), 2)
    union
    select 'fn_BeginOfLastYear',  dbo.fn_BeginOfLastYear(GetDate())
    union
    select 'fn_BeginOfMonth',  dbo.fn_BeginOfMonth(GetDate())
    union
    select 'fn_BeginOfNextMonth',  dbo.fn_BeginOfNextMonth(GetDate())
    union
    select 'fn_BeginOfNextWeek',  dbo.fn_BeginOfNextWeek(GetDate())
    union
    select 'fn_BeginOfNextYear',  dbo.fn_BeginOfNextYear(GetDate())
    union
    select 'fn_BeginOfThisMonth',  dbo.fn_BeginOfThisMonth(GetDate())
    union
    select 'fn_BeginOfThisWeek',  dbo.fn_BeginOfThisWeek(GetDate())
    union
    select 'fn_BeginOfThisYear',  dbo.fn_BeginOfThisYear(GetDate())
    union
    select 'fn_BeginOfToday',  dbo.fn_BeginOfToday(GetDate())
    union
    select 'fn_BeginOfTomorrow',  dbo.fn_BeginOfTomorrow(GetDate())
    union
    select 'fn_BeginOfWeek',  dbo.fn_BeginOfWeek(GetDate())
    union
    select 'fn_BeginOfYear',  dbo.fn_BeginOfYear(GetDate())
    union
    select 'fn_BeginOfYesterday',  dbo.fn_BeginOfYesterday(GetDate())
    union
    select 'fn_EndOfLastMonth',  dbo.fn_EndOfLastMonth(GetDate())
    union
    select 'fn_EndOfLastWeek',  dbo.fn_EndOfLastWeek(GetDate())
    union
    select 'fn_EndOfLastYear',  dbo.fn_EndOfLastYear(GetDate())
    union
    select 'fn_EndOfNextMonth',  dbo.fn_EndOfNextMonth(GetDate())
    union
    select 'fn_EndOfNextSevenDay',  dbo.fn_EndOfNextSevenDay(GetDate())
    union
    select 'fn_EndOfNextWeek',  dbo.fn_EndOfNextWeek(GetDate())
    union
    select 'fn_EndOfNextXDay',  dbo.fn_EndOfNextXDay(GetDate(), 1)
    union
    select 'fn_EndOfNextXHour',  dbo.fn_EndOfNextXHour(GetDate(), 1)
    union
    select 'fn_EndOfNextXWeek',  dbo.fn_EndOfNextXWeek(GetDate(), 1)
    union
    select 'fn_EndOfNextYear',  dbo.fn_EndOfNextYear(GetDate())
    union
    select 'fn_EndOfThisMonth',  dbo.fn_EndOfThisMonth(GetDate())
    union
    select 'fn_EndOfThisWeek',  dbo.fn_EndOfThisWeek(GetDate())
    union
    select 'fn_EndOfThisYear',  dbo.fn_EndOfThisYear(GetDate())
    union
    select 'fn_EndOfToday',  dbo.fn_EndOfToday(GetDate())
    union
    select 'fn_EndOfTomorrow',  dbo.fn_EndOfTomorrow(GetDate())
    union
    select 'fn_EndOfYesterday',  dbo.fn_EndOfYesterday(GetDate())
    union
    select 'fn_FirstDayOfMonth',  dbo.fn_FirstDayOfMonth(GetDate(), 10)
    union
    select 'fn_LastXMonth',  dbo.fn_LastXMonth(GetDate(), 1)
    union
    select 'fn_LastXYear',  dbo.fn_LastXYear(GetDate(), 1)
    union
    select 'fn_NextXMonth',  dbo.fn_NextXMonth(GetDate(), 1)
    union
    select 'fn_NextXYear',  dbo.fn_NextXYear(GetDate(), 1)

    Customization, Dynamics CRM, Reporting
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    500 views
  • Plug-In Error: The request failed with http status 400: Bad Request

    Posted on January 16th, 2009 mitch Print Print 2 comments

    I ran into this today while installing a plug-in for a customer.  A bit of research turned up articles by Ronald and George which ultimately led me to the following KB article.

    The problem lies within the fact this installation has CRM on port 81, so the URL being used internally by the plug-in is incorrect.  Here is the solution, as identified in the KB article:

    Additionally, create a LocalSdkHost registry key on the Microsoft Dynamics CRM 4.0 server. To do this, follow these steps:

    1. Click Start, click Run, type regedit, and then click OK.
    2. Locate the following registry subkey:

      HKEY_LOCAL_MACHINESOFTWAREMicrosoftMSCRM

    3. Right-click MSCRM, click New, and then click String Value.
    4. In the Name box, type LocalSdkHost.
    5. Right-click LocalSdkHost, and then click Modify.
    6. In the Value box, type the name of the Microsoft Dynamics CRM server or the host header, and then click OK.
      Note Do not specify http:// or the port number.
    7. Locate the LocalSdkPort key at the same location. Verify that the port that is listed matches the port that is being used for the CRM Web site.
    8. If the value of the LocalSdkPort key is incorrect, right-click LocalSdkPort, and then click Modify. Type the correct port number, and then click OK.

     

    The LocalSdkPort value was already in place and correct.  After the addition of the LocalSdkHost value, everything worked as expected.

     

    Note: Hotfix rollup 1 was installed on the server

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (1 votes, average: 5.00 out of 5)
    Loading ... Loading ...
    834 views
  • Working with Dynamic Entities in CRM 4.0

    Posted on August 1st, 2008 mitch Print Print No comments

    Working with Dynamic entities is a bit different in 4.0 vs. 3.0.  When writing plug-ins and using Microsoft.Crm.Sdk, you have the ability to access the attributes of a Dynamic Entity via a property bag, much like this:

    DynamicEntity updateEntity = new DynamicEntity("m3_roundrobin");
    updateEntity["m3_lastuserid"] = newOwner.ToString();
    updateEntity["m3_teamid"] = teamId.ToString();

    However, if you're writing an application that used the standard CRM web service, this methodology is not available to you, so you have to resort to the more code-intensive methods we used in CRM 3.0

    Fortunately, if you look deep into the CRM SDK, you'll find a solution to this issue.

    After installing the CRM 4.0 SDK, browse to this folder:

    sdkserverhelperscscrmhelpers

    and look for the following file:

    dynamicentitypartialtype.cs

    If you add this file to your Visual Studio project, you will have the same Dynamic Entity property bag capability using the CRM web service as you do when using the Microsoft.Crm.Sdk assembly.

    I was pleasantly surprised when I located the code and it has made working with Dynamic Entities much easier.

     

    Have a great weekend everyone.

    Customization, Dynamics CRM
    1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
    Loading ... Loading ...
    2,499 views