Service Desk

Данный Workflow используется в BiTel для обработки запросов пользователей на изменение ПО, конфигурацию, исправление ошибок.

Для обработки данных из различных источников: BGBilling HelpDesk, EMail, Форум Бител, внутренние заметки используются сообщения. Сообщения типа EMail первоначально вручную разносятся по процессам, дальнейшие ответы на письма из процесса сопоставляются процессу автоматически по коду в теме. По HelpDesk темам автоматически создаются процессы с привязанным договором и контрагентом договора.

Редактор типа процесса. Существуют несколько дочерних типов, наследующих свойства родителького. Разбиение по продуктам и услугам.

service desk process type

В системе достроена дополнительная роль: Наблюдение. Основная причина - с ролью Выполнение может быть только один исполнитель, синхронизирующийся с BGBilling HelpDesk.

Вкладка с группами в редакторе типа.

service desk process type groups

Конфигурация типа.

create.in.objectTypes=customer;contract:bitel

processShowMessages=1
processShowLinks=2
processShowProcessLinks=1
processCreateLinkModeSelect=1

# отображение заметок привязанного к процессу договора
bgbilling:processShowLinkedContractsInfo=memo

# правила по распределению процессов определённых типов по исполнителям сразу после создания
onProcessEvent.2.events=created
onProcessEvent.2.ifExpression=process.getTypeId() =~ [6,7]
onProcessEvent.2.commands=addExecutors(2)
#
# если нет исполнителей и не HD - добавление создавшего
onProcessEvent.3.events=created
onProcessEvent.3.ifExpression=process.getTypeId() != 8 && process.getProcessExecutors().size() == 0
onProcessEvent.3.doExpression= addExecutors({user.getId()})
#
# при получении входящего сообщения извне - перевод процесса в статус "открыт"
onProcessEvent.4.events=messageAdded
onProcessEvent.4.ifExpression=process.getStatusId() != 1 && !event.getMessage().getClass().getSimpleName().equals("ProcessMessageAddedEvent")
onProcessEvent.4.commands=setStatus:1
#
# уведомление исполнителей о новом сообщении в процессе
onProcessEvent.5.events=messageAdded
onProcessEvent.5.doExpression=<<END
   text = "Новое сообщение в процессе, в котором вы числитесь исполнителем." +
       NEW_LINE2 + "Сообщение:" + NEW_LINE +
       event.getMessage().getText() +
       NEW_LINE2 + "Открыть процесс: http://crm.bitel.ru/user/process#" + process.getId() +
       NEW_LINE2 + "Для быстрого ответа на сообщение ответьте на это письмо сохранив идентификатор в теме.";

    subject = "#" + process.getId() +
      " [" + (process.getDescription().length() < 30 ? process.getDescription() : process.getDescription().substring(0, 30) + "..") + "] " +
      " QA:" + event.getMessage().getId();

   emailNotifyExecutors(21, subject, text);
END
#
onProcessEvent.6.events=executorsChanged
onProcessEvent.6.doExpression=<<END
      text = "Изменён состав исполнителей процесса." + NEW_LINE2;

      searchResult = new("ru.bgcrm.model.SearchResult");
      searchResult.getPage().setPageIndex(-1);
      dao = new("ru.bgcrm.dao.message.MessageDAO", conSet.getConnection());
      dao.searchMessageList(searchResult, process.getId(), null, 1, null, null, null, null, null, true);

      lastMsg = null;

      for (m : searchResult.getList()) {
         if (lastMsg == null) lastMsg = m;

         text += "Входящее сообщение #" + m.getId() + NEW_LINE;
         text +=  "##########################" + NEW_LINE;
         text +=  m.getText() +  NEW_LINE2;
       }

      subject = "#" + process.getId() +
       " [" + (process.getDescription().length() < 30 ? process.getDescription() : process.getDescription().substring(0, 30) + "..") + "] ";
      if (lastMsg != null) subject += " QA:" + lastMsg.getId();

      emailNotifyExecutors(21, subject, text);
END
#
# уведомление исполнителей об изменении статуса процесса
onProcessEvent.7.events=statusChanged
onProcessEvent.7.doExpression=<<END
  text = process.getDescription();
  mobile.sendMessageToExecutors("Изменился процесс, в котором вы исполнитель", text);
END
#
# отображение в заголовке процесс кода и описания
processReference.1.objectTypes=processCard
processReference.1.stringExpression="#" + process.getId() + " " + u.escapeXml(process.getDescription())
#
# в отображаемых процессах в договорах и контрагентах отображение кода процесса и исполнителя
processReference.2.objectTypes=customer,contract:bitel
processReference.2.stringExpression=<<END
   result = process.getDescription();
   linkDao = new("ru.bgcrm.dao.process.ProcessLinkDAO", conSet.getConnection());
   hdLink = u.getFirst(linkDao.getObjectLinksWithType(process.getId(),"%helpdesk%"));
   if (hdLink != null )
       result += " HD: <b>" + hdLink.getLinkedObjectId() + "</b>";
   result += "<br><b>" + u.getObjectTitles(u.getObjectList(ctxUserList, process.getExecutorIds())) + "</b>";
   return result;
END
#
#
style.processCardLeftBlock=min-width: 500px;
style.processCardRightBlock=width: 100%

Внешний вид очереди процессов.

service desk queue

Процессы по-умолчанию сортируются в обратном порядке по приоритетам, далее по времени последнего сообщения. Наиболее нужные фильтры сохранены на отдельные кнопки. Количество процессов без исполнителей выводится в заголовке программы рядом с именем пользователя.

Конфигурая очереди процессов.

filter.1.type=openClose
filter.1.defaultValue=open
filter.2.type=status
#filter.2.defaultValues=1,6,7,8
filter.2.availableValues=1,6,7,8,4,5
filter.3.type=groups
filter.3.defaultValues=1
filter.3.show=0
filter.4.title=Исполн. / Набл.
filter.4.type=executors

filter.14.type=grex
filter.14.roleId=0
filter.14.groups.show=0
filter.14.groups.defaultValues=1
filter.14.executors.title=Исполнитель

filter.17.type=grex
filter.17.roleId=1
filter.17.groups.show=0
filter.17.groups.defaultValues=1
filter.17.executors.title=Наблюдение

filter.24.type=code
filter.25.type=create_date
filter.26.type=close_date
filter.27.type=type
filter.27.width=200
filter.27.availableValues=1,2,3,5,6,7,8,10
filter.28.type=linkedCustomer:title
filter.29.type=description

filter.30.type=message:systemId
filter.30.title=Тема форума

filter.35.type=linkObject
filter.35.objectType=bgbilling-helpdesk:bitel
filter.35.whatFilter=id
filter.35.title=Тема HelpDesk

#
sort.combo.count=2
sort.combo.1.default=3
sort.combo.2.default=2
sort.mode.1.columnId=12
sort.mode.1.title=Непр. сообщ обр.
sort.mode.1.desc=true
sort.mode.2.columnId=1
sort.mode.2.title=Создан об.
sort.mode.2.desc=true
sort.mode.3.columnId=16
sort.mode.3.title=Посл.сообщ. обр
sort.mode.3.desc=true
sort.mode.4.columnId=2
sort.mode.4.title=Приоритет обр.
sort.mode.4.desc=true
#
column.1.title=ID
column.1.value=id
column.2.title=Приор.
column.2.value=priority
column.3.title=Контрагент
column.3.value=linkedCustomer:title
#ссылки слишком распирают
#column.3.value=linkedCustomerLink
column.3.titleIfMore=100
column.4.title=Описание
column.4.value=descriptionLink
#column.4.formatToHtml=1
column.4.titleIfMore=100
column.5.title=Статус
column.5.value=status_title
column.5.nowrap=1
column.6.title=Исполнители
column.6.value=executors
column.6.titleIfMore=15
column.7.title=Тип
column.7.value=type_title
column.7.nowrap=1
column.8.title=Статус HD
column.8.value=param:22
column.9.title=HD ID
column.9.value=linkedObject:bgbilling-helpdesk:id
column.10.title=C
column.10.value=messageInCount
column.12.title=Н
column.12.value=messageInUnreadCount
column.14.title=Посл.
column.14.value=messageInLastDt
column.14.nowrap=1
column.16.title=NONE
column.16.value=messageInLastDt:nf
column.18.title=Перейти
column.18.value=param:20
column.18.style=text-align: center;
column.18.showAsLink=link
column.20.title=Создан
column.20.value=create_dt
column.20.nowrap=1
column.20.style=padding: 2px;
#column.22.title=Группы
#column.22.value=groups:1,2

#media.html.columns=1,2,3,4,5,6,7,8,9,10,12,14,18,20

configHead=<<END
 return
       "<td>ID</td>"
    + "<td>Описание</td>"
    + "<td>Свойства</td>"
    + "<td>Статус</td>"
    + "<td>Исполнители</td>"
    + "<td>Создан</td>";
END

configRow=<<END
   result = "<tr bgcolor='" + urgColor + "' processId='" + process.getId() + "'>";
   result += "<td>" + col1 + "</td>";
   result += "<td><b>" + col3 + "</b><br/>" + col4 + "</td>";
   result += "<td><b>" + col7 + "</b><br/>";

   // при пустом значении что-то там выходит длиной 90 символов
   if (col18.length() > 90)
    result += "<b style='color:red;'>ФОРУМ ССЫЛКА СТАРАЯ</b>";

   // HelpDesk
   if (process.getTypeId() == 8)
	result += "HD: " + col9 + " Стат.: " + col8 + " Сообщ./Нов.:" + col10 + " / <b>" + col12 + "</b> Посл.:" + col14 + "<br/>";
   else
	result += " Сообщ.:" + col10 + " Посл.:" + col14 + "<br/>";
   result += "</td>";

   result += "<td>" + col5 + "</td>";
   result += "<td>" + col6 + "</td>";
   result += "<td>" + col20 + "</td>";

   result += "</tr>";

   return result;
END

rowConfig.1.media=html
rowConfig.1.stringExpressionHead={@configHead}
rowConfig.1.stringExpressionRow={@configRow}