бизнес-процесс
пользовательский элемент бизнес-процесса
7.11
sales

Коллеги, доброго дня!

Есть такой базовый элемент бизнес-процессов "Создать задачу". В дизайнере процессов можно от него нарисовать условные потоки, которые аналитик заполняет возможными результатами из справочника "Результаты Активности".

Каким образом можно настроить аналогичную автоматизацию для пользовательского элемента бизнес-процессов? Либо где посмотреть, как это уже реализовано для элемента "Создать задачу"?

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

Нравится

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

Я не совсем понимаю, что требуется, если честно, но попытаюсь помочь :)

Возможно этот скрин поможет. Небольшой БП (чисто для примера не перенасыщал его дополнительными проверками и т.д.). Первая задача звучит так "Выяснить тип потребности в продаже". Менеджер вносит выбирает нужное значение в продаже, жмакает "Выполнена" и после этого мы читаем эту же продажу и в зависимости от того, что нужно клиенту, идем в нужную ветку и выполняем дальше задачи связанные с этим типом потребности. Если это не то, что Вам нужно, то объясните еще раз Ваш кейс :)

Добрый день!



Пример реализации данной логики в элементе "Выполнить задачу" Вы можете посмотреть открыв исходный код элемента "ActivityUserTask".

А можно чуть поподробнее, какой именно метод отвечает за указанный функционал?

Анна Журавель пишет:

Добрый день!

Пример реализации данной логики в элементе "Выполнить задачу" Вы можете посмотреть открыв исходный код элемента "ActivityUserTask".

Показать все комментарии
Мобильное приложение
ESQ
UpdateQuery
7.12
sales

Добрый час суток!

Немного не могу понять как построить запрос на UPDATE в мобильном приложении.

Например я хочу сделать такой запрос SQL

UPDATE TEST SET IsChecked = 1 WHERE Question = '' and Answer = ''

Я делаю так:

        var filters = Ext.create("Terrasoft.Filter", {

                type: Terrasoft.FilterTypes.Group,

                logicalOperation: Terrasoft.FilterLogicalOperations.And

            });

        filters.addFilter(Ext.create("Terrasoft.Filter", {

                property: "Question ",

                value: multiAnswerId // тут Id Question (для фильтра как выше)

        }));

        filters.addFilter(Ext.create("Terrasoft.Filter", {

                property: "Answer ",

                value: answerVariantId // тут Id Answer(для фильтра как выше)

        }));

        var sql = Terrasoft.Sql.UpdateBuilder.build({

                model: "Test", //Таблица в которую хочу сделать запрос

                queryConfig: queryConfig, //что это ?

                record: record, // что это?

                filter: filters,// это у меня есть

                includeOnlyModifiedColumns: true // что это?

        });

        sqls.push(sql);

        

Подскажите пожалуйста,как мне в мой обьект который я бросаю (var sql = ...) добавить по сути SET IsChecked =1 ? Ведь фильтры я уже сделал,а как сформировать немного не пойму.. может мне даже QueryConfig и includeOnlyModifiedColumns вообще ну нужны ? и достаточно как то константами сделать ? Спасибо заранее!

Нравится

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

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

Вот пример фильтрации и перебора:

// Создание экземпляра хранилища для данных модели Contact.
var store = Ext.create('Terrasoft.store.BaseStore', {
    model: 'Contact'
});
 
// Создание конфигурационного объекта с дополнительными параметрами запроса на выборку данных.
var queryConfig = Ext.create('Terrasoft.QueryConfig', {
    // Определение колонок, которые будут возвращены в запросе.
    columns: ['Name', 'Id', 'Account'],
    // Имя модели, к которой выполняется запрос.
    modelName: 'Contact'
});
 
// Загрузка данных в хранилище. Будет возвращена первая страница данных.
store.loadPage(1, {
    queryConfig: queryConfig,
    // Дополнительно указывается фильтрация возвращаемых данных по колонке Name.
    filters: Ext.create('Terrasoft.Filter', {
        // Имя колонки, по которой выполняется фильтрация.
        property: 'Name',
        // Значение для фильтрации.
        value: 'Test Name'
    }),
    // Функция обработки результатов выполнения запроса.
    callback: function(records, operation, success) {
        // Получение первой записи возвращенного набора.
        var loadedRecord = records[0];
        if (loadedRecord) {
            // Получение значения поля Account.
            var contactAccount = loadedRecord.get('Account');                                                                                                    
            if (contactAccount) {                                                                                                                   
                // Выполнение действий с полученным значением.
            }
        }
    },
    scope: this
});

Вот пример изменения и сохранения записи (но новой, а не существующей):

var record = Ext.create('Contact');
record.phantom = true;
//свойство модели phantom указывает на то, новая ли это запись или существующая
record.set('Name', 'New Contact');

record.save({
    success: function() {
        ...
    },
    failure: function(exception) {
        Terrasoft.MessageBox.showException(exception);
    },
    queryConfig: Ext.create('Terrasoft.QueryConfig', {
        modelName: record.self.modelName,
        columns: ['Name']
    }
}, this);

 

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

var store = Ext.create('Terrasoft.store.BaseStore', {

            model: 'SuInterviewAnswerChoice'

        });

        var queryConfig = Ext.create('Terrasoft.QueryConfig', {

            columns: ['SuInterviewQuestion', 'SuAnswer', 'SuIsChecked'],

            modelName: 'SuInterviewAnswerChoice'

        });

        

        store.loadPage(1, {

            queryConfig: queryConfig,

            filters: Ext.create('Terrasoft.Filter', {

                property: 'SuInterviewQuestion',

                value: multiAnswerId

            }),

            callback: function(records, operation, success) {

                for(var i =0; i<records.length;i++){

                    if(records[i].data.SuAnswer === answerVariantId){

                        var isChecked = records[i].get('SuIsChecked');

                        if(!isChecked){

                            records[i].set('SuIsChecked',true);

                            records[i].save({

                                success: function() {

                                    console.log('Successfully');

                                },

                                failure: function(exception) {

                                    Terrasoft.MessageBox.showException(exception);

                                },

                                queryConfig: Ext.create('Terrasoft.QueryConfig', {

                                    modelName: records[i].self.modelName,

                                    columns: ['SuIsChecked']

                                })

                            }, this);

                            return;

                        }

                    }

                    

                }

            },

            scope: this

        });

 

 

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

Зверев Александр,

да,меняет. Поле ModifiedOn меняется. Подскажите пожалуйста еще,как правильно написать Insert,спасибо!

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

Зверев Александр пишет:

Вот пример изменения и сохранения записи (но новой, а не существующей):


 
var record = Ext.create('Contact');
record.phantom = true;
//свойство модели phantom указывает на то, новая ли это запись или существующая
record.set('Name', 'New Contact');
 
record.save({
    success: function() {
        ...
    },
    failure: function(exception) {
        Terrasoft.MessageBox.showException(exception);
    },
    queryConfig: Ext.create('Terrasoft.QueryConfig', {
        modelName: record.self.modelName,
        columns: ['Name']
    }
}, this);

или я так понимаю вот это можно использовать вместо InsertBuilder?

 

Да, лучше вместо InsertBuilder использовать редактирование и сохранение записи.

Зверев Александр,Спасибо

Зверев Александр,

Извиняюсь,подскажите пожалуйста,какая есть возможность в мобильном приложении сделать код синхронным ?  Дело в том,что мне нужно дождаться получения данных из базы,а уже потом с ними делать манипуляции,а код соответственно идет дальше и в колбек заходит уже тогда,когда нужная мне область кода прошла. Спасибо!

Зверев Александр пишет:

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

Вот пример фильтрации и перебора:


 
// Создание экземпляра хранилища для данных модели Contact.
var store = Ext.create('Terrasoft.store.BaseStore', {
    model: 'Contact'
});
 
// Создание конфигурационного объекта с дополнительными параметрами запроса на выборку данных.
var queryConfig = Ext.create('Terrasoft.QueryConfig', {
    // Определение колонок, которые будут возвращены в запросе.
    columns: ['Name', 'Id', 'Account'],
    // Имя модели, к которой выполняется запрос.
    modelName: 'Contact'
});
 
// Загрузка данных в хранилище. Будет возвращена первая страница данных.
store.loadPage(1, {
    queryConfig: queryConfig,
    // Дополнительно указывается фильтрация возвращаемых данных по колонке Name.
    filters: Ext.create('Terrasoft.Filter', {
        // Имя колонки, по которой выполняется фильтрация.
        property: 'Name',
        // Значение для фильтрации.
        value: 'Test Name'
    }),
    // Функция обработки результатов выполнения запроса.
    callback: function(records, operation, success) {
        // Получение первой записи возвращенного набора.
        var loadedRecord = records[0];
        if (loadedRecord) {
            // Получение значения поля Account.
            var contactAccount = loadedRecord.get('Account');                                                                                                    
            if (contactAccount) {                                                                                                                   
                // Выполнение действий с полученным значением.
            }
        }
    },
    scope: this
});

 

Работайте асинхронно, через callback.

Зверев Александр,

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

Основная логика bpm'online реализуется в веб-версии, мобильная имеет вспомогательное применение, её SDK не настолько подробен и не включает всех возможных сценариев. Если у Вас есть необходимость в какой-то логике, которую не реализовать иначе, можете завести идею, описать там подробно своё пожелание и, возможно, в новых версиях такое реализуют.

Показать все комментарии
бизнес-правила
бизнес-процесс
автозаполнение
7.12
sales

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

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

Ну и соотвественно обратно снимать, если это условие перестает соблюдаться.

Каким способом лучше это сделать?

Спасибо!!)

 

 

 

Нравится

7 комментариев
Лучший ответ

Бп. А в странице к описанию детали влепить subscriber и делать loadEntity (грубо говоря — аналогично стандартной логике в заказе)

Бп. А в странице к описанию детали влепить subscriber и делать loadEntity (грубо говоря — аналогично стандартной логике в заказе)

Можно и триггером в базе вместо БП. Особенно, если на деталь льют записи извне в обход движка EntitySchemaQuery.

Доброе утро. Дабы не плодить новую тему. Не могли бы вы поделиться примером работы subscriber? Есть задача суммировать значения полей в детали и выводить их на карточке. Заполненение поля через БП проходит корректно, но не в онлайн режиме, поле обновляется только после перезагрузки страницы.

Трефилов Павел Сергеевич,

ContactAnniversary: {
	schemaName: "ContactAnniversaryDetailV2",
	filter: {
		masterColumn: "Id",
		detailColumn: "Contact"
	},
	subscriber: {
		"methodName": "sendSaveCardModuleResponse"
	}
}

Вот, допустим, из базовой страницы контакта. Метод sendSaveCardModuleResponse располагается в BasePageV2. Встаньте туда дебаггером через консоль и попробуйте поудалять/подобавлять записей в деталь "знаменательные события". При каждом изменении чего-нибудь в детали - тут же отрабатывает метод. В идеале в нём же делать this.reloadEntity (чтобы обновились поля карточки, которые были перерасчитаны по бп)

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

Данила, спасибо огромное! Сделали. 

Только тут асинхронность проявилась. Сабскрайбер отрабатывает и обновляет страницу быстрее чем отрабатывает БП. Как послать сигнал из БП чтобы страница обновилась после завершения БП. Ну кроме, как таймаут ставить). Спасибо!!!

Елена К,

Ничего адекватного, кроме переделки всех пересчётов под клиентский esq, посоветовать в таком случае не могу (ну за исключением костыля - отправки сообщения по websocket в конце бп).

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

Да, так и сделали. Спасибо вам!!)

 

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

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

В элементе процесса "Открыть страницу редактирования" не стоит галочка "Выполнять в фоновом режиме". Согласно статье на академии в таком случае будет открываться страница этого шага, прерывая любые действия пользователя. Но это элемент выполниться только в том случае если перейти на него на вкладке [Задачи по бизнес-процессам] коммуникационной панели.

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

Нравится

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

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

Зверев Александр,

Александр, в стартовом сигнале галочка тоже отсутствует, но все равно возникает такая ситуация.

Значит, процесс с какого-то момента переходит в фон. Например, из-за таймера. Или же просто в карточке той записи ответственный не текущий пользователь.

Зверев Александр,

Спасибо большое! Сейчас посмотрю.

Александр, Проверил все еще раз. Ответственный - текущий пользователь. Таймеров никаких нет, что может еще переводить процесс в фон?

 

Ещё он будет в фоне, если запускать из дизайнера процессов, поскольку в этот момент не активна вкладка, на которой должна появится страница.

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

Спасибо!

Так у Вас процесс или кейс?

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

 

Если не открывается только эта страница, то что-то не то в самой странице. Если любая — то в процессе. Попробуйте поэкспериментировать с другими страницами, со специально созданными маленькими процессами из 1 элемента, открывающего эту страницу.

odins41,

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

1. Процесс не выполняется в фоне, т.е:

- в расширенных свойствах стартового элемента не должна стоять галочка "выполнять в фоне"

- по ходе процесса не должно быть таймеров 

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

3. Пользователь должен быть ответственным за элемент

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



Если все описанные више условия выполняются, а страница все равно не открывается напишите, пожалуйста, на support@bpmonline.com

В версии 7.16 должно появиться восстановление контекста, то есть при одновременном запуске второго БП его интерактивные элементы будут тоже на экране, а не в фоне.

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

Добрый день, делаю звонок с кнопки:

 this.sandbox.publish("CallCustomer", {
                    number: mobNumber, 
                    customerId: userId, 
                    entitySchemaName: "SxCandidate",
                    callRelationFields: undefined
                });

Есть ли возможность как то узнать Id этого звонка? Той записи которая добавляется в таблицу "Call". Спасибо!

Нравится

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

Смотря какая телефония. У вебитела, например есть чудеснейшая функция UpdateDbCall в провайдере, которая делает запись в бд через insert (пока-пока событийные бп), и возвращает Id вставленной записи в onUpdateDbCall

Телефония Asterisk

mcNosferatum,

Есть у меня подозрение, что для asterisk-а интеграция встроена в dll в виде класса и звонок там вставляется через те же insert-ы. Тогда смотреть стоит в сторону обработчиков событий звонка в ctipanel. Мб там где-нибудь guid звонка приходит...

А что в Network'е? Какие запросы/ответы при звонке? Просто в стандартном Webitel запрос на звонок к серверу как раз возвращает в ответе Id звонка, может у Asterisk такая же логика.

На самый крайний случай можно просто сделать esq запрос в БД с таким количеством параметров, которые однозначно идентифицируют звонок.

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

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

Есть хорошая и простая функция для открытия мини-карточки:

this.openMiniPage({
	recordId: Terrasoft.GUID_EMPTY,
	operation: Terrasoft.ConfigurationEnums.CardOperation.ADD,
	entitySchemaName: "Case",
	valuePairs: defaultValues,
	isFixed: true,
	showDelay: 0,
	miniPageSchemaName: "UsrCaseMiniPage",
});

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

Существуют ли подобные функции для открытия полноценной карточки добавления?

Нашёл openPage в PageUtilities, но не нашёл, чтобы там был аналог valuePairs.

PushHistoryState не подходит - с его помощью можно открыть существующую страницу, а интересует именно страница добавления.

Нравится

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

Смотрите, как сделано в базовой схеме раздела BaseSectionV2. Там в обработчике кнопки добавления если есть миникарточка, вызывается Ваша функция openAddMiniPage, а если нет — openCardInChain:

/**
 * Opens new record page.
 * @protected
 */
addRecord: function(typeColumnValue) {
	if (!typeColumnValue) {
		if (this.checkEditPagesCount()) {
			return false;
		}
		var tag = this.get("AddRecordButtonTag");
		typeColumnValue = tag || Terrasoft.GUID_EMPTY;
	}
	var schemaName = this.getEditPageSchemaName(typeColumnValue);
	if (!schemaName) {
		return;
	}
	if (this.hasAddMiniPage(typeColumnValue)) {
		this.openAddMiniPage({
			entitySchemaName: this.entitySchemaName,
			valuePairs: this.getAddMiniPageDefaultValues(typeColumnValue)
		});
	} else {
		this.openCardInChain({
			schemaName: schemaName,
			operation: ConfigurationEnums.CardStateV2.ADD,
			moduleId: this.getChainCardModuleSandboxId(typeColumnValue),
			instanceConfig: {
useSeparatedPageHeader: this.get("UseSeparatedPageHeader")
			}
		});
	}
},

 

Показать все комментарии
ModalBox
модальное окно
справочник
фильтрация
7.7
sales

Добрый день,

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

 

После решил попробовать заполнить справочник вручную нужными значениями

//attributes
"RIBDocType": {
    "dataValueType": Terrasoft.DataValueType.ENUM,
    "type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
    "caption": "RIBDocType"
},
"documentTypeList": {
    "dataValueType": Terrasoft.DataValueType.ENUM,
    "type": Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
    "isCollection": true
}

//diff
{
	"operation": "insert",
	"parentName": "Header1",
	"propertyName": "items",
	"name": "RIBDocType",
	"values": {
		"bindTo": "RIBDocType",
		"caption": "Тип документа",
		"classes": {"wrapperClass": ["base-edit"]},
		"layout": {"column": 0, "row": 3, "colSpan": 24},
		"textSize": "Default",
		"contentType": Terrasoft.ContentType.ENUM,
		"labelConfig": {
			"visible": true
		},
		"controlConfig": {
			"className": "Terrasoft.ComboBoxEdit",
			"list": {
				"bindTo": "documentTypeList"
			},
			"change": {
				"bindTo": "onMyValueChange"
			},
			"prepareList": {
				"bindTo": "prepareDocumentTypeList"
			}
		}
	},
	"index": 3
}

//methods

onRender: function() {
	if (!this.get("documentTypeList")) {
		this.set("documentTypeList", this.Ext.create("Terrasoft.Collection"));
	}
	
},

prepareDocumentTypeList: function(filter, list) {
	if (list === null) {
		return;
	}
	list.clear();
	var columns = {};
	var value1 = {
		displayValue: "Type1",
		value: "e8670398-603b-43ca-820d-03e5b03fc275"
	};
	var value2 = {
		displayValue: "Type2",
		value: "14fcef3a-7d9e-4737-810f-52d57db3673a"
	};
	var value3 = {
		displayValue: "Type3",
		value: "672606f8-da25-40bd-a4ef-95c958331743"
	};
	columns[1] = value1;
	columns[2] = value2;
	columns[3] = value3;
	list.loadAll(columns);
	console.log(list);
},

onMyValueChange: function(val) {
	if (val && val.displayValue) {
		console.log("you pick: ", val.displayValue);
	}
},

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

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

 

Нравится

4 комментария
Лучший ответ

Сериков Асхат Кайратович,

Вот, например, код страницы с проекта. Если в attributes добавить lookupListConfig для поля Contact, всё подтянется

/*jshint ignore: start*/
define("UsrSchema", [],
	function() {
		return {
			mixins: {},
			messages: {},
			attributes: {},
			details: {},
			diff: /**SCHEMA_DIFF*/[
				{
					"operation": "insert",
					"name": "mainBoxContainer",
					"values": {
						"id": "mainBoxContainer",
						"itemType": Terrasoft.ViewItemType.CONTAINER,
						"items": []
					}
				},
				{
					"operation": "insert",
					"name": "mainBoxContainerGrid",
					"parentName": "mainBoxContainer",
					"propertyName": "items",
					"values": {
						"itemType": Terrasoft.ViewItemType.GRID_LAYOUT,
						"items": []
					}
				},
				{
					"operation": "insert",
					"parentName": "mainBoxContainerGrid",
					"propertyName": "items",
					"name": "Contact",
					"values": {
						"bindTo": "Contact",
						"caption": "Контакт",
						"contentType": Terrasoft.ContentType.ENUM,
						"layout": {
							"column": 0,
							"row": 0,
							"colSpan": 24
						}
					},
				}
			]/**SCHEMA_DIFF*/,
			methods: {
				init: function() {
					this.callParent(arguments);
				},
				
				getLookupQuery: function(filter, column) {
					var esq = this.callParent(arguments);
					var lookupListConfig = this.getLookupListConfig(column);
					if (lookupListConfig) {
						this.Terrasoft.each(lookupListConfig.columns, function(column) {
							if (!esq.columns.contains(column)) {
								esq.addColumn(column);
							}
						}, this);
					}
					
					var schemaColumn = this.getColumnByName(column);
					if (schemaColumn.lookupListConfig && schemaColumn.lookupListConfig.filter) {
						esq.filters.addItem(schemaColumn.lookupListConfig.filter());
					}
					
					return esq;
				},
				getLookupListConfig: function(columnName) {
					var schemaColumn = this.getColumnByName(columnName);
					if (!schemaColumn) {
						return null;
					}
					var lookupListConfig = schemaColumn.lookupListConfig;
					if (!lookupListConfig) {
						return null;
					}
					var excludedProperty = ["filters", "filter"];
					var config = {};
					this.Terrasoft.each(lookupListConfig, function(property, propertyName) {
						if (excludedProperty.indexOf(propertyName) === -1) {
							config[propertyName] = property;
						}
					});
					return config;
				}
			}
		};
	});
/*jshint ignore: end*/

 

1) Я бы попробовал 

columns[value1.value] = value1;
columns[value2.value] = value2;
columns[value3.value] = value3;

+ в каждом объекте продублировал 

var value1 = {
	displayValue: "Type1",
        name: "Type1",
	value: "e8670398-603b-43ca-820d-03e5b03fc275"
};

чисто на всякий случай

2) Если это своё модальное окно, не унаследованное от basePage, то придётся копировать логику из getLookupQuery (BasePageV2). Там как раз таки и идёт перебор аттрибутов на фильтры, доп колонки и т.п.

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

Спасибо, да не наследованное, пойду пробовать 

Сериков Асхат Кайратович,

Вот, например, код страницы с проекта. Если в attributes добавить lookupListConfig для поля Contact, всё подтянется

/*jshint ignore: start*/
define("UsrSchema", [],
	function() {
		return {
			mixins: {},
			messages: {},
			attributes: {},
			details: {},
			diff: /**SCHEMA_DIFF*/[
				{
					"operation": "insert",
					"name": "mainBoxContainer",
					"values": {
						"id": "mainBoxContainer",
						"itemType": Terrasoft.ViewItemType.CONTAINER,
						"items": []
					}
				},
				{
					"operation": "insert",
					"name": "mainBoxContainerGrid",
					"parentName": "mainBoxContainer",
					"propertyName": "items",
					"values": {
						"itemType": Terrasoft.ViewItemType.GRID_LAYOUT,
						"items": []
					}
				},
				{
					"operation": "insert",
					"parentName": "mainBoxContainerGrid",
					"propertyName": "items",
					"name": "Contact",
					"values": {
						"bindTo": "Contact",
						"caption": "Контакт",
						"contentType": Terrasoft.ContentType.ENUM,
						"layout": {
							"column": 0,
							"row": 0,
							"colSpan": 24
						}
					},
				}
			]/**SCHEMA_DIFF*/,
			methods: {
				init: function() {
					this.callParent(arguments);
				},
				
				getLookupQuery: function(filter, column) {
					var esq = this.callParent(arguments);
					var lookupListConfig = this.getLookupListConfig(column);
					if (lookupListConfig) {
						this.Terrasoft.each(lookupListConfig.columns, function(column) {
							if (!esq.columns.contains(column)) {
								esq.addColumn(column);
							}
						}, this);
					}
					
					var schemaColumn = this.getColumnByName(column);
					if (schemaColumn.lookupListConfig && schemaColumn.lookupListConfig.filter) {
						esq.filters.addItem(schemaColumn.lookupListConfig.filter());
					}
					
					return esq;
				},
				getLookupListConfig: function(columnName) {
					var schemaColumn = this.getColumnByName(columnName);
					if (!schemaColumn) {
						return null;
					}
					var lookupListConfig = schemaColumn.lookupListConfig;
					if (!lookupListConfig) {
						return null;
					}
					var excludedProperty = ["filters", "filter"];
					var config = {};
					this.Terrasoft.each(lookupListConfig, function(property, propertyName) {
						if (excludedProperty.indexOf(propertyName) === -1) {
							config[propertyName] = property;
						}
					});
					return config;
				}
			}
		};
	});
/*jshint ignore: end*/

 

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

Спасибо

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

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

Задача в общем такая: есть несколько страниц редактирования раздела (к примеру, обращения Case). То, какую страницу показывать зависит от значения специальной справочной колонки "Тип страницы редактирования", здесь всё стандартно.

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

Сервис 1 - Тип страницы 1
Сервис 2 - Тип страницы 1
Сервис 3 - Тип страницы 2

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

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

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

Нравится

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

Я так понимаю, у вас уже для раздела несколько страниц редактирования?

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

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

да.

Проблема в том, как вызывать сразу миникарточку без выпадающего меню с типом страницы.

Инструкция на академии не помогла: https://academy.terrasoft.ru/documents/technic-sdk/7-12/kak-sozdat-mini…

Смородинов Денис пишет:

без выпадающего меню с типом страницы.

Вот тут даже интересно. У вас стоит галочка в мастере "использовать миникарточку на добавление"? Если да, то он открывает одну и ту же миникарточку, но только после выбора какого-либо типа из выпадающего меню?

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

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

это 7.11. Там ещё нет галочки и мастера дизайна миникарточки((

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

Варфоломеев Данила пишет:

перегрузить initEditPages

Не подскажете, где он определяется? В BaseSectionV2 уже следует вызов в init.

Смородинов Денис,

BaseSchemaViewModel. 

Кстати, вы можете остановись дебагером, навести на функцию, вам хром выдаст её определение, щёлкаете на него, попадаете в функцию(и соответственно в модуль где она определена)

Ясно, спасибо.

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

Добрый час суток. Когда на секции задаются фильтры - то соответственно появляется запись в таблице SysProfileData и при переходах по разделам и т.д. после захода вновь на эту секцию фильтры остаются - всё ок. Нужно реализовать "сохранение" фильтров на детали,чтобы при переходах они соответственно не терялись,а сохранялись. Переопределил setFilter и переписал метод saveFilter для BaseGridDetailV2. Запись теперь появляется в SysProfileData(когда ставишь фильтры на детали,до этого не появлялась). И поле Key = Имеет структуру [Имя_страницы][Имя_детали]+"Filters". Как следствие - фильтры все так же не появляются. Правильно ли формируется для этого Key? или нужно другой шаблон/способ? Может не только 1 запись должа вставляться в таблицу а какая то вспомогательная?

Переписанное на BaseGridDetailV2 имеет вид:

methods: {

            setFilter: function(key, value, filtersValue) {

                var filters = this.get("DetailFilters");

                if (key) {

                    if (filters.find(key)) {

                        filters.remove(filters.get(key));

                    }

                    filters.add(key, value);

                    // 

                    this.saveFilter(key, filtersValue, value);

                    //

                } else if (value) {

                    value.each(function(filter) {

                        this.setFilter(filter.key, filter);

                    }, this);

                }

            },

            saveFilter: function(filterKey, filterValue, filter) {

                    if (!filterValue) {

                        return;

                    }

                    var sessionFilters = this.getSessionFilters();

                    var profileFilters = this.getProfileFilters();

                    var serializableFilter = this.getSerializableFilter(filter);

                    switch (filterKey) {

                        case "CustomFilters":

                            Terrasoft.each(filterValue, function(item) {

                                var f = item.filter = item.value || "";

                                var isSerializedFilter = (typeof f === "string" && f.indexOf("[") >= 0 && f.indexOf("]") >= 0 &&

                                    f.indexOf("{") >= 0 && f.indexOf("}") >= 0);

                                if (!isSerializedFilter) {

                                    item.filter = Terrasoft.encode(serializableFilter);

                                }

                            });

                            if (this.isNotEmpty(filterValue)) {

                                sessionFilters[filterKey] = profileFilters[filterKey] = filterValue;

                            } else {

                                delete sessionFilters.CustomFilters;

                                delete profileFilters.CustomFilters;

                            }

                            Terrasoft.saveUserProfile(this.getFiltersKey(), profileFilters, false);

                            this.set("ProfileFilters", profileFilters);

                            break;

                        case "FolderFilters":

                            sessionFilters[filterKey] = profileFilters[filterKey] = filterValue;

                            Terrasoft.saveUserProfile(this.getFiltersKey(), profileFilters, false);

                            this.set("ProfileFilters", profileFilters);

                            break;

                        case "FixedFilters":

                            filterValue.filter = Terrasoft.encode(serializableFilter);

                            profileFilters[filterKey] = {Fixed: filterValue};

                            Terrasoft.saveUserProfile(this.getFiltersKey(), profileFilters, false);

                            this.set("ProfileFilters", profileFilters);

                            break;

                        case "TagFilters":

                            serializableFilter.tags = filterValue;

                            profileFilters[filterKey] = [serializableFilter];

                            Terrasoft.saveUserProfile(this.getFiltersKey(), profileFilters, false);

                            this.set("ProfileFilters", profileFilters);

                            break;

                        default:

                            sessionFilters[filterKey] = profileFilters[filterKey] = [

                                {

                                    filter: filter.serialize()

                                }

                            ];

                            Terrasoft.saveUserProfile(this.getFiltersKey(), profileFilters, false);

                    }

            },

            getSessionFilters: function() {

                var storage = Terrasoft.configuration.Storage.Filters = Terrasoft.configuration.Storage.Filters || {};

                var sessionFilters = storage[this.name] = storage[this.name] || {};

                return sessionFilters;

            },

            getProfileFilters: function() {

                    return this.get("ProfileFilters") || {};

            },

            getSerializableFilter: function(filter) {

                    filter.serializationInfo = {serializeFilterManagerInfo: true};

                    var serializableFilter = {};

                    filter.getSerializableObject(serializableFilter, filter.serializationInfo);

                    return serializableFilter;

            },

            getFiltersKey: function() {

                    var schemaName = this.name;

                    var cardName = this.values.CardPageName;

                    return cardName + schemaName + "Filters";

            }

        },

Нравится

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

как я понимаю фильтры должны подгружаться со Storage - вот весь код функционал перенес и метод loadProfileFilters в котором идет заргрузка со Storage

define("BaseGridDetailV2", ["BaseGridDetailV2Resources", "ConfigurationEnums", "RightUtilities","TagConstantsV2",

    "ProcessModuleUtilities", "GridUtilitiesV2", "WizardUtilities", "QuickFilterModuleV2", "ProcessEntryPointUtilities"

], function(resources, enums, RightUtilities, TagConstantsV2) {

    return {

        messages: {},

        mixins: {},

        attributes: {

            "ProfileFilters": {

                    dataValueType: Terrasoft.DataValueType.CUSTOM_OBJECT

            },

            "IsDetailFiltersLoaded": {

                    dataValueType: Terrasoft.DataValueType.BOOLEAN,

                    value: false

            },

            "DetailFilters": {

                    dataValueType: Terrasoft.DataValueType.COLLECTION

            }

        },

        methods: {

            subscribeSandboxEvents: function() {

                this.callParent(arguments);

                var editPages = this.getEditPages();

                editPages.each(function(editPage) {

                    var typeColumnValue = editPage.get("Tag");

                    var cardModuleId = this.getEditPageSandboxId(editPage);

                    this.sandbox.subscribe("getCardInfo", function() {

                        var detailInfo = this.getDetailInfo();

                        var cardInfo = {

                            valuePairs: detailInfo.defaultValues || []

                        };

                        var typeColumnName = this.get("TypeColumnName");

                        if (typeColumnName && typeColumnValue) {

                            cardInfo.typeColumnName = typeColumnName;

                            cardInfo.typeUId = typeColumnValue;

                        }

                        return cardInfo;

                    }, this, [cardModuleId]);

                }, this);

                this.sandbox.subscribe("CardSaved", this.onCardSaved, this, [this.sandbox.id]);

                this.subscribeGetModuleSchema();

                this.subscribeFiltersChanged();

                this.subscribeGetShortFilterFieldsVisible();

                this.sandbox.subscribe("GetRecordInfo", this.getRecordInfo, this,

                    [this.getRecordRightsSetupModuleId()]);

                this.sandbox.subscribe("GetExtendedFilterConfig", this.getExtendedFilterConfig, this,

                    [this.getQuickFilterModuleId()]);

            },

            init: function(callback, scope) {

                this.callParent([function() {

                    this.mixins.WizardUtilities.canUseWizard(function(result) {

                        this.set("IsDetailWizardAvailable", result);

                        callback.call(scope);

                    }, this);

                }, this]);

                this.registerMessages();

                this.initDetailFilterCollection();

                //

                //this.initDetailFiltersCollection();

                this.loadProfileFilters();

                //

                this.initFilterVisibility();

                this.isFilterAdded();

                var sandboxId = this.getQuickFilterModuleId();

                this.sandbox.subscribe("InitFilterFromStorage", function() {

                    this.sandbox.publish("LoadedFiltersFromStorage", null, [sandboxId]);

                }, this, [sandboxId]);

            },

            loadProfileFilters: function(callback, scope) {

                    var isDetailFiltersLoaded = this.get("IsDetailFiltersLoaded");

                    if (isDetailFiltersLoaded) {

                        Ext.callback(callback, scope);

                        return;

                    }

                    var profileKey = Ext.String.format("profile!{0}", this.getFiltersKey());

                    Terrasoft.require([profileKey], function(profile) {

                        this.onLoadProfileFilters(callback, scope, profile);

                    }, this);

            },

            onLoadProfileFilters: function(callback, scope, profile) {

                    this.loadFiltersContainersVisibility(profile);

                    this.initFilterAttributes(profile);

                    this.set("IsDetailFiltersLoaded", true);

                    Ext.callback(callback, scope);

            },

            loadFiltersContainersVisibility: function(profile) {

                    if (profile) {

                        if (Ext.isDefined(profile.isFoldersContainerExpanded)) {

                            this.set("IsFoldersVisible", profile.isFoldersContainerExpanded);

                        }

                        if (Ext.isDefined(profile.isExtendedFiltersContainerExpanded)) {

                            this.set("IsExtendedFiltersVisible", profile.isExtendedFiltersContainerExpanded);

                        }

                    }

            },

            initFilterAttributes: function(profile) {

                    var sessionFilters = this.getSessionFilters();

                    this.set("SessionFilters", sessionFilters);

                    var profileFilters = Terrasoft.deepClone(profile);

                    var fixedSessionFilters = this._getFixedSessionFilters();

                    Terrasoft.each(profileFilters, this.applyFilters, this);

                    Terrasoft.each(fixedSessionFilters, this.applyFilters, this);

                    this.set("ProfileFilters", profileFilters);

            },

            _getFixedSessionFilters: function() {

                    var sessionFilters = this.getSessionFilters();

                    var primaryDisplayColumn = this.entitySchema.primaryDisplayColumn;

                    var customFilters = sessionFilters.CustomFilters;

                    var customFiltersPrimaryDisplayColumn = customFilters && customFilters.primaryDisplayColumn;

                    if (customFiltersPrimaryDisplayColumn && primaryDisplayColumn) {

                        var sessionFiltersFixed = {};

                        sessionFiltersFixed.CustomFilters = {};

                        sessionFiltersFixed.CustomFilters[primaryDisplayColumn.name] = customFilters;

                        sessionFilters = sessionFiltersFixed;

                    }

                    return sessionFilters;

            },

            applyFilters: function(filterValue, key) {

                    if (key.match(/filters/i) && !this.isNotEmpty(filterValue)) {

                        return;

                    }

                    var detailFiltersValue = this.get("DetailFiltersValue");

                    if (!Ext.isString(filterValue)) {

                        detailFiltersValue.removeByKey(key);

                        this._chooseFilterProccesing(filterValue, key);

                    }

                    if (key !== "FixedFilters") {

                        detailFiltersValue.add(key, filterValue);

                    }

            },

            initDetailFiltersCollection : function(){

                    this.set("ProfileFilters", {});

                    this.set("DetailFilters", this.Ext.create("Terrasoft.FilterGroup"));

                    this.set("DetailFiltersValue", this.Ext.create("Terrasoft.Collection"));

            },

            _chooseFilterProccesing: function(filterValue, key) {

                    var detailFilters = this.get("DetailFilters");

                    Terrasoft.each(filterValue, function(item, propName) {

                        if (item.key === TagConstantsV2.TagFilterKey || key === TagConstantsV2.TagFilterKey) {

                            this.applyTagFilter(key, item);

                        }

                        if (propName === "Fixed") {

                            this._applyFixedFilter(key, item);

                        }

                        if (item.filter) {

                            var filter = Terrasoft.deserialize(item.filter);

                            if (!detailFilters.contains(key)) {

                                detailFilters.add(key, filter);

                            }

                        } else if (item.primaryDisplayColumn) {

                            this.applyPrimaryColumnFilter(key, item);

                        } else if (item.folderInfo || item.folderType) {

                            this.applyFolderFilter(key, item);

                        }

                    }, this);

            },

            //

            initFilterFromStorage: function() {

                    this.loadProfileFilters(function() {

                        this.sandbox.publish("LoadedFiltersFromStorage", null, this.getFilterModulesIds());

                    }, this);

            },

            initFilterVisibility: function() {

                this.set("IsDetailFilterVisible", true);

                this.set("IsFilterAdded", true);

            },

            setFilter: function(key, value, filtersValue) {

                var filters = this.get("DetailFilters");

                if (key) {

                    if (filters.find(key)) {

                        filters.remove(filters.get(key));

                    }

                    filters.add(key, value);

                    // 

                    this.saveFilter(key, filtersValue, value);

                    //

                } else if (value) {

                    value.each(function(filter) {

                        this.setFilter(filter.key, filter);

                    }, this);

                }

            },

            saveFilter: function(filterKey, filterValue, filter) {

                    if (!filterValue) {

                        return;

                    }

                    var sessionFilters = this.getSessionFilters();

                    var profileFilters = this.getProfileFilters();

                    var serializableFilter = this.getSerializableFilter(filter);

                    switch (filterKey) {

                        case "CustomFilters":

                            Terrasoft.each(filterValue, function(item) {

                                var f = item.filter = item.value || "";

                                var isSerializedFilter = (typeof f === "string" && f.indexOf("[") >= 0 && f.indexOf("]") >= 0 &&

                                    f.indexOf("{") >= 0 && f.indexOf("}") >= 0);

                                if (!isSerializedFilter) {

                                    item.filter = Terrasoft.encode(serializableFilter);

                                }

                            });

                            if (this.isNotEmpty(filterValue)) {

                                sessionFilters[filterKey] = profileFilters[filterKey] = filterValue;

                            } else {

                                delete sessionFilters.CustomFilters;

                                delete profileFilters.CustomFilters;

                            }

                            Terrasoft.saveUserProfile(this.getFiltersKey(), profileFilters, false);

                            this.set("ProfileFilters", profileFilters);

                            break;

                        case "FolderFilters":

                            sessionFilters[filterKey] = profileFilters[filterKey] = filterValue;

                            Terrasoft.saveUserProfile(this.getFiltersKey(), profileFilters, false);

                            this.set("ProfileFilters", profileFilters);

                            break;

                        case "FixedFilters":

                            filterValue.filter = Terrasoft.encode(serializableFilter);

                            profileFilters[filterKey] = {Fixed: filterValue};

                            Terrasoft.saveUserProfile(this.getFiltersKey(), profileFilters, false);

                            this.set("ProfileFilters", profileFilters);

                            break;

                        case "TagFilters":

                            serializableFilter.tags = filterValue;

                            profileFilters[filterKey] = [serializableFilter];

                            Terrasoft.saveUserProfile(this.getFiltersKey(), profileFilters, false);

                            this.set("ProfileFilters", profileFilters);

                            break;

                        default:

                            sessionFilters[filterKey] = profileFilters[filterKey] = [

                                {

                                    filter: filter.serialize()

                                }

                            ];

                            Terrasoft.saveUserProfile(this.getFiltersKey(), profileFilters, false);

                    }

            },

            getSessionFilters: function() {

                var storage = Terrasoft.configuration.Storage.Filters = Terrasoft.configuration.Storage.Filters || {};

                var sessionFilters = storage[this.name] = storage[this.name] || {};

                return sessionFilters;

            },

            getProfileFilters: function() {

                    return this.get("ProfileFilters") || {};

            },

            getSerializableFilter: function(filter) {

                    filter.serializationInfo = {serializeFilterManagerInfo: true};

                    var serializableFilter = {};

                    filter.getSerializableObject(serializableFilter, filter.serializationInfo);

                    return serializableFilter;

            },

            getFiltersKey: function() {

                    var cardName = this.values.CardPageName;

                    var schemaName = this.name;

                    return cardName + schemaName + "Filters";

            }

        },

        diff: /**SCHEMA_DIFF*/[]/**SCHEMA_DIFF*/

    };

});

 

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

Главное чтобы вы брали фильтр по тому ключу, который сформировали.

При отладке фильтр возвращается? Где именно ошибка возникает?

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

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

this.showBodyMask()

поверх модульного окна, или справочника?

Нравится

2 комментария
Лучший ответ

По идее можно прокинуть config в функцию, а в конфиге указать селектор.

Подробнее посмотрите тут, в функции show.

Небольшой оффтоп: Там в функции createMask можно накидать и caption для маски, и прозрачность менять, и бэкграунд... Почему это нигде не задокументировано - ума не приложу. Наиполезнейшая вещь иногда.

По идее можно прокинуть config в функцию, а в конфиге указать селектор.

Подробнее посмотрите тут, в функции show.

Небольшой оффтоп: Там в функции createMask можно накидать и caption для маски, и прозрачность менять, и бэкграунд... Почему это нигде не задокументировано - ума не приложу. Наиполезнейшая вещь иногда.

Варфоломеев Данила, Спасибо, полезная штука. Я уже через css  реализовал выглядит конечно не самым лучшим способом, рад что есть еще и оригинальный метод

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