Маршрут «Активация»: интерфейс и логика настройки
============================

На этом этапе мы настраиваем путь заявки при её подаче, то есть маршрут, по которому заявка пойдёт после создания и активации.

Для этого используется маршрут типа **«Активация»**.

.. admonition:: Основная рабочая область. 
    Редактор маршрутов состоит из трёх панелей, каждая из которых отвечает за свой тип этапов маршрута.
    **Панели редактора маршрутов**

    **Предварительные этапы**
    Содержат только стандартные процессы, которые выполняются до основных действий:

    * Работа
    * Согласование
    * Утверждение
    * Ознакомление
    * Отправка документа
    * Регистрация
    * Маршрут

    **Действия**  
    Ключевая часть маршрута.

    Этапы в этой панели:

    * могут автоматически получать данные из формы (по кодам компонентов);
    * изменяют объекты и состояние процесса в системе;
    * выполняются только после успешного завершения предварительных этапов (если они есть).
    
    **Последующие этапы**
    Также содержат только стандартные процессы и выполняются после действий (при необходимости).
    
Переход к созданию маршрута
----------------------------

**Шаг 1.** По нажатию на кнопку "+" в разделе **"Маршруты реестра"**, в выпаждающем списке выбрать тип маршрута **"Активация"**

**Шаг 2.** В открывшемся окне перейти к редактированию маршрута активации нажав кнопку редактирования:

.. image:: ../resources/img/edit.jpeg

**Шаг 3.** На панеле "Действия" нажимаем кнопку **"+"** чтобы добавить новый этап

.. figure:: ../resources/img/actions_panel.jpeg


**Шаг 4.** В открывшейся справа панели настройки этапа необходимо настроить этап в соответствии с ордером.

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

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

.. figure:: ../resources/img/order_process_steps.jpeg

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

Этап 1. Заполнение заявки
---------------------------

**Входом** операции является форма заявки, разработанная в системе и данные клиента, которые он вносит при заполнении формы.

**Выход** - заполненная по форме заявка на услугу.

На этом этапе не нужен дополнительный шаг процесса, т.к отправка заявки и будет являться первым шагом процесса.

Этап 2. Отправка заявки.
------------------------

**Вход** - подписание заявки при помощи ЭЦП.

**Выход** - заявка подписанная в системе.

Для реализации данного этапа нам потребуется тип действия "Работа по форме" с типом "Согласование"

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

Предварительная настройка
^^^^^^^^^^^^^^^^^^^^^^

**Шаг 1.** Возвращаемся в форму заявки

**Шаг 2.** Добавляем на форму комопнент таблицы, и включаем для нее настройки скрытности, чтобы системные поля не отображались пользователю

**Шаг 3.** Внутрь таблицы добавляем компонент "Объекты Synergy" 

* задаем ему код например "entity_author"

* включаем настройку "Заполнять текущим пользователем"

**Шаг 4.** Полю задаем наименование "Автор"

**Шаг 5.** Сохраняем изменения
    
В результате этого, поле автоматически будет заполняться учетной записью пользователя, создавшего заявку.

Создание этапа маршрута
^^^^^^^^^^^^^^^^^^^^^

**Шаг 1.** В настройках этапа указываем 

* Тип действия - "Работа по форме"

* Название этапа - указываем согласно логике шага, например "Подписание заявки"

* Код этапа - необязательно, используется при обращении к этапу в скриптах или условиях.

* Ответственный - здесь необходимо указать код поля в котором будет находится автор заявки (entity_author) его мы создали в предварительном шаге 

* Тип работы - согласование

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

**Шаг 2.** Сохраняем этап, нажав на иконку дискеты в панели настроек этапа.

.. figure:: ../resources/img/step1.jpeg

Этап 3. Регистрация заявки

**Вход** - подписанная в системе заявка

**Выход** - заявка со сформированным регистрационным номером

Формирование номера заявки в системе осуществляется на основе **счётчика**.

.. note:: 

    Счётчик — это базовая сущность системы, предназначенная для генерации последовательных числовых значений.
    Он используется для нумерации заявок, документов и других объектов.
    
Создание счетчика
^^^^^^^^^^^^^^^^^
    
**Шаг 1.** В дереве приложений выбираем папку в которой будут храниться счетчики

**Шаг 2.** Кликаем правой кнопкой мыши и выбираем Добавить → Базовые сущности → Счётчик 

**Шаг 3.** Открывается панель редактирования информации о счётчике:

* **Код** - Обязательное поле. 
Используется для обращения к счётчику в шаблоне номера.
*  **Начальное значение** - значение, с которого начинается нумерация.
* **Следующее значение** - должно быть не меньше начального.
* **Период сброса** - определяет, когда следующее значение будет сбрасываться к начальному.
Возможные варианты:

* Никогда (по умолчанию)
* Каждый день
* Каждую неделю
* Каждый месяц
* Каждый год

**Шаг 4.** Вводим необходимые значения и нажимаем кнопку "Сохранить"

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

Привязка счетчика к шаблону номера
^^^^^^^^^^^^^

**Шаг 1.** Выбрав нужную папку кликаем по ней правой кнопкой мыши Добавить → Документооборот → Шаблон номера

**Шаг 2.** В открывшемся окне создания шаблона номера указываем:

* Наименование - название счетчика или его назначение (например "Номер заявки на услуги ATLAS")
* Код - ставим логически правильный код или оставляем автоматически сгенерированный транслит
* Значение - помещаем сюда код ранее созданного нами счетчика в формате {код_счетчика}

**Шаг 3.** Сохраняем шаблон номера.

Добавление шаблона номера на форму
^^^^^^^^^^^^^^^^^

**Шаг 1.** Возвращаемся к форме заявки

**Шаг 2.** В поле "Порядковый номер заявки" выделяем компонент "номер"

**Шаг 3.** В строке "Шаблон номера" выбираем созданный нами шаблон

**Шаг 4.** Сохраняем форму

Сгенирированный автоматически номер будет являться регистрацией заявки в системе.

Этап 3. Отправка уведомления о номере заказа.
----------------------------------------------

**Вход** - подписанная ЭЦП и зарегистрированная в системе заявка на услугу

**Выход** - уведомление на почту клиента

 На данном этапе на почту клиента отправляется уведомление о регистрации заявки с указанием её номера.
 
 **Шаг 1.** В редакторе маршрута нажимаем кнопку "+" для добавления нового этапа
 
 **Шаг 2.** В настройках этапа выбираем тип действия **"Отправка письма на почту"**
 
 **Шаг 3.** Указываем:
 
 * Наименование этапа, например **"Отправка уведомления о номере заказа"**
 
 * Код - указывается при необходимости
 
 * Код поля на форме - подразумевает поле на форме с почтой клиента, на которую необходимо отправить уведомление. У нас например это textbox_mail
 
 * Тема письма с поддержкой HTML разметки - будет содержать тему письма
 
 * Тело письма с поддержкой HTML разметки - будет содержать основной текст письма с данными из нужных нам полей
 
 В нашем случае, согласно ТЗ (ордера) HTML шаблон письма будет выглядеть следующим образом:
 
 ``Ваша заявка на “ +  $listbox_type + ” зарегистрирована под номером “  + $counter_number + ”.``
 
где listbox_type - код поля "Тип заявки"

counter_number - код поля "Номер заявки"

"Ваша заявка на" и "зарегистрирована под номером" - это статический текст, который будет одинаковым в каждом письме.

Символ $ - обращение к переменной.
Символ $ обязательный и означает, что далее указывается переменная, значение которой будет взято из данных заявки:

    *	$listbox_type — значение поля Тип заявки
    *	$counter_number — значение номера заявки, сформированного счётчиком

Во время отправки письма система автоматически подставляет фактические значения этих полей из данных заявки.

 **Шаг 3.** Проверяем корректность письма и сохраняем этап.
 
 .. note::
    Кнопка **«Отправить тестовое письмо»** позволяет проверить работу уведомления прямо из маршрута, отправкой уведомления на почту. (Для этого в приложении должна быть настроена почтовая служба). После нажатия на кнопку система предложит выбрать уже созданную заявку в реестре, для того чтобы отправить письмо на указанную в заявке почту.

Этап 4. Назначение исполнителя
-------------------------------

Вход - заявка поступившая менеджеру

Выход - заявка с назначенным по ней исполнителем

Для реализации данного шага, будем использовать тип "Работа по форме"

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

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

Предварительная настройка
^^^^^^^^^^^^^^^^^

**Шаг 1.** Возвращаемся на форму

**Шаг 2.** В скрытой таблице под полем "Автор" добавляем еще одно поле **"Менеджер"** с компонентом **"Объекты Synergy"**

**Шаг 3.** В настройках компонента присваиваем ему код содержащий смысловую нагрузку (например entity_manager) и в настройке "Тип данных" выбираем тип "Пользователь", «Должности» либо «Подразделения».

.. note:: 
    Если мы хотим чтобы заявка на данном этапе падала конкретному человеку  – мы прикрепляем к заявке конкретно его учетную запись, выбрав для этого тип данных **«Пользователь»** и выбрав в поле конкретного человека.
    
    Если же, в системе имеется должность, с назначенными на нее пользователями, отвечающими за определенную работу, то используется тип данных **«Должности»**, в котором указывается должность и работа будет падать всем пользователям, состоящим на данной должности.
    
    Если необходимо направлять работу целому подразделению, с вложенными в него должностями, то используется тип данных **«Подразделения»**, тогда работа будет поступать всем людям, состоящим на всех вложенных должностях выбранного подразделения.

**Шаг 4.** Сохраняем форму

Создание этапа маршрута
^^^^^^^^^^^^^^^^^

**Шаг 1.** В редакторе маршрута нажимаем кнопку **"+"** на панели "Действия"

**Шаг 2.** В настройках этапа указываем:

* Тип действия - работа по форме
* Название этапа - назначение исполнителя
* Ответственный - указываем код созданного нами поля с менеджером (например у нас это entity_manager)
* Тип работы - "Работа"
* Включаем настройку "Использовать форму завершения"

.. admonition:: Форма завершения.

    Форма завершения — это модальное окно, которое открывается при выполнении работы и определяет, что должен сделать пользователь для завершения этапа.
    Существует несколько типов форм завершения:
    
    * Комментарий — результатом является комментарий.
    * Файл — результатом является файл (с устройства, из хранилища или из приложений работы).
    * Документ — выбор или создание документа, связанного с работой.
    * Форма — заполнение отдельной формы (используется в нашем случае).
    * Без результата — завершение без результата.
    
Создание формы завершения
^^^^^^^^^^^^^^^^^^^^

**Шаг 1.** Создаем отдельную форму кликнув правой кнопкой мыши по нужной папке → Добавить → Базовые сущности → Форма.

**Шаг 2.** Задаем форме:

* понятное наименование, отличающее её от основной формы
* понятный код

**Шаг 3.** Добавляем поле для указания исполнителя:

* Наименование - Исполнитель
* компонент «Объекты Synergy»

делаем поле обязательным, так как исполнитель является обязательным участником следующего шага процесса.

**Шаг 4.** Сохраняем форму. 

Привязка формы завершения к этапу
^^^^^^^^^^^^^^^

**Шаг 1.** Возвращаемся к настройке этапа "Назначение исполнителя"

**Шаг 2.** В строке **«Форма завершения»** нажимаем кнопку «+».
В открывшемся окне:

* выбираем тип завершения «Форма»;
* задаём код и наименование (для уджобства делаем их аналогичными созданной форме);
* в поле «Форма» выбираем созданную форму.

**Шаг 3.** Сохраняем этап.

.. important:: 
    Если работа направляется на **должность** или **подразделение** необходимо включить настройку: **«Прервать выполнение параллельных этапов после завершения одного из них»**.
    Иначе каждому пользователю потребуется выполнить назначение исполнителя, прежде чем маршрут продолжится.
    
.. admonition:: ПРИМЕЧАНИЕ

    Т.к форма завершения является отдельной сущностью, данные, введенные в форму завершения не попадают в основную форму автоматически. Для дальнейшего использования данных, введенных в форму завершения, их нужно перенести в основную форму при помощи отдельного этапа **«Блокирующий процесс»**. Подробно будет описано в этапе 5
    
Этап 5. Смена статуса заявки
-----------------------------

**Вход** - заявка с назначенным по ней исполнителем

**Выход** - заявка со статусом "в работе"

Смена статуса выполняется автоматически с помощью скрипта интерпретатора, запускаемого этапом маршрута типа **«Блокирующий процесс»**.

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

    Подробнее о том, что такое скрипт интерпретатора можно найти здесь http://rtd.lan.arta.kz/docs/guide/ru/minsky/interpreter.html
    
Создание скрипт интерпретатора
^^^^^^^^^^^^^^^^^^^^^^^^^^

**Шаг 1.** В дереве приложения выбираем папку для хранения скриптов.
Для удобства это может быть:

* папка integrations,
* либо можно создать отдельную папку block_processes.    

**Шаг 2.** Кликаем по папке правой кнопкой мыши → Добавить → Интеграция → Скрипт интерпретатора.

**Шаг 3.** В открывшемся окне редактирования кода указываем:

* Наименование - Система автоматически задаёт базовый префикс ``event.blocking.interpreter``
Необходимо дополнить наименование и код, используя латинские буквы и отражая логику работы скрипта. Например ``event.blocking.interpreter.change.status_work``

* Код - формируется автоматически из наименования
* Описание - добавляется при необходимости для пояснения логики работы скрипта
* Комментарий по умолчанию - обязательная настройка, это сообщение которо система выведет после выполнения скрипта. (например "ОК" или "Статус изменен")
* Авторизация - скрипту для срабатывания необходимы права доступа. Здесь имеется два варианта 
        * По логину и паролю - понадобится логин и пароль учетной записи с правами администратора
        * По ключу - понадобится ключ администратора
        
**Шаг 4.** Выбираем тип авторизации "По логину и паролю" и указываем учетную запись администратора

.. important:: При смене логина и пароля учетной записи администратора логин и пароль нужно будет обновить во всех скриптах интерпретатора, использующих эту учетную запись

**Шаг 5.** В редактор кода вставляем стандартный скрипт для смены статуса

.. code-block:: javascript

 var result = true;
 var message = 'Смена значения статуса';
  try {
    var form = platform.getFormsManager().getFormData(dataUUID);
    form.load();
    form.setValue('код справочника', 'код значения');
    form.save();
 } catch (e) {
   result = false;
   message = e.message;

**Шаг 3.** Вместо слов:

* "код справочника" - указываем код поля в котором находится выпадающий список статусов, например у нас это listbox_status
* "код значения" - значение, на которое должен смениться статус, согласно справочнику у нас это значение 4 ("В работе")

Если все сделано правильно, код будет выглядеть так:

**Шаг 4.** Сохраняем скрипт интерпретатора и возвращаемся к настройке этапа

Создание этапа маршрута
^^^^^^^^^^^^^^^^^^^

**Шаг 1.** В маршруте добавляем новый этап.

**Шаг 2.** В настройках этапа:

* указываем наименование этапа (например, «Смена статуса на "В работе"»);
* в поле **«Событие»** вставляем название созданного скрипта интерпретатора.

**Шаг 3.** Сохраняем этап.

Скрипт интерпретатора для этапа 4
^^^^^^^^^^^^^^^^^^^^^^^^

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

**Шаг 1.** Для начала добавляем на основную форму заявки поле **«Исполнитель»** из формы завершения. Поле должно иметь аналогичный код.

**Шаг 2.** Сохраняем форму заявки

**Шаг 3.** Правой кнопкой мыши по нужной папке → добавить → интеграция → скрипт интерпретатора

**Шаг 4.** В редактор кода добавляем скрипт, который переносит данные с формы завершения на основную форму:

Пример такого блокирующего процесса:

.. code-block:: javascript

   var result = true;
   var message = 'ok';

   function getHttpClient() {
     let client = new org.apache.commons.httpclient.HttpClient();
     let creds = new org.apache.commons.httpclient.UsernamePasswordCredentials(login, password);
     client.getParams().setAuthenticationPreemptive(true);
     client.getState().setCredentials(org.apache.commons.httpclient.auth.AuthScope.ANY, creds);
     return client;
   }

   function httpGetMethod(methods, type) {
     let client = getHttpClient();
     let get = new org.apache.commons.httpclient.methods.GetMethod(
       "http://127.0.0.1:8080/Synergy/" + methods
     );
     get.setRequestHeader("Content-type", "application/json");
     client.executeMethod(get);
     let resp = get.getResponseBodyAsString();
     get.releaseConnection();
     return type == 'text' ? resp : JSON.parse(resp);
   }

   function httpPostMethod(methods, params, contentType) {
     let client = getHttpClient();
     let post = new org.apache.commons.httpclient.methods.PostMethod(
       "http://127.0.0.1:8080/Synergy/" + methods
     );

     if (contentType) {
       post.setRequestBody(JSON.stringify(params));
     } else {
       for (let key in params) post.addParameter(key, params[key]);
     }

     post.setRequestHeader(
       "Content-type",
       contentType || "application/x-www-form-urlencoded; charset=utf-8"
     );

     let resp = client.executeMethod(post);

     if (contentType) {
       resp = JSON.parse(post.getResponseBodyAsString());
     }

     post.releaseConnection();
     return resp;
   }

   function getProcesses(documentID) {
     return httpGetMethod("rest/api/workflow/get_execution_process?documentID=" + documentID);
   }

   function getWorkCompletionData(workID) {
     return httpGetMethod("rest/api/workflow/work/get_completion_data?workID=" + workID);
   }

   function getFormData(asfDataId) {
     return httpGetMethod("rest/api/asforms/data/" + asfDataId);
   }

   function mergeFormData(uuid, data) {
     return httpPostMethod(
       "rest/api/asforms/data/merge",
       {
         uuid: uuid,
         data: data
       },
       "application/json; charset=utf-8"
     );
   }

   let UTILS = {
     createField: function (fieldData) {
       let field = {};
       for (let key in fieldData) field[key] = fieldData[key];
       return field;
     },

     getValue: function (data, cmpID) {
       data = data.data ? data.data : data;
       for (let i = 0; i < data.length; i++) {
         if (data[i].id === cmpID) return data[i];
       }
       return null;
     },

     setValue: function (asfData, cmpID, data) {
       let field = this.getValue(asfData, cmpID);

       if (field) {
         for (let key in data) {
           if (key === 'id' || key === 'type') continue;
           field[key] = data[key];
         }
         return field;
       } else {
         asfData = asfData.data ? asfData.data : asfData;
         field = this.createField(data);
         field.id = cmpID;
         asfData.push(field);
         return field;
       }
     }
   };

   function processesFilter(processes) {
     let result = [];

     function search(p) {
       p.forEach(function (x) {
         if (x.typeID == 'ASSIGNMENT_ITEM' && x.finished) result.push(x);
         if (x.subProcesses.length > 0) search(x.subProcesses);
       });
     }

     search(processes);

     return result.sort(function (a, b) {
       return new Date(b.finished) - new Date(a.finished);
     });
   }

   try {
     let processes = processesFilter(getProcesses(documentID));

     if (!processes.length) throw new Error('Не найденно завершенной работы');

     let resultFormWork = getWorkCompletionData(processes[0].actionID);

     if (!resultFormWork || !resultFormWork.result.hasOwnProperty('dataUUID')) {
       throw new Error('Не найдена форма завершения');
     }

     let completionFormData = getFormData(resultFormWork.result.dataUUID);

     let newFormData = [];
     let matching = [];

     // поля для сопоставления с фз на основную форму
     matching.push({ from: 'поле на форме завершения', to: 'поле основной формы' });

     matching.forEach(function (id) {
       let fromData = UTILS.getValue(completionFormData, id.from);
       if (fromData) UTILS.setValue(newFormData, id.to, fromData);
     });

     mergeFormData(dataUUID, newFormData);

   } catch (err) {
     message = err.message;
   }

**Шаг 5.** Находим в скрипте блок сопоставления полей matching и заменяем текст внутри кавычек

* from - код поля "Исполнитель" в форме завершения 
* to - код поля "Исполнитель" в осноной форме, куда переносим значение

Пример:

До:

После: 

**Шаг 6.** Присваиваем скрипту интерпретатора наименование, например ``event.blocking.interpreter.completion_form_responsible``

**Шаг 7.** При необходимости добавляем описание и указываем авторизационные данные как мы это делали ранее

**Шаг 8.** После того как все готово, сохраняем сущность и переходим к добавлению этапа в маршрут.

Добавление дополнительного шага для этапа 4
------------------------------------------

**Шаг 1.** Добавляем этап с типом действия "Блокирующий процесс":

* Наименование - перенос данных с формы завершения
* Событие - вставляем код созданного нами скрипта интерпретатора для переноса данных из формы завершения (``event.blocking.interpreter.completion_form_responsible``)

**Шаг 2.** Сохраняем этап и переходим к формированию правильного порядка этапов. Нам необходимо передвинуть этап с переносом формы завершения непосредственно под этап с формой завершения.

**Шаг 3.** Для переноса этапа зажимаем кнопку (пример кнопки) рядом с цифрой этапа и двигая строку в нужное место

(скрин)

Теперь все этапы располагаются в правильном порядке.

