Необходимо расширить логику для некоей карточки использующей базовый save метод, но расширить логику необходимо уже после встроенной валидации обязательных полей.
Как необходимый кандидат подходит функция saveEntityInChain из BasePageV2
Она вызывается до непосредственного сохранения, но после проверки обязательных полей, и все замечательно пока логика - синхронная.
А вот как быть если расширение логики подразумевает вызов асинхронный ?
Сам вызов saveEntityInChain в рамках цепочки вызовов, т.е. заворачивая в коллбек this.callParent() мы фактически выходим из чейна в связи с чем имеем ряд предсказуемых ошибок.

this.Terrasoft.chain(
        this.saveCheckCanEditRight,
        this.saveAsyncValidate,
        this.saveEntityInChain,
        function() {
                this.Ext.callback(callback, scope || this);
        }, this);

Каким образом можно управлять текущим состоянием chain - и расширить его необходимым вызовом, или приостановить его исполнение до выполнения собственной асинхронной логики ?

Нравится

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

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

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

1. Создаем атрибут булевного типа, который будет описывать выполнена ли esq\асинхронная функция или еще нет. К примеру: IsEsqCompleted, с значением по умолчанию false.
2. Замещаем метод save, в нем проверяем если this.get(“IsEsqCompleted”) === false то вызываем наш esq\асинхронную функцию, если истина, то this.callParent(arguments);
3. В самом esq\асинхронной функции, в колбеке, устанавливаем IsEsqCompleted в true, и еще раз вызываем scope.save(); где scope, это this сохраненный в переменную scope вне колбека, что бы с помощью замыкания в колбеке был доступ к скоупу карточки.

Таким образом, по событию сохранения, вначале выполнится ваш метод, установится IsEsqCompleted в истину, сработает callParent и будет произведено сохранение карточки.

Спасибо, описанный Вами метод, довольно "старый прием" :) И в данном случае он наверное единственный наиболее лаконичный.
PS: К моменту написания вопроса, я забыл что в JS SDK документации есть ссылка на исходник функции, к счастью метод chain, хоть и не удостоился какого либо исчерпывающего описания, но его исходный код все таки представлен, что поставило все на свои места.

PPS: По началу я думал что это некая имплементация Ext.Promise или обертка над его использованием.
Собственно говоря, конструктив идентичный:

var promise = new Ext.Promise(function(resolve, reject){
    if(expression fulfilled){
         resolve(data);
     }
     else{
         reject(error reason);
     }
 });
 promise.then(
     function(value){ ... }, //success callback
     function(error){ ... }, //reject callback
     function(value){ ... }, //progress callback
     Scope
 );

Если бы это было так, то внутри любой функции chain присутствовал бы self.then / this.then метод, который позволил бы расширить цепочку вызова своей функцией, или даже своим чейном/промисом

 promise.then(
     function(value){ ... }, 
     function(error){
         self.then(function(){
                //добавляемый в chain код
          })}, 
     function(value){ ... }, 
 );

Но попытки найти что либо подобное среди доступных методов в отладчике - ничего не дала.

Держите пример использования террасофт чейн:

obj = {a: 2, b: 3}
 
var func1 = function(next) {
	console.log(this.a);
	next();
}
 
var func2 = function(next) {
	console.log(this.b);
	next();
}
 
var func3 = function(next) {
	console.log("end");
}
 
Terrasoft.chain(
	func1,
	func2,
	func3,
	obj
);

Да уже понятно все по исходному коду самой функции.
В случае если это замещающая схема - то там в любом случае использовать callParent,
что по сути тоже chain - но реализован он иначе, и вот где-то в изначальном объектв происходит вызов next()
Хотя в приведенном выше примере, я только что посмотрел в исходном коде - прямой вызов next() все равно не используется везде идет вызов this.Ext.callback (предполагаю что это или альтернатива или туда в callback и подставляется следующий аргумент чейна автоматически).

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

Здравствуйте последняя функция в базовой реализации в BaseEntityPage,

function() {
this.Ext.callback(callback, scope || this);
}, this);

Сделана для того, чтобы на уровень выше, можно было вызвать callParent передав свой метод\цепочку, которая(й) выполнится после базовой.
К сожалению, на уровень выше, в BasePageV2 уже такой возможности не предоставили, так что на уровне вашей карточки, отвечая на ваш вопрос, вмешаться в центр цепочки, до сохранения, но после валидации, можно полным переопределением, и вызывать свою функцию с асинхронной частью, в колбеке которой продолжать цепочку. Пример:

save: function(config) {
				this.showBodyMask({
					selector: this.get("ContainerSelector") || null,
					timeout: 0
				});
				this.Terrasoft.chain(
					this.saveCheckCanEditRight,
					this.saveAsyncValidate,
					this.myMethod,
					this.saveEntityInChain,
					this.saveDetailsInChain,
					function() {
						this.onSaved(this.cardSaveResponse, config);
						this.cardSaveResponse = null;
						delete this.cardSaveResponse;
					},
				this);
			},
 
			myMethod: function(callback, scope) {
				window.console.log("myMethod.");
 
				var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
					rootSchemaName: "Contact"
				});
				esq.addColumn("Name");
				var paramDataType = this.Terrasoft.DataValueType.GUID;
				var lookupFilter = this.Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
					"Id", "c4ed336c-3e9b-40fe-8b82-5632476472b4", paramDataType);
				esq.filters.addItem(lookupFilter);
 
				// вызов асинхронной части
				esq.getEntityCollection(function(result) {
					// ... любые синхронные действия
					// продолжение цепочки
					this.Ext.callback(callback, scope || this);
				}, this);
			}
Показать все комментарии

7.9.2
По умолчанию в системе насколько я понимаю для объекта SysSettingsValue нет прав даже у Supervisor пользователя, т.к. ESQ-запросом получить ничего невозможно.

Почему такое ограничение введено на эту таблицу ?

Нравится

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

Илья, здравствуйте!

Это системная таблица. Также через ESQ нет возможности получить данные из объекта SysAdminOperationGrantee. Пример получения значения системной настройки с кодом DataServiceQueryTimeout:

Terrasoft.SysSettings.querySysSettings(["DataServiceQueryTimeout"], function(sysSettings) {
	if (!Ext.isEmpty(sysSettings.DataServiceQueryTimeout)) {
		this.dataServiceQueryTimeout = sysSettings.DataServiceQueryTimeout;
	}
}, this);

Причина: значения системных настроек могут отличаться для разных ролей пользователей. По этой причине использовать ESQ не корректно.

Как выяснилось в число "особенных таблиц" так же входит "SysAdminUnitInRole"

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

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

Как сравнить код исходного и редактированного скриптов?

Сохраняю на диске исходный файл, добавляю изменения в коде, и сохраняю измененный файл. Далее открываю оба эти файла по Compare(MS Visual Studio Compare, UltraCompare).
Если файл - ds_xxx.xml или sq_xxx.xml, то сравнивать получается.
А если файл - xxx_Script.xml, UltraCompare открывает в таком виде: Text="FFFE2F002F002D002D002D002D002D002D002D002D00.... и сравнивать код не получается.

Спасибо.

Нравится

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

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

Выгрузите исходные файлы в js формате, после чего выполните сравнение.

Как альтернатива, можете воспользоваться сторонними приложениями для сравнения:

Здравствуйте Павел.

Как Вы сохраняете файл в js формате ?

Версия террасофт 3.2.0.90

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

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

TSMergeServices.exe /opr=Extract /cfg="ADL350" /wauth=true /mfrom="26.01.2014" /dir="C:\ExtractServises" /fmt=true /js=true

Более подробно об TSMergeServices можете почитать в статье Полезные утилиты. Часть 2. Merge Services

Спасибо !

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

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

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

В каком VIEW ограничить набор колонок и каким признаком лучше выдавать "ограниченный" набор?

Спасибо!

Нравится

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

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

Что вы имеете ввиду под "ограниченным" доступом?

"Мотков Илья" написал:Что вы имеете ввиду под "ограниченным" доступом?

Приведу пример. Я как ответственный менеджер за контрагента "X" могу видеть и редактировать его карточку (и все поля в ней). А в других контрагентах (где ответственный не я) я могу видеть только 3 поля - название, код и ответственного.

Стандартным функционалом (доступом по записям и колонкам) это не решить

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

"Зверев Александр" написал:стандартного механизма нет.

Но существуют же VIEW, которые фильтруют доступ по записям? Если в этих VIEW уменьшить количество колонок при определенных условиях, то можно решить этот вопрос

Владимир, ну это же тоже будет доработка.:wink:

Кроме того, не представляю, как можно ограничить во view для каждого, а не одного конкретного пользователя. Это в 3.Х доступ по записям был сделан при помощи view с фильтрацией по SYSTEM_USER, а в 7.Х все пользователи системы работают с БД через одну учётку, прописанную в ConnectionStrings. Фильтры накладываются программно где-то внутри EntitySchemaQuery.

"Зверев Александр" написал:Владимир, ну это же тоже будет доработка

Да, всё тут доработка :D

"Зверев Александр" написал:Фильтры накладываются программно где-то внутри EntitySchemaQuery

Это уже сложнее, конечно.. Но, возможно, реализуемо. По крайней мере, это критично важно.

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

"Зверев Александр" написал:Нужно всего лишь наряду с механизмами в ядре по выдаче прав на объекты, на поля, на записи и на записи по умолчанию сделать ещё один.

Думаю, что в банковских проектах такое реализовывали. Осталось найти, кто :)

Думаю, нет. В ходе проекта такое реализуют скриптами под конкретное поле, согласно ТЗ.

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

 

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

Добрый день коллеги, подскажите как сделать так что бы при открытии раздела в реестре не отображались записи до того момента пока не выставишь фильтры. Заранее благодарен. BPM 7.7

Нравится

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

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

define("ContactSectionV2", ["ContactSectionV2Resources"],
	function(resources) {
		return {
			entitySchemaName: "Contact",
			diff: /**SCHEMA_DIFF*/[
			]/**SCHEMA_DIFF*/,
			methods: {
				loadGridData: function() {
 
					var filters = this.getFilters();
					if (filters.collection.items.length === 0) {
						return;
					}
 
					this.beforeLoadGridData();
					var esq = this.getGridDataESQ();
					this.initQueryColumns(esq);
					this.initQuerySorting(esq);
					this.initQueryFilters(esq);
					this.initQueryOptions(esq);
					this.initQueryEvents(esq);
					esq.getEntityCollection(function(response) {
						this.destroyQueryEvents(esq);
						this.updateLoadedGridData(response, this.onGridDataLoaded, this);
						this.checkNotFoundColumns(response);
					}, this);
				}
			}
		};
	}
);

Можно так же, очищать реестр в блоке if, на случай если кто-то снимет фильтр обратно.

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

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

Нравится

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

Михаил, здравствуйте!

Вам необходимо смотреть в схему GridUtilitiesV2 (миксин loadGridData, beforeLoadGridData и afterLoadGridData).
Примечание. Изменения будут работать не только для раздела, но и для детали. Вам необходимо реализовывать конкретно в схеме раздела.

"Вильшанский Дмитрий" написал:

Михаил, здравствуйте!

Вам необходимо смотреть в схему GridUtilitiesV2 (миксин loadGridData, beforeLoadGridData и afterLoadGridData).

Примечание. Изменения будут работать не только для раздела, но и для детали. Вам необходимо реализовывать конкретно в схеме раздела.


Спасибо, но я уже нашел метод который срабатывает при прокрутке раздела вниз.
Вот пример:

initQueryColumns: function() {
     this.callParent(arguments);
     this.showInformationDialog("!!!");
}
Показать все комментарии

Для встроенного механизма авто-нумерации описанного в этой статье академии

Предлагается 2 варианта использования, собственно на стороне сервера через БП и в UI через вызов специализированного метода getIncrementCode()
Остановимся на втором варианте.
Так вот; есть тут небольшая проблема: если мы хотим показывать собственно сгенерированный номер в поле открывшейся карточки то мы вызываем его при инициализации (собственно аналогично примеру из статьи помещая вызов в onEntityInitialized.
Проблема: Итератор сдвигается при каждом вызове, т.е. открывая карточку в режиме добавления новой записи или копирования номер сдвинется, но это не гарантирует нам того что эта запись в конечном итоге вообще произойдет, т.е. пользователь может просто нажать "Отмена" или уйти с карточки - а номерок уже прирос на один.
Мы конечно можем привязаться к методу save и осуществлять вызов getIncrementCode() там, но номерок-то хочется получить на этапе открытия карточки.

Можно ли как-то вызвать его без сдвига итератора ?
PS: я безусловно понимаю что очевидный ответ - не мороч голову делай сервер-сайд, а для показа сделай ESQ запрос в системную настройку напрямую и добавь еденичку :)
Или получай номерок при открытии, ESQ Update запросом в сист.настройку делай роллбек ее на 1 назад, и повторно вызывай метод уже при сохранении.
Но может есть какие-то более лаконичные способы ?

Нравится

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

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

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

Понятно,
Хотя кстати для тех же счетов законодательство РФ требует соблюдения строгой очередности, так что надо как-то этот момент контролировать.
Идеальный вариант:
Выдавать очередной номер для демонстрации.
Но в процессе сохранения снова проверять его доступность, и в случае если за время редактирования карточки другим пользователем уже был создан документ (получил текущий номер), то получать следующий и извещать об этом пользователя, мол при сохранении номер документа был изменен т.к. текущий уже занят.
т.е. разделить на метод получения без итерирования:
showIncrementCode()
И собственно метод запроса, установки и итерирования
getIncrementCode()
незначительно расширив логи последнего проверкой на факт предоставления номера для другой записи.

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

BPMOnline: 7.9.2.2410
По какой-то причине в мастере редактирования страниц, при добавлении, у полей с типами: "Целое число", "Дробное число" и "Логическое" отсутствует чекбокс определяющий обязательность для заполнения:



А вот у типов "Справочник", "Строка" и "Дата" - есть ?


Это баг или фича :) ?

Нравится

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

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

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

Логическое поле всегда заполнено значением false (если пользователь явно не указал true), дробное/целое число - значением 0 (если пользователь не указал другое).

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

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

Сейчас попасть на мою страницу в БП могу только благодаря вопросу пользователю. А должен еще при нажатии на кнопку.

Нравится

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

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

Задачу можно решить следующей настройкой процесса.

1. Создать служебную запись. В дальнейшем эта запись будет триггером для перехода на преднастроенную страницу. Условно – создать активность.
2. Элемент [Исключающее ИЛИ по событиям].
Одно ветка ведет к таймеру.
Вторая ветка ведет к элементу [Обработка сигнала]. Ожидает изменения в объекте, где Id = Пункт 1. Id
3. Исключающее ИЛИ. Принимает на вход обе ветки.
5. Преднастроенная страница.

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

"Зарицкий Олег" написал:

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

"Зарицкий Олег" написал:

1. Создать служебную запись. В дальнейшем эта запись будет триггером для перехода на преднастроенную страницу. Условно – создать активность.


Здравствуйте, а можно подробнее про этот пункт? Не очень понял.

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

1. Создать служебную запись. Подразумевается – создать запись в любом объекте. Например, элемент [Добавить данные] в объект Активность. В дальнейшем изменения в этой записи приведут к тому, что процесс сразу перейдет к выполнению преднастроенной страницы (не дожидаясь таймера).

2. В случае с приведенным примером (когда служебная запись представлена в виде активности) – в элементе [Обработка сигнала] необходимо ожидать изменения записи в объекте Активности, где Id = Пункт 1.Id

В результате – преднастроенная страница откроется если прошло время, указанное в таймере, ИЛИ будет изменена активность.

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

Коллеги, всем привет!

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

Скриншот - https://db.tt/a3SKjesC2q
версия - 7.9

Нравится

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

Приветствую!

Попробуйте установить в Terrasoft.WebApp\Web.config

  <add key="UseSvn" value="false" />

Тогда не будет возможности использовать SVN. Он как раз нужен.

Очистите кэш SVN (содержимое %TEMP%\%APPLICATION%\%WORKSPACE%\...) и авторизируйтесь в хранилище.

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