AXForum  
Вернуться   AXForum > Блоги > CRM, SharePoint и Черная Магия
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

Добро пожаловать в мой блог! Изначально он не задумывался как блог CRM разработчика, но жизнь сама внесла нужные коррективы. Тут я публикою все свои наблюдения относительно обозначенных в заголовке систем. Если Вы найдете в нем что-то интересное для Вас, как для заказчика, то буду рад сотрудничать с Вами! В моей компетенции 100% задач по MS CRM 3.0/4.0/2011:
  • Консалтинг
  • Проектирование
  • Разработка
  • Обучение


MVP 2010, 2011
Оценить эту запись

Учет расписания пользователя при назначени Действий

Запись от Артем Enot Грунин размещена 13.11.2009 в 15:27
Обновил(-а) Артем Enot Грунин 19.11.2009 в 09:21 (ударно глючит движок блога)

Недавно на форуме обсуждался вопрос о некоторой несправедливости CRM в отношении планирования задач и действий сервиса: Выставление сроков. Дело в том, что планирование действия сервиса происходит в соответствии с расписанием пользователя, в то время как Задачу можно назначить на пять утра в воскресенье или на обеденный перерыв. В исходном посте обсуждается назначение действий из рабочего процесса - задача несомненно важная, однако в моей практике актуалнее вопрос планирования с использованием пользовательского интерфейса. Так как же отразить адекватный список опций фремени при планировании Задач? Для этого я использовал запрос планирования QueryScheduleRequest, возвращающий набор TimeInfo - объектов содержащих данные по интервалам занятости определенного ресурса. Далее, обработал результат и по аналогии с решением в посте Формат времени сформировал список опций для выпадающего списка времени. Приведенный ниже код нужно разместить на форме задачи (тегами помечено в каких обработчиках).
Код:
//<OnChange> DateTime
var RESORCE_UNAVAILABLE_MSG = "Ресурс на текущий день недоступен";
var RESORCE_NOTSELECTED_MSG = "Не выбран ресурс";
var SHED_REQUEST_FAILED_MSG = "Невозможно прочитать рассписание ресурса: ";
var SCHEDULING_INTERVAL_DEF = 30;

var oDateTime = crmForm.all.scheduledend;
var oUser = crmForm.all.ownerid;
var oDuration = crmForm.all.actualdurationminutes;

setUserShedule(oDateTime, oUser, oDuration);

function setUserShedule(oDateTime, oUser, oDuration)
{
    // Вычитываем ид ресурса
    var oUserId = oUser.DataValue != null ? oUser.DataValue[0].id : null;
    if (oUserId == null)
    {
        oDateTime.DataValue = null;
        return;
    }

    // Вычитываем день
    var oDate = oDateTime.DataValue;
    if (oDate == null) return;

    // Определяем шаг планирования
    var oScheduleInterval = oDuration.DataValue == null ? SCHEDULING_INTERVAL_DEF : oDuration.DataValue;
    
    // Вычитываем доступные опции
    var oTimeOptions = requestScheduleOptions(oUserId, oDate, oScheduleInterval);

    if (oTimeOptions == null || oTimeOptions.length == 0)
    {
        alert(RESORCE_UNAVAILABLE_MSG);
        // отключаем выбор времени, так же следует не давать сохранить форму
        oDateTime.all.time.disable();
        return;
    }
    
    // Настраиваем контрол выбора времени
    setTimeOptions(oDateTime, oTimeOptions);
}

// oDateTime - контрол даты для настройки
// oTimeOptions - массив содержащий строки опций 0:00, 00:30 и т.д.
function setTimeOptions(oDateTime, oTimeOptions)
{
    var timeField = oDateTime.all.time;
    var tables = timeField.getElementsByTagName("table");

    if ((tables != null) && (tables.length > 0))
    {
        var table = tables[1];

        while (table.firstChild != null)
        {
            table.removeChild(table.firstChild);
        }

        for (var i = 0; i < oTimeOptions.length; i++)
        {
            var row = table.insertRow();
            var cell = row.insertCell();
            var time = oTimeOptions[i];

            cell.setAttribute("val", time);
            cell.innerText = time;
        }
    }  
}

// oUserId - идентификатор пользователя
// oDate - дата на которую осуществляется планирование
// oScheduleInterval - шаг между интервалами планирования в минутах
function requestScheduleOptions(oUserId, oDate, oScheduleInterval)
{
    var oTimeOptions = new Array();

    // Начало суток        
    var oStartDate = new Date(oDate);
    oStartDate.setHours(0);
    oStartDate.setMinutes(0);
    
    // Конец суток
    var oEndDate = new Date(oStartDate);
    oEndDate.setDate(oDate.getDate() + 1);

    // Период в UTC для запроса
    var oStartDateUTC = FormatDateTime(oStartDate);
    var oEndDateUTC = FormatDateTime(oEndDate);

    var oRequestXML = "<?xml version='1.0' encoding='utf-8'?>" +
                "<soap:Envelope xmlns:soap='http://schemas.xmlsoap.org/soap/envelope/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:xsd='http://www.w3.org/2001/XMLSchema'>" +
                    GenerateAuthenticationHeader() +
                    "<soap:Body>" +
                        "<Execute xmlns='http://schemas.microsoft.com/crm/2007/WebServices'>" +
                            "<Request xsi:type='QueryScheduleRequest'>" +
                                "<ResourceId>" + oUserId + "</ResourceId>" +
                                "<Start>" + oStartDateUTC + "</Start>" +
                                "<End>" + oEndDateUTC + "</End>" +
                                "<TimeCodes>" +
                                    "<TimeCode>Available</TimeCode>" +
                                    "<TimeCode>Unavailable</TimeCode>" +
                                "</TimeCodes>" +
                            "</Request>" +
                        "</Execute>" +
                    "</soap:Body>" +
                "</soap:Envelope>";

    var oXMLHTTP = new ActiveXObject("Msxml2.XMLHTTP");
    oXMLHTTP.Open("POST", "/mscrmservices/2007/CrmService.asmx", false);
    oXMLHTTP.setRequestHeader("SOAPAction", "http://schemas.microsoft.com/crm/2007/WebServices/Execute");
    oXMLHTTP.setRequestHeader("Content-Type", "text/xml; charset=utf-8");
    oXMLHTTP.setRequestHeader("Content-Length", oRequestXML.length);
    oXMLHTTP.send(oRequestXML);

    var responseXML = oXMLHTTP.responseXML;

    // Стандартная проверка на ошибки
    var errorCount = responseXML.selectNodes('//error').length;
    if (errorCount != 0)
    {
        var msg = responseXML.selectSingleNode('//description').nodeTypedValue;
        alert(SHED_REQUEST_FAILED_MSG + msg);

        return;
    }

    var oTimeInfos = responseXML.getElementsByTagName("TimeInfo");
    if (oTimeInfos != null && oTimeInfos.length > 0)
    {
        var oPeriodStartArr = new Array(oTimeInfos.length);
        var oPeriodEndArr = new Array(oTimeInfos.length);

        for (var i = 0; i < oTimeInfos.length; i++)
        {
            var oTimeInfo = oTimeInfos[i];
            
            var oTimeCode = oTimeInfo.selectSingleNode("TimeCode").text;
            var oStartDate = ParseUtcDate(oTimeInfo.selectSingleNode("Start").text)
            var oEndDate = ParseUtcDate(oTimeInfo.selectSingleNode("End").text);

            if (oTimeCode == "Available")
            {
                oPeriodStartArr[i] = oStartDate;
                oPeriodEndArr[i] = oEndDate;
            }
            else
            {
                // Время конца перерыва - время начала работы!
                oPeriodStartArr[i] = oEndDate;
                oPeriodEndArr[i] = oStartDate;
            }
        }

        oPeriodStartArr.sort(sortDate);
        oPeriodEndArr.sort(sortDate);

        for (var i = 0; i < oTimeInfos.length; i++)
        {
            var oPeriodStart = oPeriodStartArr[i];
            var oPeriodEnd = oPeriodEndArr[i];

            while (oPeriodStart < oPeriodEnd)
            {
                var oHours = oPeriodStart.getHours();
                var oMinutes = oPeriodStart.getMinutes();

                var oTime = ((oHours < 10) ? "0" : "") + oHours + ":" + ((oMinutes < 10) ? "0" : "") + oMinutes;
                oTimeOptions.push(oTime);

                oPeriodStart.setMinutes(oMinutes + oScheduleInterval);
            }
        }

        return oTimeOptions;
    }
}

function sortDate(oArg1, oArg2)
{
    return oArg1.getTime() - oArg2.getTime();
}
//</OnChange>

//<OnChange> User
var oDateTime = crmForm.all.scheduledend;
oDateTime.FireOnChange();
//</OnChange>

//<OnChange> Duration
var oDateTime = crmForm.all.scheduledend;
oDateTime.FireOnChange();
//</OnChange>
Все бы еще ничего, если бы дело не пахло Microsoft: запрос возвращает данные не в виде интервалов доступности ресурса, а в виде набора пересечений: "Свободен с 9 до 18; Занят с 13 до 14". Так что потребовалась дополнительное осмысление данных перед генерацией опций для списка времени. Так же в коде учитывается длительность действия - задача должна полностью укладываться в интервал когда пользователь свободен.

Проблема: я пока не понял почему, но значения опций времени в выпадающем списке обновляется лишь однажды. Это значит, что после смены пользователя, срока и длительности задачи, в списке остаются неверные значения опций. Если кто-то знает как побороть этот баг, пожалуйста отпишитесь в каментах. Так же, после выбора даты в списке времени остается значение "0:00" (даже если такой опции теперь нет). Последнюю проблему относительно просто решить, но не хочется тратить на нее время, пока есть более сложная задача.
Буду признателен за помощь!
Размещено в CRM
Просмотров 18599 Комментарии 0
Всего комментариев 0

Комментарии

 


Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 03:44.