Разработка
26
Авг
0

Расчет рабочих дней

В MS Dynamics CRM есть такая функциональность как Рабочие часы. Она позволяет задавать (на карточке Полбзователя) график работы каждого отдельно взятого юзвера. Данный функционал используется для планирования работ службы сервиса (в модуле Сервис MS CRM). Помимо этого, MS CRM также позволяет добавлять и вычитать дни в бизнес-процессах, чтобы динамически заполнить атрибуты типа datetime. К сожалению эти две функции между собой никак не связане: при операции с датами никак не учитываются выходные и праздничные дни. Т.е. по «мнению» CRM Вы вполне можете поработать и в выходные 🙂

Чтобы исправить этот недочет можно пойти разными путям… Сейчас рассмотрим как использовать для этих целей кастомный бизнес-процесс и веб-сервис…

Бизнес-процесс BusinessDays

  • Зарегистрируйте сборку бизнес-процесса с помощью Plugin Registration Tool в CRM;
  • Для тестовых целей задайте для пользователя нерабочий день (или несколько дней);
  • Создайте новый Бизнес-процесс для Контакта;
  • Добавьте один из новых шагов в бизнес-процесс: Add (добавление) или Subtract (вычитание) из раздела Date Utilities. При этом задайте понятное имя для шага вычисления даты (т.к. оно дальше будет использоваться);
  • Введите стартовую дату вычисления и число рабочих дней для добавления/вычитания (но так чтобы в этот промежуток попал нерабочий день – чтобы увидеть работу шаг);
  • Далее моздайте шаг изменения Контакта. В какое-либо поле datetime поставьте динамическое вычесляемое поле, созданное на предыдущем шаге;
  • Публикуйте бизнес-процесс и запустите его вручную в отношении какой-либо записи Контакта. Ну, и смотрите на дату – она должна быть равна исходной + заданное количество рабочих дней.





Сорсы.

Web-сервис BusinessDays

Бизнес-процес это хорошой, но подобный функционал может потребоваться во многих местах. Поэтому сейчас сделаем небольшой web-сервис. Web-сервис это как aspx-страница, но которая не визуализирует ответ. Web-сервисы предназначены для общения между программами – мнжество программ отправляют запрос в web-сервис в ввиде SOAP-запроса и получают какой-то ответ в XML-формате. И у CRM есть свой веб-сервис по адресу: http://scrserver/mscrmservices/2007/CrmService.asmx. Вобсчем довольно интересно… 🙂

Создается во многом аналогично ASPX-страницам:

  • Для начала нам понадобится веб-сайт: создайте в inetpub новую папку bussinesday, а затем создайте в IIS’е новый веб-сайт с размещением в этой папке. Для сайта задайте порт 5555;


  • Откройте Visual Studio и создайте новый web-сайт (File — New — Web Site), в качестве шаблона выберите ASP.NET Web Service;
  • Переименуйте файл service.cs в BusinessDay.cs, а service.asmx в BusinessDay.asmx;
  • В BusinessDay.cs добавьте такой код:
    using System;
    using System.Web;
    using System.Web.Services;
    using System.Web.Services.Protocols;
    using Microsoft.Crm.Sdk;
    using Microsoft.Crm.SdkTypeProxy;
    using Microsoft.Crm.Sdk.Query;
    using System.Collections;
    using System.Collections.Generic;
    using System.Xml;
    using System.Net;
    
    [WebService(Namespace = "http://win-n22hj23d1b1:5555/BusinessDays.asmx")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class BusinessDays : System.Web.Services.WebService
    {
        // Определеяем первый метод, добавляющий дни к исходной дате
        [WebMethod]
        public CrmDateTime AddBusinessDays(string userId, string startDate, int numDays)
        {
            // Возвращаем праздничные дни
            List<string> CrmHolidays = GetHolidays(userId);
    
            // Начальная дата и количество дней прироста
            DateTime CounterDate = DateTime.Parse(startDate);
            int intNumberOfDays = numDays;
    
            // Подсчитываем дату, которая наступит после N рабочих дней
            if (CounterDate.DayOfWeek == DayOfWeek.Saturday || CounterDate.DayOfWeek == DayOfWeek.Sunday || CrmHolidays.Contains(CounterDate.ToShortDateString()))
            {
                intNumberOfDays++;
            }
    
            for (int i = 0; i < intNumberOfDays; i++)
            {
                CounterDate = CounterDate.AddDays(1);
                if (CounterDate.DayOfWeek == DayOfWeek.Saturday || CounterDate.DayOfWeek == DayOfWeek.Sunday || CrmHolidays.Contains(CounterDate.ToShortDateString()))
                {
                    i--;
                }
            }
    
            // Возвращаем рассчитанную дату
            return CrmDateTime.FromUniversal(CounterDate);
        }
    
        // Определеяем второй метод, вычитающий дни из исходной даты
        [WebMethod]
        public CrmDateTime SubtractBusinessDays(string userId, string startDate, int numDays)
        {
            // Возвращаем выходные и праздничные дни
            List<string> CrmHolidays = GetHolidays(userId);
    
            // Начальная дата и количество дней прироста
            DateTime CounterDate = DateTime.Parse(startDate);
            int intNumberOfDays = numDays;
    
            // Подсчитываем дату, которая наступит после N рабочих дней
            for (int i = 0; i < intNumberOfDays; i++)
            {
                CounterDate = CounterDate.AddDays(-1);
                if (CounterDate.DayOfWeek == DayOfWeek.Saturday || CounterDate.DayOfWeek == DayOfWeek.Sunday || CrmHolidays.Contains(CounterDate.ToShortDateString()))
                {
                    i--;
                }
            }
    
            // Возвращаем рассчитанную дату
            return CrmDateTime.FromUniversal(CounterDate);
        }
    
        // Функция подсчитывающая количество праздничных дней для конкретного юзвера, заданной даты и на заданное количество дней
        public static List<string> GetHolidays(string userId)
        {
            List<string> CrmHolidays = new List<string>();
    
            // Настраиваем CRM Service (логинемся под админом, а запрос выполняем в контексте заданного юзвера))
            CrmAuthenticationToken token = new CrmAuthenticationToken();
            token.OrganizationName = "superfirma";
            token.CallerId = new Guid(userId); // Выполняем запрос в контексте заданного юзвера  
            CrmService crmService = new CrmService();
            crmService.CrmAuthenticationTokenValue = token;
            crmService.Credentials = new NetworkCredential("administrator", "1qaz@WSX", "d2008");
            crmService.Url = "http://win-n22hj23d1b1/MSCrmServices/2007/CrmService.asmx";
    
    
            string strFetch = null;
            strFetch += "<fetch mapping='logical'>";
            strFetch +=     "<entity name='calendar'>";
            strFetch +=         "<attribute name='name' />";
            strFetch +=         "<filter>";
            strFetch +=             "<condition attribute='name' operator='eq' value='Business Closure Calendar' />";
            strFetch +=         "</filter>";
            strFetch +=         "<link-entity name='calendarrule' from='calendarid' to='calendarid'>";
            strFetch +=             "<attribute name='effectiveintervalstart' />";
            strFetch +=             "<attribute name='effectiveintervalend' />";
            strFetch +=         "</link-entity>";
            strFetch +=     "</entity>";
            strFetch += "</fetch>";
            string strResultXml = crmService.Fetch(strFetch);
    
            XmlDocument xmlResultDoc = new XmlDocument();
            xmlResultDoc.LoadXml(strResultXml);
            XmlNodeList xmlResultNodes =
            xmlResultDoc.GetElementsByTagName("result");
    
            if (xmlResultNodes.Count > 0)
            {
                foreach (System.Xml.XmlNode resNode in xmlResultNodes)
                {
                    string strStart = resNode.SelectSingleNode("calendarid.effectiveintervalstart").Attributes["date"].Value;
                    string strEnd = resNode.SelectSingleNode("calendarid.effectiveintervalend").Attributes["date"].Value;
                    DateTime Start = DateTime.Parse(strStart);
                    DateTime End = DateTime.Parse(strEnd);
                    TimeSpan diff = End - Start;
                    int NumberofDays = diff.Days;
                    for (int i = 0; i < NumberofDays; i++)
                    {
                        if (!CrmHolidays.Contains(Start.AddDays(i).ToShortDateString()))
                        {
                            CrmHolidays.Add(Start.AddDays(i).ToShortDateString());
                        }
                    }
                }
            }
    
            return CrmHolidays;
        }
    }
    

    В этом коде:

    • Объявляется веб-сервис и задется его начальный URL (измените его если он у Вас будет отличаеться);
    • Объявляется два метода этого веб-сервиса и одна функция;
    • Методы подсчитывают количество праздничных и выходных дней, начиная от исходной даты +/- указанное количество дней и для заданного пользователя;
    • Функция же возвращает выходные дни для какого либо пользователя (в функции происходит подключение к CRM, поэтому измените учетку на Вашего админа).
  • Добавьте в проект ссылки на сборки SDK;
  • Перйдите в BusinessDay.asmx и переименуте класс service на BusinessDay;
  • В web.config в раздел system.web добавьте такую строчку:
  • Публикуем веб-сервис: Build — Publish Web Site — укажите путь к папке bussinesday (созданной в первом шаге).




Теперь если перейдете по URL: http://server:5555/BusinessDay.asmx, то сможете поиграться с методами web-сервиса, позапускать их с различными параметрами. Тут же, кстати, приведены примеры SOAP-запросов. Давайте рассмотрим парочку примеров, как пользоваться этим веб-сервисом:


Плагин

  • Создайте новый плагин;
  • Подключите к нему сборки SDK и подпишите сложным ключом;
  • Вставьте в него такой код:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Crm.Sdk;
    using Microsoft.Crm.SdkTypeProxy;
    using ClassLibrary1.BusinessDays;
    using System.Net;
    
    namespace ClassLibrary1
    {
        public class Class1 : IPlugin 
        {
            public void Execute(IPluginExecutionContext context)
            {
                // Получаем контекст плагина
                ICrmService crmService = context.CreateCrmService(true);
                
                // Вытаскиваем из пдагина GUID вызвавшего его пользователя
                Key userGUID = new Key();
                userGUID.Value = context.UserId;
                
                // Создаем эклемпляр веб-сервиса BusinessDays
                ClassLibrary1.BusinessDays.BusinessDays bd = new ClassLibrary1.BusinessDays.BusinessDays();
    
                // Вызываем метод AddBusinessDays и передаем ему три параметра: GUID пользователя (в данном примере - пользователя вызвавшего плагин), начальная дата, количество рабочих дней прироста
                ClassLibrary1.BusinessDays.CrmDateTime cdt = bd.AddBusinessDays(userGUID.Value.ToString(), "22.08.2010", 3);
     
                // Выводим сообщение с возвращенной датой
                throw new Exception(cdt.Value.ToString());
            }
        }
    }
    

    Разбор полетов:

    • Получаем контекст плагина и вытаскиваем из него юзвера, вызвавшего его;
    • Создаем экземпляр созданного веб-сервиса;
    • Вызываем метод сервиса AddBusinessDays для подсчета рабочих дней;
    • Ну, и выводим результирующую дату.
  • Добавьте в плагин ссылку на сам веб-сервис: правый щелчок мыши на заголовке проекта — Add Service Reference — Advanced… — Add Web Reference — укажите URL веб-сервиса. В качестве имени укажите BusinessDays;
  • Соберите сборку и зарегистрируйте ее в CRM на какое-либо событие (с помощью Plugin Registration Tool);
  • Вызовите это событие в CRM и смотрите на результат.




JavaScript

Ну, тут совсем все просто… повесьте на онлоад какой-либо формы такой js-код:

// Подготавливаем SOAP-запрос
var xml = "<?xml version='1.0' encoding='utf-8'?>" +
"<soap:Envelope xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema' xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/'>" +
    "<soap:Header>" +
        "<AUTHHEADER xmlns='http://win-n22hj23d1b1:5555/BusinessDays.asmx'>" +
            "<USERNAME>d2008\administrator</USERNAME>" +
            "<PASSWORD>1qaz@WSX</PASSWORD>" +
        "</AUTHHEADER>" +
    "</soap:Header>" +
    "<soap:Body>" +
        "<AddBusinessDays xmlns='http://win-n22hj23d1b1:5555/BusinessDays.asmx'>" +
            "<userId>3B9DC90C-4DA6-DF11-A623-000C29F6B309</userId>" +
            "<startDate>25.08.2010</startDate>" +
            "<numDays>5</numDays>" +
        "</AddBusinessDays>" +
    "</soap:Body>" +
"</soap:Envelope>";

// Отправляем запрос в веб-сервис
var xHReq = new ActiveXObject("Msxml2.XMLHTTP");
xHReq.Open("POST", "http://win-n22hj23d1b1:5555/BusinessDays.asmx", false);
xHReq.setRequestHeader("SOAPAction", "http://win-n22hj23d1b1:5555/BusinessDays.asmx/AddBusinessDays");
xHReq.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
xHReq.setRequestHeader("Content-Length", xml.length);
xHReq.send(xml);

// Получаем результат
var resultXml = xHReq.responseXML;
alert(resultXml.text);

В этом коде просто формируется SOAP-запрос и отправляется в веб-сервис. А результат выводится с помощью алерта. Заметьте, что в загаловке указывается админ – поменяйте его на Вашего 🙂

Ну, и загрузите эту форму 🙂


Комментарии (0)

*

code