Добрый день. Просьба помочь в ответе на следующий вопрос по очередям:

1) С чем может быть связана проблема, что после нажатия кнопки [Вернуть в очередь], в одном случае объект очереди остается в очереди, но увеличивается счетчик [Количество переносов], а в другом объект очереди исключается из очереди и через 5 минут снова входит в ней, но счетчик не сработал (т.к. создался новый объект очередь).

Какое поведение корректно при нажатии на кнопку [Вернуть в очередь]? Остаться в очереди с увеличенным счетчиком или исключение из очереди, и создание нового объекта очереди?

В документации описано такое поведение:

В результате выполнится возврат в раздел [Домашняя страница]. Отложенное

обращение будет помещено в конец очереди, независимо от настроенных

правил сортировки записей в данной очереди.

//из описания следует, что из очереди выпадать обращение не должно, верно?

Нравится

2 комментария

Логика этой кнопки сделана в схеме BasePageV2 в одном из пакетов OperatorSingleWindow:

    onPostponeQueueItemButtonClick: function() {
     var queueItemId = this.get("QueueItemId");
     if (!queueItemId) {
      this.error(this.get("Resources.Strings.QueueItemIsEmptyError"));
      //Страница не связана с элементом очереди единого окна. Действие отменено.
      return;
     }
     this.postponeQueueItem(queueItemId, function(success) {
      var messageString = (success)
       ? "QueueItemPostponeSucceedMessage"//Элемент успешно возвращен в очередь
       : "QueueItemPostponeFailedMessage";//Ошибка при возвращении элемента в очередь
      var message = this.get("Resources.Strings." + messageString);
      this.showInformationDialog(message, function() {
       if (success) {
        this.onQueueItemPostponed();
       }
      }.bind(this));
     }.bind(this));
    }
   },
    postponeQueueItem: function(queueItemId, callback) {
     var serviceCallBack = function(response, success) {
      if (!success) {
       var errorMessage =
        this.Ext.String.format(this.get("Resources.Strings.PostponeQueueItemError"),
         response.responseText);
         //Ошибка возвращения элемента в очередь. Сообщение об ошибке: {0}
       this.error(errorMessage);
       if (callback) {
        callback(false);
       }
       return;
      }
      if (callback) {
       callback(response === 1);
      }
 
     };
     ServiceHelper.callService({
      serviceName: "QueuesService",
      methodName: "PostponeQueueItem",
      data: {
       queueItemId: queueItemId
      },
      callback: serviceCallBack,
      scope: this
     });
    },

Там клиентская логика вызывает серверную функцию из QueuesService:

  [OperationContract]
  [WebInvoke(Method = "POST", UriTemplate = "PostponeQueueItem", BodyStyle = WebMessageBodyStyle.WrappedRequest,
   RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)]
  public int PostponeQueueItem(Guid queueItemId) {
   var userConnection = HttpContext.Current.Session["UserConnection"] as UserConnection;
   int updateCount = 0;
   string message = string.Empty;
   try {
    if (userConnection == null) {
     throw new ArgumentNullException("UserConnection");
    }
    var update =
     new Update(userConnection, "QueueItem")
      .Set("PostponesCount", new QueryColumnExpression(ArithmeticOperation.Addition,
       new QueryColumnExpression("PostponesCount"), new QueryColumnExpression(Column.Const(1))))
      .Set("OperatorId", Column.Parameter(null, "Guid"))
      .Set("NextProcessingDate", Column.Const(null))
     .Where("Id").IsEqual(new QueryParameter("QueueItemId", queueItemId));
    updateCount = update.Execute();
    message = string.Format(GetResourceValue(userConnection, "QueueItemPostponeUpdatedMessage"),
     queueItemId);
    QueuesUtilities.LogDebug(message);
   } catch (Exception e) {
    message = string.Format(GetResourceValue(userConnection, "InvokeMethodErrorMessage"),
     "PostponeQueueItem", e.Message);
    QueuesUtilities.LogError(message, e);
    throw;
   }
   return updateCount;
  }

По коду видно, что меняется существующая запись: счётчик, дата и очищается ссылка на оператора.

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

Зверев Александр,

Спасибо большое за ваш комментарий, взял на вооружение!

Показать все комментарии

Добры день, Коллеги. Есть ли какая нибудь документация, описывающая действия/ограничения системы по одновременной работе с одним и тем же объектом (объект очереди/заявка/обращение/Клиент) разными пользователями

 

Нравится

2 комментария

Добрый день. Подскажите, есть ли документация, где есть описание блоков "Следующие шаги (0)" в карточке "Обращение/Клиент".

В частности интересует:

  1. Можно ли скрывать с карточки этот блок? 
  2. Можно ли скрывать с карточки этот блок при определенных условиях, например, в зависимости от значений на индикаторе стадий)? 
  3. Можно ли дизейблить работу этого блока при определенных условиях (нельзя звонить/отправлять письмо при определенных условиях, например, в зависимости от значений на индикаторе стадий? 
  4. Можно ли убрать часть функционала в блоке "Следующие шаги (0)" (например, с данной карточки нельзя отправлять электронное письмо)?

Нравится

2 комментария

Этот механизм сделан в схеме CaseSectionActionsDashboard. В статье есть пример его доработки, добавления нового элемента для сообщений портала, в другой статье — ещё одной кнопки для звонков. Для изменения, скрытия кнопок используйте diff с merge и remove, как тут и тут. Для добавления логики видимости и доступности попробуйте использовать биндинги, как к обычным полям. Также см. обсуждение.

Зверев Александр пишет:

Этот механизм сделан в схеме CaseSectionActionsDashboard. В статье есть пример его доработки, добавления нового элемента для сообщений портала, в другой статье — ещё одной кнопки для звонков. Для изменения, скрытия кнопок используйте diff с merge и remove, как тут и тут. Для добавления логики видимости и доступности попробуйте использовать биндинги, как к обычным полям. Также см. обсуждение.

Зверев Александр,

Спасибо за комментарий, очень много полезной информации!

Показать все комментарии

Как скрыть коммуникационную панель от портальных пользователей?

Нравится

2 комментария
Лучший ответ

Коммуникационная панель по умолчанию и так скрыта у пользователей портала

Коммуникационная панель по умолчанию и так скрыта у пользователей портала

Схема CommunicationPanel в пакете SSP. 

				{
					"operation": "merge",
					"name": "email",
					"values": {
						"visible": {
							"bindTo": "getIsNotPortalUser"
						}
					}
				},

 

Показать все комментарии

Добрый день.

 

Подскажите, где почитать про настройку балансировщика для bpm`online на Windows? Есть статья про HAProxy, но она не подходит, ввиду отсутствия серверов на линуксе.

 

Как можно выйти из ситуации, когда есть три сервера приложения, один из которых отдан под балансировщик. Встроенная роль WS Network Load Balancer или использовать ARR (К слову, не очень то работать хочет)?

Нравится

1 комментарий

Добрый день, Илья.



Мы рекомендуем использовать haproxy для балансировки нагрузки в режиме sticky session. Haproxy разворачивается на пограничном сервере с ОС Linux. Другие способы балансировки мы не тестировали и не рекомендуем.

https://academy.terrasoft.ru/documents/technic-sdk/7-14/infrastruktura-prilozheniya

 

Показать все комментарии

В окне групп фильтров верхними уровнями являются "Все" и "Избранное". Можно ли как-то создать свой фильтр верхнего уровня наравне с ними?

Нравится

1 комментарий

На самом деле «Все» и «Избранные» — это не группы, в таблице групп (ContactFolder, AccountFolder и др.) их нет. Они создаются программно в схеме FolderManagerViewModel, см. там логику, связанную с AllFolders и FavoriteFolders.

Пример добавления нового пункта верхнего уровня можно увидеть в разделе продуктов, там добавили ещё «Каталог».

Показать все комментарии

Добрый день. Просьба помочь в ответе на следующий вопрос по очередям:

1) У каждого объекта очереди есть поле "Дата следующей обработки", каким образом оно заполняется? По кнопке [Запланировать на дату] или с помощью другого механизма?

2) С чем может быть связана проблема, что после заполнения по кнопке [Запланировать на дату] времени и даты, объект очереди всё равно при наполнении очереди попадает в очередь.

//вместо того, чтобы сработал алгоритм: "Объект очереди не будет отображаться в едином окне до того момента, когда наступит указанное время обработки. При наступлении запланированного времени обработки объект очереди отобразится вверху списка в очереди у того оператора, который взял в работу данный объект очереди"

3) Корректно ли поведение системы: если с домашней страницы по кнопке [Открыть] открыть объекта очереди, то объект очереди исключается из очереди, но через 1-5 минут (в зависимости от [Частота обновления]) снова отображается в очереди

Нравится

2 комментария

По инструкции:

Дата следующей обработки

Если оператор отложил обработку элемента, данная колонка отобразит дату, в которую элемент снова будет предложен оператору.

Это поле NextProcessingDate в объекте OperatorSingleWindow. Оно меняется в серверной логике схемы QueuesService, в методах ChangeQueueItemDate (заполнение) и PostponeQueueItem (очистка). А также в клиентской схеме QueueItemEditPage в методе updateQueueItemData и BasePageV2 пакета OperatorSingleWindow, которая взаимодействует с вышеупомянутым сервисом.

Логика фильтрации по этому полю реализована в схеме QueueGridUtilities:

addNextProcessingDateFilters: function(filters) {
	var nextProcessingDateFilters = new this.Terrasoft.createFilterGroup();
	nextProcessingDateFilters.logicalOperation = this.Terrasoft.LogicalOperatorType.OR;
	var nextProcessingColumnName = "NextProcessingDate";
	nextProcessingDateFilters.add("NextProcessingDateFilter", this.Terrasoft.createColumnFilterWithParameter(
		this.Terrasoft.ComparisonType.LESS_OR_EQUAL, nextProcessingColumnName, new Date()));
	nextProcessingDateFilters.add("IsNullNextProcessingDateFilter",
		this.Terrasoft.createColumnIsNullFilter(nextProcessingColumnName));
	filters.add("NextProcessingDateFilters", nextProcessingDateFilters);
},

И её применение там же ниже:

  addQueueFilters: function(filters, config) {
   filters.addItem(this.Terrasoft.createColumnFilterWithParameter(this.Terrasoft.ComparisonType.EQUAL,
    "[Queue:Id:QueueId].QueueEntitySchema", this.get("EntitySchemaUId")));
   var queueId = this.get("QueueId");
   if (!Ext.isEmpty(queueId)) {
    filters.addItem(this.Terrasoft.createColumnFilterWithParameter(this.Terrasoft.ComparisonType.EQUAL,
     "Queue", queueId));
   }
   var isSupervisorMode = this.get("IsSupervisorMode");
   if (!isSupervisorMode) {
    filters.addItem(this.Terrasoft.createColumnFilterWithParameter(this.Terrasoft.ComparisonType.EQUAL,
     "Status.IsFinal", false));
    filters.addItem(this.Terrasoft.createColumnFilterWithParameter(this.Terrasoft.ComparisonType.EQUAL,
     "[Queue:Id:QueueId].Status.IsInitial", false));
    filters.addItem(this.Terrasoft.createColumnFilterWithParameter(this.Terrasoft.ComparisonType.EQUAL,
     "[Queue:Id:QueueId].Status.IsFinal", false));
    this.addNextProcessingDateFilters(filters);
    this.addOperatorFilters(filters);
    if (!config || !config.skipOperatorFilter) {
     this.addCloseQueueFilters(filters);
    }
   }
  },

Там же есть и другой фильтр, реализованный в функции addOperatorFilters.

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

Спасибо за информацию. Основная проблема была в том, что некорректно был настроен процесс: Обработка обращений/заявок из очереди в Едином окне (в частности правило "когда считать элемент выполненным)

Показать все комментарии

Добрый день.

Я занимаюсь аудитами защищенности веб-приложений. В одном из проектов наш заказчик использовал bpm'online. В его конфигурации было выявлено ряд уязвимостей к SQL-инъекции.

Типичный пример уязвимого кода (имена метода и переменных изменены; значение параметра email контролируется пользователем):

using Terrasoft.Core.DB
…
 
private Guid GetUserId(string email)
{
    var select = new Select(Connection)
        .Column("Id")
        .From("Users")
        .Where("Email")
        .IsEqual(Column.Const(email)) as Select;
    return select.ExecuteScalar<Guid>();
}

Данный код использует метод Column.Const для передачи параметра, полученного от пользователя в SQL-запрос. Этот метод осуществляет конкатенацию этого параметра с запросом, не фильтруя специальные символы.

Это позволяет пользователю, контролирующему передаваемый в метод Column.Const параметр, выполнить атаку типа SQL-инъекция. То есть получить доступ ко всей информации в базе данных.

Мы порекомендовали нашему Заказчику использовать Column.Parameter вместо Column.Const в данном случае.

Документация Террасофт не сообщала (на момент публикации) об опасности некорректного примененеия метода Column.Const. В документации на Академии даже содержались некорректные примеры использования.

Я связался с поддержкой Террасофт и изложил проблему. Их позиция - ответственность лежит на разработчиках, но документацию они обещали поправить (и уже частично поправили).

Я полагаю, что многие разработчики могут не знать об этой особенности метода Column.Const, как и разработчики нашего заказчика. По этому я публикую эту информацию здесь, надеюсь это поможет избежать уязвимостей.

Рекомендую вообще не использовать Column.Const, заменить на Column.Parameter, либо делать это крайне осторожно, будучи абсолютно уверенным что в настоящий момент, или при будущих изменениях значение параметра не будет контролироваться пользователем.

Для отслеживания данной проблемы зарегистрирован идентификатор: CVE-2019-15301.

Нравится

1 комментарий

Вообще, в самом Column.Const нет ничего криминального, правильное его использование упомянуто тут в начале статьи:

new Insert(UserConnection)
.Into("Table")
.Values()
    .Set("Column1", Column.Const(1))
    .Set("Column2", Column.Const(1))
    .Set("Column3", Column.Const(1))
.Values()
    .Set("Column1", Column.Const(2))
    .Set("Column2", Column.Const(2))
    .Set("Column3", Column.Const(2))
.Values()
    .Set("Column1", Column.Const(3))
    .Set("Column2", Column.Const(3))
    .Set("Column3", Column.Const(3))
.Execute();

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

Если ещё остались статьи с неправильным применением, нажмите кнопку внизу их и оставьте замечание со ссылкой на эту тему.

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

Показать все комментарии

Создана преднастроенная страница.

При открытии два обязательных поля

Проблема. Обязательность первого поля не работает. Система позволяет сохранить данные с незаполненным полемИзображение удалено.

В конфигурации страницы добавлен признак обязательности

Так же на атрибут повешен признак isRequired: true

Однако это не дает ровным счетом ничего

Так же, ни последующая валидация при сохранении, ни правила не помогли

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

Спасибо

 

Нравится

4 комментария

Попробуйте для установки обязательности поля использовать бизнес-правила. 

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

Алла Савельева, Пробовал бизнес-правила. Не помогло.

Спасибо

Дубов Андрей Владимирович,

Приведите пример Вашего кода

Без изучения кода карточки сложно сказать.

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

Показать все комментарии

Доброго дня.

Необходима интеграция с корзиной сайта, откуда заказы валятся на Magento 1.

Настроено API: есть URL-адрес для подключения с Magento, user и login.

Не понятна сама настройка веб-сервиса в bpm'online. Сталкивался ли кто-то с данной интеграцией, либо может кто-то теоретически понимает это?

Нравится

2 комментария
Лучший ответ

Добрый день!

Не уверен, что есть какой-то готовый коннектор (по крайней мере на маркете я ничего не нашёл), но в любом случае, можно запилить свою интеграцию через свой SOAP сервис. Правда для этого нужны компетенции и в bpm и в api magento.

Дополнение. Подключение посредством SOAP.

Добрый день!

Не уверен, что есть какой-то готовый коннектор (по крайней мере на маркете я ничего не нашёл), но в любом случае, можно запилить свою интеграцию через свой SOAP сервис. Правда для этого нужны компетенции и в bpm и в api magento.

Показать все комментарии