понедельник, 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 примочек, возможных, только в коробочной версии.

9 комментариев:

Анонимный комментирует...

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

Анонимный комментирует...

Дня каждого вида документа нужна своя форма pdf. Поэтому не совсем понятно, как сделать общее действие. А вот для вашего случая на Б24 можно воспользоваться rest-действиями и реализовать задуманное.

Юлия комментирует...

Нужна именно ссылка на страницу конкретного таска. Она формируется с использованием ID таска.

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

Анонимный комментирует...

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

Юлия комментирует...

Я вас поняла. Это не эквивалентно. Проверьте свой вариант, когда аппрувящих несколько. Один - проаппрувил, а другой уже потом - когда таск не актуален - заходит по той же ссылке из письма.

Rushan комментирует...

На заметку по документации напишу что CSalePdf основан на библиотеке tfpdf, которая является модификацией библиотеки fpdf с поддержкой utf-8.
Описание методов можете найти в блоге http://www.uamedwed.com/blog/web/fpdf-biblioteka-dlja-sozdanija-pdf-fajlov-na-php.html или в официальном мануале http://www.fpdf.org/en/doc/index.php

Unknown комментирует...

Юля, насколько вы оцениваете облачный битрикс24 для решения задач клиентов?

CHAIN комментирует...

Юля, спасибо за код для толчка по заданию бизнес процесса.
У меня вопрос, поскольку я ноль в PHP, то суть процесса по коду я уловить могу, но изменить что-то вряд ли. Мне нужна не только ссылка на задание по бизнес процессу. Мне нужен список пользователей, которые никаких действий не предприняли, чтобы автоматизировать рассылку только по ним.
Из описания метода (https://dev.1c-bitrix.ru/rest_help/bizproc/bizproc_task/tasklist.php) я понимаю, что информация хранится в USER_ID и USER_STATUS (мне нужен 0 соответственно).
Что добавить в код чтобы вытащить в переменную список пользователей со статусом 0?

Сравнение массивов по списку согласующих и списку тех, кто не действовал, я с грехом пополам сделал.
$root = $this->GetRootActivity();

$users = $root->GetVariable('soglas');
$myusers = $root->GetVariable('my_soglas');

$result = array_diff_assoc($users, $myusers);
$root->SetVariable("sogl_diff", $result);

Спасибо.

CHAIN комментирует...

Решил вопрос самостоятельно.
Перебор по списку голосующих, который начинается с паузы на заданное время.
Передаем ID пользователя в код и потом получаем его статус. 0 - значит не приступал к согласованию.

CModule::IncludeModule("bizproc");
$root = $this->GetRootActivity();
$users = $root->GetVariable('next_user');
$arSelectFields = array("ID", "WORKFLOW_ID", "ACTIVITY", "ACTIVITY_NAME", "MODIFIED", "OVERDUE_DATE", "NAME", "DESCRIPTION", "PARAMETERS", "STATUS","USER_ID","USER_STATUS");
$dbRecordsList = CBPTaskService::GetList(
array("ID" => "DESC"),

array('USER_ID'=>$users),
false,
false,
$arSelectFields
);
$arRecord = $dbRecordsList->getNext();
$rootActivity = $this->GetRootActivity();
$rootActivity->SetVariable("next_user",$arRecord['USER_STATUS']);

Если надо постоянно напоминать то зацикливаем, выход из цикла по изменению статуса документа.