Коллеги, приветствую.

Подскажите, пожалуйста, как можно перенести набор системных настроек через svn?

Во вкладке Данные переносимого пакета я создал новую схему для всех созданных системных настроек с фильтром "Код начинается на ...", схема показала 3 нужные записи.

Попробовал создать схему привязки Значений системной настройки (с фильтром Системная настройка.Код начинается на ... - т.е. зеркально предыдущему фильтру), при показе данных отображает 3 корректных значения, но при сохранении выдаётся ошибка для всех трёх значений: "Не привязаны данные для связанного объекта "SysSettings" по колонке "SysSettings"" и дальше Id системных настроек.

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

Подскажите, пожалуйста, корректный способ переноса настроек.

Нравится

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

Что интересно. Сейчас (в процессе тестирования) попробовал изменить в настройке фильтров ограничение с И на ИЛИ для единственного фильтра и все записи корректно привязались, вторая схема также увидела привязанные данные.

Правда до этого я указал один раз в фильтре конкретное значение записи в режиме фильтра "равно".

Теперь изменения фильтров на И или ИЛИ не отменяют успешность привязки данных к пакету.

Похоже на ошибку в работе привязки данных...

"Фёдоров Александр. Афлекс" написал:Подскажите, пожалуйста, как можно перенести набор системных настроек через svn?

Наиболее гарантированный, а так же с наиболее ожидаемым поведением - это SQL-скрипт.

"Севостьянов Илья Сергеевич" написал:
Наиболее гарантированный, а так же с наиболее ожидаемым поведением - это SQL-скрипт.

Это всё-таки хардкор без поддержки логики приложения...

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

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

"Фёдоров Александр. Афлекс" написал:Если есть штатные рабочие инструменты, хочется сначала пользоваться ими, пока не будет доказано, что они неработоспособны.

Они не предназначаются для переноса:
1) Системных настроек и их значений
2) Настройки колонок и их значений
3) Организационной структуры
4) Пользовательских прав.

Об этом, не однократно заявляли специалисты тех.поддрежки.

Если Вы хотите "надежно" переносить Ваши системные настройки - Вам необходимо написать SQL-сценарий, при этом желательно с логикой проверки предварительного существования, а так же проверкой - возможно, связанных данных.
ИМХО самый подходящий вариант (для TSQL) это MERGE скрипт, я о нем писал.

PS: Через данные, нормально переносятся группы системных настроект, включение настроек в группы, но самими значениями настроек - иногда бывают проблемы.

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

Они не предназначаются для переноса:

1) Системных настроек и их значений

...

Об этом неоднократно заявляли специалисты тех.поддержки.

...

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

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

При этом SQL-скрипт - это слишком низкоуровневый инструмент, для которого требуется разработчик с неплохим знанием SQL.

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

1. Создать привязку на системную настройку
2. Создать привязку на само значение System settings value
Скриншот примера во вложении.

да нормально они переносятся - привяжите нужные данные для таблиц
SysSettings (обязательно)
SysSettingsFolder (группы настроек)
SysSettingsFolderLcz (локализация групп настроек)
SysSettingsGrantor (вхождение пользователей, если настройка "персональная")
SysSettingsInFolder (вхождение настройки в группу настроек)
SysSettingsLcz (локализация настроек)
SysSettingsReferenceSchema (не знаю, что это, вроде не нужно)
SysSettingsValue (обязательно)

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

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

"Мотков Илья" написал:
1. Создать привязку на системную настройку

2. Создать привязку на само значение System settings value


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

Дмитрий, спасибо за столь развёрнуты ответ. За "Обязательно для обновления" - отдельное спасибо.

"Фёдоров Александр. Афлекс" написал:За "Обязательно для обновления" - отдельное спасибо.

Поддерживаю... мы как-то отказались от переноса настроек данными именно по этой причине, эта "особенность" нам была не известна.

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

Коллеги, добрый день.

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

TypeError: rootItem is undefined 1 ViewModelSchemaValidationMixin.js:52:1

И дальше перечисление стека вызовов.

Подскажите, пожалуйста, если кто-то сталкивался с подобным - что это и в какую сторону смотреть, чтобы исправить?

Нравится

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

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

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

Спасибо, Илья.

Да, дело было в текущем пакете. Спасибо.

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

Добрый день.
У меня такая проблема: по БП пользователю ставиться задача, но ее может закрыть любой другой пользователь, а нужно, чтобы только ответственный мог ее закрыть. В правах доступа настроено следующем образом: Раздел Активность: администрирование по записям(права на изменение) - указано кто создает и кому дается право. Если зайти в раздел "Активности" и там начать закрывать задачу, то Система не даст это сделать, а если это делать через объект "Продажа" через БП, то все возможно. Может кто-то подскажет как решить данную проблему? Я не программист, поэтому много не понимаю, но решить как-то хочу проблему. Заранее спасибо!

Нравится

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

А каким образом вы через объект Продажа, закрываете активность?

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

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

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

"Демьяник Алексей" написал:
...что менять задачу может только автор/ответственный.

Но Татьяна говорит о том, что закрыть активность должен иметь возможность только ответственный.

"D.T." написал:
Демьяник Алексей пишет:

...что менять задачу может только автор/ответственный.

Но Татьяна говорит о том, что закрыть активность должен иметь возможность только ответственный.

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

Аналогичный процесс нужно сделать на изменение активности.

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

Если закрытие происходит в рамках бизнесс-логики только через UI
(графический интерфейс, т.е. не бывает/пердусмотрено, так что закрывается например в другом БП)
То задачу можно решить на уровне JS-логики карточки Активности (Задачи).
Для того чтобы не "мучаться" определяя входит ли пользователь в необходимую функц.роль или организационный юнит и т.д. можете использовать разработанный мной миксин

Спасибо всем за ответы. Буду пробовать. Отпишусь по результатам.

"Демьяник Алексей" написал:Сделайте процесс, который при создании активности будет забирать права у всех и выдавать только ответственному.

Не совсем логично, так как автору надо оставить возможность менять содержание задачи (или отменять её)

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

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

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

BPM'Online 7.10.0.1742

Нравится

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

Либо я чего-то не понял либо всё должно работать аналогично базовым операциям.
Ты считываешь нужную активность(что не обязательно), а потом элементом изменить данные изменешь значения своей кастомной колонки. Вы вывели вашу колонку в реестр карточки или нет? Уверены что в нужную карточку вывели эту колонку? В базе смотрели, значение меняется или нет? В чем проблема то собственно?

Делаете простенький бп:
1) Начальное событие: "Стартовый сигнал" -> срабатывает при добавлении/изменении записи в активность
2) Далее элемент "Изменить данные", фильтр по id активности(приходит в событие), устанавливаете поле.
3) profit

"Кисловский Михаил Андреевич" написал:

Либо я чего-то не понял либо всё должно работать аналогично базовым операциям.

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


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

"Варфоломеев Данила" написал:

Делаете простенький бп:

1) Начальное событие: "Стартовый сигнал" -> срабатывает при добавлении/изменении записи в активность

2) Далее элемент "Изменить данные", фильтр по id активности(приходит в событие), устанавливаете поле.

3) profit


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

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

Хм а обязательно именно такую задачу создавать?
Может лучше создавать Активность с типом Задача и Категорией выполнить?
Тогда тот кто будет ответственным по этой активности получит уведомление и обработает эту активность. Как такой вариант вам?
Т.е. использовать не "Выполнить задачу", а Добавить данные и добавить активность что описал выше.

"Zaitova Liubov" написал: у меня есть БП, который срабатывает на создание Обращения. В рамках обработки обращения нужно создать активность "Выполнить задачу" и заполнить поле в ней значением из текущего обрабатываемого обращения.

Почему бы просто не добавить ещё 1 элемент "изменить данные" после "Выполнить задачу 2"?? "Выполнить задачу 2" возвращает Id активности, соответственно мы можем взять и изменить поле. Значение поля берём из "Читать данные 1"

"Кисловский Михаил Андреевич" написал:Т.е. использовать не "Выполнить задачу", а Добавить данные и добавить активность что описал выше.

Тоже неплохой вариант.

"Варфоломеев Данила" написал:Почему бы просто не добавить ещё 1 элемент "изменить данные" после "Выполнить задачу 2"?? "Выполнить задачу 2" возвращает Id активности, соответственно мы можем взять и изменить поле. Значение поля берём из "Читать данные 1"

когда изменятся эти данные? после того.как мы закроем задачу "Выполнить задачу 2"? а нужно чтобы было заполнено при открытии

"Кисловский Михаил Андреевич" написал:Т.е. использовать не "Выполнить задачу", а Добавить данные и добавить активность что описал выше.

все гениальное просто) спасибо!

"Zaitova Liubov" написал:
Кисловский Михаил Андреевич пишет:

Т.е. использовать не "Выполнить задачу", а Добавить данные и добавить активность что описал выше.

все гениальное просто) спасибо!


Решение хорошее, но непонятно как заставить БП ждать задачи созданной таким образом. Если использовать действие "Выполнить задачу",то БП создает активность и ждет её выполнения, а если добавить активность таким образом, то добавится активность и БП пойдет выполнять следующие за ним действия.

Zaitova Liubov, для этого нужно создать второй БП который будет отлавливать изменения по вашим активностям и в зависимости значения в поле результат "Условными потоками" можно вариантов исхода этого БП понаделать целую кучу.
МЫ используем такой подход в тех случаях, когда необходимо фильтровать значения в справочном поле Результат, для конкретно наших "подвидов" активностей. А не довольствоваться базовыми "результатами" этого поля.

"Тёскин Дмитрий Валерьевич" написал:
Zaitova Liubov пишет:

Кисловский Михаил Андреевич пишет:

Т.е. использовать не "Выполнить задачу", а Добавить данные и добавить активность что описал выше.

все гениальное просто) спасибо!

Решение хорошее, но непонятно как заставить БП ждать задачи созданной таким образом. Если использовать действие "Выполнить задачу",то БП создает активность и ждет её выполнения, а если добавить активность таким образом, то добавится активность и БП пойдет выполнять следующие за ним действия.


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

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

"Zaitova Liubov" написал:
Тёскин Дмитрий Валерьевич пишет:

Zaitova Liubov пишет:

Кисловский Михаил Андреевич пишет:

Т.е. использовать не "Выполнить задачу", а Добавить данные и добавить активность что описал выше.

все гениальное просто) спасибо!

Решение хорошее, но непонятно как заставить БП ждать задачи созданной таким образом. Если использовать действие "Выполнить задачу",то БП создает активность и ждет её выполнения, а если добавить активность таким образом, то добавится активность и БП пойдет выполнять следующие за ним действия.

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


Любовь, спасибо за совет! Это действительно то что нужно!

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

Добрый день.
Занимаюсь реализацией интеграции с BPM'Online 7.10 через DataService.

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

Код примерно такой

var list = new Dictionarystring, ColumnExpression>();
foreach(....)
{

        list.Add(c1.Attribute.FieldName, new ColumnExpression()
        {
                ExpressionType = EntitySchemaQueryExpressionType.Parameter,
                Parameter = new Parameter()
                {
                        Value = value,
                        DataValueType = c1.Attribute.DataType
                }
        });
}

var insertQuery = new InsertQuery()
{
        RootSchemaName = EntityName,
        ColumnValues = new ColumnValues()
};
insertQuery.ColumnValues.Items = new Dictionarystring, ColumnExpression>(list);

Как только в список полей для создания попадается поле с датой и временем, то при попытке создать запись выходит ошибка.

Позже я пытался вместо объекта DataTime передавать строку с форматированием которое указано в https://community.terrasoft.ru/forum/topic/24597 , но это так же не помогло.

1) Подскажите, пожалуйста, как работать с полями дата и время?
2) Есть ли способ чтобы DataService выдавал более UserFrendly ошибку чем "Удаленный сервер возвратил ошибку: (500) Внутренняя ошибка сервера."? Т.к. выяснение того, что ошибка связана именно с типом DataTime заняло много времени, причем формат запроса корректен и "вроде бы" DataService не должен ругаться на него 500 ошибкой...

Нравится

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

Добрый день!

Для передачи даты через DataService формат должен быть следующий : [""\""2000-12-31T09:41:59\""""""""]. Например:

[quote="Мотков Илья"]

Добрый день!

Для передачи даты через DataService формат должен быть следующий : [""\""2000-12-31T09:41:59\""""""""]. Например:

"Габбазов Искандер Рустемович" написал:На сколько я понимаю ваш пример это кусок "правильного" JSON'а.

нет, JSON тут совсем не при чем.
Это текстовое представление формата TimeStamp,
Оператор @ - интерпритирует строку без управляющих последовательностей, ну и двойные кавычки "схлопываются" по две в одну, на выходе имеем

const string startDate = @"""\""2000-12-31T09:41:59\"""""""""";

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

Другим Ильей :) формат указан правильный:
[quote="Мотков Илья"]
const string startDate = @"""\""2000-12-31T09:41:59\"""""""""";

значение

@"""\""2000-12-31T09:41:59\"""""""""";

Илья, нет, формат все же верный, то есть тайм-стамп в экранированных кавычках, и в кавычках. Я не имел ввиду, что это должен быть валидный json, это просто формат, в виде которого по определенным архтиктурным причинам передается дата в приложении.

"Кот Владимир Владимирович" написал:то есть тайм-стамп в экранированных кавычках, и в кавычках.

вот, смысл прояснился :) понятно, спасибо за пояснения.

На данный момент сделал так:

switch (t.Name)
{
	...
 
	case "DateTime":
		value = @"""\""2000-12-31T09:41:59\"""""""""";

Те кто все-таки забрел в эту тему, достаточно передать DateTime обернутым в экранированные кавычки.

var date = DateTime.Now;
 
insertQuery.ColumnValues.Items.Add("....", new ColumnExpression()
{
	ExpressionType = EntitySchemaQueryExpressionType.Parameter,
	Parameter = new Parameter()
	{
		Value = "\""+date+ ""\""""
Показать все комментарии

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

Версия 7.10 sales.

При эскалации обращения открывается отдельная страница, её схема EscalationEditPage, объект - то же самый Case.

При этом доступны не все колонки объекта Case. В частности, не доступна колонка Id, а также пользовательские колонки (при попытке вывести значение в консоль после onEntityInitialized выводится undefined).

Как получить данные пользовательских колонок?

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

Нравится

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

Попробовал передавать через сообщения:

На странице casePage внутри runEscalation добавил:

this.sandbox.publish("setCaseType", this.get("UsrCaseType"), [this.sandbox.id + "_EscalationEditPage"]);

И подпись на сообщение внутри init на странице эскалации.

Не помогло.

Как вообще формируется страница эскалации?

Решил задачу.

Просто проставляю группы ответственных уже на уровне Обращения. При открытии эскалации они копируются.

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

Зачастую требуется корректировать набор фильтров для справочных полей карточки как говорится "на лету", в зависимости от тех или иных условий/событий/действий пользователя

Собственно к коллекции фильтров можно подступиться через
this.columns.{ИмяКолонки}.lookupListConfig
Но основной вопрос - корректно ли манипулировать этой коллекцией через непосредственный доступ - вот таким образом, или разработчик предусмотрел другую методику корректировки набора фильтров для справочных полей ?
Специализированные методы возможно,
Возможно состояние this.columns.{ИмяКолонки}.lookupListConfig в жизненном цикле контекста может быть сброшено - и необходимо иметь вспомогательные сущности, для повторного его построения. (Т.к. изначально тот-же lookupListConfig задается например при описании атрибута одноименного колонке, но в конечном итоге среди полей получаемых из модели по имени поля - не содержится) ?

Нравится

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

Не встречал такого подхода работы в рамках базовых конфигураций.
Если вам необходимо анализировать внешние асинхронные условия пере формированием той или иной логики фильтр метода, то рекомендуем вам:
1. Сделать подписку на изменения всех колонок, которые могут влиять на логику формирования фильтрации колонки в lookupListConfig;
2. При изменении данных колонок кешировать асинхронный результат, который должен повлиять на логику фильтрации в атрибутах;
3. В фильтр методе lookupListConfig-а: анализировать кешированные атрибуты, и формировать согласно вашей логики нужный вам esq, блоками if else.

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

7.10
Для примера возьмем Lead
Создаем в схеме карточки (LeadPageV2)

                attributes: {
                        "firstAttr": {
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        },
                        "secondAttr": {
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        },
                        "depAttr": {
                                "dependencies": [
                                        {
                                                "columns": ["secondAttr", "firstAttr"],
                                                "methodName": "depMethod"
                                        }
                                ],
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        }
                },
                methods: {
                        "onEntityInitialized": function() {
                                document.LeadPageScope = this;
                                this.callParent(arguments);
                         },
                        "depMethod": function() {
                               alert("Dep Trigged!!!")
                         }
                }

Создаем в схеме секции (LeadSectionV2) почти идентично
                attributes: {
                        "firstAttr": {
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        },
                        "secondAttr": {
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        },
                        "depAttr": {
                                "dependencies": [
                                        {
                                                "columns": ["secondAttr", "firstAttr"],
                                                "methodName": "depMethod"
                                        }
                                ],
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        }
                },
                methods: {
                        "init": function() {
                                document.LeadSectionScope = this;
                                this.callParent(arguments);
                         },
                        "depMethod": function() {
                               alert("Dep Trigged!!!")
                         }
                }

Открываем карточку Лида в Combined режиме,
Устанавливаем точки останова внутри метода depMethod в карточке и секции соответственно,
Открываем консоль, поехали

document.LeadSectionScope.set("firstAttr", true);
//НИЧЕГО НЕ ПРОИСХОДИТ !
//Исполнение в метод depMethod секции не переходит.
document.LeadPageScope.set("firstAttr", true);
//ОТЛАДЧИК ВСТАЕТ ВНУТРИ МЕТОДА depMethod карточки !

И как это понимать ?
Двойные стандарты для функциональности атрибутов в отношении зависимостей ?

Нравится

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

Здравствуйте, Илья.

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

Ну в рамках карточки мы не обязательно привязаны к колонкам,
мы можем просто создать атрибуты (свои, пользовательские не связанные с колонками - что собственно и показано в примере) и на их изменение - зависимости выраженные в вызове методов текущего контекста.
Почему то же самое с атрибутами не доступно в рамках карточки секции ?
Возможно есть иной способ привязки логики к изменению атрибута ?
Например биндинг value на метод:
Что в таком случае будет происходить -
При установке значения в аттрибут - каждый раз будет вызываться забинженный метод - в аргументы которого будет поступать устанавливаемое значение, а его return значение - будет установлено в качестве текущего value ?

Потому что applyDependencies из BusinessRulesApplierV2 работает только для BasePageV2 и её наследников. По сути это сахар над обычным бекбоновским this.on("change: ...
так что в секции можете в ините просто написать:
this.on("change:MyAttrColumn", this.myMethod, this);
где MyAttrColumn имя атрибута\колонки для которой будет тригерится по изменению метод myMethod описанный в блоке методов.

"Максим Шевченко" написал:По сути это сахар над обычным бекбоновским this.on("change

ну вот именно это я и подразумевал :)
Спасибо что уточнили.
А можно подключить applyDependencies из BusinessRulesApplierV2 в качестве зависимостей схемы секции - в таком случае тоже будет работать ?
Просто интересует однообразие.

Прям её не подключить, все же в базовой реализации этого метода анализируется атрибут IsEntityInitialized которого нет в секции. Но, вы можете попробовать написать свой миксин который будет пробегать по атрибутам, искать у них свойство "зависимости" и делать в этом форе this.on на указанные методы.

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

Может кто-нибудь сталкивался с подобной проблемой.
Использую LookupUtilities:

var config = {
        entitySchemaName: "UserReport",
        columns: ["Id", "Name", "Parameters", "ReportSchemaUId"],
        multiSelect: false,
        actionsButtonVisible: false,
        lookupPageName: "LookupPage"
};
LookupUtilities.Open(this.sandbox, config, this.onReportSelected, this, null, false, false);

Все красиво, и работает корректно. Открывается окно выбора ().
Но предположим, что мне необходимо добавить дополнительную кнопку, либо изменить страницу LookupPage.
Что я делаю - наследую LookupPage.
define("CustomLookupPage", [],
        function() {
                return Ext.define("Terrasoft.configuration.CustomLookupPage",
                        {
                                extend: "Terrasoft.LookupPage",
                                alternateClassName: "Terrasoft.CustomLookupPage"
                        }
                );
        }
);

Затем, меняю config.lookupPageName = "CustomLookupPage".

var config = {
        entitySchemaName: "UserReport",
        columns: ["Id", "Name", "Parameters", "ReportSchemaUId"],
        multiSelect: false,
        actionsButtonVisible: false,
        lookupPageName: "CustomLookupPage"
};
LookupUtilities.Open(this.sandbox, config, this.onReportSelected, this, null, false, false);

Но теперь, при открытии получаю ошибку (
).
Что-то попало в бесконечный цикл, вижу ошибку переполнения стэка.

Как такое лечится?

Нравится

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

Добрый день, Евгений!

Не понятно, что могло вызвать такой странный результат. Я бы порекомендовал обратиться в support Террасофта, пускай посмотрят.

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

Предлагаю поступить немного иначе.
Не наследоваться через зависимости (в карточку),
А унаследоваться от схемы справочника (LookupPageV2) (создать свою схему наследника, не замещающую),
там в схеме "играться" с "дифами" и замещенеим, так как мы это делаем при классическом замещении клиентских схем.
И уже своего наследника подключать в зависимости.
(Мне кажется это более каноничный подход)

PS:
У меня в ближайшем будущем есть аналогичная задача - скастомить окно на базе типового окна справочника, со своими кнопками и т.д.
Я планировал действовать так.
Прошу Вас отписаться если предложенный мною вариант окажется рабочим, чтобы душа была спокойна :)

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

Спасибо за идею, на первой неделе мая попробую реализовать и отпишусь!

Вообщем Вы были изначально на правильном пути, но упустили несколько моментов, по сути Ваши начинания "в купе с моей идеей" и есть итоговое решение, т.к. наследование на уровне схем все равно оказалось необходимым условием (правда через зависимости, а не механизм наследования)
Разобраться помогла схема "MultiLookupModule" входящая в базовую поставку
1) Вы реализуете модуль, зависимости которого необходимо указать не только в исходном коде но и в меню структуры

2) Сам модуль не должен быть чьим либо наследником, всё необходимое идет в его зависимости, в т.ч. и LookupPage который мы идеологически наследуем, но архитектурно через наследование в схемах - это почему-то не работает (Если у казать "Выбор из справочника" как родителя - получаем очень много разного рода ошибок при вызове).
3) Далее вот вообщем-то пример самого кода

define("MyCustomLookupPage", ["LookupPage", "LookupPageViewGenerator", "LookupUtilities", "css!LookupPageCSS"],
	function(LookupPage, LookupPageViewGenerator) {
		return Ext.define("Terrasoft.configuration.KmGMSLookupPage", {
			alternateClassName: "Terrasoft.KmGMSLookupPage",
			extend: "Terrasoft.LookupPage",
			gridWrapClasses: ["my-custom-lookup-control"]
		});
	});

перчим солим, замещаем, дополняем по вкусу и
ну и вызываем его через LookupUtilites или же есть более простой способ через this.openLookup (что само собой является оберткой над вышеуказанным и "сахаром" по сути).
Уже в таком виде - Ваш "отнаследованный" lookupPage можно вызвать следующим образом

var config = {
        entitySchemaName: "Lead",
        columns: ["Id"],
        multiSelect: false,
        actionsButtonVisible: false,
        lookupPageName: "MyCustomLookupPage"
};
this.openLookup(config, function(result){console.log(result)})

Вот.

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

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

Как оказалось - решение было близко)
Спасибо большое, что поделились окончательной реализацией! Я то уже переключился на более приоритетные задачи, так как посчитал эту затею бесперспективной.

На самом деле - решение не окончательное... бой продолжается :)
Есть прям несколько моментов - которые весьма и весьма удивляют... например что кнопки по факту "захардкожены" в LookupPageViewGenerator и собственно не зависят от каких либо конфигурационных параметров вызова этого самого генератора, что приводит к необходимости наследовать и его, с переопределением некоторых функций, в свою очередь "копия" генератора подобным образом не работает полностью или частично, т.к. используя наследника в качестве зависимости - получаем ряд ошибок про "sunbox of undefined" из core.js (что практически убивает надежду "по быстрому разобраться") при попытке выбора записей из такого справочника и т.д.

Кстати по какой-то причине не удается подключить к карточке страницы LookupUtilities
В конечном итоге в контексте карточки нет объекта "LookupUtilities"

define("MyCustomPage", ["LookupUtilities"],
	function(LookupUtilities) {
...

В конечном итоге в контексте карточки LookupUtilities - undefined

Не поделитесь, как вы подключали LookupUtilities к странице ?
(Я уже и в dependecies структуры добавил его, так же безрезультатно)

Там необходимо использовать V2

define("MyCustomPage", ["LookupUtilitiesV2"],
        function(LookupUtilities) {
...

Победа.

Кастом окна справочника через наследование его базового Ext-класса возможен, и даже, как оказалось не сложен.

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

1) Создаем пользовательский модуль например "MyCustomLookupWindow"
[color=red]Внимание![/color]

  • Модуль не должен являться чьим либо наследником
  • В структуре метаданных модуля, дублируем зависимости, которые объявим в самом листинге

  • схемы при создании необходимо выбирать по заголовкам (давняя боль), облегчаю задачу:
    LookupPage - Выбор из справочника ( NUI )
    LookupPageCSS - Стили модуля справочника ( NUI )
    LookupPageViewGenerator, LookupUtilities, MaskHelper - имеют такие же заголовки как и их имена

define("MyCustomLookupWindow", ["LookupPage", "LookupPageViewGenerator", "LookupUtilities", "css!LookupPageCSS"],
	function(LookupPage, LookupPageViewGenerator) {
		return Ext.define("Terrasoft.configuration.MyCustomLookupWindow", {
			alternateClassName: "Terrasoft.MyCustomLookupWindow",
			extend: "Terrasoft.LookupPage",
			gridWrapClasses: ["km-gms-lookup-control"]
		});
	});

2) Для вызова lookup-наследника используйте в вашей логике this.openLookup метод (присутствует в контексте любой карточки/схемы/детали), если же случилось так что его нет - то см.выше - надо будет подключить LookupUtilitesV2 в зависимости схемы, и использовать метод Open предоставляемый объектом зависимости. (Примеры использования см.в исходниках и в начале темы)

//Подготовим минимальный конфигурационный объект
var config = {
        //Для примера справочник "нацелен" на раздел "Лиды"
        entitySchemaName: "Lead",
        columns: ["Id"],
        multiSelect: true,
        actionsButtonVisible: true,
        //Вот сюда передаем имя модуля-наследника
        lookupPageName: "MyCustomLookupWindow"
};
//пример вызова
this..openLookup(
         //наш конфигурационный объект
         config, 
         //callback принимающий результат/результаты выбора в виде коллекции 
         function(selected){console.log(selected)},
         //контекст для коллбэка
         this
)

3) Для того чтобы закастомить кнопки необходимо в нашем наследнике переопределить метод renderLookupView
в который произвести инъекцию своей логики

define("MyCustomLookupWindow", ["LookupPage", "LookupPageViewGenerator", "LookupUtilities", "css!LookupPageCSS"],
	function(LookupPage, LookupPageViewGenerator) {
		return Ext.define("Terrasoft.configuration.MyCustomLookupWindow", {
			alternateClassName: "Terrasoft.MyCustomLookupWindow",
			extend: "Terrasoft.LookupPage",
			gridWrapClasses: ["km-gms-lookup-control"],
			//Переопределяем метод в котором мы можем управлять сформированной конфигурацией до рендеринга.
			renderLookupView: function(schema, profile) {
				var config = this.getLookupConfig(schema, profile);
				var topPanelConfig = LookupPageViewGenerator.generateFixed(config);
				//----------------------- инъекция логики (начало) ----------------------
				//переменная для хранения ссылки на массив объектов-конфигурайий кнопок	
				var buttonsConfig;
				//Получаем ссылку на аттрибут-массив конфигурационных объектов-кнопок
				//Используем Underscore.some с возможностью прерывания переборы по возврату от предиката "true"
				_.some(topPanelConfig.items, function(target) {
					//выделяем объект группы кнопок (Wrapper) по id контейнера
					if (target.id === "selectionControlsContainerLookupPage") {
						//в нем ищем подчиненные объекты являющиеся массивом
						_.some(target, function(target) {
								//согласно структуры конфигурационного объекта панели
								//"чистым" массивом является только объект с конфигами кнопок
							if (Array.isArray(target)) {
								//сохраняем ссылку на него в переменной для дальнейшего использования
								buttonsConfig = target;
								//Прерываем перебор
								return true;
							}
						});
						//Прерываем перебор
						return true;
					}
				});
				//Создаем новую кнопку, клонируя любую, первой как правило идет кнопка "Отмена"
				var newButton = Terrasoft.deepClone(buttonsConfig[0]);	
				//Используем Underscore.extend для объединения склонированного объекта
				//с анонимным объектом-разницы в котором опишем необходимые изменения
				_.extend(
					newButton,
					{
						caption: "Добавленная кнопка",
						click: {
							bindTo: "AddTender"
						},
						//Внимаение! свойство "tag" должено быть уникальным для каждой кнопки
						tag: "AddTender",
						//меняем стиль
						style: "green"
					}
				);
				//Добавляем конфигурационный объект кнрпки в массив конф.объектов
				buttonsConfig.push(newButton);				
				//Поиск конфигурационного объекта кнопки "Выбрать" в искомом массиве по caption
				_.some(buttonsConfig, function(target) {
					if (target.caption === "Выбрать") {
						//В найденном объекте меняем значение аттрибута caption на "Создать тендеры".
						target.caption = "Переименованная кнопка" ;
						return true;
					}
				});
				//Поиск конфигурационного объекта кнопки "Добавить" в искомом массиве по caption
				_.some(buttonsConfig, function(target) {
					if (target.caption === "Действия") {
						//В найденном объекте меняем значение аттрибута visible на "false"
						//тем самым скрывая кнопку-меню
						target.visible = false ;
						return true;
					}
				});
				//----------------------- инъекция логики (конец) ----------------------
				this.renderLookupControls(config, topPanelConfig);
			}
		});
	});

Вот какое-то такое получится окно

Классно и подробно - хоть в официальный SDK добавляй! :)

Илья,

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

Севостьянов Илья Сергеевич, спасибо за подробность, как раз искал подобный кейс

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

Скажите пожалуйста а где вызвать метод 

this.openLookup, я никак не могу реализовать пример. 

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

Собственно вопрос:
Существует ли способ определить, есть ли подписчики у события, до его публикации ?

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

Нравится

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

Вы же имеете в виду sandbox? Если да, то да. Подключите кор, у него есть переменная observable, там есть все события и списки их подписчиков:

define("ContactPageV2", ["core"], function(core) {
	return {
		entitySchemaName: "Contact",
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
		]/**SCHEMA_DIFF*/,
		methods: {
			init: function() {
				this.callParent(arguments);
				document.scope = this;
				document.coreScope = core;
			}
		},
		rules: {},
		businessRules: /**SCHEMA_BUSINESS_RULES*/{}/**SCHEMA_BUSINESS_RULES*/
	};
});

Большое спасибо, за "раскрытый прием" - открывает много возможностей по построению еще более интересной бизнес-логики.

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