Добрый день.

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

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

При этом после сохранения сущности необходимо каким-то образом актуализировать наполнение полей на открытой ранее странице редактирования. Вот тут начинаются танцы с бубном в виде прикручивания к методу 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, потеряем сделанные ранее на странице изменения.

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

Итак вопрос вот в чём. 

В поставке Marketing есть некие разделы Email и Мероприятия, в этих раздел есть деталь с аудиторией для рассылки или в Мероприятиях это деталь Контакты, где можно как добавить непосредственно самих контактов (из представленного списка) или же выбрать некую группу контактов.

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

Собственно покопавшись внутри схем этих деталей я заметил что они используют метод миксина SegmentsStatusUtils, который в свою очередь вызывает сервис MandrillService, который вызывает BulkEmailAudienceHelper.

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

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

Кто с таким родом задач связывался и кто может подсказать куда копать всё-таки?

Или есть обходной путь, чтобы не проходить эти круги сами знаете чего.

Нравится

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

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

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

Для статических груп действительно нужно просто получить записи из ContactInFolder(если это контакты).

Для динамических групп:

1. Получить id группы (из ContactInFolder)

2. По id группы получить соответствующий фильтр для этой группы.

3. Преобразовать полученный фильтр в sql-запрос.

4.Завернуть sql-запрос в insert.

5. Выполнить insert в таблицу детали.

Все эти действия можно реализовать как через бизнес процесс так и через сервис или класс.

 

 

Demchenko Olha,

Где можно найти пример преобразование этого фильтра который мы получим по id в обычный фильтр который можно додать в выборку?

Радчук Виталий Владимирович,

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

В схеме FolderHelper содержатся все методы для работы с таблицей ContactFolder.

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

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

Прикрепленные файлы

Нравится

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

Вам в эту тему. первый комментарий. Uid схемы объекта в принципе легко, а вот UId колонки в активности нового кастомного раздела - это надо в метаданных покапаться. Ну ещё если прям совсем красиво, то можно переопределить EntityConnectionLinksResourceUtilities и добавить к новому пункту в связях иконку.

Варфоломеев Данила, Добрый день Данила добавил в таблицу EntityConnection записи

на место ColumnUId подставлял значения uid поля кастомного раздела из метаданных

в SysEntitySchemaUId подставлял значения как uid нового раздела, так и uid раздела активности  при этом в связях не добавилось новое значение, не подскажешь что может быть еще

Варфоломеев Данила,спасибо, разобрался оказывается не тот uid схемы указывал

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

о возможностях его работы на BPM где почитать? поиском не ищется по сайту

Нравится

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

Вам сюда

ATTENTION!

Террасофт ложил болт на гео-штуки (ну кроме планирования) в field sales в версиях 7.8, 7.9. Только с 7.10 появилась функция отслеживания на карте (поправьте кто-нибудь, если неправ)

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

Добрый день, Данила!

Что касается предыдущих версий, то в и в них GPS координаты хранились в базе данных в таблице "Результат выполнения чек-ина/аута" - [CheckInOutResult]. И на основании этого объекта можно создать, например, справочник и там выводить колонки с gps-координатами. Таким образом и в указанных версиях можно было отслеживать координаты.

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

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

Вопрос по календарю, он отображается в формате 7 дней и планирование активностей начинается с 00-00, я не слышал чтобы пожарные или девушки с низкой социальной отвественностью использовали CRM, можно календарь заточить под график обычных людей?  чтобы активности начинались с 08-00 и не приходилось ночные часы перелистывать? а выходные (сб, вс) по умолчанию скрыть ,на планшете и так места мало 

Нравится

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

Здравствуйте, Шамиль!

В версии 7.10 есть возможность регулировать настройку времени отображения календаря с помощью системных настроек. Рекомендуем Вам выполнить обновления Вашей системы до указанной версии.

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

Добрый день!

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

var requestUrl = "?vm=SchemaDesigner#processLog/" + process.value;
this.sandbox.publish("PushHistoryState", {
                                hash: requestUrl,
                            });

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

http://bpmonline.com/0/Nui/ViewModule.aspx#?vm=SchemaDesigner#processLog/933722a3-f4ca-4e4d-9bbb-2160c57bebee

Но ссылка не совсем корректна, так как this.sandbox.publish("PushHistoryState") возвращает строку с символом # на конце:

http://bpmonline.com/0/Nui/ViewModule.aspx#

Как можно исключить этот символ? Чтобы ссылка выглядела так:

http://bpmonline.com/0/Nui/ViewModule.aspx?vm=SchemaDesigner#processLog/933722a3-f4ca-4e4d-9bbb-2160c57bebee

 

 

Нравится

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

Добрый день, Антон.

Вам стоит смотреть в сторону использования  метода showProcessDiagram модуля ProcessModuleUtilities, который используется в схеме SysProcessLogSectionV2. В своей реализации метод использует window.open.

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

Добрый день, столкнулся с проблемой установки пакета через workspaceconsole, при запуске скрипта появляется ошибка, подскажите в чем может быть причина (скриншет прилогается) версия  bpm 7.8Изображение удалено.

Нравится

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

Дмитрий, здравствуйте.

Такая ошибка может возникать в случае, когда некорректно заполнены параметры WorkspaceConsole. Необходимо проверить параметры подключения в секциях <db> и <connectionString>  в файле \Terrasoft.WebApp\DesktopBin\WorkspaceConsole\Terrasoft.Tools.WorkspaceConsole.exe.config. Необходимые параметры подключения можно найти в файле ConnectionStrings.config

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

Коллеги, всем добрый день!

Может кто-нибудь сталкивался с требованием заказчика сделать доступ к Redis закрытым, т.е. установить пароль на доступ.

На стороне redis, насколько я понимаю, нужно включить в redis.conf настройку requirepass, а как реализовать это на стороне нашего приложения? В ТП ответили так: "поскольку это стороннее ПО, с данным вопросом обратитесь к поставщику продукта redis. Мы предоставляем поддержку продукта bpm'online."

Нравится

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

попробуйте по аналогии с ConnectionString из StackExchange

<add name="redis" connectionString="name=user;password=123;host=localhost;db=1;port=6379;maxReadPoolSize=25;maxWritePoolSize=25" />

в любом случае дайте знать получилось или нет :)

Севостьянов Илья Сергеевич,

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

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

<add name="redis" connectionString="host=password@127.0.0.1;db=1;port=6379;maxReadPoolSize=25;maxWritePoolSize=25" />

ответ был найден здесь: https://stackoverflow.com/questions/8862552/authenticated-servicestack-…

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

Добрый день!

Мне необходимо добавить Email контакта в карточку обращения:

Для этого я создал замещающий  клиентский модуль схемы CasePage:

define("CasePage", ["CasePageResources", "BusinessRuleModule", "ServiceHelper", "TimezoneUtils",
	"ConfigurationConstants", "ServiceDeskConstants", "EvrazConstants", "CaseSectionActionsDashboard"],
		function(resources, BusinessRuleModule, ServiceHelper, TimezoneUtils, ConfigurationConstants,
			ServiceDeskConstants, EvrazConstants) {
	return {
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"name": "Email",
				"values": {
					"layout": {
						"column": 0,
						"row": 2,
						"colSpan": 24,
						"rowSpan": 1
					},
					"bindTo": "Contact"
				},
				"parentName": "ProfileContainer",
				"propertyName": "items",
				"index": 2
			}
		]/**SCHEMA_DIFF*/,
	};
});

Поле появилось, но оно отображает только ФИО контакта, как мне провалиться глубже и добраться до поля Email? Подскажите пожалуйста

Нравится

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

Вам необходимо:

1) в конфигурационном объекте одноименного атрибута,

пробросить колонку с Email

установить обработчик для контроля изменения значения в поле

 

attributes: {
...
	"Email": {
		lookupListConfig: {
			columns: ["Email"]
		},
		dependencies: [
			{
				columns: ["Email"],
				methodName: "onEmailChange"
			}
		]
	}
...
},



2) В методе загрузки страницы для поля Email заменить его displayValue

methods: {
...
    "onEntityInitialized": function() {
         this.callParent(arguments);
         //инициируем подмену displayValue на значении в поле
         //при первоначальной загрузке страницы
         this.onEmailChange();
    },
    "onEmailChange": function() {
         //получим объект текущего значения в поле
         var contactLookupObject = this.get("Email");
         //Если: значение в поле установлено
         if (!Ext.isEmpty(contactLookupObject)){
              //то: изменим в полученном объекте справочного поля 
              //displayValue на значение "проброшенной" колонки Email
              contactLookupObject.displayValue = currentContactLookupObject.Email;
              //Установим обновленный объект обратно в атрибут
              this.set("Email", contactLookupObject);
         }
    },
 
...
}



 

Севостьянов Илья Сергеевич,

Илья, спасибо что откликнулись!

Хотел уточнить, "в конфигурационном объекте одноименного атрибута, пробросить колонку с Email" - это значит добавить в объект Case поле, которое ссылается на колонку Email справочника Контакт?

Молчанов Антон Сергеевич,

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

см. статью на академии https://academy.terrasoft.ru/documents/technic-sdk/7-10/atributy-svoyst…

по конфигурационному объекту для атрибутов.

в частности описание свойства lookupListConfig и его полей.

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

Севостьянов Илья Сергеевич,

Примерно понял, у меня получилась такая замещающая схема:

define("CasePage", ["CasePageResources", "BusinessRuleModule", "ServiceHelper", "TimezoneUtils",
	"ConfigurationConstants", "ServiceDeskConstants", "EvrazConstants", "CaseSectionActionsDashboard"],
		function(resources, BusinessRuleModule, ServiceHelper, TimezoneUtils, ConfigurationConstants,
			ServiceDeskConstants, EvrazConstants) {
	return {
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		attributes: {
			"Email": {
				lookupListConfig: {
					columns: ["Email"]
				},
				dependencies: [
					{
						columns: ["Email"],
						methodName: "onEmailChange"
					}
				]
			}
		},
		methods: {
			"onEntityInitialized": function() {
				this.callParent(arguments);
				this.onEmailChange();
			},
			"onEmailChange": function() {
			var contactLookupObject = this.get("Email");
			if (!Ext.isEmpty(contactLookupObject)) {
			contactLookupObject.displayValue = currentContactLookupObject.Email;
			this.set("Email", contactLookupObject);
			}
			}
		},
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"name": "Email",
				"values": {
					"layout": {
						"column": 0,
						"row": 2,
						"colSpan": 24,
						"rowSpan": 1
					},
					"bindTo": "Email"
				},
				"parentName": "ProfileContainer",
				"propertyName": "items",
				"index": 2
			}
		]/**SCHEMA_DIFF*/,		
	};
});

При попытке сохранить схему возникает ошибка: 

'currentContactLookupObject.Email' is not defined

Подскажите пожалуйста, как мне приравнять displayValue к значению "проброшенной" колонки Email?

contactLookupObject.displayValue = currentContactLookupObject.Email;
замените на 
contactLookupObject.displayValue = contactLookupObject.Email;

опечаточка в имени объекта, копипаста :)

currentContactLookupObject нигде не объявлятся

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

Заменил строку currentContactLookupObject.Email на contactLookupObject.Email

При попытке зайти на страничку CasePage, консоль браузера выдаёт ошибку:

Uncaught Terrasoft.NullOrEmptyException: Заголовок для подписи "Email" не был найден. 

Подскажите пожалуйста, в чем может быть проблема. Я так понимаю Email содержит пустое значение?

попробуйте в определении вашего поля, явно задать заголовок

diff: /**SCHEMA_DIFF*/[
	{
		"operation": "insert",
		"name": "Email",
		"caption": "Заголовок",
		"values": {
			"layout": {
				"column": 0,
				"row": 2,
				"colSpan": 24,
				"rowSpan": 1
			},
			"bindTo": "Email"
		},
		"parentName": "ProfileContainer",
		"propertyName": "items",
		"index": 2
	}
]/**SCHEMA_DIFF*/,

 

Илья, добрый день! 
Задать заголовок таким образом: 
"caption": "Заголовок" 
у меня не получилось (опять также ошибка: Заголовок для подписи "Email" не был найден)

сделал так:
"caption": {"bindTo": "Resources.Strings.Email"} 

Соответственно добавил "Email" в LocalizableString 

В итоге получился вот такой код:

define("CasePage", ["CasePageResources", "BusinessRuleModule", "ServiceHelper", "TimezoneUtils",
	"ConfigurationConstants", "ServiceDeskConstants", "EvrazConstants", "CaseSectionActionsDashboard"],
		function(resources, BusinessRuleModule, ServiceHelper, TimezoneUtils, ConfigurationConstants,
			ServiceDeskConstants, EvrazConstants) {
	return {
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		attributes: {
			"Email": {
				lookupListConfig: {
					columns: ["Email"]
				},
				dependencies: [
					{
						columns: ["Email"],
						methodName: "onEmailChange"
					}
				]
			}
		},
		methods: {
			"onEntityInitialized": function() {
				this.callParent(arguments);
				this.onEmailChange();
			},
			"onEmailChange": function() {
				var contactLookupObject = this.get("Email");
				if (!Ext.isEmpty(contactLookupObject)) {
					contactLookupObject.displayValue = contactLookupObject.Email;
					this.set("Email", contactLookupObject);
				}
			}
		},
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"name": "Email",
				"values": {
					"layout": {
						"column": 0,
						"row": 2,
						"colSpan": 24,
						"rowSpan": 1
					},
					"bindTo": "Email",
					"labelConfig": {"caption": {"bindTo": "Resources.Strings.Email"}}
				},
				"parentName": "ProfileContainer",
				"propertyName": "items",
				"index": 2
			}
		]/**SCHEMA_DIFF*/		
	};
});

При попытке зайти на страничку CasePage - она не загружается, а консоль браузера выдаёт ошибки:

Может быть дело в том, что в объекте Case нет колонки Email?

Колонку Email мне необходимо получить из объекта Contact.

Но в моём коде нет ссылок на объект Contact
 
Показать все комментарии

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

Нравится

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

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

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