Friday, 6 March 2015

The E-mail Router service could not run the service main background thread. The E-mail Router service cannot continue and will now shut down.

Because of some issues 'Microsoft CRM Email Router' service stopped automatically. On manual start it stopped immediately saying SystemState.xml is corrupt.

I found the actual error message (following) in event log. But most useful message was prior to this error message saying "Error accesing SystemState.xml. Restore file with last backup."

#16192 - The E-mail Router service could not run the service main background thread. The E-mail Router service cannot continue and will now shut down. System.Configuration.ConfigurationErrorsException: The E-mail router service cannot access system state file Microsoft.Crm.Tools.EmailAgent.SystemState.xml. The file may be missing or may not be accessible. The E-mail Router service cannot continue and will now shut down. ---> System.Xml.XmlException: Root element is missing.
   at System.Xml.XmlTextReaderImpl.ThrowWithoutLineInfo(String res)
   at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
   at System.Xml.XmlLoader.Load(XmlDocument doc, XmlReader reader, Boolean preserveWhitespace)
   at System.Xml.XmlDocument.Load(XmlReader reader)
   at System.Xml.XmlDocument.Load(String filename)
   at Microsoft.Crm.Tools.Email.Providers.ConfigFileReader..ctor(String filePath, ServiceLogger serviceLogger)
   at Microsoft.Crm.Tools.Email.Providers.SystemState.Initialize(ServiceLogger serviceLogger)
   at Microsoft.Crm.Tools.Email.Agent.ServiceCore.InitializeSystemConfiguration()
   --- End of inner exception stack trace ---
   at Microsoft.Crm.Tools.Email.Agent.ServiceCore.InitializeSystemConfiguration()
   at Microsoft.Crm.Tools.Email.Agent.ServiceCore.ExecuteService()

It gave me an idea that I need to find and restore the file from backup (if there is any). File was located at C:\Program Files\Microsoft CRM Email\Service\Microsoft.Crm.Tools.EmailAgent.SystemState.xml.

But Unfortunately there was not any backup. So I just renamed the file and restarted the service. It worked and started picking up pending emails gradually. 

For future use I have taken backup of SystemState.xml and Configuration.bin as suggested by Microsoft

P.S. Hayer

How to reset Auto-numbering and where does CRM stores Prefix and Number

I Recently came across a question on a forum about how to update Auto-numbering number and where does CRM stores it in Database? I have looked in CRM tables and found that CRM stores it in [OrganizationBase] table. But as it is unsupported to update in SQL, I was not sure that if updating the number will work or not. I personally avoid doing unsupported things. But to find out the consequences I decided to give it a go in my Development environment. 

Note: To update Auto-Numbering by Supported way, have a look at How to reset Auto-numbering in Dynamics CRM Online.

Following query returned the current auto number values.

SELECT [KbPrefix]
      ,[CurrentKbNumber]
      ,[CasePrefix]
      ,[CurrentCaseNumber]
      ,[ContractPrefix]
      ,[CurrentContractNumber]
      ,[QuotePrefix]
      ,[CurrentQuoteNumber]
      ,[OrderPrefix]
      ,[CurrentOrderNumber]
      ,[InvoicePrefix]
      ,[CurrentInvoiceNumber]
      ,[UniqueSpecifierLength]     
  FROM [dbo].[OrganizationBase]

Then executed the following query to reset number:

Update [dbo].[OrganizationBase]
set [CurrentKbNumber] = 10000;

Resetting number only can cause duplication if you already have records with that number. So to avoid it I have updated the Prefix as well. 

CRM allows to update Prefix using UI but Number field is locked as shown below:

Got to Settings > Administration > Auto-numbering


Now on new Article creation, I can notice that Number has been set to 10000. Done some light testing and could not find any issues.

Note: It is not a Microsoft supported way of updating Auto-numbering, I would recommend to update Auto-Numbering Supported Way by updating the Organization using sdk.

P.S. Hayer

Wednesday, 1 October 2014

Dynamics CRM: Create ServiceAppointment

A service appointment represents an appointment for service. The schema name for this entity is ServiceAppointment. The service appointment entity represents a block of time on a calendar. This entity stores the availability blocks for resources and can be used to determine resource availability and scheduling.
A service appointment can be customized independently from other activities to accommodate the customer's business requirements for service delivery. A service appointment must have a corresponding service. It can be already bound with a set of resources specified by an activity party (ActivityParty) list.
Following code can be used to create a Service Activity.

                // Create the ActivityParty. Resource can be a system user and user's Calendar will be booked for the duration.
                var resource = new ActivityParty
                {
                    PartyId = new EntityReference(SystemUser.EntityLogicalName, _userId)
                };

                // Create and instance of ServiceAppointment
                var appointment = new ServiceAppointment
                {
                    Subject = "Test Service Appointment",
                    ServiceId =
                        new EntityReference(Service.EntityLogicalName, new Guid("B39F04D6-7C93-E111-9675-005056B41D39")),
                    ScheduledStart = DateTime.Now.AddDays(5),
                    ScheduledEnd = DateTime.Now.AddDays(15),
                    Location = "On Site",
                    Description = "Test Appointment created for testing.",
                    Resources = new ActivityParty[] { resource },
                    IsAllDayEvent = true,
                    IsBilled = true
                };

                var request = new CreateRequest {Target = appointment};
                var response = (CreateResponse) _service.Execute(request);

                var serviceAppointmentId = response.id;

P. S. Hayer

Thursday, 28 August 2014

CRM 2011: Get list of audited attributes from AuditBase table

Recently, I was asked to get all the fields and their entities which were audited in a day so that we can decide if we really need to audit all of those fields and disable auditing where its not required.

As we know there is no Filtered view for audit entity and even advance find don't support Audit search, so it is bit difficult to get the list of fields audited. But Kelvin's blog put me on right path to write this query and get desired result.

-- Declaring Variable and Temp Tables
DECLARE @AttributeMask VARCHAR(MAX), @ObjectTypeCode INT, @EntityName VARCHAR(100), @LogDATETIME DATETIME
DECLARE @CurrentAttribute VARCHAR(MAX)
DECLARE @Audit Table(AttributeMask VARCHAR(MAX), ObjectTypeCode INT, EntityName VARCHAR(100), LogDATETIME DATETIME);                               
DECLARE @Result Table (AttributeId INT, ObjectTypeCode INT, EntityName VARCHAR(100), LogDATETIME DATETIME);
DECLARE @todaysdate DATETIME;

-- Set the date to bring all the fields Audited today
SET @todaysdate = CAST(GETDATE() AS DATE);

-- Get all todays records from AuditBase; You can remove where clause to get everything
INSERT INTO @Audit
SELECT a.AttributeMask, a.ObjectTypeCode, e.Name, a.CreatedOn FROM Audit AS a
INNER JOIN MetadataSchema.Entity e on a.ObjectTypeCode = e.ObjectTypeCode
WHERE CAST(a.CreatedOn AS DATE) = @todaysdate;

-- Using Cursor to go through each and every record in @Audit Table
DECLARE DataAuditCursor CURSOR FOR
SELECT * FROM @Audit

OPEN DataAuditCursor

FETCH NEXT FROM DataAuditCursor
INTO @AttributeMask, @ObjectTypeCode, @EntityName, @LogDATETIME

WHILE @@FETCH_STATUS = 0
BEGIN
      -- Run while Attribute mask have comma(s) in it
      WHILE CHARINDEX(',',@AttributeMask,0) <> 0
    BEGIN
            SELECT
            @CurrentAttribute=RTRIM(LTRIM(SUBSTRING(@AttributeMask,1,CHARINDEX(',',@AttributeMask,0)-1))),
            @AttributeMask=RTRIM(LTRIM(SUBSTRING(@AttributeMask,CHARINDEX(',',@AttributeMask,0)+1,LEN(@AttributeMask))))
           
        IF LEN(@CurrentAttribute) > 0
        INSERT INTO @Result Values(@CurrentAttribute, @ObjectTypeCode, @EntityName, @LogDATETIME)
    END
   
    INSERT INTO @Result VALUES((CASE WHEN ISNULL(@AttributeMask, '') = '' THEN NULL ELSE @AttributeMask END),
                                                @ObjectTypeCode, @EntityName, @LogDATETIME)
   
    FETCH NEXT FROM DataAuditCursor
    INTO @AttributeMask, @ObjectTypeCode, @EntityName, @LogDATETIME
END

CLOSE DataAuditCursor;
DEALLOCATE DataAuditCursor;

-- Select Distinct to get all the Attributes and their entities
SELECT DISTINCT
    r.EntityName
    ,(SELECT TOP 1 a.Name FROM MetadataSchema.Attribute a
            INNER JOIN MetadataSchema.Entity e ON
            a.EntityId = e.EntityId
            and a.ColumnNumber = r.AttributeId
            and e.ObjectTypeCode = r.ObjectTypeCode) 'AttributeName'
FROM @Result r;



Happy Coding

P. S. Hayer
(ਪ੍ਰੇਮਜੀਤ ਸਿੰਘ ਹੇਰ)

Please check my other (non-CRM) blog here: Programming Blogs 

Monday, 23 June 2014

Dynamics CRM: How to get Business Closure Dates in Plugin


using System;
using System.Linq;
using HayerCrmPackage.Plugins.Entities;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;

namespace HayerCrmPackage.Plugins
{
    public class PreCaseCreate : Plugin
    {
        public PreCaseCreate()
            : base(typeof(PreCaseCreate))
        {
            base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Create", "incident", new Action<LocalPluginContext>(ExecutePreCaseCreate)));            
        }

        protected void ExecutePreCaseCreate(LocalPluginContext localContext)
        {
            if (localContext == null)
            {
                throw new ArgumentNullException("localContext");                
            }

            var pluginContext = localContext.PluginExecutionContext;
            var service = localContext.OrganizationService;
            var context = new OrganizationServiceContext(service);

            // Get Organization Business Closure Calendar Id
            var organization = service.Retrieve("organization", context.OrganizationId, new ColumnSet("businessclosurecalendarid"));

            var query = new QueryExpression("calendar")
            {
                ColumnSet = new ColumnSet(true),
                Criteria = new FilterExpression()
            };

            // Add condition to get Get Calander where CalanderId is equal to Organization's businessclosurecalendarid
            query.Criteria.AddCondition(new ConditionExpression("calendarid", ConditionOperator.Equal, organization["businessclosurecalendarid"].ToString()));

            // Get Calendar
            var businessClosureCalendar = service.RetrieveMultiple(query).Entities[0];

            // Get the Calendar rules
            IEnumerable<Entity> calanderRules = businessClosureCalendar != null ? businessClosureCalendar.GetAttributeValue<EntityCollection>("calendarrules").Entities : null;
        }
    }
}



To know how to add working days by calculating business closure dates and weekends, please check my blog Here.

Happy Coding

P. S. Hayer
(ਪ੍ਰੇਮਜੀਤ ਸਿੰਘ ਹੇਰ)

Please check my other (non-CRM) blog here: Programming Blogs

Friday, 20 June 2014

Dynamics CRM: How to Add working days by calculating business closure and weekends


Recently I received a requirement to set the Incident Auto Closure date to 'CreatedOn' + 7 working days. To add working days to 'CreatedOn' date, we need to exclude all the weekend and business closures. I came up with the following solution to achieve the required outcome.
Add the following Class to your project to calculate working day and business closures.

using System;
using System.Collections.Generic;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Query;

namespace HayerCrmPackage.Plugins
{
    public class AddWorkingDaysClass
    {
        /// 
        /// Add working days to a specific date by calculating Business Closure and weekends
        /// 
        public static DateTime AddWorkingAndBusinessClosureDays(DateTime specificDate, int workingDaysToAdd, 
            IOrganizationService service, IPluginExecutionContext context)
        {
            var currentDate = specificDate;
            var businessClosures = GetBusinessClosureCalendarRules(context, service);

            // Calculate the working days by taking out the weekends
            int completeWeeks = workingDaysToAdd / 5;
            DateTime date = specificDate.AddDays(completeWeeks * 7);
            workingDaysToAdd = workingDaysToAdd % 5;
            for (int i = 0; i < workingDaysToAdd; i++)
            {
                date = date.AddDays(1);
                while (!IsWorkingDayOfWeek(date))
                {
                    date = date.AddDays(1);
                }
            }

            // Calculate the working days by taking out Business Closures
            for (var i = currentDate; i <= date; i = i.AddDays(1))
            {
                if (i.DayOfWeek == DayOfWeek.Saturday || i.DayOfWeek == DayOfWeek.Sunday)
                    continue;

                foreach (var closure in businessClosures)
                {
                    var startDate = (DateTime)closure["effectiveintervalstart"];
                    var endDate = (DateTime)closure["effectiveintervalend"];
                    var range = new DateRange(startDate, endDate);

                    if (range.Includes(i))
                    {
                        date = date.AddDays(1);

                        if (date.DayOfWeek == DayOfWeek.Saturday)
                        {
                            date = date.AddDays(2);
                            i = i.AddDays(1);
                        }
                        else if (date.DayOfWeek == DayOfWeek.Sunday)
                        {
                            date = date.AddDays(1);
                        }
                    }
                }
            }
            return date;
        }

        /// 
        /// Get Business Closure Calendar Rules
        /// 
        private static IEnumerable<Entity> GetBusinessClosureCalendarRules(IPluginExecutionContext context,
            IOrganizationService service)
        {
            // Get Organization Business Closure Calendar Id
            var organization = service.Retrieve("organization", context.OrganizationId, new ColumnSet("businessclosurecalendarid"));

            var query = new QueryExpression("calendar")
            {
                ColumnSet = new ColumnSet(true),
                Criteria = new FilterExpression()
            };

            // Add condition to get Get Calander where CalanderId is equal to Organization's businessclosurecalendarid
            query.Criteria.AddCondition(new ConditionExpression("calendarid", ConditionOperator.Equal, organization["businessclosurecalendarid"].ToString()));

            // Get Calendar
            var businessClosureCalendar = service.RetrieveMultiple(query).Entities[0];

            // Return the Calendar rules
            return businessClosureCalendar != null ? businessClosureCalendar.GetAttributeValue<EntityCollection>("calendarrules").Entities : null;
        }

        private static bool IsWorkingDayOfWeek(DateTime date)
        {
            var day = date.DayOfWeek;
            return day != DayOfWeek.Saturday && day != DayOfWeek.Sunday;
        }

        public interface IRange<T>
        {
            T Start { get; }
            T End { get; }
            bool Includes(T value);
        }

        public class DateRange : IRange<DateTime>
        {
            public DateRange(DateTime start, DateTime end)
            {
                Start = start;
                End = end;
            }

            public DateTime Start { get; private set; }
            public DateTime End { get; private set; }

            public bool Includes(DateTime value)
            {
                return (Start <= value) && (value < End);
            }
        }
    }
}



To consume above class in your plugin, use the following code:
using System;
using System.Linq;
using HayerCrmPackage.Plugins.Entities;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;

namespace HayerCrmPackage.Plugins
{
    public class PreCaseCreate : Plugin
    {
        public PreCaseCreate()
            : base(typeof(PreCaseCreate))
        {
            base.RegisteredEvents.Add(new Tuple<int, string, string, Action<LocalPluginContext>>(20, "Create", "incident", new Action<LocalPluginContext>(ExecutePreCaseCreate)));            
        }

        protected void ExecutePreCaseCreate(LocalPluginContext localContext)
        {
            if (localContext == null)
            {
                throw new ArgumentNullException("localContext");                
            }

            var pluginContext = localContext.PluginExecutionContext;
            var service = localContext.OrganizationService;
            var context = new OrganizationServiceContext(service);

            var targetEntity = (pluginContext.InputParameters != null && pluginContext.InputParameters.Contains("Target"))
            ? (Entity)pluginContext.InputParameters["Target"]
            : null;

            // Converting Entity to Incident
            var incident = (targetEntity != null && targetEntity.LogicalName == Incident.EntityLogicalName) ? targetEntity.ToEntity<Incident>() : null;
            
            // If you want to update the number of working days to add, change the value of 2nd parameter below
            incident.new_AutoClosureDate = AddWorkingDaysClass.AddWorkingAndBusinessClosureDays(DateTime.Now, 7, service,
                        pluginContext);
        }
    }
}

Happy Coding

P. S. Hayer
(ਪ੍ਰੇਮਜੀਤ ਸਿੰਘ ਹੇਰ)

Ref:
 1. Adding Working Week Days to a Specific Date
 2. Check if Date Time is in Date Range
Please check my other (non-CRM) blog here: Programming Blogs

Thursday, 19 June 2014

CRM 2013: Timer button is currently disabled

Service pack 1 of Dynamics CRM 2013 came up with an exciting feature of SLA Timer. Timers can be used to track service level agreement KPI to ensure customer commitments are met. Today I was trying to get my hands on it but after installing Service Pack 1, I discovered that Timer option was greyed out on form editor. It was saying:

"This button is currently disabled".

"You may not have selected the item that works with this feature. If you don not have permissions to use this feature, correct your system."


 Solution: 

Go to Settings > Administration > Install Product Updates

You will get the following screen.

Install the updated by clicking on 'Update' at the bottom of the screen.

it will install the updated and in the mean time you can watch the video on installation screen to know more about this brilliant feature. Once updated you will get the confirmation screen. 

Open case form in form editor. You will see that 'Timer' button is enabled not and it is ready to use. 



P. S. Hayer
(ਪ੍ਰੇਮਜੀਤ ਸਿੰਘ ਹੇਰ)
Please check my other (non-CRM) blog here: Programming Blogs