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

Путь в лоб 

attributes: {
			"CreatedOffer": {
				dataValueType: Terrasoft.DataValueType.LOOKUP,
				type: Terrasoft.ViewModelColumnType.CALCULATED_COLUMN,
				caption: "Коммерческое предложение",
				referenceSchemaName: "UsrDocument"
			},

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

Нравится

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

а что есть у Вас "разные типы документов" - 

это разные страницы для одного и того же раздела или это разные объекты в принципе ?

Скорее всего Вам необходимо то что обычно происходит, если для раздела добавлено несколько страниц редактирования, тогда справочные поля ссылающиеся на этот раздел, при открытии окна справочника, кнопка-действие "Добавить" превращается в выпадающее меню, как раз содержащее 2 и более вариантов.

Рискну предположить что такое поведение обуславливается наличием дополнительной записи в SysModuleEdit нескольких записей для одного Entity

 

Разные страницы, конечно. Там где разные объекты, нет вопросов. У меня не кнопка. У меня LOOKUP, который не доступен для редактирования, по сути, ссылка. И ссылка формируется системой неправильно.

Вопрос снят. Если кому интересно, сделал так. Добавил UsrType.

scope.set("CreatedOffer",
{
	value: item.get("Id"),
	displayValue: item.get("UsrNumber"),
	UsrType: {value: "e76200fd-6a52-4b54-b8d2-a2f1454a3a36"}
});

 

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

Есть в БП автогенерируемая страница, за которой идет завершение. 

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

Нравится

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

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

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

нажал закрыть или закрыл окно

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

Но если вы используете элементы "Открыть страницу редактирования" или "Открыть преднастроенную страницу", то в данном случае на пользователя создается Активность, а так же появляется специальный вариант, кнопка "Выполнить позже" (обуславливающая напоминание пользователю).

Как вариант решения кейса - заменить автогенерируемую страницу - преднастроенной.

Зарицкий Олег,

Значит в этой базовой схеме "баг" - ее логика не правильная. Как разработчику ее исправить? Или как построить БП, так чтобы обойти этот момент?

Может, как-то можно поймать сигнал, что БП показал страницу? И по этому сигналу завершить БП?

Владимир Соколов,

можно перед автогенерируемой страницей параллельную пустить ветку с таймером в 1 минуту.

Вне зависимости от того, что пользователь выберет на странице (и выберет ли что-то вообще), процесс будет завершен.

Севостьянов Илья Сергеевич пишет:

Как вариант решения кейса - заменить автогенерируемую страницу - преднастроенной.

Таким образом пользователь получит Активность и сможет выполнить ее позже... 

1) Преднастроеная страница - сразу после открытия, элемент считается выполненным и БП двигается дальше

2) Страница редактирования - присутствует специальная настройка

можно задать условие - всегда выполняющееся.

БП - двинется дальше после любого пользовательского действия с карточкой: "Сохранить" / "Закрыть" / "Отмена"

Как-то так...



У Вас кейс какой: Вам необходимо чтобы пользователь что-то сделал, и только потом БП продолжился, или вне зависимости от этого ?

Демьяник Алексей пишет:

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

А если этот пользователь сейчас не в системе? И когда он появится в системе, то БП уже завершится, и он не увидит страницу 

Демьяник Алексей,Спасибо!

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

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

Однако, в детали просто не получается сделать добавление неактивным. Вот такой код вызывается (проверено в отладчике), но не приводит ни к какому результату. Как правильно?

define("AccountAddressDetailV2", [], function() {
	return {
		entitySchemaName: "AccountAddress",
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[]/**SCHEMA_DIFF*/,
		methods: {
			getEditPages: function() {
				var menuItems = this.callParent(arguments);
				for (var i = 0; i < menuItems.collection.items.length; i++) {
					menuItems.collection.items[i].values.Enabled = false;
//						{bindTo: "AddressNotExists_" + menuItems.collection.items[i].values.Tag};
				}
				return menuItems;
			}
		}
	};
});



 

Нравится

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

деталь AccountAddressDetailV2 вероятнее всего является наследником базовой AccountCommunicationDetail или является отдельной ее имплементацией.

По этому можно предположить, что с ней можно "управиться" аналогичным образом, вам необходимо предусмотреть 2 варианта добавления, через пункт меню детали "Добавить", а так же не забыть о том что в контекстном меню отдельно взятого элемента тип записи можно изменить, т.е. "Домашний адрес" можно запросто сделать "Юридическим".

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

Приведу пример замещающей схемы которая решает кейс:

Ограничить возможность добавления более 1-го пункта с типом "Основной телефон"

define("AccountCommunicationDetail", [], function() {
	return {
		methods: {
			//Далее в коментариях, "элемент детали" это отдельно взятая запись в коллекции детали,
			//т.е. одно отдельное средство связи в детали.
			"init": function() {
				this.callParent(arguments);
				//Подписка на изменение атрибута-флага, который принимает значение в true после полной загрузки данных
				//Установим метод-обработчик см.листинг-метку TAG1
				this.on(
					"change:IsDataLoaded",
					function() {
						if (arguments[1] === true) {
							this.mainPhoneInSubMenuExistsController();
						}
					},
					this
				);
			},
			//метод вызываемый при реализации действия "Добавить" или "Изменить" элемент детали
			"createOrUpdateCommunicationItem": function(typeId, config) {
				this.callParent(arguments);
				this.mainPhoneInSubMenuExistsController();
			},
			//метод вызываемый при реализации действия "Удалить" элемент детали
			"deleteCommunicationItem": function() {
				this.callParent(arguments);
				this.mainPhoneInSubMenuExistsController();
			},
			//метод формирующий список элементов для viewModel контекствного меню отдельного элемента детали
			//вызывается единожды при инициализации детали
			"getTypeMenuItems": function() {
				//получим список сформированный родительским методом
				var typeMenuItems = this.callParent(arguments);
 
				//ДЛЯ КАЖДОГО: элемента меню
				for (var each in typeMenuItems) {
					//ЕСЛИ: заголовок элемента "Основной телефон"
					if (typeMenuItems[each].caption === "Основной телефон") {
						//ТО: Удаляем соответствующий элемент из массива
						typeMenuItems.splice(each, 1);
						//Прерываем перебор массива
						break;
					}
				}
 
				//возвращаем модифицированное меню
				return typeMenuItems;
			},
			//TAG1
			//Метод осуществляющий контроль пункта подменю: "+" -&gt; "Телефон" -&gt; "Основной телефон"
			//Основополагающая логика: пункт не должен быть доступен если запись с типом "Основной телефон"
			//присутствует в детали (в т.ч. состоянии view), и становиться доступным при его удалении (в т.ч. состоянии view)
			"mainPhoneInSubMenuExistsController": function() {
				//Получим коллекцию ранее внесенных в деталь средств связи
				var existsCommunicationsCollection = this.get("Collection"),
					//Получим коллекцию сформированных элементов меню
					MenuItemsCollection = this.get("ToolsMenuItems");
 
				//Определим флаг наличия в ранее внесенных средствах связи, средства с типом "Основной телефон"
				var    mainPhoneNumberExistsFlag = existsCommunicationsCollection.collection.find(function(item) {
						var targetCollection = item.values ? item.values : item.changedValues;
						if (targetCollection.CommunicationType.displayValue === "Основной телефон") {
							return true;
						}
					}, this) ? true : false;
				//Определим объект элемента меню "Телефон"
				var    phoneMenuCollection = MenuItemsCollection.collection.find(function(item) {
						if (item.values.Caption === "Телефон") {
							return true;
						}
					}, this);
 
				if (mainPhoneNumberExistsFlag) {
					//Найдем в его коллекции элементов подменю пункт с заголовком "Основной телефон"
					var mainPhoneMenuItem = phoneMenuCollection.values.Items.collection.find(function(item) {
						if (item.values.Caption === "Основной телефон") {
							return true;
						}
					}, this);
					//попытка выполняется в блоке try...catch т.к. в некоторых случаях вызов
					//метода remove приводит к ошибке исполнения JS которая не влияет на результат
					//выполнения метода относительно коллекции, тем не менее прерывает выполнение здесь.
					//Проявляется при добавлении единственной записи в деталь в новой карточке контрагента
					//через поле в карточке, а не через меню детали
					try {
						//Удалим этот пункт меню из колекции подпунктов viewModel
						phoneMenuCollection.values.Items.collection.remove(mainPhoneMenuItem);
					} catch (e) {}
					//инициируем перестроение viewModel по его измененной модели
					MenuItemsCollection.loadAll();
				} else {
					//Вернем этот пункт в меню
					//выполнив новое построение колекции пунктов меню для viewModel
					this.initToolsMenuItems();
				}
			}
		}
	};
});

далее 

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

Вернусь к вопросу валидации, который я поднимал ранее...

1. Есть раздел "Подписанты" с полями:

    ФиоКонтакта, РазрешеннаяСумма

    

2. Есть раздел "Договора" с полями

    СуммаДоговора

    ФиоПодписанта = Подписанты.ФиоКонтакта

    

Необходимо произвести валидацию полей на схеме раздела "Договора"

    СуммаДоговора < Подписанты.РазрешеннаяСумма 

где Договор.ФиоПодписанта = Подписанты.ФиоКонтакта

По примерам https://academy.terrasoft.ru/documents/technic-sdk/7-10/postroenie-putey-k-kolonkam-otnositelno-kornevoy-shemy и https://academy.terrasoft.ru/documents/technic-sdk/7-10/poluchenie-rezultata-zaprosa  построил свой запрос 

            

methods: {
	// Метод-валидатор значения 
	SummValidator: function() {
		var message = "";
		// Создаем экземпляр класса Terrasoft.EntitySchemaQuery с корневой схемой [Contact].
		var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
			rootSchemaName: "UsrContract1Page"
		});
		// Добавляем колонку 
		esq.addColumn("UsrLimitAmount2.[UsrSignatories1Page:Id:UsrSignatoriesId].UsrLimitAmount",
			"UsrSignatoriesLimitAmount");
		// Получаем одну запись из выборки по [Id] объекта карточки и отображаем ее
		// в информационном окне.
		esq.getEntityCollection(function(result) {
			if (!result.success) {
				// обработка/логирование ошибки, например
				this.showInformationDialog("Ошибка запроса данных");
				return;
			}
			result.collection.each(function(item) {
				message += item.get("UsrSignatoriesLimitAmount");
			});
			this.showInformationDialog(message);
		}, this);
	},			
	// Переопределение базовой функции, инициализирующей пользовательские валидаторы.
	setValidationConfig: function() {
		// Вызывает инициализацию валидаторов родительской модели представления.
		this.callParent(arguments);				
		this.addColumnValidator("UsrLimitAmount2", this.SummValidator);
		this.addColumnValidator("UsrContractAmount", this.SummValidator);
	}
},

Но при проверке работы проваливаюсь в ошибку this.showInformationDialog("Ошибка запроса данных");

Помогите с решением данного кейса

Нравится

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

 

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

И это еще не все, далее в вашем методе валидаторе не предусматривается логика возвращение объекта, со строго установленными свойствами

{
   fullInvalidMessage: invalidMessage,
   invalidMessage: invalidMessage
};

которые там обязаны быть внимательно ознакомьтесь с примером https://academy.terrasoft.ru/documents/technic-sdk/7-10/dobavlenie-validacii-k-polyu-stranicy

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

 

 

 

Лог консоли:

Севостьянов Илья Сергеевич пишет:

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

Есть на схеме Договора поле типа Справочник имеющее ссылку в раздел Подписанты. Но тут скорей отсутствие навыка разработки в BPM - как подставить сумму в это поле в зависимости от выбранного Подписанта? Как-то "костыльно" получится...

как подставить сумму в это поле 

в это же поле ? в справочное - никак.

в другое поле - с численным типом, по зависимости, см. dependensies свойство в описании атрибутов

Ответ supporta:

Валидацию вы можете применить только к полям или атрибутам текущей схемы, так что для решения задачи вам понадобится виртуальный атрибут, по которому вы будете валидировать. Атрибуты создаются в блоке attributes: {

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

Вам необходимо создать атрибут для вашей «РазрешеннаяСумма»

Далее данный атрибут нужно будет заполнять как при инициализации карточки, метод, onEntityInitialized: function() {

Так и при изменении лукапов от которых зависит получаемая информация. В вашем случае при изменении лукапа "ФиоПодписанта"

И последнее, как получить данные которыми вы хотите заполнить данный атрибут: Как вы правильно поняли, с помощью esq, ссылку на статью которой вы предоставили второй. Вам достаточно сделать запрос к корневой таблице «Подписанты» с условием ФиоКонтакта = this.get(“ФиоПодписанта”)



 

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

Последовал совету supporta и вот что у меня получилось:

1. Создал виртуальную колонку в атрибутах

2. В onEntityInitialized  вызываю функцию setVirtLimitAmount с использованием EntitySchemaQuery для чтения данных из БД

3. dueSummValidator - проверяю значения и setValidationConfig - вызываю инициализацию валидаторов

4. Все это работает, но я бы сказал через одно место... работает синхронно, а мне бы хотелось асинхронно... По сути для этого мне было бы достаточно, каждый раз при изменении Подписанта (UsrSignatories) вызывать функцию setVirtLimitAmount. Возможно ли отследить изменение поля UsrSignatories? Одного Lookup-а недостаточно.

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

Подскажите, как в Процессе изменить Бизнес-Правило ограничивающее доступ на редактирование поля? Какой элемент процесса нужно использовать?

Нравится

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

имеется в виду бизнес-процесс ?

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

Да.

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

Итоговый результат, ИМХО - это тот кейс, который вы хотите реализовать, насколько я понял.

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

Подскажите как добавить связь раздела "Согласующие" в список выбора?

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

Нравится

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

Для того чтобы добавлять произвольные "Связи" в штатную механику, Вам необходимо добавить ее описание в таблицу EntityConnection

  • SysEntitySchemaUId (это реплика к значению колонки UId из таблицы SysSchema запись в которой описывает схему объекта для которого устанавливается связь
  • ColumUId (это реплика к одноименному полю в таблице SysEntitySchemaReference запись в которой отражает связь объекта с субъектом и его полем, фактически является описанием соотношения справочных полей в объектах)

изучите на примере уже существующих связей.



PS: в помощь Вам SQL скрипт который поможет Вам найти нужное значение по имени колонки и схемы от которой строится связь

-- Ищем колонку Лида (Lead) в схеме Активности (Activity)
SELECT SysEntitySchemaReference.*, Name FROM SysEntitySchemaReference SysEntitySchemaReference
INNER JOIN dbo.SysSchema SysSchema ON SysEntitySchemaReference.SysSchemaId = SysSchema.Id
WHERE ColumnName = 'Lead' AND Name = 'Activity'

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

Все намного проще:

1. Добавить в объект Activity колонку, ссылающуюся на нужный справочник .

2. Найти UId колонки добавленной в первом пункте (в метаданных справочника).

3) Выполнить скрипт предоставленный раннее

     INSERT INTO EntityConnection (SysEntitySchemaUId, ColumnUId) VALUES ('C449D832-A4CC-4B01-B9D5-8A12C42A9F89', 'UId_колонки_из_п.2');

     Где UId колонки - UId колонки для связи из метаданных обьекта Activity.

4) Выполнить очистку кэша и перезайти в систему.

Последовательность правильная. В п.3 можно использовать более удобный скрипт для MSSQL:

 

DECLARE @columnName varchar(max) = 'UsrColumn';
INSERT INTO [EntityConnection] ([SysEntitySchemaUId], [ColumnUId])
  VALUES (
	(SELECT
	  [UId]
	FROM [SysSchema]
	WHERE [Name] = 'Activity'
	AND [ExtendParent] = 0), 
	(SELECT DISTINCT
	  [ColumnUId]
	FROM [SysEntitySchemaReference]
	WHERE [SysSchemaId] IN (SELECT
	  [Id]
	FROM [SysSchema]
	WHERE [Name] = 'Activity')
	AND [ColumnName] = @columnName));

 

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

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

Возможно, я не понял, как это правильно готовить.

Нравится

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

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

По умолчанию у активности есть четыре состояния:

  1. Не начата
  2. В работе
  3. Завершена (конечное состояние)
  4. Отменена (конечное состояние)

По сути все задачи проходят через описанные стадии (состояния).
В случае, если эти состояний недостаточно, вы можете добавить новое справочное поле в раздел "Активности". Далее, необходимо наполнить справочник, на который ссылается добавленное поле. Кейсы необходимо строить на основании добавленного поля.

Алексей, день добрый!

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

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

Так, а в чем проблема?
Создайте на состояние "В работе" несколько задач по факту выполнения которых текущая задача будет завершена.

Не совсем понятна бизнес-задача.

"Vitkauskas Vadim" написал:у меня может быть кейс в рамках задач

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

"Демьяник Алексей" написал:

Так, а в чем проблема?

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

"Владимир Соколов" написал:
Vitkauskas Vadim пишет:

у меня может быть кейс в рамках задач

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

Здесь, насколько я понял другая иерархия.
В рамках активности есть кейс, в в рамках кейса стадии (которые привязаны к статусу активности), в рамках стадии шаги.

"Vitkauskas Vadim" написал:Пока не очень понятно зачем. Но не в этом проблема

Боюсь, что проблема именно в этом :)

"Vitkauskas Vadim" написал:Я к тому, что у меня может быть кейс в рамках задачи, все стадии которого указывают на то, что активность в работе, и только завершение последней стадии говорит о том, что задача завершена.

Сделайте БП - при завершении кейса менять стадию задачи на завершенную.

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