КП: Универсальная форма подачи заявки

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

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

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

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

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

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

Если заявка приемлема и для заявки не требуется согласование (также устанавливается в настройках услуг) - выполняет заявку и завершает ее нажатием на соответствующую кнопку.

Если заявка не приемлема - отклоняет заявку

Если заявка требует согласования, то вместо кнопки "Закрыть заявку" (которая положительно завершает заявку) ответственному доступна кнопка "Отправить на согласование", после чего запускается простой процесс согласования всеми, указанными для услуги лицами.

Вот такой вот вкратце алгоритм. Он, естественно, может быть на много шире с более тонким ветвлением условий (по данной задаче он и был немного шире, но тут опишу суть, может кому пригодится ...).

Реализация

Для реализации создаем 2 инфоблока: "Заявки пользователей" - для хранения всех заявок от пользователей и "Услуги" - для описания всех услуг.

Инфоблок с услугами имеет свойства:

  • "Ответственный"  (тип "Привязка к сотруднику") - для установки ответственного лица для заявки;
  • Множественное свойство "Список согласующих" (тип "Привязка к сотруднику") - для установки в случае необходимости листа согласования заявки;
  • Множественное свойство "Дополнительные txt-поля" (тип - текст). Данное свойство будем использовать для вывода дополнительных полей для каждой из услуг. В настройках свойства включаем галочку о выводе описания для значений. В описании будем отмечать, является ли данное поле обязательным (Y-поле обязательно для заполнения)

Анонсовое значение инфоблока с заявками будем использовать для записи описательного сопроводительного текста к заявке, а детальное описание - для комментариев по заявке (Если комментарии нужны древовидные с возможностью ведения комментариев к комментариям - придется создавать еще один инфоблок. В моем случае было достаточно и одного доступного поля). Для данного инфоблока создаем набор свойств:

  • "Услуга" (типа "Привязка к элементу") - для связки услуг и заявок;
  • "Статус" (типа "Список") с предопределенным списком доступных для заявок статусов;
  • "Приоритет" (типа "Список") с предопределенным списком вариаций приоритета;
  • "Инициатор" (типа "Привязка к пользователю") - для указания автора заявки (специально не привязывался к автору элемента инфоблока, потому как а вдруг придется автора сменить...);
  • "Ответственный" (тип - "Привязка к сотруднику") - для привязки к заявке ответственного лица;
  • Множественное свойство "Файлы" (типа "Файл") - для прикрепления к заявке файлов;
  • "Дата завершения" (типа "Дата/время") - для указания даты, когда заявка была закрыта;
  • Множественное свойство типа "Список согласующих" (тип - "Привязка к сотруднику") - для указания списка согласующих заявку лиц

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

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

Для выбора списка услуг взял компонент news.list и просто допилил его шаблон. При выборе нужной услуги с помощью JS формируется набор полей, которые должны быть дополнительно заполнены для выбранной услуги. Для поля "Файлы" использовал типовой компонент "main.file.input" с предустановленным шаблоном "drag_n_drop". Суть данного компонента в том, что при его использовании файлы автоматически отправляются на сервер и в форму помещаются ID уже сохраненных файлов. Т.е. нет необходимости в обработке на стороне сервера еще и файлов.

Итак, пользователь выбрал услугу, ввел все необходимые данные, загрузил все файлы и нажал на кнопку "Подать заявку". Отправка реализована средствами JS: скрипт проверяет, заполнены ли все обязательные для заполнения поля и, если да, - отправляет пост-запросом по аяксу данные в специальный скрипт.

У данного скрипта на сервере есть 3 основные функции:

  • NotifyUser - для отправки уведомления пользователю
  • addCommentToRequest - для добавления комментария к заявке
  • SaveRequest - для добавления новой заявки и сохранения всех изменений

Остановлюсь подробнее на каждой из них. Начну с самой простой:

     function NotifyUser($UID,$text,$EID){
         if (IsModuleInstalled("im") && CModule::IncludeModule("im"))
         {
             $arMessageFields = array(
                 "TO_USER_ID" => $UID,
                 "FROM_USER_ID" => 1,
                 "NOTIFY_TYPE" => IM_NOTIFY_SYSTEM,
                 "NOTIFY_MODULE" => "iblock",
                 "NOTIFY_TAG" => "BLOG|POST|".$EID,
                 "NOTIFY_MESSAGE" => $text,
                 "NOTIFY_MESSAGE_OUT" => $text
             );
             CIMNotify::Add($arMessageFields);
         }
     } 

Данная функция просто отправляет текст $text пользователю с ID $UID.

Далее - функция добавления комментария.

     function addCommentToRequest($item_id, $comment, $UID){
         $arItem = CIBlockElement::GetByID($item_id)->Fetch();
     
         $presentComment = $arItem['DETAIL_TEXT'];
         $commenterName = CUser::GetFullName($UID);
         global $DB;
         $addComment = '<p class="commenter"><b>'.$commenterName.'</b> '.date($DB->DateFormatToPHP(CLang::GetDateFormat("FULL"))).'</p>';
         $addComment .= '<div>'.$comment.'</div>';
     
         $el = new CIBlockElement;
         $arLoadProductArray = Array(
             "MODIFIED_BY"    => $GLOBALS['USER']->GetID(),
             "DETAIL_TEXT"   => $presentComment. $addComment,
             "DETAIL_TEXT_TYPE"   => "html",
         );
         $el->Update($item_id, $arLoadProductArray);
         return $presentComment.$addComment;
     }

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

И последняя, самая важная функция - функция сохранения изменений по заявке.

     function  SaveRequest($DATA,$ID=false){
         $el = new CIBlockElement;
     
         if(is_array($DATA['APPEND_FIELDS'])){
             $append = '<h4>Дополнительные поля</h4> <ul>';
             foreach ($DATA['APPEND_FIELDS'] as $key=>$appnd) {
                 $append .= '<li><b>'.$key.'</b>: '.$appnd.'</li>';
             }
             $append .= '</ul>';
             $DATA['REQTEXT'].= $append;
         }
         $PROP = array(
             'SERVICE'=>$DATA['SERVICE'],
             'STATUS'=>304,
             'PRIORITY'=>308,
             'OWNER'=>$GLOBALS['USER']->GetID(),
             'RESPONSIBLE'=>$DATA['RESPONSIBLE'],
             'TERM'=>$DATA['TIMER_END'],
             'MATCHING_LIST'=>$DATA['MATCHING_LIST']
         );
     
         if(!empty($DATA['FILES'])){
             $PROP['FILES'] = $DATA['FILES'];
         }
     
         $arLoadProductArray = Array(
             "MODIFIED_BY"    => $GLOBALS['USER']->GetID(),
             "IBLOCK_SECTION_ID" => false,
             "IBLOCK_ID"      => IBLOCK_REQUESTS,
             "PROPERTY_VALUES"=> $PROP,
             "NAME"           => $DATA['SERVICENAME'],
             "ACTIVE"         => "Y",
             "PREVIEW_TEXT"   => $DATA['REQTEXT'],
             "PREVIEW_TEXT_TYPE"   => "html",
         );
     
         if($PRODUCT_ID = $el->Add($arLoadProductArray))
             return array('success'=> $PRODUCT_ID);
         else
             return array('error'=>"Error: ".$el->LAST_ERROR);
     } 

Все дополнительные поля дописываем к опистельному тексту заявки. Статус заявки устанавливаем в состояние "Новая" (id статуса = 304), приоритет - "Нормальный" (id значения приоритета 308). Функция возвращет ID новой созданной заявки.

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

Бизнес-процесс согласования заявки

Самое главное - первый блок с запросом данных из инфоблока:

     if (\Bitrix\Main\Loader::includeModule('iblock')) { 
             function ParseDateFromString($datetime){ 
                 $format = "DD.MM.YYYY HH:MI:SS"; 
                 if ($arr = ParseDateTime($datetime, $format)) { 
                     return $arr; 
                 } else { 
                     return false; 
                 } 
             } 
         
             $ID = {=Document:ID}; 
             $IblockD = {=Document:IBLOCK_ID}; 
             $db_props = CIBlockElement::GetProperty($IblockD, $ID, array("sort" =&gt; "asc"), Array("CODE"=&gt;"RESPONSIBLE")); 
             $RESPONSIBLE = false; 
             if($ar_props = $db_props-&gt;Fetch()) 
                 $RESPONSIBLE = IntVal($ar_props["VALUE"]); 
             if($RESPONSIBLE &amp;&amp; intval($RESPONSIBLE)&gt;0){ 
                 $this-&gt;SetVariable('Responsible', $RESPONSIBLE); 
         
             $db_props = CIBlockElement::GetProperty($IblockD, $ID, array("sort" =&gt; "asc"), Array("CODE"=&gt;"TERM")); 
             $TERM = false; 
             if($ar_props = $db_props-&gt;Fetch()){ 
                 $TERM = ParseDateFromString($ar_props["VALUE"]); 
                 $Seconds = mktime($TERM['HH'],$TERM['MI'],$TERM['SS'],$TERM['MM'],$TERM['DD'],$TERM['YYYY']) - time(); 
                 $this-&gt;SetVariable('Term', $Seconds); 
                 $db_props = CIBlockElement::GetProperty($IblockD, $ID, array("sort" =&gt; "asc"), Array("CODE"=&gt;"FILES")); 
                 $FILES = array(); 
                 while($ar_props = $db_props-&gt;Fetch()){ 
                     $img = CFile::GetPath($ar_props['VALUE']); 
                     $link = '[url=http://sitename.ru'.$img.']link[/url]'; 
                     $FILES[] = $link; 
                 } 
                 $FILES = implode(' ',$FILES); 
                 if(!empty($FILES)) 
                     $this-&gt;SetVariable('attach', 'Вложения по заявке: ' . $FILES); } 
             } 
         }

Данный блок получает ответственного по заявке, получает крайний срок согласования по заявке, а также формирует текст с вложениями по заявкам.

P.S. во вложении - результаты работ. pai-bx-requests.tar.gz - модификация компонента "news" со всей логикой работы (Помещать в папку /bitrix/components/). requests_from_user.tar.gz - подключение механизма в публичной части.

Кількість показів: 3270
21.06.2015

Повернення до списку

Если вам была полезна статья можете отблагодарить автора: