Вопрос

Каким образом настраиваться профайл вертикального реестра?

Создал новый раздел, а DataGridVerticalProfile туда не прогружается, понятно что его надо настроить, но где и как?

Вот этот:

 

У меня такой же вопрос

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

На странице нужно выбрать "Вид" -> "Настройка списка"

 

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

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Здравствуйте! Пытаюсь воспользоваться плагином, для настройки отчета.
Но при коннекте и выборе нужной печатной формы, в соответствующем окне, дальше ничего не происходит, а ведь должно по идее открыть новое окно, в котором были бы поля настроенные для данной печатной формы. Может кто-нибудь, подскажет в чем причина?

У меня такой же вопрос

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

Александр, здравствуйте!

Данная проблема в работе с печатными формами может возникать из-за дополнения Marketplace MS Word printables setup wizard (наблюдается в версиях приложения 7.11.0+). Мы передали разработчику дополнения информацию для внесения правок в работу данного дополнения. На данный момент рекомендуем удалить данное дополнение, чтоб иметь возможность работать с печатными формами в MS Word.

Если у Вас отсутствует данное дополнение, напишите нам на support@terrasoft.ru, опишите, пожалуйста, проблему и предоставьте временный доступ к Вашему сайту для более детального анализа.

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

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

Подскажите пожалуйста как это поправить.

У меня такой же вопрос

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

А она на самом деле есть, видна в конфигураторе (карточка редактирования)? Если есть, надо смотреть таблицу SysModuleEdit, прописана ли нужная страница редактирования (карточка) для объекта.

Алексей-Карягин,

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

Саломатников Алексей Сергеевич,

Тогда надо логи смотреть и stack-trace. Тут лучше в ТП обратиться, т.к. покопаться серьезно придется.

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Здравствуйте! Пытался обновить верcию bpm'online bank customer jorney, с 7.11.0 до 7.11.1 (Oracle), после обновления попытался скомпилировать приложение, и в результате возник вот такой вот перечень ошибок, в результате чего могли возникнуть эти ошибки?

У меня такой же вопрос

7 комментариев

Александр, здравствуйте!

Пришлите, пожалуйста, логи приложения за время обновления, по умолчанию хранятся по пути:C:\Windows\Temp\BPMonline\Site_Х.

Так же хочу уточнить, не было ли ошибок, при компиляции до обновления ? 

Maksym Naumovets,

До обновления ошибок не было, вот log 

https://yadi.sk/i/wpnUGfK23TKEiq

 

Александр, судя по логам, у вас не до конца отработал сценарий обновления.

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

Попробуйте повторно выполнить обновление.

Maksym Naumovets,

"Utility finished working." разве не говорит о том, что утилита завершила свою работу?

.

Александр, говорит. Но не говорит что все было выполнено успешно.

Произошла ошибка и утилита завершила работу.

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

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

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Всем доброго времени суток!

Нужна помощь с написанием запроса след.типа для DevExpress:

"SELECT
EName,
SUM(PTotal),
SUM(Pacq),
ROUND(100*(SUM(Pacq)/NULLIF(SUM(PTotal),0)),2) AS "Pprcnt",
FROM "VwAsd"
WHERE CreatedOn BETWEEN '...' AND '...'
GROUP BY EName"

1) Пробовал использовать класс Select:

Select aSelect = new Select(userConnection)
.Column("EName")
.Column(Func.Sum("PTotal"))
.Column(Func.Sum("Pacq"))
????????????????.As("Pprcnt")
.From("VwAsd")
.Where("CreatedOn").IsGreaterOrEqual(new QueryParameter(startDate))
.And("CreatedOn").IsLessOrEqual(new QueryParameter(endDate))
.GroupBy("EName")
as Select;

Не совсем понятно как можно вычислить соотношение для "Pprcnt", может через CustomFunction() класса Func в библиотеке Terrasoft.Core.DB? Но, не смог разобраться как.

2) Пробовал выполнить запрос через CustomQuery:

string sqlText = @"SELECT
EName,
SUM(PTotal),
SUM(Pacq),
ROUND(100*(SUM(Pacq)/NULLIF(SUM(PTotal),0)),2) AS "Pprcnt",
FROM "VwAsd"
WHERE CreatedOn BETWEEN " + startDate + "AND" + endDate + "
GROUP BY EName";

var report = ((Report)sender);
var userConnection = report.UserConnection;
System.Data.DataSet ds = report.CreateDataSet();
var resultQuery = new CustomQuery(userConnection, sqlText);
using (DBExecutor dbExecutor = userConnection.EnsureDBConnection()) {
using(var dr = resultQuery.ExecuteReader(dbExecutor)) {
ds.Tables["VwAsd"].Load(dr);
}
}
report.DataSource = ds;

Тут не совсем ясно как resultQuery мапится в столбцы отчета.

3) Использование EntitySchemaQuery. Тут я тоже не совсем понимаю как выполнять вычисление процентного соотношения.

У меня такой же вопрос

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

Добрый день

Лучшим вариантом будет создание View в БД и использовать ее как обычный объект

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Пытаюсь построить отчет в DevExpress. Вывожу данные за заданный период, сгруппировав их в самом DevExpress по ФИО сотрудника. К каждой записи сотрудника необходимо выводить 3 поля: сумму Total, сумму Asd и процентное соотношение Asd к Total. С выводом сумм все понятно: в настройках ячейки указал «Группа, Сумма». Проблемы с процентным соотношением. Для вычисления его добавил вычисляемое поле, в выражении которого указал Asd/Total*100. Расчет выполняется до группировки. Прошу подсказать что можно сделать. Данные тянутся из вьюшки через ESQ. P.S: пробовал использовать прямой селект во вьюшку через CustomQuery, но непонятно как вывод этого селекта будет мапится к ячейкам таблицы в отчете.

У меня такой же вопрос

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

Мы так налоги вычисляем. Сделали View, сделали объект на базе этого View, привязали как стандартный объект к отчету - берем оттуда все данные.

Но это годится, если заранее знаем параметры отчета

Если делать на уровне отчета, то скорее всего поможет Custom Summary - https://documentation.devexpress.com/XtraReports/4817/Creating-Reports-…

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Здравствуйте! При написании кода в "сценарии" бизнес-процесса, и попытке его отладить, увидел следующую "особенность". Вот код запроса данных:
 

var esqResult = new EntitySchemaQuery(userConnection.EntitySchemaManager, "City");
            esqResult.AddColumn("Name");
            esqResult.AddColumn("CreatedOn");
            esqResult.AddColumn("ModifiedOn"); 
            esqResult.AddColumn("Country");
            esqResult.AddColumn("Country.Code");
            esqResult.AddColumn("TimeZone");
            esqResult.AddColumn("TimeZone.Code");
            var entities = esqResult.GetEntityCollection(userConnection);

Но вот запрос, категорически не хочет видеть мое поле "Code" поля "Country", вот типа того.

У меня такой же вопрос

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

Приветствую, Александр!

Уточните, пожалуйста, в какой именно строке кода сценария Вы остановились, для просмотра информации об обьекте/переменной?
А также, какой обьект Вы исследовали?
Спасибо.

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Добрый день. Подскажите, пожалуйста, как разрешить следующую проблему.

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

 

PS. Может, я чего-то не знаю и возможность удаления без удаления связанных записей можно отключить?

У меня такой же вопрос

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

Здравствуйте, Александр!

Запуск механизма массового удаления осуществляется при помощи GridUtilitiesService. метод DeleteRecordsAsync который принимает массив идентификаторов записей, название сущности и словарь, который отвечает за конфигурирование параметров, так же метод подготавливает параметры и запускает MultiDeleteExecutor. Особенностью метода является управляемый запуск операции удаления при помощи системной настройки с кодом DefaultNumberItemsReturned, которая отвечает за запуск операции в фоне или в общем потоке.

Вы можете полностью заместить и изменить схему GridUtilitiesService для решения вашей бизнес цели.

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Коллеги, требуется помощь!

Программирую регулярное выполнение бизнес-процесса через планировщик таким образом:
первый элемент бизнес-процесса проверяет наличие триггера, и в случае отсутствия - создаёт триггер в БД. Т.е. при первом ручном запуске БП - он формирует себе "расписание" последующих запусков.

Пробовал использовать несколько способов, но все они не работают, в Библиотеке БП процесс присутствует, триггеры в БД заводятся. Но последующих запусков не происходит.

В чём может быть причина?

Доп. инфоормация:

Версия BPM 7.7 Bank Customer Journey. БД Oracle.
Триггеры находятся в состоянии WAITNG.
Пользователь для выполнения системных операций (SystemUser) Supervisor.
IIS перезапускал. Application Pool находится в режиме Универсальный.

Способы, которые я пробовал использовать:

CronTriggerImpl:

var cronExp = "0 0/5 * ? * * *";
 
if (!AppScheduler.DoesJobExist(jobName, jobGroup))
{
	var job = AppScheduler.CreateProcessJob(jobName, jobGroup, procName, userConnection.Workspace.Name, userConnection.CurrentUser.Name);
	var trigger = new CronTriggerImpl(jobName + "Trigger", jobGroup, cronExp);
	AppScheduler.Instance.ScheduleJob(job, trigger);
}

 

CronTriggerImpl:

var cronExp = "0 0/5 * ? * * *";
 
if (!AppScheduler.DoesJobExist(jobName, jobGroup))
{
	var job = AppScheduler.CreateProcessJob(jobName, jobGroup, procName, userConnection.Workspace.Name, userConnection.CurrentUser.Name);
	var trigger = new CronTriggerImpl(jobName + "Trigger", jobGroup, cronExp);
 
	trigger.MisfireInstruction = MisfireInstruction.CronTrigger.FireOnceNow;
	trigger.TimeZone = TimeZoneInfo.Utc;
 
	AppScheduler.Instance.ScheduleJob(job, trigger);
}

 

ScheduleMinutelyProcessJob:

int periodInMinutes = 3;
 
if (!AppScheduler.DoesJobExist(jobName, jobGroup))
{
	AppScheduler.ScheduleMinutelyProcessJob(jobName, jobGroup, procName,
		userConnection.Workspace.Name, userConnection.CurrentUser.Name, periodInMinutes);
}

 

SimpleTriggerImpl:

int startOffset = 4;
 
if (AppScheduler.DoesJobExist(jobName, jobGroup))
{
   AppScheduler.RemoveJob(jobName, jobGroup);
}
 
var job = AppScheduler.CreateProcessJob(jobName, jobGroup, procName, userConnection.Workspace.Name, userConnection.CurrentUser.Name);
var trigger = new SimpleTriggerImpl(jobName + "Trigger", jobGroup, DateTime.UtcNow.AddMinutes(startOffset));
AppScheduler.Instance.ScheduleJob(job, trigger);

 

SimpleTriggerImpl от системного пользователя:

int startOffset = 4;
 
if (AppScheduler.DoesJobExist(jobName, jobGroup))
{
   AppScheduler.RemoveJob(jobName, jobGroup);
}
 
var job = AppScheduler.CreateProcessJob(jobName, jobGroup, procName, userConnection.Workspace.Name, userConnection.CurrentUser.Name, null, true);
var trigger = new SimpleTriggerImpl(jobName + "Trigger", jobGroup, DateTime.UtcNow.AddMinutes(startOffset));
AppScheduler.Instance.ScheduleJob(job, trigger);

 

При использовании всех вариантов - триггеры создаются в БД, но повторно не срабатывают. Куда копать?

У меня такой же вопрос

6 комментариев

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

Сайт пингуется. Так же есть уже заведённые (не мной, до меня) Job'ы c триггерами, которые регулярно запускаются с периодичностью в 2 минуты. Мои же триггеры не запускаются

Может, оно попыталось запуститься и свалилось. Иногда можно понять, глядя в базе на записи об этих триггерах в таблицах с названиями, начинающимися с QRTZ. Может, там найдутся подробности о произошедшем или видно разницу с нормально работающим.

Для контроля запускал такой скрипт (БД Oracle) -

SELECT sched_name,
       trigger_group,
       job_name,
       job_group,
       trigger_state,
         TO_DATE ('03-jan-0001', 'dd-mon-yyyy')
       + next_fire_time / (10000000 * 60 * 60 * 24)
           AS next_fire_time,
       TO_DATE ('03-jan-0001', 'dd-mon-yyyy')
       + prev_fire_time / (10000000 * 60 * 60 * 24)
           AS prev_fire_time
  FROM qrtz_triggers

он показывает что у моих триггеров prev_fire_time - пустое, а next_fire_time, к примеру, "28.11.2017 8:41:49".  У работающих же триггеров - оба значения заполнены, и примерно например, prev_fire_time: "28.11.2017 8:40:48"

 

 

Проверьте что бы название (не заголовок) целевых процессов совпадал со значением в переменной procName (при создании джоба).

Если не поможет напишите в тех. поддержку.

Олег, спасибо)

Вроде перепроверил заголовки. Уже !!! параллельно написал в поддержку платную. Давайте вместе найдём в чём затык!

Войдите или зарегистрируйтесь, чтобы комментировать
Вопрос

Добрый день.

Интересует следующее.

Часто возникает необходимость реализовывать автовычисление полей на стороне сервера во внутреннем процессе объекта, как правило это связано с безопасностью и требованием запретить пользователю напрямую изменять значения каких-то полей на странице (запрещаем на уровне администрирования прав доступа).

При этом после сохранения сущности необходимо каким-то образом актуализировать наполнение полей на открытой ранее странице редактирования. Вот тут начинаются танцы с бубном в виде прикручивания к методу onSaved всяких loadEntity или onDiscardChangesClick. как правило, сразу корректно эти варианты не работают в 90% случаев. Подскажите, пожалуйста, как наиболее корректно реализовать обновление полей?

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

PS. Использовать просто this.set... в колбэке EntitySchemaQuery не предлагать.

У меня такой же вопрос

8 комментариев

Добрый день! Если речь идет о загрузке актуальных данных перед сохранением, то я бы посоветовал использовать вот такой метод:

/**
 * Обновляет поля карточки для сущности страницы.
 * @param {Array} fields Список обновляемых полей.
 * @param {Function} callback Функция обратного вызова.
 * @param {Object} scope Контекст функции обратного вызова.
 */
var reloadCardFromPage = function (fields, callback, scope) {
	var showSaveButton = scope.get("ShowSaveButton");
	var showDiscardButton = scope.get("ShowDiscardButton");
	var showCloseButton = scope.get("ShowCloseButton");
	if (!scope.Ext.isArray(fields) || fields.length === 0) {
		return ;
	}
	var fieldsQuantity = fields.length;
	var selectNewValues = scope.Ext.create("Terrasoft.EntitySchemaQuery", {
		rootSchemaName: scope.entitySchemaName
	});
	for (var i = 0; i < fieldsQuantity; i++) {
		selectNewValues.addColumn(fields[i]);
	}
	var id = scope.get("Id");
	selectNewValues.getEntity(id, function(result) {
		var entity = result.entity;
		if (entity ) {
			fields.forEach(function(element) {
				var newValue = entity.get(element);
				scope.set(element, newValue);
			}, scope);
			scope.set("ShowSaveButton", showSaveButton);
			scope.set("ShowDiscardButton", showDiscardButton);
			scope.set("ShowCloseButton", showCloseButton);
			callback.call(scope);
		}
	}, scope );
};

Т.е. на вход подаем список полей для обновления.

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

Чубко Илья,

Добрый день.

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

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

 

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

 

PPS. Хотелось бы услышать ещё варианты.

Чубко Илья, 
Да и вообще, не совсем понятно, для чего актуализировать перед сохранением. Помимо того, что сохранить после такой актуализации не получится.

Добрый день, 

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

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

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

Пример: https://academy.terrasoft.ru/documents/technic-sdk/7-8/clientmessagebri…

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

Есть соображение, что это должно делаться похожим образом с тем, как это делается при начальной инициализации, но когда страница уже загружена - на ней начинают работать dependencies, бизнес-правила и прочее, что было бы, возможно, лишним в данной ситуации. Кроме того при начальном заполнении и изменении значений на загруженной странице, контролы (например, справочные поля, кнопки), ведут себя по-разному. Поэтому тут надо действовать "тонко". Понятно, что придётся использовать EntitySchemaQuery с this.set в цикле в кол-бэке для получения и присвоения значений, но какие-то системные атрибуты страницы, видимо, придётся приводить к значениям "до onEntityInitialized", чтобы, например, не менялось состояние объекта changedValues. Но, в то же время, надо понимать, что при этом могут пострадать "параллельно" работающие процессы в экземпляре страницы. Тут, наверняка, много подводных камней, поэтому задача представляется довольно комплексной.

Уффф... много текста...

Добрый день.

Для загрузки актуальных данных с сервера у наследников BasePage есть метод this.reloadEntity(). В системе есть примеры его вызова на onSaved.

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

Пример:
* меняем что-то на странице физ-лица (не сохраняем)
* заходим на страницу документа - меняем документ на основной
* сохраняем (при этом в физ. лице заполняется текстовое поле документа данными из сохранённой сущности документа)
* закрываем документ - возвращаемся на страницу физ. лица - вот тут надо обновить текстовое поле документ, и, если это сделать при помощи reloadEntity, потеряем сделанные ранее на странице изменения.

Войдите или зарегистрируйтесь, чтобы комментировать