Validate Opportunity CloseDate when it is closed as won or lost in Dynamics CRM 2011

Create a Plugin step with following details
Message: Create
Primary Entity: opportunityclose
Execution Stage: Pre-operation
Execution Mode: Synchronous


// Validate Opportunity CloseDate for preventing Opportunity closed as won or lost in the future.
 public void Execute(IServiceProvider serviceProvider)
        {
            try
            {
                IPluginExecutionContext context = (IPluginExecutionContext)serviceProvider.GetService(typeof(IPluginExecutionContext));

                if (context.Stage == 20) // Pre-Stage
                {
                    if (context.InputParameters.Contains("Target") && context.InputParameters["Target"] is Entity)
                    {
                        Entity entity = (Entity)context.InputParameters["Target"];

                        if (entity.Attributes.Contains("actualend") && entity.Attributes["actualend"] != null)
                        {
                            DateTime closedOn = entity.GetAttributeValue("actualend");

                            if (closedOn.Date > DateTime.Now.Date)
                            {
                                throw new InvalidPluginExecutionException(OperationStatus.Canceled, "Close Date cannot be in the future");
                            }
                        }
                    }
                }
            }
            catch (InvalidPluginExecutionException ex)
            {
                throw ex;
            }
        }

We can also use following JavsScript code to validate Opportunity CloseDate when it is closed as Won or Lost. Call ValidateOpportunityCloseDate() in the OnSave method. In fact, you can use this code to validate other fields(Status Reason, Actual Revenue, Close Date, Competitor) on Close Opportunity dialog.


function ValidateOpportunityCloseDate() {
    var arry = GetCloseOpportunityInformation();
    if (arry[3] != undefined && arry[3] != null) {
        var today = new Date();
        var formattedDate = ('' + arry[3]).replace(/-/g, "/").replace(/[TZ]/g, " ");
        var actualend = new Date(formattedDate);
        if (actualend > today) {
            alert("Close Date cannot be in the future");
            AbortSave();
        }
    }
}
function GetCloseOpportunityInformation() {
    var arr = new Array();
    var arrFields = new Array();
    arrFields[0] = 'statecode';  //1 = Won, 2 = Lost    
    arrFields[1] = 'statuscode'; //Status Reason   
    arrFields[2] = 'actualrevenue'; //Actual Revenue   
    arrFields[3] = 'actualend';  //Close Date   
    arrFields[4] = 'competitorid'; //Competitor 
    arrFields[5] = 'description'; //Description  
    var xml = crmFormSubmit.crActivityXml.value;
    var XmlDoc = new ActiveXObject("Microsoft.XMLDOM");
    XmlDoc.async = false;
    XmlDoc.loadXML(xml);
    for (var i = 0; i < arrFields.length; i++) {
        //get close out information     
        if (i > 1) {
            var xmlnode = XmlDoc.selectSingleNode("//opportunityclose/" + arrFields[i]);
            if (xmlnode != null) {
                arr[i] = xmlnode.nodeTypedValue;
            } else {

                arr[i] = "";
            }
        }
    }
    return arr;
}
function AbortSave() {
    event.returnValue = false;
    return false;
}
Advertisements

Log error details from the plugins in Dynamics CRM 2011

Create a custom entity to save all exception details occurs in Plugins and other CRM related applications.

Entity Name: ls_errordetails

Attributes: Name, Exception Type, Exception Details, Exception Message

Call below method LogErrorInCRM() in your catch blocks to save exception details.

        // This method is used to log error details into CRM entity(ls_errordetails)
        private void LogErrorInCRM(IOrganizationService service, Exception ex)
        {
            try
            {
                Entity ls_errorDetails = new Entity("ls_errordetails");

                ls_errorDetails["ls_name"] = Assembly.GetExecutingAssembly().GetName().Name;

                ls_errorDetails["ls_exceptiontype"] = ex.GetType().Name;

                if (ex.InnerException != null)
                    ls_errorDetails["ls_exceptiondetails"] = ex.StackTrace + "\n" + ex.InnerException.ToString();
                else
                    ls_errorDetails["ls_exceptiondetails"] = ex.StackTrace;

                ls_errorDetails["ls_exceptionmessage"] = ex.Message;

                Guid errorID = service.Create(ls_errorDetails);

                if (errorID == Guid.Empty)
                {
                    throw new InvalidPluginExecutionException("Unable to save Error details in CRM " + ex.Message, ex);
                }
            }
            catch (Exception excn)
            {
                //Log error details into text file,if there is any exception while saving error details into CRM
                LogErrorInTextFile(ex);
                LogErrorInTextFile(excn);
            }
        }

        // This method is used to log error details into Text file
        private static bool LogErrorInTextFile(Exception objException)
        {
            string strException = string.Empty;
            StreamWriter sw;
            string strPathName;
            bool bReturn = false;
            try
            {
                strPathName = GetLogFilePath();

                sw = new StreamWriter(strPathName, true);
                sw.WriteLine("Source        : " + objException.Source.ToString().Trim());
                sw.WriteLine("Method        : " + objException.TargetSite.Name.ToString());
                sw.WriteLine("Date        : " + DateTime.Now.ToLongTimeString());
                sw.WriteLine("Time        : " + DateTime.Now.ToShortDateString());
                sw.WriteLine("Computer    : " + Dns.GetHostName().ToString());
                sw.WriteLine("Error        : " + objException.Message.ToString().Trim());
                sw.WriteLine("Stack Trace    : " + objException.StackTrace.ToString().Trim());
                sw.WriteLine("^^-------------------------------------------------------------------^^");
                sw.Flush();
                sw.Close();
                bReturn = true;
            }
            catch (Exception)
            {
                bReturn = false;
            }
            return bReturn;
        }

        private static string GetLogFilePath()
        {
            String strLogFilePath = "C:\\ErrorLog\\PluginErrorLog.txt";
            try
            {
                // if exists, return the path
                if (File.Exists(strLogFilePath) == true)
                    return strLogFilePath;
                //create a text file
                else
                {
                    if (CheckDirectory(strLogFilePath) == false)
                        return string.Empty;

                    FileStream fs = new FileStream(strLogFilePath, FileMode.OpenOrCreate, FileAccess.ReadWrite);
                    fs.Close();
                }

                return strLogFilePath;
            }
            catch (Exception)
            {
                return string.Empty;
            }
        }

        private static bool CheckDirectory(string strLogPath)
        {
            try
            {
                int nFindSlashPos = strLogPath.Trim().LastIndexOf("\\");
                string strDirectoryname = strLogPath.Trim().Substring(0, nFindSlashPos);

                if (false == Directory.Exists(strDirectoryname))
                    Directory.CreateDirectory(strDirectoryname);
                return true;
            }
            catch (Exception)
            {
                return false;

            }
        }

Call WCF service from plugin in Dynamics CRM 2011

Create a custom entity to maintain all configuration values, In my case I created entity like below and added caseservice details(Name: CaseServiceURL, Column: CaseService, Value: http://localhost/CaseService/Case.svc)

Entity Name: ls_settings

Attributes: Name, Column, Value

After you have created a service, generate the service proxy file using svcutil.exe tool from visual studio command line.

Svcutil.exe http://localhost/CaseService/Case.svc?wsdl

This will generate a configuration file and a code file that contains the client class. Add proxy code file to your plugin project and use the generated client class to call the Service.

Here CaseProxy is ServiceProxy class, ICase is Service interface.

        // send CaseId to other application
        private void SendCase(IOrganizationService service, Guid caseId)
        {
            //Use same binding used in WCF service config
            var binding = new BasicHttpBinding();

            //Get Case service URL
            string endPointAddress = GetEndpointAddress(service);

            if (!string.IsNullOrEmpty(endPointAddress))
            {
                //specify EndpointURL
                var endPoint = new EndpointAddress(endPointAddress);

                //ChannelFactory to specify binding and EndpointURL

                //Here CaseProxy is my ServiceProxy class, ICase is my service interface
                var channelFactory = new ChannelFactory<CaseProxy.ICase>(binding, endPoint);

                //create instance of CaseService
                CaseProxy.ICase caseClient = null;

                try
                {
                    //Create a channel
                    caseClient = channelFactory.CreateChannel();

                    //Call Service Method to send the case
                    caseClient.SendCase(caseId);

                    //Close WCF service
                    ((ICommunicationObject)caseClient).Close();
                }
                catch (Exception ex)
                {
                    if (caseClient != null)
                    {
                        ((ICommunicationObject)caseClient).Abort();
                    }
                    throw new Exception(string.Format("Error occured while sending case request to other application: {0}", ex.Message), ex);
                }
            }
        }

        // Get Case service URL from Settings entity
        private string GetEndpointAddress(IOrganizationService service)
        {
            string endPointAddress = string.Empty;

            string fetchXML = "<fetch version='1.0' output-format='xml-platform' mapping='logical' no-lock='true' distinct='false'>
                                                    <entity name='ls_settings'>
                                                        <attribute name='ls_value' />
                                                        <filter type='and'>
                                                            <condition attribute='ls_column' operator='eq' value='CaseServiceURL'/>
                                                        </filter>
                                                    </entity>
                                                </fetch>";

            var req = new FetchExpression(fetchXML);

            EntityCollection results = service.RetrieveMultiple(req);

            if (results.Entities.Count > 0)
            {
                endPointAddress = results.Entities[0].GetAttributeValue<string>("value");
            }
            return endPointAddress;
        }