понедельник, 29 августа 2016 г.

Обработка уведомлений с подтверждениями в Битрикс



Всем привет!
В последнее время приходится программировать под Битрикс “Корпоративный портал”. Впечатления весьма неоднозначные. Нет в этой системе той стройности и упорядоченности, как например  в фреймворке Laravel,  в коде компонентов редко используется ООП,  часто шаблоны компонентов пестрят жуткой смесью из PHP, JavaScript и html–кода.  Как сказано в одной замечательной критичной к Битрикс статье, "документация по Битрикс отстает от кода системы на 1 – 1.5 года”. Поэтому приходится залезать в дебри системных классов Битрикс и изучать все самостоятельно.
Недавно мне поступило задание реализовать следующий алгоритм на портале:


  1.  Постановщик создает задачу для ответственного.
  2. Ответственный должен открыть карточку задачи  и отредактировать крайний срок для исполнения данной задачи
  3. Постановщику должно придти сообщение с указанием данного крайнего срока и кнопками “Принять” и “Отменить”
  4. Если постановщик принимает крайний срок по задаче, то данное поле блокируется для редактирования и ответственный начинает решать задачу, иначе  - ответственному приходит сообщение о том, чтобы он предложил другой крайний срок





Алгоритм достаточно простой и понятный. Отсылка сообщений должна происходить через веб-мессенджер портала.   Основная сложность здесь – это отсылка постановщику сообщения для подтверждения или отмены и соответственно обработчик нажатия кнопок “Принять” или “Отменить”. Погуглив, я понял, что не все так просто. Вот эта статья от разработчика Битрикс https://dev.1c-bitrix.ru/community/blogs/hazz/im-post-one.php объясняет, как отсылать такое сообщение, требующее подтверждения. Однако в качестве обработчика нажатия кнопок предлагается использовать код своего самописного модуля. Модуль должен быть зарегистрирован следующей функцией.
 
RegisterModuleDependences("im", "OnBeforeConfirmNotify", 
"yourmodule","CYourModuleEvents", "CYourModuleEventsIMCallback");




Как? Неужели для этого я должен писать свой собственный модуль?
Однако выяснилось, что есть альтернатива – функция AddEventHandler, которая регистрирует обработчик события. Отличие данного регистратора события от RegisterModuleDependences в том, что последний сохраняется в базе данных и работает с событиями в модулях.
Итак, вызов функции для отправки сообщения с подтверждением в коде компонента bitrix:task.task.edit :



$mdeadline=ConvertDateTime($arFields["DEADLINE"], "DD.MM.YYYY", "ru"); 
$notify="Задача № {$arParams['TASK_ID']} будет выполнена до {$mdeadline} включительно. Прошу нажать  'Принять', если согласны, и мы приступим к решению данной задачи";
$buttons=Array(
// 1. название кнопки, 2. значение, 3. шаблон кнопки, 4. переход по адресу после нажатия (не обязательный параметр)
Array('TITLE' => 'Принять', 'VALUE' => 'Y', 'TYPE' => 'accept' /*, 'URL' => 'http://test.ru/?confirm=Y' */),
Array('TITLE' => 'Отказаться', 'VALUE' => 'N', 'TYPE' => 'cancel' /*, 'URL' => 'http://test.ru/?confirm=N' */),
);
CMessagesHelper::SendConfirmNotify($arTask['RESPONSIBLE_ID'],$arTask['CREATED_BY'],
'tasks',"tasks|CONFIRM_DEADLINE|{$arParams['TASK_ID']}|{$arTask['RESPONSIBLE_ID']}|{$arTask['CREATED_BY']}|{$task_path}",
$notify,'',$buttons,'');

Функция класса-хелпера для отсылки сообщения с подтверждением:


public static function SendConfirmNotify($from,$to,$module,$tag,$message,$message_email,$buttons,$email_template)
{
if (IsModuleInstalled("im") && CModule::IncludeModule("im"))
{
 $arMessageFields = array(
     // получатель
     "TO_USER_ID" => $to,
     // отправитель
     "FROM_USER_ID" => $from, 
     // тип уведомления
     "NOTIFY_TYPE" => 1,
     // модуль запросивший отправку уведомления
     "NOTIFY_MODULE" => $module,
     // символьный тэг для группировки (будет выведено только одно сообщение), если это не требуется - не задаем параметр
     "NOTIFY_TAG" => $tag,
     // текст уведомления на сайте (доступен html и бб-коды)
     "NOTIFY_MESSAGE" => $message,
     // текст уведомления для отправки на почту (или XMPP), если различий нет - не задаем параметр
     "NOTIFY_MESSAGE_OUT" => $message_email,
     // массив описывающий кнопки уведомления
     "NOTIFY_BUTTONS" => $buttons,
     // символьный код шаблона отправки письма, если не задавать отправляется шаблоном уведомлений
     "NOTIFY_EMAIL_TEMPLATE" => 
     $email_template,
 );
 return CIMNotify::Add($arMessageFields);
}
}

А вот необходимый обработчик нажатия кнопок в уведомлении, который располагается в файле bitrix/php_interface/init.php:

AddEventHandler("im", "OnAfterConfirmNotify", "OnAnswerNotifyHandler");

function OnAnswerNotifyHandler($module,$tags,$value,$arRes,$resultMessages)
{

if (IsModuleInstalled("im") && CModule::IncludeModule("im") && CModule::IncludeModule("tasks"))
{ 
if($module=='tasks' && !empty($tags))
{
 $tag_ar=explode ('|',$tags);

 if($tag_ar && $tag_ar[1]=='CONFIRM_DEADLINE')
 {
 $task_id=$tag_ar[2];
 if(!$task_id) return;
 
 $responsible_id=$tag_ar[3];
 if(!$responsible_id) return;
 
 $author_id=$tag_ar[4];
 if(!$author_id) return;
 
    $task_path="/company/personal/user/{$responsible_id}/tasks/task/view/{$task_id}/";
    
    function ChangeDeadLineCounter($val,$task_id)
    {
        global $USER;
        $loggedInUserId = (int) $USER->getId();
            //отмечаем, что автор ответил на запрос
        $oTask = CTaskItem::getInstanceFromPool($task_id, $loggedInUserId);
        if(!$oTask) 
            return false;
        $arTask = $oTask->getData();


        if(!$arTask) 
            return false;

        //уменьшаем счетчик, чтобы можно было еще раз поменять крайний срок
        if(!empty($arTask['UF_DEADLINE_COUNTER']))
            $arTask['UF_DEADLINE_COUNTER']+=$val;

        if($arTask['UF_DEADLINE_COUNTER']<0 artask="" otask-="">update(array('UF_DEADLINE_COUNTER'=>$arTask['UF_DEADLINE_COUNTER']));
        return true;
    }
         
 if($value=='Y')
 {
 //инициатор подтвердил крайний срок
   $arMessageFields = array(
            "TO_USER_ID" => $responsible_id,
            "FROM_USER_ID" => $author_id,
            "NOTIFY_TYPE" => IM_NOTIFY_SYSTEM,
            "NOTIFY_MODULE" => "tasks",
            "NOTIFY_TAG" => "",
            "NOTIFY_MESSAGE" => "Уважаемый сотрудник! Инициатор задачи № {$task_id} подтвердил установленный Вами крайний срок. Можете приступать к исполнению.",
            "MESSAGE_TYPE" => "S"
        );

    ChangeDeadLineCounter(1,$task_id);
            //echo mydump($arMessageFields);
 
 }
 else
 {

 //инициатор не подтвердил крайний срок - высылаем уведомление ответственному
      $arMessageFields = array(
            "TO_USER_ID" => $responsible_id,
            "FROM_USER_ID" => $author_id,
            "NOTIFY_TYPE" => IM_NOTIFY_SYSTEM,
            "NOTIFY_MODULE" => "tasks",
            "NOTIFY_TAG" => "",
            "NOTIFY_MESSAGE" => "Уважаемый сотрудник! Инициатор задачи № {$task_id} не подтверждает Ваш крайний срок по данной задаче. 
            Редактирование крайнего срока вновь доступно в карточке задачи. Скорректируйте крайний срок еще раз.",
            "MESSAGE_TYPE" => "S"
        );
 
    ChangeDeadLineCounter(-1,$task_id);
 }
    $notifyID = CIMNotify::Add($arMessageFields);

 }
}
}

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