Всем доброго времени суток. Версия 7.12.

По ссылке - https://community.terrasoft.ua/articles/kak-sdelat-filtraciu-pola-po-ex… - хорошая инструкция как задать фильтр для поиска всех записей объекта Account для которых в объекте AccountCommunication значение колонки SearchNumber равно searchValue.

А можно ли отфильтровать список всех записей объекта Account для которых нет ни одной записи в AccountCommunication?

NOT_EQUAL, createColumnIsNullFilter - не работают, т.к. поиск идёт среди существующих записей, а нужно найти все, кроме тех, которые существуют.

Нравится

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

Смородинов Денис,

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

select *

from Account A

where not exists (select * from AccountCommunication AC

                    where AC.AccountId = A.Id)

Такой запрос можно построить с помощью createNotExistsFilter.

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

а есть примеры использования?

Интересует только клиентская часть, т.е. ESQ-фильтр https://academy.terrasoft.ru/documents/technic-sdk/7-13/rabota-s-filtra…

Примерно так

var esq = this.Ext.create(Terrasoft.EntitySchemaQuery, {
    rootSchemaName: "Activity"
});
esq.addAggregationSchemaColumn("DurationInMinutes", Terrasoft.AggregationType.COUNT, "UniqueActivitiesCount", Terrasoft.AggregationEvalType.DISTINCT);

Подробнее олисано тут

Всё равно до конца не понимаю. Это же расчёт суммы или количества.

Я могу посчитать количество средств коммуникации для конкретного контрагента. Т.е. чтобы отобрать контрагентов, у которых кол-во средств == 0, нужно перебрать значения количества для всех контрагентов по списку?

А если их тысячи?

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

Смородинов Денис,

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

select *

from Account A

where not exists (select * from AccountCommunication AC

                    where AC.AccountId = A.Id)

Такой запрос можно построить с помощью createNotExistsFilter.

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

Всем доброго времени суток. Версия 7.12.

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

Изображение удалено.

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

Нравится

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

Это нелогично, Вы пользователю показываете список выбора, а потом оказывается, что что-то он выбрать не может. Правильно, не отображать в списке то, что выбрать нельзя и это проще реализовать с помощью фильтрации данных.

Посмотрите на метод isAnySelected

Это нелогично, Вы пользователю показываете список выбора, а потом оказывается, что что-то он выбрать не может. Правильно, не отображать в списке то, что выбрать нельзя и это проще реализовать с помощью фильтрации данных.

и тем не менее, пожелания клиента.

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

Нужно смотреть в LookupPageViewModelGenerator, который генерирует такую страницу. Случайно не оно?

/**
 * Subscribe to changes selectedRows.
 * @private
 */
_subscribeOnSelectedRowsChange: function() {
	this.on("change:selectedRows", this.onSelectedRowsChange, this);
},
/**
 * Handles selected rows change event.
 */
onSelectedRowsChange: function() {
	if (!this.isMultiSelect()) {
		return;
	}
	let selectedRowsCount = 0;
	if (this.get("SelectAllMode") && this.get("usingMultiAddMixin")) {
		var filteredRowsCount = this.get("filteredRowsCount");
		if (filteredRowsCount) {
var unselectedItems = this.getUnselectedItems();
selectedRowsCount = filteredRowsCount - unselectedItems.length;
		}
	} else {
		selectedRowsCount = this.getSelectedRecords().length;
	}
	this.set("selectedRowsCount", selectedRowsCount);
},

 

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

Добрый день!



Столкнулся с задачей следующего характера:



Имеются 2 детали, по обоим надо посчитать сумму и выдать в печатную форму итоговую сумму. Можно ли это как-то компактно реализовать или только созданием дополнительного поля в которое и будет писаться эта сумма? 



Пример: В первой детали услуги на сумму в 100р, во второй детали материалы на 200р. В итоге, в печатной форме я увижу 300р.

Нравится

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

Быстров Сергей пишет:

Да, верно, вы передаете в макрос параметр по которому хотите найти ваши значения. Делаете 2 выборки, одну из таблицы А и вторую из таблицы Б, суммируете их между собой, а затем result = А + Б;

return result; = это значение вернется в место вызова на самой печатной форме. Могу сбросить примеры

Если делаете в Word, то воспользуйтесь макросами и за 5 минут решите вашу проблему

https://academy.terrasoft.ua/documents/technic-sdk/7-13/kak-sozdat-makr…

Литвинко Павел,

Интересное решение, но текущих примеров недостаточно. Я правильно понимаю, что в моем случае я буду вытаскивать в печатную форму ID обращения (к которому подключены 2 детали), а потом искать в макросе эту самую сумму путем перебора?

Быстров Сергей пишет:

Да, верно, вы передаете в макрос параметр по которому хотите найти ваши значения. Делаете 2 выборки, одну из таблицы А и вторую из таблицы Б, суммируете их между собой, а затем result = А + Б;

return result; = это значение вернется в место вызова на самой печатной форме. Могу сбросить примеры

namespace Terrasoft.Configuration
{
    using System;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Web;
    using System.ServiceModel.Activation;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Web;
    using Terrasoft.Common;
    using Terrasoft.Core;
    using Terrasoft.Core.DB;
    using Terrasoft.Core.Entities;
    using Terrasoft.Core.Packages;
    using Terrasoft.Core.Factories;
 
    [ExpressionConverterAttribute("Название макроса по которому будете вызывать его из печатной формы")]
    class CurrentDateConveter : IExpressionConverter
    {
        private UserConnection _userConnection;
 
        public string Evaluate(object value, string arguments = "")
        {
            try
            {
                _userConnection = (UserConnection)HttpContext.Current.Session["UserConnection"];
                Guid paramId = new Guid(value.ToString());
                return GetAmount(_userConnection, paramId);
            }
            catch (Exception err)
            {
                return err.Message;
            }
        }
        private string GetAmount(UserConnection userConn, Guid value)
        {
            string result = "";
            EntityCollection collection1 = GetEntity(userConn, "Таблица", "Колонка для фильтра", value.ToString());
            EntityCollection collection2 = GetEntity(userConn, "Таблица2", "Колонка для фильтра", value.ToString());
            foreach (var entity in collection1)
            {
               // var recCol1 = Посчитали сумму
            }
            foreach (var entity in collection2)
            {
                // var recCol2 = Посчитали сумму
            }
            result = recCol1 + recCol2;
            return result;
        }
        private EntityCollection GetEntity(UserConnection userConn, string schemaName, string filterColumnName, string filterValue)
        {
            var esq = new EntitySchemaQuery(userConn.EntitySchemaManager, schemaName);
            esq.AddAllSchemaColumns();
            esq.Filters.Add(esq.CreateFilterWithParameters(
                FilterComparisonType.Equal,
                filterColumnName,
                filterValue));
            var entityCollection = esq.GetEntityCollection(userConn);
            return entityCollection;
        }
    }
}

 

Литвинко Павел,

Неловкий вопрос - result  в данном случае строка, а у меня же это сумма. Или я чего-то не понимаю? Да и сумма у меня дробная. 

В итоге получилось вот так:

 

private string GetAmount(UserConnection userConn, Guid value)
{
	EntityCollection collection1 = GetEntity(userConn, "UsrPrice", "UsrWorkObr", value.ToString());
	EntityCollection collection2 = GetEntity(userConn, "UsrMaterialCost", "UsrCase", value.ToString());
	float recCol1 = 0;
	float recCol2 = 0;
	foreach (var entity in collection1)
	{
		recCol1 = float.Parse(entity.GetTypedColumnValue<string>("UsrCostSummVAT")) + recCol1;
	}
	foreach (var entity in collection2)
	{
		recCol2 = float.Parse(entity.GetTypedColumnValue<string>("UsrCostSumm")) + recCol2;
	}
	return (recCol1 + recCol2).ToString("R");
}

 

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

Изображение удалено.

 

О компании

«Мультиплекс Холдинг» — крупнейшая сеть кинотеатров Украины, которая объединяет 22 современных заведения в крупных городах Украины. 121 зал собирает наибольшую зрительскую аудиторию в стране — каждый день проводится 700 сеансов и продается 20000 билетов.

 

Предпосылки внедрения bpmonline

До использования bpm’online обработка обращений была реализована только для внутренних клиентов (сотрудников Multiplex) и осуществлялась в программном продукте Jira, функциональных возможностей которого не хватало для решения всех задач. Обращения от внешних клиентов не обрабатывались с использованием автоматизированных систем.

 

Задачей внедрения bpm'online было внедрить эффективный инструмент обработки обращений как от внутренних клиентов, так и от внешних клиентов. Также компания уже пользовалась продуктом bpmonline marketing, в связи с чем перед командой внедрения стояла глобальная задача построить единую ИТ-экосистему для управления бизнес-процессами.

Выполненные настройки

В ходе реализации проекта в раздел [Сервисы] было добавлены такие отраслевые сервисы как: возврат билетов, демонстрация киноконтента, загрузка кинотеатра, и т.д.

В качестве основного процесса обработки обращений пользователей был взят базовый эталонный процесс.

Для сопровождения основного процесса обработки обращений был создан ряд вспомогательных бизнес-процессов:

  1. Автоматическое назначения ответственного по обращению исходя из заполненного сервиса. Логика процесса заключается в том, чтобы после заполнения сервиса в обращении в справочнике «Сервисный инженер» происходит фильтрация согласно указанного сервиса, если сервис найден, то в карточку обращения подтягивается ответственный. Если нет, то по обращению ответственным указывается первая линия.
  2. Возврат билетов. Логика этого процесса выполняется при создании обращения с контактом «Возврат билетов». В таких условиях происходит автоматическое заполнение соответствующие контакту сервис и ответственный.

Изображение удалено.

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

Изображение удалено.

В процессах обработки обращений в bpm’online заложена логика оценивания уровня удовлетворенности клиента после решения его обращения.  Этот инструмент активно используется сотрудниками для получения обратной связи.

Результат

Внедрение bpm’online позволило решить задачу обработки обращений от внешних клиентов, которая ранее не была охвачена. Кроме этого удалось улучшить процесс обработки обращений от внутренних пользователей. Работа в единой ИТ-экосистеме позволила сократить и упростить коммуникации между сотрудниками смежных подразделений внутри компании. Сбор и анализ удовлетворенности клиентов услугами компании позволяет развиваться еще быстрее и сохранять лидирующие позиции в отрасли.

Нравится

Поделиться

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

Добрый день, собственно вопрос: можно ли из системы открыть модальное окно с выбором из объекта, который представляет собой view?



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

Простой способ 

var config = {
  multiSelect: false,
  entitySchemaName: "UsrContact"
};
this.openLookup(config, this.lookupCallback, this);

приводит к ошибке "Cannot read property 'caption' of undefined"

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

Нравится

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

Вы наложили вьюху на объект ( подробнее обсуждалось тут ) Проверьте что у  вас выставлено отображаемое поле и опубликуйте схему

Вы наложили вьюху на объект ( подробнее обсуждалось тут ) Проверьте что у  вас выставлено отображаемое поле и опубликуйте схему

Григорий Чех,

Действительно забыл указать "отображаемое значение"

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

Здравствуйте!

Появилась необходимость создать несколько почтовых ящиков, из входящих писем которых будет автоматически создаваться обращение. Есть ли возможность реализовать это стандартными опциями?

Спасибо!

Нравится

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

Если не путаю Список почтовых ящиков службы поддержки формируется в справочнике “Список почтовых ящиков для регистрации обращений”.

Подробнее тут

Опишите более подробно что вы хотите, не совсем понятно что значит 

оздать несколько почтовых ящиков, из входящих писем

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

Если не путаю Список почтовых ящиков службы поддержки формируется в справочнике “Список почтовых ящиков для регистрации обращений”.

Подробнее тут

Греков Дмитрий Александрович пишет:

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

Обращения будут создаваться стандартными средствами, добавив ящик в справочник.

Мы реализовывали подобный проект, там попутно решали несколько задач на уровне разработки и кастомных бизнес-процессов:



1) Определение SLA, исходя из ящика, на который получено письмо

2) Распределение прав на обращения и активности, исходя из отдела, который решает данное обращение

3) Различные форматы уведомлений пользователям, исходя из отдела, который решает данное обращение

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

Где можно изменить стандартную страницу для заявок портала по умолчанию?

Ситуация: базовая страница заявок портала самообслуживания была использована для специфического БП, добавлено множество новых полей. Зачем так сделали я и сам не знаю, исполнял кастомизатор. Теперь использовать ее для обычных заявок портала не получится.

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

Нравится

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

Стандартно страницы «Базовая страница заявок портала самообслуживания» в системе нет. Видимо, у Вас она была переименована или полностью самодельная.

Страница, которая будет отображаться при входе пользователя на портал задаётся в системной настройке SSPMainPage. По умолчанию это раздел «Главная страница».

А другие разделы на портале задаются как обычные разделы рабочего места «Портал» в мастере разделов.

Информация о портале с точки зрения разработки есть тут.

Добавить комментарий

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

Спасибо за ответ.

Я имел ввиду стандартную страницу обращения, которая открывается при нажатии "Создать" обращение на главной странице портала

По кнопке открывается страница PortalCasePage.

А как настраивать главную страницу портала и её блоки, описано тут. В дизайнере системы есть специальное действие.

Блок со списком обращений задан в схеме UserCasesListModule. 

Видимо, в нём потребуется переделать функцию getAddCardUrl, чтобы открывало вместо PortalCasePage нужную Вам карточку.

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

Добрый день.

Подскажите, правила в коде кастомных модулей применяются по порядку как и везде?

То есть, более специфические правила я напишу в начале модуля, сначала применятся они, потом более общие?

Например, мне нужно отображать некоторые колонки если значение колонки 1 = 1.

И отображать другие колонки, если значения колонки 1 = 1 И колонки 2 = 2.

Сопутствующий вопрос: перечисление условий нужно делать в блоке conditionalColumns?

 

Нравится

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

Константин, здравствуйте!

Вам необходимо реализовать одно пользовательское бизнес-правило с учетом всех условий (https://academy.terrasoft.ru/documents/technic-sdkmob/7-12/polzovatelsk…).

На порядок влияет:

- иерархия пакетов;

- в каком порядке было добавлено бизнес-правило в схему (читается сверху-вниз);

- positions (https://academy.terrasoft.ru/documents/technic-sdkmob/7-12/biznes-pravi…).

 

Также в бизнес-правилась срабатывает способ организации FIFO (https://ru.wikipedia.org/wiki/FIFO).

Что такое "conditionalColumns" описано на Академии - https://academy.terrasoft.ru/documents/technic-sdkmob/7-12/biznes-pravi…

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

Добрый день!



Решаем следующую задачу:

Имеется таблица контактов с полями (6 штук). Необходимо по каждому контакту запустить БП со всеми значениями. Пока посмотрели в пример на академии и ничего не выходит. Идея простая - идти по строчкам таблицы и запускать БП со значениями полей. Но как это реализовать абсолютно непонятно. Как "превратить" классический select в что-то понятное?

Нравится

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

Быстров Сергей,

Не все так строго. Можно создать пустой объект без наследований, добавить UsrId (указать его в качестве Индентификатора в св-вах объекта, во вью генерить через newId), накидать других полей через Usr как и во вью. Ну и проставить галку "Представление в БД".

В таком случае мало мусорных полей и из C# работать с объектом можно. Но. Такие объекты нельзя использовать в аналитике. Т.к. там в каком-то генераторе зашито(!!!), что у объектов первичный ключ всегда называется "Id". Такие пироги.

"Как "превратить" классический select в что-то понятное?" - Что, простите?))

Попробуйте так в скрипттаске:

var connection = Get<UserConnection>("UserConnection");
var esq = new EntitySchemaQuery(connection.EntitySchemaManager, "Contact");
esq.AddAllSchemaColumns();
//esq.Filters.Add(esq.CreateFilterWithParameters(FilterComparisonType.Equal, "Name", somename));
EntityCollection entities = esq.GetEntityCollection(connection);
foreach (Entity contact in entities) {
    connection.ProcessEngine.ProcessExecutor.Execute("MyCoolProcess",
    new Dictionary<string, string> {
 
        { "ContactId", contact.PrimaryColumnValue.ToString() },
        { ".....", .... }
 
    });
}

 

Варфоломеев Данила,

Забыл сказать, объекта у меня нет - обращаюсь к view таблице. Поэтому и использовал селект - он работах хорошо.

Быстров Сергей,

Сделайте объект под view. Все лучше, чем dbexecutor-ами ковыряться)

Так создайте объект и привяжите к view (в свойствах есть галочка).

А если хотите именно Select, там тоже можно цикл и читать несколько колонок:

string schemaName = string.Empty;
Guid objectId = Guid.Empty;
Guid groupId = Guid.Empty;
var selectObjectSchemaName = new Select(userConnection)
	.Column("VwSysSchemaInfo", "Name")
	.Column("MonitoringIndicators", "Object")
	.Column("MonitoringIndicators", "MonitoringGroup")
	.From("MonitoringIndicators")
	.LeftOuterJoin("VwSysSchemaInfo").On("VwSysSchemaInfo","SysSchemaId").IsEqual("MonitoringIndicators", "Object")
	.Where("VwSysSchemaInfo", "SysSolutionId").IsEqual(Column.Parameter(userConnection.Solution.Id))
	.And("MonitoringIndicators","Id").IsEqual(Column.Parameter(Id)) 
as Select;
using (var dbExecutor = userConnection.EnsureDBConnection()) {
	using (IDataReader dr = selectObjectSchemaName.ExecuteReader(dbExecutor)) {
		while (dr.Read()) {
			schemaName = dr[0] == DBNull.Value ? string.Empty : dr.GetString(0);
			objectId = dr[1] == DBNull.Value ? Guid.Empty : UserConnection.DBTypeConverter.DBValueToGuid(dr[1]);
			groupId = dr[2] == DBNull.Value ? Guid.Empty : UserConnection.DBTypeConverter.DBValueToGuid(dr[2]);
		}
	}

 

Вариант с объектом мне нравится, но есть два нюанса:

- таблица view и поля должны быть названы с префиксом Usr (а то ругается)?

- делается наследование от базового объекта, добавляются все колонки из таблицы и ставится галочка "представление в базе". Все так?

Да. Смотрите, как сделаны существующие схемы в конфигурации, начинающиеся на «Vw».

Быстров Сергей,

Не все так строго. Можно создать пустой объект без наследований, добавить UsrId (указать его в качестве Индентификатора в св-вах объекта, во вью генерить через newId), накидать других полей через Usr как и во вью. Ну и проставить галку "Представление в БД".

В таком случае мало мусорных полей и из C# работать с объектом можно. Но. Такие объекты нельзя использовать в аналитике. Т.к. там в каком-то генераторе зашито(!!!), что у объектов первичный ключ всегда называется "Id". Такие пироги.

Варфоломеев Данила,

А у меня почему-то не получается выбрать свой UsrID. Просто ничего не отображается. Поэтому и подумал что только наследование можно использовать.

Быстров Сергей,

Можно пример кода, как вы пытаетесь UsrId получить?

Варфоломеев Данила,

С этим разобрался. Немного коряво создал объект. Пересоздал его и все заработало. Теперь осталось запустить процессы по каждой строке. И тут ошибки уже пошли в самих процессах, где я думаю уже разберусь что к чему. Так что - большое спасибо за помощь :3

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

Добрый день!



Имеется простая задача - запускать БП с разными параметрами из другого БП. Дело в том, что в БП получаем выборку из базы (контакты с соответствующими критериями), и необходимо запустить кастомный БП с рядом значений на основе этой выборки. 



Может у кого есть понятный пример или такое реализовать проблемно?

Нравится

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

Добрый день,

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

 

UserConnection.ProcessEngine.ProcessExecutor.Execute("SxRecalcPotentialInOppNeed", 
new Dictionary<string, string> {
 
	{ "OpportunityId", opportunityId.ToString() }
 
});

 

Добрый день,

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

 

UserConnection.ProcessEngine.ProcessExecutor.Execute("SxRecalcPotentialInOppNeed", 
new Dictionary<string, string> {
 
	{ "OpportunityId", opportunityId.ToString() }
 
});

 

UserConnection.ProcessEngine.ProcessExecutor.Execute("SxRecalcPotentialInOppNeed", new Dictionary<string, string> {   { "OpportunityId", opportunityId.ToString() }   });

Пащенко Александр Сергеевич,

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

{ "OpportunityId", opportunityId.ToString() }

а сам процесс в примере называется "SxRecalcPotentialInOppNeed"?

Это один параметр и его значение. Второй был бы следующим элементом внутри этого Dictionary, через запятую.

Быстров Сергей,

Все верно, как и замечания Александра. Также проверьте, чтобы в using были подключены нужные библиотеки.

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