TimelineJS
В CRM имеется календарик… Довольно скучный 🙂 и не менявшийся уже лет эдак 100 🙂
Поэтому заменим его на более интересную версию – TimelineJS. TimelineJS это готовый визуальный фреймворк, состоящий из временной шкалы, на которую выносятся события, и слайдера, на котором выводится подробная информация о событии.
Приступим:
- Импортируйте в CRM следующие Веб-ресурсы:
- timeline.png
- timeline@2x.png
- timeline.min.js
- jquery.min.js
- storyjs-embed.js
- timeline.css (важно: в данном файле содержатся ссылки на файлы .png в CRM, поэтому если выкладываете файлы по пути отличного от указанного здесь – не забудьте их поменять)
- Создайте HTML Веб-ресурс timeline.html с таким кодом:
<!DOCTYPE html <html lang="en" xmlns="http://www.w3.org/1999/xhtml"> <head> <title>timeline</title> <link rel="stylesheet" type="text/css" href="/WebResources/new_timeline.css"> <script type="text/javascript" src="/WebResources/new_jquery.min.js"></script> <script type="text/javascript" src="/WebResources/new_timeline.min.js"></script> <script type="text/javascript" src="/WebResources/new_storyjs.embed.js"></script> <script type="text/javascript" src="/WebResources/ClientGlobalContext.js.aspx"></script> <script> $(function () { activitys = []; var today = new Date(); var datetime = "datetime'" + today.getFullYear() + "-" + (today.getMonth() + 1) + "-" + today.getDate() + "T00:00:00.000Z'"; var currentUserId = Xrm.Page.context.getUserId().replace("{", "").replace("}", "").toLowerCase(); // Запрашиваем Действия CRM $.ajax({ type: "GET", contentType: "application/json; charset=utf-8", datatype: "json", url: Xrm.Page.context.getClientUrl() + "/XRMServices/2011/OrganizationData.svc/ActivityPointerSet?$select=ActivityId,ActivityTypeCode,Description,RegardingObjectId,ScheduledEnd,ScheduledStart,StatusCode,Subject,activitypointer_activity_parties/PartyId&$expand=activitypointer_activity_parties&$filter=(StateCode/Value eq 3 or StateCode/Value eq 0) and (ScheduledStart ge " + datetime + " or ScheduledEnd ge " + datetime + ") and OwnerId/Id eq guid'" + currentUserId + "'&$top=100", beforeSend: function (XMLHttpRequest) { XMLHttpRequest.setRequestHeader("Accept", "application/json"); }, async: false, success: function (data, textStatus, xhr) { var results = data.d.results; for (var i = 0; i < results.length; i++) { var ActivityTypeCode = results[i].ActivityTypeCode; // Отбираем только Звонки, Встречи и Задачи if (!(ActivityTypeCode == "appointment" || ActivityTypeCode == "phonecall" || ActivityTypeCode == "task")) return; // Формируем описание if (!results[i].Description || results[i].Description == "") { var Description = "_"; } else { var Description = results[i].Description; } // Формируем поле В отношении if (results[i].RegardingObjectId.Name && results[i].RegardingObjectId.Name != null) { var RegardingObject = "<a target=\"_blank\" href=\"http://crm2015/superfirma/main.aspx?etn=" + results[i].RegardingObjectId.LogicalName + "&pagetype=entityrecord&id=%7B" + results[i].RegardingObjectId.Id + "%7D\">" + results[i].RegardingObjectId.Name + "</a>"; } else { var RegardingObject = "_"; } // Формируем дату Действия if (results[i].ActivityTypeCode == "appointment") { // Для Встреч заполняем интервал var ScheduledStart = parseDate(results[i].ScheduledStart); var ScheduledEnd = parseDate(results[i].ScheduledEnd); } else { // Для Звонков и Задач только срок var ScheduledStart = parseDate(results[i].ScheduledEnd); var ScheduledEnd = ""; } // Формируем участников действия var partiesList = ""; var partiesArr = results[i].activitypointer_activity_parties.results; for (var z = 0; z < partiesArr.length; z++) { // Исключяем текущего Ответственного и значение из поля В отношении if (partiesArr[z].PartyId.Id != currentUserId && partiesArr[z].PartyId.Id != results[i].RegardingObjectId.Id) { partiesList += "<a target=\"_blank\" href=\"http://crm2015/superfirma/main.aspx?etn=" + partiesArr[z].PartyId.LogicalName + "&pagetype=entityrecord&id=%7B" + partiesArr[z].PartyId.Id + "%7D\">" + partiesArr[z].PartyId.Name + "</a>, "; } } if (partiesList != "") { var partiesList = partiesList.slice(0, -2); } else { var partiesList = "_"; } // Помещаем найденные Действия в массив var obj = { startDate: ScheduledStart, endDate: ScheduledEnd, headline: results[i].Subject, text: "<a target=\"_blank\" href=\"http://crm2015/superfirma/main.aspx?etn=" + ActivityTypeCode + "&pagetype=entityrecord&id=%7B" + results[i].ActivityId + "%7D\">Открыть</a>", tag: getActivityType(ActivityTypeCode), asset: { media: Description, thumbnail: getActivityIcon(ActivityTypeCode), credit: "В отношении: " + RegardingObject, caption: "Усчастники: " + partiesList } }; activitys.push(obj); } }, error: function (xhr, textStatus, errorThrown) { alert(textStatus + " " + errorThrown); } }); var tomorrow = new Date(); tomorrow.setDate(tomorrow.getDate() + 1); // Формируем заголовок Timeline и помещяем в него данные storyjs_json_data = { "timeline": { "headline": "Календарь CRM", "type": "default", "text": "<p>Выберите Действие, которое хотите просмотреть...</p>", "date": activitys, "era": [ // Подсвечиваем текущую дату { "startDate": today.getFullYear() + "," + (today.getMonth() + 1) + "," + today.getDate(), "endDate": tomorrow.getFullYear() + "," + (tomorrow.getMonth() + 1) + "," + tomorrow.getDate() } ] } } // Инициируем создание Timeline createStoryJS({ type: 'timeline', width: '100%', height: '500', source: storyjs_json_data, embed_id: 'my-timeline', lang: 'ru' }); }); // Парсим дату возвращаемую CRM function parseDate(dt) { var formatDate = ""; if (dt) { dt = dt.replace("/Date(", ""); dt = dt.replace(")/", ""); var date = new Date(parseInt(dt, 10)); formatDate = date.getFullYear() + "," + (date.getMonth() + 1) + "," + date.getDate() + "," + date.getHours() + "," + date.getMinutes() + "," + date.getSeconds(); } return formatDate; }; // Переводим название типа Действия function getActivityType(at) { var type = ""; switch (at) { case "appointment": type = "Встречи" break case "phonecall": type = "Звонки" break case "task": type = "Задачи" break } return type; } // Возвращаем иконку соответствующую типу Действия function getActivityIcon(at) { var iconURL = ""; switch (at) { case "appointment": iconURL = "http://crm2015/_imgs/ico_16_4201.gif" break case "phonecall": iconURL = "http://crm2015/_imgs/ico_16_4210.gif" break case "task": iconURL = "http://crm2015/_imgs/ico_16_4212.gif" break } return iconURL; } </script> </head> <body> <div id="my-timeline"></div> </body> </html>
Что тут происходит:
- Подключаем необходимые ресурсы;
- С помощью REST-запроса отбираем будущие активные Действия текущего пользователя;
- По очереди обрабатываем возвращённые записи и форматируем их данные до необходимого состояния;
- Отформатированные данные помещаем в специальный массив и объект, после чего инициируем создание TimelineJS.
- Вынести HTML Веб-ресурс timeline.html куда-нибудь в CRM: на панель навигации или на Панель мониторинга…
Смотрим…
З.Ы.
- Для данного примера я отбирал данные из объекта ActivityPointer (который является родительским для всех Действий). Если же расширить пример и отбирать данные из конкретных объектов (task или phonecall), то можно построить более индивидуальный календарь для каждого из типов Действий;
- Важным отличием TimelineJS от стандартного календаря является то, что Вы можете изменять его в определенных пределах, редактируя исходные файлы фреймворка.
Ошибка и не одна но это все не критично, а вот
https://////TEST/%7B635833765250000132%7D/WebResources/js/locale/ru.js 404 (Not Found) — это реально проблема
А подробнее можно? Что это?