понедельник, 12 октября 2015 г.

Бизнес-процессы в Битрикс 24: сложности внедрения

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

Однако же потребности реальных пользователей, их ожидания, порой серьезно расходятся с реализованными в Битрикс 24 возможностями, и процесс внедрения Битрикс 24 либо превращается, как в том анекдоте про экскурсовода - в обучение пользователей обходиться без того, что они считают естественным, либо растягивается на месяцы работы.

Возьмем, к примеру, банальный бизнес-процесс оформления больничного листа, который не раз автоматизировал, наверное, каждый внедренец Битрикс 24. И в каждой новой компании -  по-разному.

Дано: трехэтапное утверждение больничного листа:

Сначала Больничный лист утверждает начальник отдела  Business Manager, потом ответственный из отдела кадров HR, а уже потом ответственный за начисление заработной платы Payroll.

Какие сложности могут возникнуть при проектировании такого простого бизнес-процесса?

Сложность №1: тестирование в условиях когда пользователи уже активно используют портал. Понятное дело: разработку БП приходится вести в девелоперской копии, перелогиниваясь для теста под различными участниками процесса. Перелогиниваться бывает достаточно муторно, а кроме того, должны быть протестированы и email-уведомления, к-е до поры - до времени не должны приходить реальным пользователям.

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


Естественно, для этого переменная, хранящая пользователей-тестеров, должна быть добавлена во все необходимые активити.


Сложность №2: когда пользователь должен что-то проаппрувить, у него появляется соответсвующее задание в бизнес-процессах (не путать с задачами). Клиенты хотят, чтобы пользователь получал емейл в тот момент, когда от него ожидается одобрение, емейл, содержащий ссылку не на весь раздел с заданиями БП, а на конкретную страничку задания.

Для решения этой проблемы шаблон БП для входа в состояние "Approve from HR" из нашего примера превращается вот в такую громоздкую конструкцию (картинка кликабельна):


Прикол в том, что пока в активити аппрува не произошел этот самый аппрув, стандартными средствами БП мы не можем узнать ИД таска, поэтому его приходится доставать, используя php-код:

CModule::IncludeModule("bizproc");
$arSelectFields = array("ID", "WORKFLOW_ID", "ACTIVITY", "ACTIVITY_NAME", "MODIFIED", "OVERDUE_DATE", "NAME", "DESCRIPTION", "PARAMETERS", "STATUS","USER_STATUS");
$dbRecordsList = CBPTaskService::GetList(
array("ID" => "DESC"),
array('WORKFLOW_ID'=>'{=Workflow:ID}'),
false,
false,
$arSelectFields
);
$arRecord = $dbRecordsList->getNext();
$rootActivity = $this->GetRootActivity();
$rootActivity->SetVariable("TaskLink", 'http://адреспортала/company/personal/bizproc/'.$arRecord['ID'].'/');

Этот код выбирает все таски данного БП, в порядке от последнего, к первому, нам как раз нужен последний.

Сложность №3: электронного прогона по всем инстанциям недостаточно компании, где исторически использовался бумажный документооборот, и на этапе, когда больничный лист из примера попадает к Payroll  (к бухгалтеру), бухгалтер должен помимо всего прочего иметь возможность распечатать себе бумажку "для отчетности". Поэтому наш бизнес-процесс должен еще формировать PDF-файл и сохранять ее в соответствующем разделе библиотеки документов.

Сформировать PDF файл для данного примера можно следующей php-вставкой (спасибо, что в Битрикс 24 есть класс CSalePdf для работы с PDF):

CModule::IncludeModule("sale");
use Bitrix\Main\Type\DateTime;
$date = new DateTime('{=Document:DATE_CREATE}');
$date=$date->format("d-m-Y");
if (!CSalePdf::isPdfAvailable()) die();
$ID={=Document:ID};
$IBLOCK_ID={=Document:IBLOCK_ID};
$PROPS=array();
$db_props = CIBlockElement::GetProperty($IBLOCK_ID, $ID);
while($ar_props = $db_props->Fetch())
{
$PROPS[$ar_props['ID']]=$ar_props;
}
$pdf = new CSalePdf('P', 'pt', 'A4');
$pageWidth  = $pdf->GetPageWidth();
$pageHeight = $pdf->GetPageHeight();
$pdf->AddFont('Font', '', 'pt_sans-regular.ttf', true);
$pdf->AddFont('Font', 'B', 'pt_sans-bold.ttf', true);
$fontFamily = 'Font';
$fontSize   = 10.5;
$margin = array(
'top' => 15 * 72/25.4,
'right' => 15 * 72/25.4,
'bottom' => 15 * 72/25.4,
'left' => 15 * 72/25.4
);
$width = $pageWidth - $margin['left'] - $margin['right'];
$pdf->SetDisplayMode(100, 'continuous');
$pdf->SetMargins($margin['left'], $margin['top'], $margin['right']);
$pdf->SetAutoPageBreak(true, $margin['bottom']);
$pdf->AddPage();
$pdf->SetFont($fontFamily, 'B', $fontSize*2);
$pdf->Cell(0, 30, $pdf->prepareToPdf('{=Document:NAME}`s personal leave '.$date), 0, 0, 'C');
$pdf->Ln();
$pdf->Ln();
$pdf->Ln();
$pdf->Ln();
$pdf->SetFont($fontFamily, '', $fontSize);
$pdf->Cell(0, 15, $pdf->prepareToPdf('Request content:'), 0, 0, 'L');
$pdf->Ln();
$pdf->Ln();
$ROW1=150;
$Y=15;
$pdf->Cell($ROW1, $Y, $pdf->prepareToPdf('Employee Name:'), 0, 0, 'L');
$pdf->MultiCell(0, $Y, $pdf->prepareToPdf('{=Document:NAME}'), 0, 'L');
$pdf->Cell($ROW1, $Y, $pdf->prepareToPdf('First day of leave:'), 0, 0, 'L');
$pdf->MultiCell(0, $Y, $pdf->prepareToPdf('{=Document:PROPERTY_225}'), 0, 'L');
$pdf->Cell($ROW1, $Y, $pdf->prepareToPdf('Last day of leave:'), 0, 0, 'L');
$pdf->MultiCell(0, $Y, $pdf->prepareToPdf('{=Document:PROPERTY_226}'), 0, 'L');
$pdf->Cell($ROW1, $Y, $pdf->prepareToPdf('Type of leave:'), 0, 0, 'L');
$pdf->MultiCell(0, $Y, $pdf->prepareToPdf('{=Document:PROPERTY_227}'), 0, 'L');

$myfile='temp.pdf';
$pdf->Output($myfile, 'F');
if (!copy($myfile,$_SERVER['DOCUMENT_ROOT'].'/docs/appforms/forms/personal_leave_requests/request_'.$ID.'.pdf'))
{  }
else{  unlink($myfile);  }

Можно ли реализовать  бизнес-процесс обработки больничного листа в облачной версии Битрикс24? Можно, но ущербно - отказавшись от перечисленных в пунктах 2 и 3 примочек, возможных, только в коробочной версии.

Битрикс 24 - опыт расширения функционала модуля "Техподдержка"

В сентябре у нас было несколько интересных внедрений коробочной версии Битрикс24. Об одном из них расскажу в данном посте. Клиент хотел расширить функционал модуля технической поддержки.

В стандартном модуле Битрикс24 нормативное время ответа на обращение в техподдержку зависит только от уровня поддержки (SLA). Критичность обращения и категория обращения не влияют на формирование крайнего срока реакции на обращение.

Нужно было сделать, чтобы подсчет времени для ответа на обращение вычислялся исходя из:

уровня поддержки
критичности обращения
категории обращения

Кроме того, в стандартном функционале «1С-Битрикс24: Корпоративный портал» сущности «Обращения в техподдержку» и «Задачи» никак не связаны. Необходимо было в интерфейсе обработки обращения техподдержки (в административной части) добавить кнопку «Создать задачу», при нажатии на которую должна была автоматически создаваться задача на проведение работ на основании данного обращения с возможностью включения данной задачи в отчеты и возможностью учета времени по данной задаче. 

Так как весь оставшийся функционал модуля технической поддержки должен был работать так же, как и раньше, а так же должна была оставаться возможность обновлять модуль и получать с обновлениями новый функционал модуля, кастомизировать поведение модуля было решено через перехват событий:

OnAfterTicketAdd - это событие возникает при добавлении нового обращения в ТП;
OnAfterTicketUpdate - это событие возникает при изменении обращения (в том числе при добавлении сообщения в обращение);


Апи модуля технической поддержки Битрикс 24 не отличается подробной документацией, поэтому исследовать то, как работает стандартный модуль ТП, вычисляя дедлайн для ответа на обращение было достаточно сложно. Анализируя то, как хранится обращение в БД модуля, я заметила, что дедлайн по обращению, к счастью, нигде не вычисляется динамически, а хранится в одной таблице с информацией о самом обращении и пересчитывается при апдейте обращения. Именно этот факт и позволили использовать обработчики событий OnAfterTicketAdd  и OnAfterTicketUpdate  для решения первой части задачи.

Для хранения расширенной таблицы настроек модуля технической поддержки мы завели в Битрикс 24 отдельный хайлоадблок, хранящий соответствия сочетания уровня/критичности/категории и времени реакции на обращение.


Благодаря тому, что в админке Битрикс24 реализован механизм фильтрации хайлоадблока по значениям полей, пользоваться данной настроечной таблицей - достаточно удобно.

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

Соответсвенно, задача свелась к тому, чтобы в вышеуказанных обработчиках получать из хайлоадблока нормативное время на основании данных о критичности/уровне/категории по данным обращения, затем вычислять новый дедлайн ответа на обращение с учетом расписания работы технической поддержки, а затем записывать найденный дедлайн в обращение.

Не обошлось без небольших курьезов: оказалось, что в обработчик события OnAfterTicketAdd не передаются данные SLA_ID для обращения, при том, что на самом деле в этот момент обращение уже записано и SLA_ID - известен - пришлось доставать его дополнительно.

Со второй частью задачи не возникло особых проблем - мы кастомизировали в папке local админские страницы редактирования обращения в техподдержку и списка обращений в техподдержку и добавили функционал создания задачи на основании обращения:

Можно создать задачи сразу для нескольких обращений, а можно только для одного.

Задачи содержат ссылку на обращение непосредственно в своем описании:

Реализация данного кейса заняла около 20 человеко-часов, и это было бы невозможно сделать с такими же трудозатратами в облачной версии Битрикс 24. Покупая коробочную версию Битрикс 24 - клиенты могут серьезно сэкономить на дальнейшем расширении функционала.