Thursday 9 June 2011

CRM 2011 - Setting State from JScript

With the introduction of the new OData service endpoint for Microsoft Dynamics CRM 2011 has made making simple queries, creates, deletes and updates from JScript a lot cleaner and quicker than using the older SOAP method.
With that said though setting the state of a record cannot be performed using the OData endpoint.  You can write an OData request that will not error when you try to set the state of the record but it will simply do nothing.  The answer to this problem is to use the new SOAP service endpoint and the SetStateRequest.
The SDK comes with a nice tool call SoapLogger that will output a properly formed SOAP message from C# code.  See the SDK on how to use this tool to generate the message. Once you have your message all you need to do is copy the message into you JScript library, modify the required values and your on your way.


The following is an example of and update appointment state message as generated by the SoapLogger tool:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <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:SetStateRequest" 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:KeyValuePairOfstringanyType>
            <c:key>EntityMoniker</c:key>
            <c:value i:type="a:EntityReference">
              <a:Id>cecbc035-978d-e011-9465-000c297f4e3a</a:Id>
              <a:LogicalName>appointment</a:LogicalName>
              <a:Name i:nil="true" />
            </c:value>
          </a:KeyValuePairOfstringanyType>
          <a:KeyValuePairOfstringanyType>
            <c:key>State</c:key>
            <c:value i:type="a:OptionSetValue">
              <a:Value>2</a:Value>
            </c:value>
          </a:KeyValuePairOfstringanyType>
          <a:KeyValuePairOfstringanyType>
            <c:key>Status</c:key>
            <c:value i:type="a:OptionSetValue">
              <a:Value>4</a:Value>
            </c:value>
          </a:KeyValuePairOfstringanyType>
        </a:Parameters>
        <a:RequestId i:nil="true" />
        <a:RequestName>SetState</a:RequestName>
      </request>
    </Execute>
  </s:Body>
</s:Envelope>

This was then converted into a JScript/JQuery request to the server to cancel the current appointment:


function closeAppointment() {
 
    if (Xrm.Page.data.entity.getIsDirty()) {
        alery("Please save your changes before cancelling the appointment.");
        return;
    }
 
    // create the request
    var request = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\">";
    request += "<s:Body>";
    request += "<Execute xmlns=\"http://schemas.microsoft.com/xrm/2011/Contracts/Services\" xmlns:i=\"http://www.w3.org/2001/XMLSchema-instance\">";
    request += "<request i:type=\"b:SetStateRequest\" xmlns:a=\"http://schemas.microsoft.com/xrm/2011/Contracts\" xmlns:b=\"http://schemas.microsoft.com/crm/2011/Contracts\">";
    request += "<a:Parameters xmlns:c=\"http://schemas.datacontract.org/2004/07/System.Collections.Generic\">";
    request += "<a:KeyValuePairOfstringanyType>";
    request += "<c:key>EntityMoniker</c:key>";
    request += "<c:value i:type=\"a:EntityReference\">";
    request += "<a:Id>" + Xrm.Page.data.entity.getId() + "</a:Id>";
    request += "<a:LogicalName>appointment</a:LogicalName>";
    request += "<a:Name i:nil=\"true\" />";
    request += "</c:value>";
    request += "</a:KeyValuePairOfstringanyType>";
    request += "<a:KeyValuePairOfstringanyType>";
    request += "<c:key>State</c:key>";
    request += "<c:value i:type=\"a:OptionSetValue\">";
    request += "<a:Value>2</a:Value>";
    request += "</c:value>";
    request += "</a:KeyValuePairOfstringanyType>";
    request += "<a:KeyValuePairOfstringanyType>";
    request += "<c:key>Status</c:key>";
    request += "<c:value i:type=\"a:OptionSetValue\">";
    request += "<a:Value>4</a:Value>";
    request += "</c:value>";
    request += "</a:KeyValuePairOfstringanyType>";
    request += "</a:Parameters>";
    request += "<a:RequestId i:nil=\"true\" />";
    request += "<a:RequestName>SetState</a:RequestName>";
    request += "</request>";
    request += "</Execute>";
    request += "</s:Body>";
    request += "</s:Envelope>";
 
    //send set state request
    $.ajax({
        type: "POST",
        contentType: "text/xml; charset=utf-8",
        datatype: "xml",
        url: Xrm.Page.context.getServerUrl() + "/XRMServices/2011/Organization.svc/web",
        data: request,
        beforeSend: function (XMLHttpRequest) {
            XMLHttpRequest.setRequestHeader("Accept""application/xml, text/xml, */*");
            XMLHttpRequest.setRequestHeader("SOAPAction""http://schemas.microsoft.com/xrm/2011/Contracts/Services/IOrganizationService/Execute");
        },
        success: function (data, textStatus, XmlHttpRequest) {
            Xrm.Page.ui.close();
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            alert(errorThrown);
        }
    });    
}

This particular example has allowed me to override the default function of the close appointment.

2 comments:

  1. I have been looking for this example a long time, but a question before i start trying it out.. Are the state codes here related to the following?

    http://msdn.microsoft.com/en-us/library/gg309665.aspx

    In this case you are closing the appointment as
    2 (canceled) 4 (canceled.

    Have i understood you correctly?

    ReplyDelete
    Replies
    1. You are correct. State Code of 2 = Cancelled and Status Reason of 4 = Cancelled.

      Delete